diff --git a/conf/nginx.conf b/conf/nginx.conf index 58b691d..bc3fa3d 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -4,8 +4,6 @@ location __PATH__/ { # Path to source alias __FINALPATH__/ ; - 20 Euro.html; - # Force usage of https if ($scheme = http) { rewrite ^ https://$server_name$request_uri? permanent; diff --git a/scripts/install b/scripts/install index c560a3e..ad5adaa 100755 --- a/scripts/install +++ b/scripts/install @@ -57,7 +57,10 @@ ynh_script_progression --message="Setting up source files..." --weight=1 ynh_app_setting_set --app=$app --key=final_path --value=$final_path # Download, check integrity, uncompress and patch the source from app.src -ynh_setup_source --dest_dir="$final_path" +#ynh_setup_source --dest_dir="$final_path" + +mkdir -p $final_path +cp -a ../sources/* $final_path #================================================= # NGINX CONFIGURATION diff --git a/sources/README.md b/sources/README.md new file mode 100755 index 0000000..6bf2010 --- /dev/null +++ b/sources/README.md @@ -0,0 +1,8 @@ +20euros +======= + +A 2048 variant with Euro coins and notes. + +You'll find the game forum here: http://20euros.freeforums.net/ + +Feel free to fork my version and make pull requests! diff --git a/sources/comment.html b/sources/comment.html new file mode 100755 index 0000000..bd0b53c --- /dev/null +++ b/sources/comment.html @@ -0,0 +1,32 @@ + + + + + + 20 Euros Forum + + + + + + + + + + + + + diff --git a/sources/faq.html b/sources/faq.html new file mode 100755 index 0000000..b36cc61 --- /dev/null +++ b/sources/faq.html @@ -0,0 +1,79 @@ + + + + + 20 Euros FAQ + + + + + + + + + + + + + +
+
+

20 Euros FAQ

+
+

Frequently asked questions about my game 20 Euros.

+ +
+

Rules

+

Q: The game is buggy, sometimes it won't let me add two equal coins.
+A: Read the rules under the game first! For example, you can't add up two 2's to make 5, it wouldn't even make sense! To make a 5 cent coin, you have to combine two 2's and a 1.

+ +

Q: Why doesn't 2+2+2+2+2 make 10?
+A: It's clearly written in the rules that at most 3 coins can merge at a time. Otherwise it would be too easy.

+ +

Q: Can I continue after winning and make a 50 Euro note?
+A: Yes! You can even go all the way up to the 500 Euro note, if you can! That has actually been achieved (with an AI).

+ +
+

Variants

+

Q: Does the game have any variants?
+A: The only official variant is the undo version.

+ +

Q: Can I make my own variant/fork?
+A: Yes! You are free to make your own forks based on my variant, you can download it or fork it on GitHub.

+ +
+ +

Others

+

+Q: Is there a forum about the game?
+A: Yes, it's here. Feel free to add comments, suggestions or further questions!

+ +

Q: Are the statistics of the game public?
+A: Yes, you can find them here.

+ +
+ +If you didn't find the answer to your question, you can ask it on the forum. + +
+ + + + + + + + diff --git a/sources/images/bg_hr.png b/sources/images/bg_hr.png new file mode 100755 index 0000000..7973bd6 Binary files /dev/null and b/sources/images/bg_hr.png differ diff --git a/sources/images/blacktocat.png b/sources/images/blacktocat.png new file mode 100755 index 0000000..6e264fe Binary files /dev/null and b/sources/images/blacktocat.png differ diff --git a/sources/images/icon_download.png b/sources/images/icon_download.png new file mode 100755 index 0000000..a2a287f Binary files /dev/null and b/sources/images/icon_download.png differ diff --git a/sources/images/sprite_download.png b/sources/images/sprite_download.png new file mode 100755 index 0000000..f2babd5 Binary files /dev/null and b/sources/images/sprite_download.png differ diff --git a/sources/index.html b/sources/index.html new file mode 100755 index 0000000..d56b4f9 --- /dev/null +++ b/sources/index.html @@ -0,0 +1,161 @@ + + + + + 20 Euros + + + + + + + + + + + + + + + + +
+
+

20 Euros

+ +
FAQ - FORUM
+

+

+
0
+
0
+
+
+ +

Join the coins until you reach 20 Euro! + + +

+ +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
+ + +

+ HOW TO PLAY: Use your arrow keys to move the coins. If two or three mergeable coins touch, they merge into one! Your goal is to reach the 20 Euro note! Restart with Space. +

+
+

+ Mergeable combinations:
+ 1+1=2
+ 2+2+1=5
+ 5+5=10
+ 10+10=20 or 5+10+5=20
+ 20+20+10=50
+ and so on. +

+
+

+FAQ: Click here for the frequently asked questions.
+ Forum: Click here for the game's forum.
+Statistics: Click here for the statistics. +

+
+

+Undo version: You can play the undo version of the game here. +
+GITHUB: You can see and fork my game on GitHub here. +

+
+

+This is not the original version of 2048.
+Original created by Gabriele Cirulli. +

+
+ + + + + + + + + + + + + + + + + diff --git a/sources/javascripts/main.js b/sources/javascripts/main.js new file mode 100755 index 0000000..d8135d3 --- /dev/null +++ b/sources/javascripts/main.js @@ -0,0 +1 @@ +console.log('This would be the main JS file.'); diff --git a/sources/js/animframe_polyfill.js b/sources/js/animframe_polyfill.js new file mode 100755 index 0000000..c45a13e --- /dev/null +++ b/sources/js/animframe_polyfill.js @@ -0,0 +1,26 @@ +(function() { + var lastTime = 0; + var vendors = ['webkit', 'moz']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = + window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } +}()); diff --git a/sources/js/application.js b/sources/js/application.js new file mode 100755 index 0000000..7cf796a --- /dev/null +++ b/sources/js/application.js @@ -0,0 +1,3 @@ +// Wait till the browser is ready to render the game (avoids glitches) +window.requestAnimationFrame(function () { + new GameManager(5, KeyboardInputManager, HTMLActuator, LocalScoreManager);}); diff --git a/sources/js/game_manager.js b/sources/js/game_manager.js new file mode 100755 index 0000000..c5e145b --- /dev/null +++ b/sources/js/game_manager.js @@ -0,0 +1,328 @@ +function GameManager(size, InputManager, Actuator, ScoreManager) { + this.size = size; // Size of the grid + this.inputManager = new InputManager; + this.scoreManager = new ScoreManager; + this.actuator = new Actuator; + + this.startTiles = 2; + + this.inputManager.on("move", this.move.bind(this)); + this.inputManager.on("restart", this.restart.bind(this)); + this.inputManager.on("keepPlaying", this.keepPlaying.bind(this)); + + this.setup(); +} + +// Restart the game +GameManager.prototype.restart = function () { + this.actuator. + continue (); + this.setup(); +}; + +// Keep playing after winning +GameManager.prototype.keepPlaying = function () { + this.keepPlaying = true; + this.actuator. + continue (); +}; + +GameManager.prototype.isGameTerminated = function () { + if (this.over || (this.won && !this.keepPlaying)) { + return true; + } else { + return false; + } +}; + +// Set up the game +GameManager.prototype.setup = function () { + this.grid = new Grid(this.size); + + this.score = 0; + this.highestTile = 5; + this.over = false; + this.won = false; + this.keepPlaying = false; + + // Add the initial tiles + this.addStartTiles(); + + // Update the actuator + this.actuate(); +}; + +// Set up the initial tiles to start the game with +GameManager.prototype.addStartTiles = function () { + for (var i = 0; i < this.startTiles; i++) { + this.addRandomTile(); + } +}; + +// Adds a tile in a random position +GameManager.prototype.addRandomTile = function () { + if (this.grid.cellsAvailable()) { + var value = Math.random() < 0.9 ? 1 : 5; + var tile = new Tile(this.grid.randomAvailableCell(), value); + + this.grid.insertTile(tile); + } +}; + +// Sends the updated grid to the actuator +GameManager.prototype.actuate = function () { + if (this.scoreManager.get() < this.score) { + this.scoreManager.set(this.score); + } + + this.actuator.actuate(this.grid, { + score: this.score, + highest: this.highestTile, + over: this.over, + won: this.won, + bestScore: this.scoreManager.get(), + terminated: this.isGameTerminated() + }); + +}; + +// Save all tile positions and remove merger info +GameManager.prototype.prepareTiles = function () { + this.grid.eachCell(function (x, y, tile) { + if (tile) { + tile.mergedFrom = null; + tile.savePosition(); + } + }); +}; + +// Move a tile and its representation +GameManager.prototype.moveTile = function (tile, cell) { + this.grid.cells[tile.x][tile.y] = null; + this.grid.cells[cell.x][cell.y] = tile; + tile.updatePosition(cell); +}; + +// Move tiles on the grid in the specified direction +GameManager.prototype.move = function (direction) { + // 0: up, 1: right, 2:down, 3: left + var self = this; + + if (this.isGameTerminated()) return; // Don't do anything if the game's over + var cell, tile; + + var vector = this.getVector(direction); + var traversals = this.buildTraversals(vector); + var moved = false; + + // Save the current tile positions and remove merger information + this.prepareTiles(); + + // Traverse the grid in the right direction and move tiles + traversals.x.forEach(function (x) { + traversals.y.forEach(function (y) { + cell = { + x: x, + y: y + }; + tile = self.grid.cellContent(cell); + + if (tile && tile.merged) { + self.grid.removeTile(tile); + } else if (tile) { + var positions = self.findFarthestPosition(cell, vector); + var next = self.grid.cellContent(positions.next); + var pos2 = null; + var next2 = null; + if (next) { + pos2 = self.findFarthestPosition({ + x: next.x, + y: next.y + }, vector); + next2 = self.grid.cellContent(pos2.next); + } + // Only one merger per row traversal? + if (next && next2 && !next.mergedFrom && next != next2 && (((tile.value == 1) && (next.value == 2) && (next2.value == 2)) || ((tile.value == 2) && (next.value == 1) && (next2.value == 2)) || ((tile.value == 2) && (next.value == 2) && (next2.value == 1)) || ((tile.value == 10) && (next.value == 20) && (next2.value == 20)) || ((tile.value == 20) && (next.value == 10) && (next2.value == 20)) || ((tile.value == 20) && (next.value == 20) && (next2.value == 10)) || ((tile.value == 100) && (next.value == 200) && (next2.value == 200)) || ((tile.value == 200) && (next.value == 100) && (next2.value == 200)) || ((tile.value == 200) && (next.value == 200) && (next2.value == 100)) || ((tile.value == 1000) && (next.value == 2000) && (next2.value == 2000)) || ((tile.value == 2000) && (next.value == 1000) && (next2.value == 2000)) || ((tile.value == 2000) && (next.value == 2000) && (next2.value == 1000)) || ((tile.value == 10000) && (next.value == 20000) && (next2.value == 20000)) || ((tile.value == 20000) && (next.value == 10000) && (next2.value == 20000)) || ((tile.value == 20000) && (next.value == 20000) && (next2.value == 10000)) || ((tile.value == 5) && (next.value == 10) && (next2.value == 5)) || ((tile.value == 50) && (next.value == 100) && (next2.value == 50)) || ((tile.value == 500) && (next.value == 1000) && (next2.value == 500)) || ((tile.value == 5000) && (next.value == 10000) && (next2.value == 5000)))) { + + var merged = new Tile(pos2.next, tile.value + next.value + next2.value); + merged.mergedFrom = [next2, next, tile]; + tile.merged = true; + next.merged = true; + next2.merged = true; + + self.grid.removeTile(tile); + self.grid.removeTile(next); + self.grid.removeTile(next2); + + self.grid.insertTile(merged); + // Converge the two tiles' positions + tile.updatePosition(pos2.next); + next.updatePosition(pos2.next); + // Update the score + self.score += merged.value; + + if (merged.value > self.highestTile) self.highestTile = merged.value; + + // The mighty 20 Euro tile + if (merged.value === 2000) self.won = true; + } else if (next && next.value === tile.value && !next.mergedFrom && next.value != 2 && next.value != 20 && next.value != 200 && next.value != 2000 && next.value != 20000 && next.value != 50000) { + + var merged = new Tile(positions.next, tile.value * 2); + merged.mergedFrom = [tile, next]; + tile.merged = true; + next.merged = true; + + self.grid.insertTile(merged); + self.grid.removeTile(tile); + + // Converge the two tiles' positions + tile.updatePosition(positions.next); + + // Update the score + self.score += merged.value; + + if (merged.value > self.highestTile) self.highestTile = merged.value; + + // The mighty 20 Euro tile + if (merged.value === 2000) self.won = true; + } else if (!tile.merged) { + self.moveTile(tile, positions.farthest); + } + + if (tile && !self.positionsEqual(cell, tile)) { + moved = true; // The tile moved from its original cell! + } + } + + }); + }); + + if (moved) { + this.addRandomTile(); + + if (!this.movesAvailable()) { + this.over = true; // Game over! + } + + this.actuate(); + } +}; + +// Get the vector representing the chosen direction +GameManager.prototype.getVector = function (direction) { + // Vectors representing tile movement + var map = { + 0: { + x: 0, + y: -1 + }, + // up + 1: { + x: 1, + y: 0 + }, + // right + 2: { + x: 0, + y: 1 + }, + // down + 3: { + x: -1, + y: 0 + } // left + }; + + return map[direction]; +}; + +// Build a list of positions to traverse in the right order +GameManager.prototype.buildTraversals = function (vector) { + var traversals = { + x: [], + y: [] + }; + + for (var pos = 0; pos < this.size; pos++) { + traversals.x.push(pos); + traversals.y.push(pos); + } + + // Always traverse from the farthest cell in the chosen direction + if (vector.x === 1) traversals.x = traversals.x.reverse(); + if (vector.y === 1) traversals.y = traversals.y.reverse(); + + return traversals; +}; + +GameManager.prototype.findFarthestPosition = function (cell, vector) { + var previous; + + // Progress towards the vector direction until an obstacle is found + do { + previous = cell; + cell = { + x: previous.x + vector.x, + y: previous.y + vector.y + }; + } while (this.grid.withinBounds(cell) && this.grid.cellAvailable(cell)); + + return { + farthest: previous, + next: cell // Used to check if a merge is required + }; +}; + +GameManager.prototype.movesAvailable = function () { + return this.grid.cellsAvailable() || this.tileMatchesAvailable(); +}; + +// Check for available matches between tiles(more expensive check) +GameManager.prototype.tileMatchesAvailable = function () { + var self = this; + + var tile; + var i = 0; + while (i < 4) { + for (var x = 0; x < this.size; x++) { + for (var y = 0; y < this.size; y++) { + var vector = this.getVector(i); + tile = this.grid.cellContent({ + x: x, + y: y + }); + + cell = { + x: x, + y: y + }; + tile = self.grid.cellContent(cell); + + if (tile) { + var positions = self.findFarthestPosition(cell, vector); + var next = self.grid.cellContent(positions.next); + var pos2 = self.findFarthestPosition({ + x: cell.x + vector.x, + y: cell.y + vector.y + }, vector); + var next2 = self.grid.cellContent(pos2.next); + // Only one merger per row traversal? + if (next && next2 && next != next2 && (((tile.value == 1) && (next.value == 2) && (next2.value == 2)) || ((tile.value == 2) && (next.value == 1) && (next2.value == 2)) || ((tile.value == 2) && (next.value == 2) && (next2.value == 1)) || ((tile.value == 10) && (next.value == 20) && (next2.value == 20)) || ((tile.value == 20) && (next.value == 10) && (next2.value == 20)) || ((tile.value == 20) && (next.value == 20) && (next2.value == 10)) || ((tile.value == 100) && (next.value == 200) && (next2.value == 200)) || ((tile.value == 200) && (next.value == 100) && (next2.value == 200)) || ((tile.value == 200) && (next.value == 200) && (next2.value == 100)) || ((tile.value == 1000) && (next.value == 2000) && (next2.value == 2000)) || ((tile.value == 2000) && (next.value == 1000) && (next2.value == 2000)) || ((tile.value == 2000) && (next.value == 2000) && (next2.value == 1000)) || ((tile.value == 10000) && (next.value == 20000) && (next2.value == 20000)) || ((tile.value == 20000) && (next.value == 10000) && (next2.value == 20000)) || ((tile.value == 20000) && (next.value == 20000) && (next2.value == 10000)) || ((tile.value == 5) && (next.value == 10) && (next2.value == 5)) || ((tile.value == 50) && (next.value == 100) && (next2.value == 50)) || ((tile.value == 500) && (next.value == 1000) && (next2.value == 500)) || ((tile.value == 5000) && (next.value == 10000) && (next2.value == 5000)))) { + return true; + } + if (next && next.value === tile.value && next.value != 2 && next.value != 20 && next.value != 200 && next.value != 2000 && next.value != 20000 && next.value != 50000) { + return true; + } + + } + } + } + i += 1; + } + + return false; +}; + +GameManager.prototype.positionsEqual = function (first, second) { + return first.x === second.x && first.y === second.y; +}; \ No newline at end of file diff --git a/sources/js/grid.js b/sources/js/grid.js new file mode 100755 index 0000000..05fe057 --- /dev/null +++ b/sources/js/grid.js @@ -0,0 +1,84 @@ +function Grid(size) { + this.size = size; + + this.cells = []; + + this.build(); +} + +// Build a grid of the specified size +Grid.prototype.build = function () { + for (var x = 0; x < this.size; x++) { + var row = this.cells[x] = []; + + for (var y = 0; y < this.size; y++) { + row.push(null); + } + } +}; + +// Find the first available random position +Grid.prototype.randomAvailableCell = function () { + var cells = this.availableCells(); + + if (cells.length) { + return cells[Math.floor(Math.random() * cells.length)]; + } +}; + +Grid.prototype.availableCells = function () { + var cells = []; + + this.eachCell(function (x, y, tile) { + if (!tile) { + cells.push({ x: x, y: y }); + } + }); + + return cells; +}; + +// Call callback for every cell +Grid.prototype.eachCell = function (callback) { + for (var x = 0; x < this.size; x++) { + for (var y = 0; y < this.size; y++) { + callback(x, y, this.cells[x][y]); + } + } +}; + +// Check if there are any cells available +Grid.prototype.cellsAvailable = function () { + return !!this.availableCells().length; +}; + +// Check if the specified cell is taken +Grid.prototype.cellAvailable = function (cell) { + return !this.cellOccupied(cell); +}; + +Grid.prototype.cellOccupied = function (cell) { + return !!this.cellContent(cell); +}; + +Grid.prototype.cellContent = function (cell) { + if (this.withinBounds(cell)) { + return this.cells[cell.x][cell.y]; + } else { + return null; + } +}; + +// Inserts a tile at its position +Grid.prototype.insertTile = function (tile) { + this.cells[tile.x][tile.y] = tile; +}; + +Grid.prototype.removeTile = function (tile) { + this.cells[tile.x][tile.y] = null; +}; + +Grid.prototype.withinBounds = function (position) { + return position.x >= 0 && position.x < this.size && + position.y >= 0 && position.y < this.size; +}; diff --git a/sources/js/html_actuator.js b/sources/js/html_actuator.js new file mode 100755 index 0000000..5cdf9cf --- /dev/null +++ b/sources/js/html_actuator.js @@ -0,0 +1,337 @@ +function HTMLActuator() { + this.tileContainer = document.querySelector(".tile-container"); + this.scoreContainer = document.querySelector(".score-container"); + this.bestContainer = document.querySelector(".best-container"); + this.messageContainer = document.querySelector(".game-message"); + this.sharingContainer = document.querySelector(".score-sharing"); + + this.score = 0; + this.highestTile = 1; +} + +HTMLActuator.prototype.actuate = function (grid, metadata) { + var self = this; + + window.requestAnimationFrame(function () { + self.clearContainer(self.tileContainer); + + grid.cells.forEach(function (column) { + column.forEach(function (cell) { + if (cell) { + self.addTile(cell); + } + }); + }); + + self.updateScore(metadata.score); + self.updateBestScore(metadata.bestScore); + self.highestTile = metadata.highest; + + if (metadata.terminated) { + if (metadata.over) { + self.message(false); // You lose + } else if (metadata.won) { + self.message(true); // You win! + } + } + + }); +}; + +// Continues the game (both restart and keep playing) +HTMLActuator.prototype.continue = function () { + if (typeof ga !== "undefined") { + ga("send", "event", "game", "restart"); + } + + this.clearMessage(); +}; + +HTMLActuator.prototype.clearContainer = function (container) { + while (container.firstChild) { + container.removeChild(container.firstChild); + } +}; + +HTMLActuator.prototype.addTile = function (tile) { + var self = this; + + var wrapper = document.createElement("div"); + var inner = document.createElement("div"); + var position = tile.previousPosition || { x: tile.x, y: tile.y }; + var positionClass = this.positionClass(position); + + // We can't use classlist because it somehow glitches when replacing classes + var classes = ["tile", "tile-" + tile.value, positionClass]; + + if (tile.value > 50000) classes.push("tile-super"); + + this.applyClasses(wrapper, classes); + + inner.classList.add("tile-inner"); + inner.textContent = tile.value; + + if (tile.previousPosition) { + // Make sure that the tile gets rendered in the previous position first + window.requestAnimationFrame(function () { + classes[2] = self.positionClass({ x: tile.x, y: tile.y }); + self.applyClasses(wrapper, classes); // Update the position + }); + } else if (tile.mergedFrom) { + classes.push("tile-merged"); + this.applyClasses(wrapper, classes); + + // Render the tiles that merged + tile.mergedFrom.forEach(function (merged) { + self.addTile(merged); + }); + } else { + classes.push("tile-new"); + this.applyClasses(wrapper, classes); + } + + // Add the inner part of the tile to the wrapper + wrapper.appendChild(inner); + + // Put the tile on the board + this.tileContainer.appendChild(wrapper); +}; + +HTMLActuator.prototype.applyClasses = function (element, classes) { + element.setAttribute("class", classes.join(" ")); +}; + +HTMLActuator.prototype.normalizePosition = function (position) { + return { x: position.x + 1, y: position.y + 1 }; +}; + +HTMLActuator.prototype.positionClass = function (position) { + position = this.normalizePosition(position); + return "tile-position-" + position.x + "-" + position.y; +}; + +HTMLActuator.prototype.updateScore = function (score) { + this.clearContainer(this.scoreContainer); + + var difference = score - this.score; + this.score = score; + + this.scoreContainer.textContent = this.score; + + if (difference > 0) { + var addition = document.createElement("div"); + addition.classList.add("score-addition"); + addition.textContent = "+" + difference; + + this.scoreContainer.appendChild(addition); + } +}; + +HTMLActuator.prototype.updateBestScore = function (bestScore) { + this.bestContainer.textContent = bestScore; +}; + +HTMLActuator.prototype.message = function (won) { + var type = won ? "game-won" : "game-over"; + var message = won ? "You won!" : "No more moves. Game over."; + + // Build some firebase references. + var rootRef = new Firebase('https://20-euros.firebaseio.com'); + var scoreListRef = rootRef.child("scoreList"); + var highestScoreRef = rootRef.child("highestScore"); + var RefPlays = rootRef.child("plays"); + var Ref50 = rootRef.child("reached_50c"); + var Ref100 = rootRef.child("reached_1e"); + var Ref200 = rootRef.child("reached_2e"); + var Ref500 = rootRef.child("reached_5e"); + var Ref1000 = rootRef.child("reached_10e"); + var Ref2000 = rootRef.child("reached_20e"); + var Ref5000 = rootRef.child("reached_50e"); + var Ref10000 = rootRef.child("reached_100e"); + var Ref20000 = rootRef.child("reached_200e"); + var Ref50000 = rootRef.child("reached_500e"); + + // Keep a mapping of firebase locations to HTML elements, so we can move / remove elements as necessary. + var htmlForPath = {}; + + // Helper function that takes a new score snapshot and adds an appropriate row to our leaderboard table. + function handleScoreAdded(scoreSnapshot, prevScoreName) { + var newScoreRow = $(""); + newScoreRow.append($("").append($("").text(scoreSnapshot.val().name))); + newScoreRow.append($("").text(scoreSnapshot.val().score)); + + // Store a reference to the table row so we can get it again later. + htmlForPath[scoreSnapshot.name()] = newScoreRow; + + // Insert the new score in the appropriate place in the table. + if (prevScoreName === null) { + $("#leaderboardTable").append(newScoreRow); + } + else { + var lowerScoreRow = htmlForPath[prevScoreName]; + lowerScoreRow.before(newScoreRow); + } + } + + // Helper function to handle a score object being removed; just removes the corresponding table row. + function handleScoreRemoved(scoreSnapshot) { + var removedScoreRow = htmlForPath[scoreSnapshot.name()]; + removedScoreRow.remove(); + delete htmlForPath[scoreSnapshot.name()]; + } + +function makeid() +{ + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for( var i=0; i < 8; i++ ) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; +} + + // Create a view to only receive callbacks for the last LEADERBOARD_SIZE scores + var scoreListView = scoreListRef.limit(10); + + // Add a callback to handle when a new score is added. + scoreListView.on('child_added', function (newScoreSnapshot, prevScoreName) { + handleScoreAdded(newScoreSnapshot, prevScoreName); + }); + + // Add a callback to handle when a score is removed + scoreListView.on('child_removed', function (oldScoreSnapshot) { + handleScoreRemoved(oldScoreSnapshot); + }); + + // Add a callback to handle when a score changes or moves positions. + var changedCallback = function (scoreSnapshot, prevScoreName) { + handleScoreRemoved(scoreSnapshot); + handleScoreAdded(scoreSnapshot, prevScoreName); + }; + scoreListView.on('child_moved', changedCallback); + scoreListView.on('child_changed', changedCallback); + + var newScore = this.score; + var name = makeid(); + $("#scoreInput").val(""); + + var userScoreRef = scoreListRef.child(name); + + // Use setWithPriority to put the name / score in Firebase, and set the priority to be the score. + if ((newScore <= 500)||(this.highestTile <= 20)||(this.highestTile >= 2000)){ + userScoreRef.setWithPriority({ name:name, score:newScore, highest:this.highestTile }, newScore);} + +// Track the highest score using a transaction. A transaction guarantees that the code inside the block is + // executed on the latest data from the server, so transactions should be used if you have multiple + // clients writing to the same data and you want to avoid conflicting changes. + highestScoreRef.transaction(function (currentHighestScore) { + if (currentHighestScore === null || newScore > currentHighestScore) { + // The return value of this function gets saved to the server as the new highest score. + return newScore; + } + // if we return with no arguments, it cancels the transaction. + return; + }); + + RefPlays.transaction(function(current_value) { + return current_value + 1; + }); + + if (this.highestTile >= 50){ + Ref50.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 100){ + Ref100.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 200){ + Ref200.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 500){ + Ref500.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 1000){ + Ref1000.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 2000){ + Ref2000.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 5000){ + Ref5000.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 10000){ + Ref10000.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 20000){ + Ref20000.transaction(function(current_value) { + return current_value + 1; + }); + } + +if (this.highestTile >= 50000){ + Ref50000.transaction(function(current_value) { + return current_value + 1; + }); + } + + // Add a callback to the highest score in Firebase so we can update the GUI any time it changes. + highestScoreRef.on('value', function (newHighestScore) { + $("#highestScoreDiv").text(newHighestScore.val()); + }); + + if (typeof ga !== "undefined") { + ga("send", "event", "game", "end", type, this.score); + } + + this.messageContainer.classList.add(type); + this.messageContainer.getElementsByTagName("p")[0].textContent = message; + + this.clearContainer(this.sharingContainer); + this.sharingContainer.appendChild(this.scoreTweetButton()); + twttr.widgets.load(); +}; + +HTMLActuator.prototype.clearMessage = function () { + // IE only takes one value to remove at a time. + this.messageContainer.classList.remove("game-won"); + this.messageContainer.classList.remove("game-over"); +}; + +HTMLActuator.prototype.scoreTweetButton = function () { + var tweet = document.createElement("a"); + tweet.classList.add("twitter-share-button"); + tweet.setAttribute("href", "https://twitter.com/share"); + tweet.setAttribute("data-via", "gabrielecirulli"); + tweet.setAttribute("data-url", "http://git.io/2048"); + tweet.setAttribute("data-counturl", "http://gabrielecirulli.github.io/2048/"); + tweet.textContent = "Tweet"; + var text = "I got " + this.score + " points in 20 Euro, a game where you " + + "join numbers to score high! #20eurogame"; + tweet.setAttribute("data-text", text); + + return tweet; +}; diff --git a/sources/js/keyboard_input_manager.js b/sources/js/keyboard_input_manager.js new file mode 100755 index 0000000..a29744c --- /dev/null +++ b/sources/js/keyboard_input_manager.js @@ -0,0 +1,130 @@ +function KeyboardInputManager() { + this.events = {}; + + if (window.navigator.msPointerEnabled) { + //Internet Explorer 10 style + this.eventTouchstart = "MSPointerDown"; + this.eventTouchmove = "MSPointerMove"; + this.eventTouchend = "MSPointerUp"; + } else { + this.eventTouchstart = "touchstart"; + this.eventTouchmove = "touchmove"; + this.eventTouchend = "touchend"; + } + + this.listen(); +} + +KeyboardInputManager.prototype.on = function (event, callback) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(callback); +}; + +KeyboardInputManager.prototype.emit = function (event, data) { + var callbacks = this.events[event]; + if (callbacks) { + callbacks.forEach(function (callback) { + callback(data); + }); + } +}; + +KeyboardInputManager.prototype.listen = function () { + var self = this; + + var map = { + 38: 0, // Up + 39: 1, // Right + 40: 2, // Down + 37: 3, // Left + 75: 0, // vim keybindings + 76: 1, + 74: 2, + 72: 3, + 87: 0, // W + 68: 1, // D + 83: 2, // S + 65: 3 // A + }; + + document.addEventListener("keydown", function (event) { + var modifiers = event.altKey || event.ctrlKey || event.metaKey || + event.shiftKey; + var mapped = map[event.which]; + + if (!modifiers) { + if (mapped !== undefined) { + event.preventDefault(); + self.emit("move", mapped); + } + + if (event.which === 32) self.restart.bind(self)(event); + } + }); + + var retry = document.querySelector(".retry-button"); + retry.addEventListener("click", this.restart.bind(this)); + retry.addEventListener(this.eventTouchend, this.restart.bind(this)); + + var keepPlaying = document.querySelector(".keep-playing-button"); + keepPlaying.addEventListener("click", this.keepPlaying.bind(this)); + keepPlaying.addEventListener("touchend", this.keepPlaying.bind(this)); + + // Listen to swipe events + var touchStartClientX, touchStartClientY; + var gameContainer = document.getElementsByClassName("game-container")[0]; + + gameContainer.addEventListener(this.eventTouchstart, function (event) { + if (( !window.navigator.msPointerEnabled && event.touches.length > 1) || event.targetTouches > 1) return; + + if(window.navigator.msPointerEnabled){ + touchStartClientX = event.pageX; + touchStartClientY = event.pageY; + } else { + touchStartClientX = event.touches[0].clientX; + touchStartClientY = event.touches[0].clientY; + } + + event.preventDefault(); + }); + + gameContainer.addEventListener(this.eventTouchmove, function (event) { + event.preventDefault(); + }); + + gameContainer.addEventListener(this.eventTouchend, function (event) { + if (( !window.navigator.msPointerEnabled && event.touches.length > 0) || event.targetTouches > 0) return; + + var touchEndClientX, touchEndClientY; + if(window.navigator.msPointerEnabled){ + touchEndClientX = event.pageX; + touchEndClientY = event.pageY; + } else { + touchEndClientX = event.changedTouches[0].clientX; + touchEndClientY = event.changedTouches[0].clientY; + } + + var dx = touchEndClientX - touchStartClientX; + var absDx = Math.abs(dx); + + var dy = touchEndClientY - touchStartClientY; + var absDy = Math.abs(dy); + + if (Math.max(absDx, absDy) > 10) { + // (right : left) : (down : up) + self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0)); + } + }); +}; + +KeyboardInputManager.prototype.restart = function (event) { + event.preventDefault(); + this.emit("restart"); +}; + +KeyboardInputManager.prototype.keepPlaying = function (event) { + event.preventDefault(); + this.emit("keepPlaying"); +}; diff --git a/sources/js/local_score_manager.js b/sources/js/local_score_manager.js new file mode 100755 index 0000000..ec4575d --- /dev/null +++ b/sources/js/local_score_manager.js @@ -0,0 +1,48 @@ +window.fakeStorage = { + _data: {}, + + setItem: function (id, val) { + return this._data[id] = String(val); + }, + + getItem: function (id) { + return this._data.hasOwnProperty(id) ? this._data[id] : undefined; + }, + + removeItem: function (id) { + return delete this._data[id]; + }, + + clear: function () { + return this._data = {}; + } +}; + +function LocalScoreManager() { + this.key = "bestScore"; + + var supported = this.localStorageSupported(); + this.storage = supported ? window.localStorage : window.fakeStorage; +} + +LocalScoreManager.prototype.localStorageSupported = function () { + var testKey = "test"; + var storage = window.localStorage; + + try { + storage.setItem(testKey, "1"); + storage.removeItem(testKey); + return true; + } catch (error) { + return false; + } +}; + +LocalScoreManager.prototype.get = function () { + return this.storage.getItem(this.key) || 0; +}; + +LocalScoreManager.prototype.set = function (score) { + this.storage.setItem(this.key, score); +}; + diff --git a/sources/js/tile.js b/sources/js/tile.js new file mode 100755 index 0000000..de08333 --- /dev/null +++ b/sources/js/tile.js @@ -0,0 +1,17 @@ +function Tile(position, value) { + this.x = position.x; + this.y = position.y; + this.value = value || 2; + + this.previousPosition = null; + this.mergedFrom = null; // Tracks tiles that merged together +} + +Tile.prototype.savePosition = function () { + this.previousPosition = { x: this.x, y: this.y }; +}; + +Tile.prototype.updatePosition = function (position) { + this.x = position.x; + this.y = position.y; +}; diff --git a/sources/statistics.html b/sources/statistics.html new file mode 100755 index 0000000..9b51d9d --- /dev/null +++ b/sources/statistics.html @@ -0,0 +1,122 @@ + + + + + 20 Euros Statistics + + + + + + + + + + + + + + + + + + + + +
+
+

Statistics

+
+

Statistics of my game 20 Euros.

+ +
+

+Statistics since 2014-04-06. +

+ + Out of the plays,
+ reached the 50 cent coin,
+ reached the 1 euro coin,
+ reached the 2 euro coin,
+ reached the 5 euro note,
+ reached the 10 euro note,
+ and won the game!
+

+

+ The highest score achieved was . +

+
+ + + + + + + + diff --git a/sources/style/example.css b/sources/style/example.css new file mode 100755 index 0000000..c941903 --- /dev/null +++ b/sources/style/example.css @@ -0,0 +1,50 @@ +/* Global */ + + + +#leaderboardTable { + background-color: white; + overflow: auto; + width: 100%; + padding: 10px; + border: 8px solid #424547; + margin-bottom: 5px; +} + +#scoreInput { + width: 68%; +} + +#highestscore { + margin-top: 20px; + font-size: 14px; +} + +/* Presence */ + +#presenceDiv { + text-align: center; +} + +/* Tetris */ + +.tetris-body { + width: 600px; +} + +#canvas0, #canvas1 { + display: inline-block; + border: 4px solid #424547; +} + +#restartButton { + margin-top: 5px; +} + +#gameInProgress { + font-size: 14px; +} + +.hide { + display: none; +} \ No newline at end of file diff --git a/sources/style/main.css b/sources/style/main.css new file mode 100755 index 0000000..1d9a609 --- /dev/null +++ b/sources/style/main.css @@ -0,0 +1,869 @@ +@import url(fonts/clear-sans.css); +html, body { + margin: 0; + padding: 0; + background: #faf8ef; + color: #776e65; + font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif; + font-size: 18px; } + +body { + margin: 80px 0; } + +.heading:after { + content: ""; + display: block; + clear: both; } + +h1.title { + font-size: 80px; + font-weight: bold; + margin: 0; + display: block; + float: left; } + +@-webkit-keyframes move-up { + 0% { + top: 25px; + opacity: 1; } + + 100% { + top: -50px; + opacity: 0; } } + +@-moz-keyframes move-up { + 0% { + top: 25px; + opacity: 1; } + + 100% { + top: -50px; + opacity: 0; } } + +@keyframes move-up { + 0% { + top: 25px; + opacity: 1; } + + 100% { + top: -50px; + opacity: 0; } } + +.scores-container { + float: right; + text-align: right; } + +.score-container, .best-container { + position: relative; + display: inline-block; + background: #bbada0; + padding: 15px 25px; + font-size: 25px; + height: 25px; + line-height: 47px; + font-weight: bold; + border-radius: 3px; + color: white; + margin-top: 8px; + text-align: center; } + .score-container:after, .best-container:after { + position: absolute; + width: 100%; + top: 10px; + left: 0; + text-transform: uppercase; + font-size: 13px; + line-height: 13px; + text-align: center; + color: #eee4da; } + .score-container .score-addition, .best-container .score-addition { + position: absolute; + right: 30px; + color: red; + font-size: 25px; + line-height: 25px; + font-weight: bold; + color: rgba(119, 110, 101, 0.9); + z-index: 100; + -webkit-animation: move-up 600ms ease-in; + -moz-animation: move-up 600ms ease-in; + animation: move-up 600ms ease-in; + -webkit-animation-fill-mode: both; + -moz-animation-fill-mode: both; + animation-fill-mode: both; } + +.score-container:after { + content: "Score"; } + +.best-container:after { + content: "My best"; } + +p { + margin-top: 0; + margin-bottom: 10px; + line-height: 1.65; } + +a { + color: #776e65; + font-weight: bold; + text-decoration: underline; + cursor: pointer; } + +strong.important { + text-transform: uppercase; } + +hr { + border: none; + border-bottom: 1px solid #d8d4d0; + margin-top: 20px; + margin-bottom: 30px; } + +.container { + width: 640px; + margin: 0 auto; } + +@-webkit-keyframes fade-in { + 0% { + opacity: 0; } + + 100% { + opacity: 1; } } + +@-moz-keyframes fade-in { + 0% { + opacity: 0; } + + 100% { + opacity: 1; } } + +@keyframes fade-in { + 0% { + opacity: 0; } + + 100% { + opacity: 1; } } + +.game-container { + margin-top: 40px; + position: relative; + padding: 15px; + cursor: default; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + background: #bbada0; + border-radius: 6px; + width: 640px; + height: 640px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + + .game-container .game-message { + display: none; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(238, 228, 218, 0.5); + z-index: 100; + text-align: center; + -webkit-animation: fade-in 800ms ease 1200ms; + -moz-animation: fade-in 800ms ease 1200ms; + animation: fade-in 800ms ease 1200ms; + -webkit-animation-fill-mode: both; + -moz-animation-fill-mode: both; + animation-fill-mode: both; +} + +.game-container .game-message p { + font-size: 60px; + font-weight: bold; + height: 60px; + line-height: 60px; + margin-top: 222px; } + .game-container .game-message .lower { + display: block; + margin-top: 59px; } + .game-container .game-message a { + display: inline-block; + background: #8f7a66; + border-radius: 3px; + padding: 0 20px; + text-decoration: none; + color: #f9f6f2; + height: 40px; + line-height: 42px; + margin-left: 9px; } + .game-container .game-message a.keep-playing-button { + display: none; } + .game-container .game-message.game-won { + background: rgba(237, 194, 46, 0.5); + color: #f9f6f2; } + .game-container .game-message.game-won a.keep-playing-button { + display: inline-block; } + .game-container .game-message.game-won, .game-container .game-message.game-over { + display: block; } + +.grid-container { + position: absolute; + z-index: 1; } + +.grid-row { + margin-bottom: 15px; } + .grid-row:last-child { + margin-bottom: 0; } + .grid-row:after { + content: ""; + display: block; + clear: both; } + +.grid-cell { + width: 110px; + height: 110px; + margin-right: 15px; + float: left; + border-radius: 3px; + background: rgba(238, 228, 218, 0.35); } + .grid-cell:last-child { + margin-right: 0; } + +.tile-container { + position: absolute; + z-index: 2; } + +.tile, .tile .tile-inner { + width: 110px; + height: 110px; + line-height: 116.25px; } + +.tile.tile-position-1-1 { + -webkit-transform: translate(0px, 0px); + -moz-transform: translate(0px, 0px); + transform: translate(0px, 0px); } +.tile.tile-position-1-2 { + -webkit-transform: translate(0px, 125px); + -moz-transform: translate(0px, 125px); + transform: translate(0px, 125px); } +.tile.tile-position-1-3 { + -webkit-transform: translate(0px, 250px); + -moz-transform: translate(0px, 250px); + transform: translate(0px, 250px); } +.tile.tile-position-1-4 { + -webkit-transform: translate(0px, 375px); + -moz-transform: translate(0px, 375px); + transform: translate(0px, 375px); } +.tile.tile-position-1-5 { + -webkit-transform: translate(0px, 500px); + -moz-transform: translate(0px, 500px); + transform: translate(0px, 500px); } + +.tile.tile-position-2-1 { + -webkit-transform: translate(125px, 0px); + -moz-transform: translate(125px, 0px); + transform: translate(125px, 0px); } +.tile.tile-position-2-2 { + -webkit-transform: translate(125px, 125px); + -moz-transform: translate(125px, 125px); + transform: translate(125px, 125px); } +.tile.tile-position-2-3 { + -webkit-transform: translate(125px, 250px); + -moz-transform: translate(125px, 250px); + transform: translate(125px, 250px); } +.tile.tile-position-2-4 { + -webkit-transform: translate(125px, 375px); + -moz-transform: translate(125px, 375px); + transform: translate(125px, 375px); } +.tile.tile-position-2-5 { + -webkit-transform: translate(125px, 500px); + -moz-transform: translate(125px, 500px); + transform: translate(125px, 500px); } + +.tile.tile-position-3-1 { + -webkit-transform: translate(250px, 0px); + -moz-transform: translate(250px, 0px); + transform: translate(250px, 0px); } +.tile.tile-position-3-2 { + -webkit-transform: translate(250px, 125px); + -moz-transform: translate(250px, 125px); + transform: translate(250px, 125px); } +.tile.tile-position-3-3 { + -webkit-transform: translate(250px, 250px); + -moz-transform: translate(250px, 250px); + transform: translate(250px, 250px); } +.tile.tile-position-3-4 { + -webkit-transform: translate(250px, 375px); + -moz-transform: translate(250px, 375px); + transform: translate(250px, 375px); } +.tile.tile-position-3-5 { + -webkit-transform: translate(250px, 500px); + -moz-transform: translate(250px, 500x); + transform: translate(250px, 500px); } + +.tile.tile-position-4-1 { + -webkit-transform: translate(375px, 0px); + -moz-transform: translate(375px, 0px); + transform: translate(375px, 0px); } +.tile.tile-position-4-2 { + -webkit-transform: translate(375px, 125px); + -moz-transform: translate(375px, 125px); + transform: translate(375px, 125px); } +.tile.tile-position-4-3 { + -webkit-transform: translate(375px, 250px); + -moz-transform: translate(375px, 250px); + transform: translate(375px, 250px); } +.tile.tile-position-4-4 { + -webkit-transform: translate(375px, 375px); + -moz-transform: translate(375px, 375px); + transform: translate(375px, 375px); } +.tile.tile-position-4-5 { + -webkit-transform: translate(375px, 500px); + -moz-transform: translate(375px, 500px); + transform: translate(375px, 500px); } + +.tile.tile-position-5-1 { + -webkit-transform: translate(500px, 0px); + -moz-transform: translate(500px, 0px); + transform: translate(500px, 0px); } +.tile.tile-position-5-2 { + -webkit-transform: translate(500px, 125px); + -moz-transform: translate(500px, 125px); + transform: translate(500px, 125px); } +.tile.tile-position-5-3 { + -webkit-transform: translate(500px, 250px); + -moz-transform: translate(500px, 250px); + transform: translate(500px, 250px); } +.tile.tile-position-5-4 { + -webkit-transform: translate(500px, 375px); + -moz-transform: translate(500px, 375px); + transform: translate(500px, 375px); } +.tile.tile-position-5-5 { + -webkit-transform: translate(500px, 500px); + -moz-transform: translate(500px, 500px); + transform: translate(500px, 500px); } + + +.tile { + position: absolute; + -webkit-transition: 100ms ease-in-out; + -moz-transition: 100ms ease-in-out; + transition: 100ms ease-in-out; + -webkit-transition-property: -webkit-transform; + -moz-transition-property: -moz-transform; + transition-property: transform; } + .tile .tile-inner { + border-radius: 3px; + background: #eee4da; + text-align: center; + font-weight: bold; + z-index: 10; + font-size: 0px; } + + +.tile.tile-1 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_1cent.gif'); + background-size: cover; + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } + + +.tile.tile-2 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_2cent.gif'); + background-size: cover; + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } + + + +.tile.tile-5 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_5cent.gif'); + background-size: cover; + box-shadow: 0 0 30px 10px rgba(243, 215, 116, 0), inset 0 0 0 1px rgba(255, 255, 255, 0); } + + + +.tile.tile-10 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_10cent.gif'); + background-size: cover;} + + + +.tile.tile-20 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_20cent.gif'); + background-size: cover;} + + + +.tile.tile-50 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_50cent.gif'); + background-size: cover;} + + + + .tile.tile-100 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_1euro.gif'); + background-size: cover;} + + + + .tile.tile-200 .tile-inner { + background: #f0f url('https://www.ecb.europa.eu/euro/coins/common/shared/img/common_2euro.gif'); + background-size: cover; +} + + + .tile.tile-500 .tile-inner { + + background: #ffffff url('http://www.126bay.com/images/banknote/europe/eURO/5%20Euro%202002%20-%20F.jpg'); + background-size: contain; background-repeat: no-repeat;} + + +.tile.tile-1000 .tile-inner { + + background: #ffffff url('http://www.126bay.com/images/banknote/europe/eURO/10%20Euro%202002%20-%20F.jpg'); + background-size: contain; background-repeat: no-repeat;} + +.tile.tile-2000 .tile-inner { + background: #ffffff url('http://www.126bay.com/images/banknote/europe/eURO/20%20Euro%202002%20-%20F.jpg'); + background-size: contain; background-repeat: no-repeat;} + + +.tile.tile-5000 .tile-inner { + background: #ffffff url('http://www.126bay.com/images/banknote/europe/eURO/50%20Euro%202002%20-%20F.jpg'); + background-size: contain; background-repeat: no-repeat;} + + +.tile.tile-10000 .tile-inner { + background: #ffffff url('http://www.blogger42.com/wp-content/uploads/100euro.jpg'); + background-size: contain; background-repeat: no-repeat;} + + +.tile.tile-20000 .tile-inner { + background: #ffffff url('http://www.blogger42.com/wp-content/uploads/e.jpg'); + background-size: contain; background-repeat: no-repeat;} + + +.tile.tile-50000 .tile-inner { + background: #ffffff url('http://www.rivistastudio.com/wp-content/uploads/2014/03/500euro.jpg'); + background-size: contain; background-repeat: no-repeat;} + + +.tile.tile-super .tile-inner { + + background: #ffffff url('http://upload.wikimedia.org/wikipedia/commons/c/c3/Euro_symbol_gold.svg'); + background-size: cover; + font-size: 30px; } + + + @media screen and (max-width: 520px) { + .tile.tile-super .tile-inner { + font-size: 10px; } } + +@-webkit-keyframes appear { + 0% { + opacity: 0; + -webkit-transform: scale(0); + -moz-transform: scale(0); + transform: scale(0); } + + 100% { + opacity: 1; + -webkit-transform: scale(1); + -moz-transform: scale(1); + transform: scale(1); } } + +@-moz-keyframes appear { + 0% { + opacity: 0; + -webkit-transform: scale(0); + -moz-transform: scale(0); + transform: scale(0); } + + 100% { + opacity: 1; + -webkit-transform: scale(1); + -moz-transform: scale(1); + transform: scale(1); } } + +@keyframes appear { + 0% { + opacity: 0; + -webkit-transform: scale(0); + -moz-transform: scale(0); + transform: scale(0); } + + 100% { + opacity: 1; + -webkit-transform: scale(1); + -moz-transform: scale(1); + transform: scale(1); } } + +.tile-new .tile-inner { + -webkit-animation: appear 200ms ease 100ms; + -moz-animation: appear 200ms ease 100ms; + animation: appear 200ms ease 100ms; + -webkit-animation-fill-mode: backwards; + -moz-animation-fill-mode: backwards; + animation-fill-mode: backwards; } + +@-webkit-keyframes pop { + 0% { + -webkit-transform: scale(0); + -moz-transform: scale(0); + transform: scale(0); } + + 50% { + -webkit-transform: scale(1.2); + -moz-transform: scale(1.2); + transform: scale(1.2); } + + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + transform: scale(1); } } + +@-moz-keyframes pop { + 0% { + -webkit-transform: scale(0); + -moz-transform: scale(0); + transform: scale(0); } + + 50% { + -webkit-transform: scale(1.2); + -moz-transform: scale(1.2); + transform: scale(1.2); } + + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + transform: scale(1); } } + +@keyframes pop { + 0% { + -webkit-transform: scale(0); + -moz-transform: scale(0); + transform: scale(0); } + + 50% { + -webkit-transform: scale(1.2); + -moz-transform: scale(1.2); + transform: scale(1.2); } + + 100% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + transform: scale(1); } } + +.tile-merged .tile-inner { + z-index: 20; + -webkit-animation: pop 200ms ease 100ms; + -moz-animation: pop 200ms ease 100ms; + animation: pop 200ms ease 100ms; + -webkit-animation-fill-mode: backwards; + -moz-animation-fill-mode: backwards; + animation-fill-mode: backwards; } + +.game-intro { + margin-bottom: 0; } + +.game-explanation { + margin-top: 50px; } + +.sharing { + margin-top: 20px; + text-align: center; } + .sharing > iframe, .sharing > span, .sharing > form { + display: inline-block; + vertical-align: middle; } + +@media screen and (max-width: 520px) { + html, body { + font-size: 15px; } + + body { + margin: 20px 0; + padding: 0 20px; } + + h1.title { + font-size: 27px; + margin-top: 15px; } + + .container { + width: 280px; + margin: 0 auto; } + + .score-container, .best-container { + margin-top: 0; + padding: 15px 10px; + min-width: 40px; } + + .heading { + margin-bottom: 10px; } + + .game-container { + margin-top: 40px; + position: relative; + padding: 10px; + cursor: default; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + background: #bbada0; + border-radius: 6px; + width: 280px; + height: 280px; + -ms-touch-action: none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } + .game-container .game-message { + display: none; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(238, 228, 218, 0.5); + z-index: 100; + text-align: center; + -webkit-animation: fade-in 800ms ease 1200ms; + -moz-animation: fade-in 800ms ease 1200ms; + animation: fade-in 800ms ease 1200ms; + -webkit-animation-fill-mode: both; + -moz-animation-fill-mode: both; + animation-fill-mode: both; } + .game-container .game-message p { + font-size: 60px; + font-weight: bold; + height: 60px; + line-height: 60px; + margin-top: 222px; } + .game-container .game-message .lower { + display: block; + margin-top: 59px; } + .game-container .game-message a { + display: inline-block; + background: #8f7a66; + border-radius: 3px; + padding: 0 20px; + text-decoration: none; + color: #f9f6f2; + height: 40px; + line-height: 42px; + margin-left: 9px; } + .game-container .game-message a.keep-playing-button { + display: none; } + .game-container .game-message .score-sharing { + display: inline-block; + vertical-align: middle; + margin-left: 10px; } + .game-container .game-message.game-won { + background: rgba(237, 194, 46, 0.5); + color: #f9f6f2; } + .game-container .game-message.game-won a.keep-playing-button { + display: inline-block; } + .game-container .game-message.game-won, .game-container .game-message.game-over { + display: block; } + + .grid-container { + position: absolute; + z-index: 1; } + + .grid-row { + margin-bottom: 10px; } + .grid-row:last-child { + margin-bottom: 0; } + .grid-row:after { + content: ""; + display: block; + clear: both; } + + .grid-cell { + width: 57.5px; + height: 57.5px; + margin-right: 10px; + float: left; + border-radius: 3px; + background: rgba(238, 228, 218, 0.35); } + .grid-cell:last-child { + margin-right: 0; } + + .tile-container { + position: absolute; + z-index: 2; } + + .tile, .tile .tile-inner { + width: 58px; + height: 58px; + line-height: 67.5px; } + .tile.tile-position-1-1 { + -webkit-transform: translate(0px, 0px); + -moz-transform: translate(0px, 0px); + transform: translate(0px, 0px); } + .tile.tile-position-1-2 { + -webkit-transform: translate(0px, 67px); + -moz-transform: translate(0px, 67px); + transform: translate(0px, 67px); } + .tile.tile-position-1-3 { + -webkit-transform: translate(0px, 135px); + -moz-transform: translate(0px, 135px); + transform: translate(0px, 135px); } + .tile.tile-position-1-4 { + -webkit-transform: translate(0px, 202px); + -moz-transform: translate(0px, 202px); + transform: translate(0px, 202px); } + .tile.tile-position-2-1 { + -webkit-transform: translate(67px, 0px); + -moz-transform: translate(67px, 0px); + transform: translate(67px, 0px); } + .tile.tile-position-2-2 { + -webkit-transform: translate(67px, 67px); + -moz-transform: translate(67px, 67px); + transform: translate(67px, 67px); } + .tile.tile-position-2-3 { + -webkit-transform: translate(67px, 135px); + -moz-transform: translate(67px, 135px); + transform: translate(67px, 135px); } + .tile.tile-position-2-4 { + -webkit-transform: translate(67px, 202px); + -moz-transform: translate(67px, 202px); + transform: translate(67px, 202px); } + .tile.tile-position-3-1 { + -webkit-transform: translate(135px, 0px); + -moz-transform: translate(135px, 0px); + transform: translate(135px, 0px); } + .tile.tile-position-3-2 { + -webkit-transform: translate(135px, 67px); + -moz-transform: translate(135px, 67px); + transform: translate(135px, 67px); } + .tile.tile-position-3-3 { + -webkit-transform: translate(135px, 135px); + -moz-transform: translate(135px, 135px); + transform: translate(135px, 135px); } + .tile.tile-position-3-4 { + -webkit-transform: translate(135px, 202px); + -moz-transform: translate(135px, 202px); + transform: translate(135px, 202px); } + .tile.tile-position-4-1 { + -webkit-transform: translate(202px, 0px); + -moz-transform: translate(202px, 0px); + transform: translate(202px, 0px); } + .tile.tile-position-4-2 { + -webkit-transform: translate(202px, 67px); + -moz-transform: translate(202px, 67px); + transform: translate(202px, 67px); } + .tile.tile-position-4-3 { + -webkit-transform: translate(202px, 135px); + -moz-transform: translate(202px, 135px); + transform: translate(202px, 135px); } + .tile.tile-position-4-4 { + -webkit-transform: translate(202px, 202px); + -moz-transform: translate(202px, 202px); + transform: translate(202px, 202px); } + + .game-container { + margin-top: 20px; } + + .tile .tile-inner { + font-size: 35px; } + + .game-message p { + font-size: 30px !important; + height: 30px !important; + line-height: 30px !important; + margin-top: 90px !important; } + .game-message .lower { + margin-top: 30px !important; } + + .sharing > iframe, .sharing > span, .sharing > form { + display: block; + margin: 0 auto; + margin-bottom: 20px; } } +.pp-donate button { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border: none; + font: inherit; + color: inherit; + cursor: pointer; + display: inline-block; + background: #8f7a66; + border-radius: 3px; + padding: 0 20px; + text-decoration: none; + color: #f9f6f2; + height: 40px; + line-height: 42px; } + .pp-donate button img { + vertical-align: -4px; + margin-right: 8px; } + +.btc-donate { + position: relative; + margin-left: 10px; + display: inline-block; + background: #8f7a66; + border-radius: 3px; + padding: 0 20px; + text-decoration: none; + color: #f9f6f2; + height: 40px; + line-height: 42px; + cursor: pointer; } + .btc-donate img { + vertical-align: -4px; + margin-right: 8px; } + .btc-donate a { + color: #f9f6f2; + text-decoration: none; + font-weight: normal; } + .btc-donate .address { + cursor: auto; + position: absolute; + width: 340px; + right: 50%; + margin-right: -170px; + padding-bottom: 7px; + top: -30px; + opacity: 0; + pointer-events: none; + -webkit-transition: 400ms ease; + -moz-transition: 400ms ease; + transition: 400ms ease; + -webkit-transition-property: top, opacity; + -moz-transition-property: top, opacity; + transition-property: top, opacity; } + .btc-donate .address:after { + position: absolute; + border-top: 10px solid #bbada0; + border-right: 7px solid transparent; + border-left: 7px solid transparent; + content: ""; + bottom: 0px; + left: 50%; + margin-left: -7px; } + .btc-donate .address code { + background-color: #bbada0; + padding: 10px 15px; + width: 100%; + border-radius: 3px; + line-height: 1; + font-weight: normal; + font-size: 15px; + font-family: Consolas, "Liberation Mono", Courier, monospace; + text-align: center; } + .btc-donate:hover .address, .btc-donate .address:hover .address { + opacity: 1; + top: -45px; + pointer-events: auto; } + @media screen and (max-width: 480px) { + .btc-donate { + width: 120px; } + .btc-donate .address { + margin-right: -150px; + width: 300px; } + .btc-donate .address code { + font-size: 13px; } + .btc-donate .address:after { + left: 50%; + bottom: 2px; } } diff --git a/sources/stylesheets/pygment_trac.css b/sources/stylesheets/pygment_trac.css new file mode 100755 index 0000000..e65cedf --- /dev/null +++ b/sources/stylesheets/pygment_trac.css @@ -0,0 +1,70 @@ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f0f3f3; } +.highlight .c { color: #0099FF; font-style: italic } /* Comment */ +.highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ +.highlight .k { color: #006699; font-weight: bold } /* Keyword */ +.highlight .o { color: #555555 } /* Operator */ +.highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #009999 } /* Comment.Preproc */ +.highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ +.highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ +.highlight .go { color: #AAAAAA } /* Generic.Output */ +.highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #99CC66 } /* Generic.Traceback */ +.highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #006699 } /* Keyword.Pseudo */ +.highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #FF6600 } /* Literal.Number */ +.highlight .s { color: #CC3300 } /* Literal.String */ +.highlight .na { color: #330099 } /* Name.Attribute */ +.highlight .nb { color: #336666 } /* Name.Builtin */ +.highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ +.highlight .no { color: #336600 } /* Name.Constant */ +.highlight .nd { color: #9999FF } /* Name.Decorator */ +.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #CC00FF } /* Name.Function */ +.highlight .nl { color: #9999FF } /* Name.Label */ +.highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #003333 } /* Name.Variable */ +.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #FF6600 } /* Literal.Number.Float */ +.highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ +.highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ +.highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ +.highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ +.highlight .sc { color: #CC3300 } /* Literal.String.Char */ +.highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #CC3300 } /* Literal.String.Double */ +.highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ +.highlight .si { color: #AA0000 } /* Literal.String.Interpol */ +.highlight .sx { color: #CC3300 } /* Literal.String.Other */ +.highlight .sr { color: #33AAAA } /* Literal.String.Regex */ +.highlight .s1 { color: #CC3300 } /* Literal.String.Single */ +.highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ +.highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #003333 } /* Name.Variable.Class */ +.highlight .vg { color: #003333 } /* Name.Variable.Global */ +.highlight .vi { color: #003333 } /* Name.Variable.Instance */ +.highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ + +.type-csharp .highlight .k { color: #0000FF } +.type-csharp .highlight .kt { color: #0000FF } +.type-csharp .highlight .nf { color: #000000; font-weight: normal } +.type-csharp .highlight .nc { color: #2B91AF } +.type-csharp .highlight .nn { color: #000000 } +.type-csharp .highlight .s { color: #A31515 } +.type-csharp .highlight .sc { color: #A31515 } diff --git a/sources/stylesheets/stylesheet.css b/sources/stylesheets/stylesheet.css new file mode 100755 index 0000000..7a08b01 --- /dev/null +++ b/sources/stylesheets/stylesheet.css @@ -0,0 +1,423 @@ +/******************************************************************************* +Slate Theme for GitHub Pages +by Jason Costello, @jsncostello +*******************************************************************************/ + +@import url(pygment_trac.css); + +/******************************************************************************* +MeyerWeb Reset +*******************************************************************************/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +ol, ul { + list-style: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +/******************************************************************************* +Theme Styles +*******************************************************************************/ + +body { + box-sizing: border-box; + color:#373737; + background: #212121; + font-size: 16px; + font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +h1, h2, h3, h4, h5, h6 { + margin: 10px 0; + font-weight: 700; + color:#222222; + font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; + letter-spacing: -1px; +} + +h1 { + font-size: 36px; + font-weight: 700; +} + +h2 { + padding-bottom: 10px; + font-size: 32px; + background: url('../images/bg_hr.png') repeat-x bottom; +} + +h3 { + font-size: 24px; +} + +h4 { + font-size: 21px; +} + +h5 { + font-size: 18px; +} + +h6 { + font-size: 16px; +} + +p { + margin: 10px 0 15px 0; +} + +footer p { + color: #f2f2f2; +} + +a { + text-decoration: none; + color: #007edf; + text-shadow: none; + + transition: color 0.5s ease; + transition: text-shadow 0.5s ease; + -webkit-transition: color 0.5s ease; + -webkit-transition: text-shadow 0.5s ease; + -moz-transition: color 0.5s ease; + -moz-transition: text-shadow 0.5s ease; + -o-transition: color 0.5s ease; + -o-transition: text-shadow 0.5s ease; + -ms-transition: color 0.5s ease; + -ms-transition: text-shadow 0.5s ease; +} + +a:hover, a:focus {text-decoration: underline;} + +footer a { + color: #F2F2F2; + text-decoration: underline; +} + +em { + font-style: italic; +} + +strong { + font-weight: bold; +} + +img { + position: relative; + margin: 0 auto; + max-width: 739px; + padding: 5px; + margin: 10px 0 10px 0; + border: 1px solid #ebebeb; + + box-shadow: 0 0 5px #ebebeb; + -webkit-box-shadow: 0 0 5px #ebebeb; + -moz-box-shadow: 0 0 5px #ebebeb; + -o-box-shadow: 0 0 5px #ebebeb; + -ms-box-shadow: 0 0 5px #ebebeb; +} + +p img { + display: inline; + margin: 0; + padding: 0; + vertical-align: middle; + text-align: center; + border: none; +} + +pre, code { + width: 100%; + color: #222; + background-color: #fff; + + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + font-size: 14px; + + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; +} + +pre { + width: 100%; + padding: 10px; + box-shadow: 0 0 10px rgba(0,0,0,.1); + overflow: auto; +} + +code { + padding: 3px; + margin: 0 3px; + box-shadow: 0 0 10px rgba(0,0,0,.1); +} + +pre code { + display: block; + box-shadow: none; +} + +blockquote { + color: #666; + margin-bottom: 20px; + padding: 0 0 0 20px; + border-left: 3px solid #bbb; +} + + +ul, ol, dl { + margin-bottom: 15px +} + +ul { + list-style: inside; + padding-left: 20px; +} + +ol { + list-style: decimal inside; + padding-left: 20px; +} + +dl dt { + font-weight: bold; +} + +dl dd { + padding-left: 20px; + font-style: italic; +} + +dl p { + padding-left: 20px; + font-style: italic; +} + +hr { + height: 1px; + margin-bottom: 5px; + border: none; + background: url('../images/bg_hr.png') repeat-x center; +} + +table { + border: 1px solid #373737; + margin-bottom: 20px; + text-align: left; + } + +th { + font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; + padding: 10px; + background: #373737; + color: #fff; + } + +td { + padding: 10px; + border: 1px solid #373737; + } + +form { + background: #f2f2f2; + padding: 20px; +} + +/******************************************************************************* +Full-Width Styles +*******************************************************************************/ + +.outer { + width: 100%; +} + +.inner { + position: relative; + max-width: 640px; + padding: 20px 10px; + margin: 0 auto; +} + +#forkme_banner { + display: block; + position: absolute; + top:0; + right: 10px; + z-index: 10; + padding: 10px 50px 10px 10px; + color: #fff; + background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; + font-weight: 700; + box-shadow: 0 0 10px rgba(0,0,0,.5); + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +#header_wrap { + background: #212121; + background: -moz-linear-gradient(top, #373737, #212121); + background: -webkit-linear-gradient(top, #373737, #212121); + background: -ms-linear-gradient(top, #373737, #212121); + background: -o-linear-gradient(top, #373737, #212121); + background: linear-gradient(top, #373737, #212121); +} + +#header_wrap .inner { + padding: 50px 10px 30px 10px; +} + +#project_title { + margin: 0; + color: #fff; + font-size: 42px; + font-weight: 700; + text-shadow: #111 0px 0px 10px; +} + +#project_tagline { + color: #fff; + font-size: 24px; + font-weight: 300; + background: none; + text-shadow: #111 0px 0px 10px; +} + +#downloads { + position: absolute; + width: 210px; + z-index: 10; + bottom: -40px; + right: 0; + height: 70px; + background: url('../images/icon_download.png') no-repeat 0% 90%; +} + +.zip_download_link { + display: block; + float: right; + width: 90px; + height:70px; + text-indent: -5000px; + overflow: hidden; + background: url(../images/sprite_download.png) no-repeat bottom left; +} + +.tar_download_link { + display: block; + float: right; + width: 90px; + height:70px; + text-indent: -5000px; + overflow: hidden; + background: url(../images/sprite_download.png) no-repeat bottom right; + margin-left: 10px; +} + +.zip_download_link:hover { + background: url(../images/sprite_download.png) no-repeat top left; +} + +.tar_download_link:hover { + background: url(../images/sprite_download.png) no-repeat top right; +} + +#main_content_wrap { + background: #f2f2f2; + border-top: 1px solid #111; + border-bottom: 1px solid #111; +} + +#main_content { + padding-top: 40px; +} + +#footer_wrap { + background: #212121; +} + + + +/******************************************************************************* +Small Device Styles +*******************************************************************************/ + +@media screen and (max-width: 480px) { + body { + font-size:14px; + } + + #downloads { + display: none; + } + + .inner { + min-width: 320px; + max-width: 480px; + } + + #project_title { + font-size: 32px; + } + + h1 { + font-size: 28px; + } + + h2 { + font-size: 24px; + } + + h3 { + font-size: 21px; + } + + h4 { + font-size: 18px; + } + + h5 { + font-size: 14px; + } + + h6 { + font-size: 12px; + } + + code, pre { + min-width: 320px; + max-width: 480px; + font-size: 11px; + } + +}