diff --git a/Games/Bejeweled/Bejeweled.png b/Games/Bejeweled/Bejeweled.png
new file mode 100644
index 0000000000..a07d103421
Binary files /dev/null and b/Games/Bejeweled/Bejeweled.png differ
diff --git a/Games/Bejeweled/README.md b/Games/Bejeweled/README.md
new file mode 100644
index 0000000000..60fd9491ee
--- /dev/null
+++ b/Games/Bejeweled/README.md
@@ -0,0 +1,30 @@
+# **Game_Name**
+
+## Bejeweled
+
+
+
+## **Description 📃**
+
+
+
+- Bejeweled is a puzzle game where the player's objective is to swap adjacent gems in a grid to create matches of three or more identical gems. When a match is made, those gems are removed from the grid, and new gems fall from the top to fill the empty spaces.
+
+## **functionalities 🎮**
+
+
+
+- At the beginning of the game, the grid is checked for any existing matches. If there are any matches, those gems are removed, and new gems fall from the top to fill the empty spaces.
+
+
+## **How to play? 🕹️**
+
+
+
+- The player can select two adjacent gems to swap their positions. The selected gems are checked for a valid move, meaning the swap would result in a match. If it's a valid move, the gems are swapped, and the grid is checked for matches.
+ The grid is scanned for matches after every valid move. Matches can be horizontal or vertical. If a match is found, the matched gems are removed from the grid, and new gems fall to fill the empty spaces.
+ Points are awarded to the player based on the number of gems cleared in each match. Bonus points can be given for creating special combinations or making consecutive matches.
+
+
+
+
diff --git a/Games/Bejeweled/assets/Bejeweled.mp4 b/Games/Bejeweled/assets/Bejeweled.mp4
new file mode 100644
index 0000000000..97d0a007a4
Binary files /dev/null and b/Games/Bejeweled/assets/Bejeweled.mp4 differ
diff --git a/Games/Bejeweled/assets/Bejeweled.png b/Games/Bejeweled/assets/Bejeweled.png
new file mode 100644
index 0000000000..d33e65d779
Binary files /dev/null and b/Games/Bejeweled/assets/Bejeweled.png differ
diff --git a/Games/Bejeweled/css/game.css b/Games/Bejeweled/css/game.css
new file mode 100644
index 0000000000..3ac3b63635
--- /dev/null
+++ b/Games/Bejeweled/css/game.css
@@ -0,0 +1,130 @@
+@charset "UTF-8";
+
+#game_content{
+ width: 750px;
+ height: 520px;
+ margin: auto;
+ background-color: rgba(51, 51, 85, 0.4);
+ border: 3px solid rgba(34, 34, 238, 0.4);
+ border-top: 15px solid rgba(34, 34, 238, 0.4);
+ border-radius: 5px;
+}
+
+#grid{
+ position: relative;
+ width: 525px;
+ height: 525px;
+ background: url('../images/grid.png') rgba(32, 32, 32, 0.4);
+ background-repeat: no-repeat;
+}
+
+#player_info{
+ position: relative;
+ float: right;
+ top: 60px;
+ width: 230px;
+ text-align: center;
+}
+
+#player_info p, #game_level{
+ font-size: 18px;
+ margin-left: 30px;
+ color: #226622;
+}
+
+#total_score, #level{
+ padding: 70px 0 20px 0;
+ margin-left: 30px;
+ margin-top: -70px;
+ font-size: 20px;
+ height: 130px;
+ font-weight: bold;
+ color: #FFF;
+ background-image: url('../images/rose.png');
+ background-repeat: no-repeat;
+ background-position: top center;
+}
+
+#total_score{
+ padding-top: 52px;
+}
+
+#goal_score{
+ font-size: 14px;
+}
+
+#level{
+ margin-bottom: -60px;
+}
+
+#level_text{
+ margin-top: 80px;
+}
+
+#time_gauge{
+ position: absolute;
+ border-radius: 20px;
+ left: 30px;
+ top: -10px;
+ width: 15px;
+ height: 400px;
+ background-color: #666;
+}
+
+#current_gauge{
+ position: absolute;
+ border-radius: 20px;
+ bottom: 0px;
+ left: 2px;
+ width: 11px;
+ height: 100%;
+ background-color: #00F000;
+}
+
+.score_gain{
+ position: absolute;
+ left: 112px;
+ font-size: 15px;
+ font-weight: bold;
+ color: #222;
+}
+
+.gem, .bomb{
+ position: absolute;
+ width: 60px;
+ height: 60px;
+ background-position: center center;
+ background-repeat: no-repeat;
+}
+
+#content button{
+ cursor:pointer;
+ height: 30px;
+ margin: 0px -5px 15px 0px;
+ padding: 0 9px 0 9px;
+}
+
+#content button:hover{
+ border-color: #999;
+ -o-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
+}
+
+#content button:active{
+ border-color: #444;
+}
+
+#player_info button{
+ cursor: pointer;
+ margin-left: 10px;
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border: 3px solid rgba(68 ,68, 68, 1);
+ background-color: rgba(238, 34, 34, 0.6);
+ font-size: 10pt;
+ font-family: 'Electrolize', 'Segoe UI', Helvetica, Arial, 'Trebuchet MS', sans-serif;
+ color: #FFF;
+}
\ No newline at end of file
diff --git a/Games/Bejeweled/css/screen.css b/Games/Bejeweled/css/screen.css
new file mode 100644
index 0000000000..295e6e7f25
--- /dev/null
+++ b/Games/Bejeweled/css/screen.css
@@ -0,0 +1,72 @@
+@charset "UTF-8";
+
+html {
+ position: relative;
+ height: 100%;
+}
+
+body{
+ position: relative;
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background: #000000;
+ font-family: 'Electrolize', 'Segoe UI', Helvetica, Arial, 'Trebuchet MS', sans-serif;
+ font-size: 14px;
+ text-align: justify;
+}
+
+#content{
+ position: relative;
+ width: 1000px;
+ min-height: 700px;
+ margin: auto;
+ background-image: url('../images/background_hover.png'), url('../images/background1.jpg');
+ background-repeat: no-repeat;
+ color: #FFFFFF;
+}
+
+#site_content, #rules_content{
+ padding: 50px;
+ font-size: 16px;
+ color: #DEDEDE;
+}
+a {
+ text-decoration: none;
+ color: #000000;
+}
+
+header{
+ padding-bottom: 30px;
+ position: relative;
+ z-index: 30;
+ background-color: #000000;
+}
+
+nav{
+ background-color: #111122;
+ border-width: 0px 2px 2px 2px;
+ border-color: #CCCCCC;
+ border-style: solid;
+ border-radius: 20px;
+}
+
+nav a{
+ display: inline-block;
+ padding: 10px 20px 15px 20px;
+ color: #FFFFFF;
+ font-weight: bold;
+}
+
+nav a.current_menu{
+ background-color: rgba(200, 200, 200, 0.4);
+}
+
+nav a:first-child{
+ border-top-left-radius: 20px;
+ -webkit-border-top-left-radius: 20px;
+ -moz-border-radius-topleft: 20px;
+ border-bottom-left-radius: 20px;
+ -webkit-border-bottom-left-radius: 20px;
+ -moz-border-radius-bottomleft: 20px;
+}
\ No newline at end of file
diff --git a/Games/Bejeweled/images/background_hover.png b/Games/Bejeweled/images/background_hover.png
new file mode 100644
index 0000000000..9256eedf26
Binary files /dev/null and b/Games/Bejeweled/images/background_hover.png differ
diff --git a/Games/Bejeweled/images/favicon.ico b/Games/Bejeweled/images/favicon.ico
new file mode 100644
index 0000000000..8f182eba2e
Binary files /dev/null and b/Games/Bejeweled/images/favicon.ico differ
diff --git a/Games/Bejeweled/images/grid.png b/Games/Bejeweled/images/grid.png
new file mode 100644
index 0000000000..bcc03c9262
Binary files /dev/null and b/Games/Bejeweled/images/grid.png differ
diff --git a/Games/Bejeweled/images/sprites/0.png b/Games/Bejeweled/images/sprites/0.png
new file mode 100644
index 0000000000..35dcddc2ce
Binary files /dev/null and b/Games/Bejeweled/images/sprites/0.png differ
diff --git a/Games/Bejeweled/images/sprites/0_explosion0.png b/Games/Bejeweled/images/sprites/0_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/0_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/0_explosion1.png b/Games/Bejeweled/images/sprites/0_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/0_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/1.png b/Games/Bejeweled/images/sprites/1.png
new file mode 100644
index 0000000000..ad7685ab75
Binary files /dev/null and b/Games/Bejeweled/images/sprites/1.png differ
diff --git a/Games/Bejeweled/images/sprites/1_explosion0.png b/Games/Bejeweled/images/sprites/1_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/1_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/1_explosion1.png b/Games/Bejeweled/images/sprites/1_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/1_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/2.png b/Games/Bejeweled/images/sprites/2.png
new file mode 100644
index 0000000000..bbc0c77bab
Binary files /dev/null and b/Games/Bejeweled/images/sprites/2.png differ
diff --git a/Games/Bejeweled/images/sprites/2_explosion0.png b/Games/Bejeweled/images/sprites/2_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/2_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/2_explosion1.png b/Games/Bejeweled/images/sprites/2_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/2_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/3.png b/Games/Bejeweled/images/sprites/3.png
new file mode 100644
index 0000000000..3aae90738e
Binary files /dev/null and b/Games/Bejeweled/images/sprites/3.png differ
diff --git a/Games/Bejeweled/images/sprites/3_explosion0.png b/Games/Bejeweled/images/sprites/3_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/3_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/3_explosion1.png b/Games/Bejeweled/images/sprites/3_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/3_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/4.png b/Games/Bejeweled/images/sprites/4.png
new file mode 100644
index 0000000000..60b28ccfa1
Binary files /dev/null and b/Games/Bejeweled/images/sprites/4.png differ
diff --git a/Games/Bejeweled/images/sprites/4_explosion0.png b/Games/Bejeweled/images/sprites/4_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/4_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/4_explosion1.png b/Games/Bejeweled/images/sprites/4_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/4_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/5.png b/Games/Bejeweled/images/sprites/5.png
new file mode 100644
index 0000000000..4b7066b737
Binary files /dev/null and b/Games/Bejeweled/images/sprites/5.png differ
diff --git a/Games/Bejeweled/images/sprites/5_explosion0.png b/Games/Bejeweled/images/sprites/5_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/5_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/5_explosion1.png b/Games/Bejeweled/images/sprites/5_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/5_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/6.png b/Games/Bejeweled/images/sprites/6.png
new file mode 100644
index 0000000000..c65760f4c1
Binary files /dev/null and b/Games/Bejeweled/images/sprites/6.png differ
diff --git a/Games/Bejeweled/images/sprites/6_explosion0.png b/Games/Bejeweled/images/sprites/6_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/6_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/6_explosion1.png b/Games/Bejeweled/images/sprites/6_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/6_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/7.png b/Games/Bejeweled/images/sprites/7.png
new file mode 100644
index 0000000000..5e3e835f5c
Binary files /dev/null and b/Games/Bejeweled/images/sprites/7.png differ
diff --git a/Games/Bejeweled/images/sprites/7_explosion0.png b/Games/Bejeweled/images/sprites/7_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/7_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/7_explosion1.png b/Games/Bejeweled/images/sprites/7_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/7_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/8.png b/Games/Bejeweled/images/sprites/8.png
new file mode 100644
index 0000000000..c27c6e4e25
Binary files /dev/null and b/Games/Bejeweled/images/sprites/8.png differ
diff --git a/Games/Bejeweled/images/sprites/8_explosion0.png b/Games/Bejeweled/images/sprites/8_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/8_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/8_explosion1.png b/Games/Bejeweled/images/sprites/8_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/8_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/9.png b/Games/Bejeweled/images/sprites/9.png
new file mode 100644
index 0000000000..b92ea647f2
Binary files /dev/null and b/Games/Bejeweled/images/sprites/9.png differ
diff --git a/Games/Bejeweled/images/sprites/9_explosion0.png b/Games/Bejeweled/images/sprites/9_explosion0.png
new file mode 100644
index 0000000000..ea67723b18
Binary files /dev/null and b/Games/Bejeweled/images/sprites/9_explosion0.png differ
diff --git a/Games/Bejeweled/images/sprites/9_explosion1.png b/Games/Bejeweled/images/sprites/9_explosion1.png
new file mode 100644
index 0000000000..684ca2e61f
Binary files /dev/null and b/Games/Bejeweled/images/sprites/9_explosion1.png differ
diff --git a/Games/Bejeweled/images/sprites/bomb.png b/Games/Bejeweled/images/sprites/bomb.png
new file mode 100644
index 0000000000..405776cdf8
Binary files /dev/null and b/Games/Bejeweled/images/sprites/bomb.png differ
diff --git a/Games/Bejeweled/images/sprites/gemSelected.gif b/Games/Bejeweled/images/sprites/gemSelected.gif
new file mode 100644
index 0000000000..7b6ce83b73
Binary files /dev/null and b/Games/Bejeweled/images/sprites/gemSelected.gif differ
diff --git a/Games/Bejeweled/images/sprites/h_arrow.png b/Games/Bejeweled/images/sprites/h_arrow.png
new file mode 100644
index 0000000000..b17a796a22
Binary files /dev/null and b/Games/Bejeweled/images/sprites/h_arrow.png differ
diff --git a/Games/Bejeweled/images/sprites/v_arrow.png b/Games/Bejeweled/images/sprites/v_arrow.png
new file mode 100644
index 0000000000..8ffa8395a4
Binary files /dev/null and b/Games/Bejeweled/images/sprites/v_arrow.png differ
diff --git a/Games/Bejeweled/index.html b/Games/Bejeweled/index.html
new file mode 100644
index 0000000000..30cef24b80
--- /dev/null
+++ b/Games/Bejeweled/index.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Level
1
+
Score
0 /5000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/bonus.js b/Games/Bejeweled/js/game/bonus.js
new file mode 100644
index 0000000000..84469bfb03
--- /dev/null
+++ b/Games/Bejeweled/js/game/bonus.js
@@ -0,0 +1,30 @@
+// Bonus related functions
+
+/**
+ * Adds a bonus item : the bomb
+ */
+Game.winBomb = function() {
+ if (Game.bonus.bomb) {
+ return;
+ }
+ // We randomly place a bomb
+ var bomb = new Game.Bomb(),
+ x = 0,
+ y = 0,
+ itemToReplace;
+
+ Game.bonus.bomb = bomb;
+ do {
+ x = parseInt(Math.random() * Game.GRID_SIZE);
+ y = parseInt(Math.random() * Game.GRID_SIZE);
+ }while (get('#tile' + y + '_' + x) == null || get('#tile' + y + '_' + x).timer != undefined);
+ Game.winBomb(x+1, y+1);
+ bomb.style.left = ((60 * x) + (5 * (x + 1))) + 'px';
+ bomb.style.top = ((60 * y) + (5 * (y + 1))) + 'px';
+ bomb.id = 'tile' + y + '_' + x;
+ itemToReplace = get('#tile' + y + '_' + x);
+ if (itemToReplace != null && itemToReplace.parentNode) {
+ get('#grid').removeChild(itemToReplace);
+ }
+ get('#grid').appendChild(bomb);
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/capabilities/item.js b/Games/Bejeweled/js/game/capabilities/item.js
new file mode 100644
index 0000000000..4114449f2b
--- /dev/null
+++ b/Games/Bejeweled/js/game/capabilities/item.js
@@ -0,0 +1,142 @@
+/**
+ * Adds some methods to a given item to make it a displayable and movable item on the game grid
+ */
+function addItemCapabilities(item) {
+ /**
+ * Returns (and sets, if a value is passed as an argument) the gem's "left" CSS property in px
+ */
+ item.left = function(value) {
+ if (value != undefined) {
+ if (typeof(value) == 'number' && parseInt(value) == value) // If value is an integer
+ item.style.left = value + 'px';
+ else if (typeof(value) == 'string') // If value is a string
+ item.style.left = value;
+ return value;
+ }
+ return item.style.left;
+ };
+
+ /**
+ * Returns (and sets, if a value is passed as an argument) the item's "top" CSS property in px
+ */
+ item.top = function(value) {
+ if (value != undefined) {
+ if (typeof(value) == 'number' && parseInt(value) == value) // If value is an integer
+ item.style.top = value + 'px';
+ else if (typeof(value) == 'string') // If value is a string
+ item.style.top = value;
+ return value;
+ }
+ return item.style.top;
+ };
+
+ /**
+ * Returns (and sets, if a value is passed as an argument) the item's x position on the map
+ */
+ item.x = function(value) {
+ if (value != undefined) {
+ item.id = (item.id != '') ? (item.id.substr(0, item.id.length - 1) + value) : 'tile0_' + value;
+ // item.innerHTML = item.y()+'_'+item.x();
+ }
+ if (item.id != '')
+ return parseInt(item.id.substr(item.id.length - 1));
+ return null;
+ };
+
+ /**
+ * Returns (and sets, if a value is passed as an argument) the item's y position on the map
+ */
+ item.y = function(value) {
+ if (value != undefined) {
+ item.id = (item.id != '') ? (item.id.substring(0, 4) + value + item.id.substr(item.id.indexOf('_'))) : 'tile' + value + '_0';
+ // item.innerHTML = item.y()+'_'+item.x();
+ }
+ if (item.id != '')
+ return parseInt(item.id.substring(4, item.id.indexOf('_')));
+ return null;
+ };
+
+ /**
+ * Animates an element's CSS property from start value to end value (only values in pixels)
+ */
+ item.animate = function(property, start, end, speed, callback) {
+ if (start == end)
+ return;
+
+ this.style[property] = start;
+ start = parseInt(start.substr(0, start.length - 2));
+ end = parseInt(end.substr(0, end.length - 2));
+
+ var doAnimation = function(start) {
+ for (var i = 0; i < speed; i++) {
+ // If the property has reached the end value
+ if ((direction == 1 && start >= end) || (direction == -1 && start <= end)) {
+ clearInterval(item.timer); // We stop the animation timer
+ delete item.timer;
+
+ if (callback != undefined && callback != null && !item.falling) {
+ callback(item);
+ }
+ if (item.falling) {
+ item.onFallComplete();
+ }
+ return;
+ }
+ start += direction;
+ item.style[property] = start + 'px';
+ };
+ return start;
+ };
+
+ var delta = end - start,
+ direction = (delta > 0) ? 1 : -1;
+
+ // We start the item's timer
+ item.timer = setInterval(function() {
+ start = doAnimation(start);
+ }, 30);
+ };
+
+ /**
+ * Makes the item fall vertically
+ */
+ item.fallStreak = function () {
+ var x = item.x(),
+ y = item.y(),
+ currentGem = null;
+
+ // We make all the items on the column fall by 1 slot
+ item.fall();
+ for (var i = y; i >= -(Game.GRID_SIZE - 1); i--) {
+ currentGem = get('#tile' + i + '_' + x);
+ if (currentGem != null) {
+ currentGem.fall();
+ }
+ };
+ };
+
+ /**
+ * Makes the item fall by one slot
+ */
+ item.fall = function () {
+ var top = item.top(),
+ height = parseInt(top.substring(0, top.length - 2));
+
+ height += Game.TILE_SIZE;
+ item.falling = true;
+ item.y(parseInt(item.y() + 1)); // We set the new Y position after the fall
+ item.animate('top', top, height + 'px', Game.GRID_SIZE);
+ };
+
+ /**
+ * Trggiers everytime the item's fall is finished
+ */
+ item.onFallComplete = function() {
+ item.falling = false;
+ if (get('#tile' + (item.y() + 1) + '_' + item.x()) == null && (item.y() + 1) != Game.GRID_SIZE) { // If there is still an empty slot below the item
+ item.fall(); // We make it fall again
+ }else if(item.className == 'gem item') { // Otherwise, the fall is over
+ Game.checkStreak(item); // We look for a streak
+ }
+ };
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/classes/Bomb.js b/Games/Bejeweled/js/game/classes/Bomb.js
new file mode 100644
index 0000000000..933ede8906
--- /dev/null
+++ b/Games/Bejeweled/js/game/classes/Bomb.js
@@ -0,0 +1,46 @@
+/**
+ * A bonus item for Ore : the Bomb (destoys all the immediatly surrounding gems)
+ */
+Game.Bomb = function() {
+ if (this == window) {
+ throw new Error('Bomb() is a constructor, you can only call it with the keyword "new"');
+ }
+ var bomb = document.createElement('span');
+ Game.addBombCapabilities(bomb);
+
+ bomb.className = 'bomb item';
+ bomb.style.backgroundImage = 'url("./images/sprites/bomb.png")';
+ bomb.addEventListener('click', bomb.explode, false);
+ bomb.active = false;
+ bomb.isGem = false;
+
+ return bomb;
+};
+
+Game.addBombCapabilities = function(bomb) {
+ addItemCapabilities(bomb);
+ /**
+ * Makes the bomb, and the surrounding gems explode
+ */
+ bomb.explode = function(event) {
+ if (!bomb.active)
+ return;
+ bomb.active = false;
+ var gemsToRemove = [],
+ x = bomb.x(),
+ y = bomb.y(),
+ item;
+ for (var i = (x > 0 ? x - 1 : x); i <= (x < 7 ? x + 1 : x); i++) {
+ gemsToRemove[i] = [];
+ for (var j = (y > 0 ? y - 1 : y); j <= (y < 7 ? y + 1 : y); j++) {
+ item = get('#tile' + j + '_' + i);
+ if (i == x && j == y || item == null || !item.isGem)
+ continue;
+ gemsToRemove[i].push(item);
+ };
+ };
+ get('#grid').removeChild(bomb);
+ Game.removeStreak(gemsToRemove);
+ delete Game.bonus.bomb;
+ };
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/classes/Gem.js b/Games/Bejeweled/js/game/classes/Gem.js
new file mode 100644
index 0000000000..5581bcd39d
--- /dev/null
+++ b/Games/Bejeweled/js/game/classes/Gem.js
@@ -0,0 +1,295 @@
+/**
+ * Creates an gem from its coordinates and value
+ */
+Game.Gem = function(x, y, value) {
+ if (this == window) {
+ throw new Error('Gem() is a constructor, you can only call it with the keyword "new"');
+ }
+ var left = ((60 * x) + (5 * (x + 1))) + 'px',
+ top = ((60 * y) + (5 * (y + 1))) + 'px',
+ gem = document.createElement('span');
+
+ gem.className = 'gem item';
+ gem.val = value;
+ gem.id = 'tile' + y + '_' + x;
+ // gem.innerHTML = y+'_'+x;
+
+ gem.style.top = top;
+ gem.style.left = left;
+ gem.style.backgroundImage = 'url("./images/sprites/' + value + '.png")';
+
+ gem.falling = false; // Is the element falling ?
+ gem.inStreak = false;
+ gem.isGem = true;
+
+ Game.addGemCapabilities(gem); // We add useful functions relative to gem objects
+ return gem;
+};
+
+Game.addGemCapabilities = function(gem) {
+ addItemCapabilities(gem); // We add useful functions relative to displayable items
+
+ /**
+ * Returns (and sets, if a value is passed as an argument) the gem's y tile value
+ */
+ gem.value = function(val) {
+ if (val != undefined)
+ gem.val = val;
+ if (gem.className != '')
+ return gem.val;
+ return null;
+ };
+
+ /**
+ * Makes the gem pop on the grid
+ */
+ gem.pop = function(grid) {
+ grid.appendChild(gem);
+ };
+
+ /**
+ * Compares a gem's value with this gem's value
+ * @param {Gem} target The gem to compare with the current object
+ */
+ gem.equals = function(target) {
+ if (target != null && target.isGem && target.value && target.value() == gem.value() && target != gem && !target.falling) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Checks if a given gem is adjacent to the object
+ * @param {Gem} target The gem to compare with the current object
+ */
+ gem.isNeighbour = function(target) {
+ return (((gem.x() === target.x() - 1 || gem.x() === target.x() + 1) && gem.y() === target.y())
+ || ((gem.y() === target.y() - 1 || gem.y() === target.y() + 1) && gem.x() === target.x()));
+ };
+
+ /**
+ * Searches for the presence of an gem streak
+ * @return The streak array with the streaked gems in it
+ */
+ gem.getStreak = function() {
+ var x = gem.x(),
+ y = gem.y(),
+ row = [],
+ column = [],
+ streak = {};
+
+ row = gem.checkRow(true, true);
+ column = gem.checkColumn(true, true);
+
+ // If we have a row of three identical gems
+ if (row.length > 1) {
+ for (var i = 0; i < row.length; i++) {
+ streak[row[i].x()] = [];
+ if (streak[row[i].x()].indexOf(row[i]) == -1) {
+ streak[row[i].x()].push(row[i]);
+ row[i].inStreak = true;
+ }
+ };
+ }
+
+ // If we have a column of three identical gems
+ if (column.length > 1) {
+ for (var i = 0; i < column.length; i++) {
+ if (streak[column[i].x()] == undefined) {
+ streak[column[i].x()] = [];
+ }
+ if (streak[column[i].x()].indexOf(column[i]) == -1) {
+ streak[column[i].x()].push(column[i]);
+ column[i].inStreak = true;
+ }
+ };
+ }
+ // If we have a row or a column of three identical gems
+ if ((row.length > 1 || column.length > 1) && (streak[gem.x()] == undefined || streak[gem.x()].indexOf(gem) == -1)) {
+ if (streak[gem.x()] == undefined) {
+ streak[gem.x()] = [];
+ }
+ streak[gem.x()].push(gem); // We know the moved gem will be removed
+ gem.inStreak = true;
+ }
+ return streak;
+ };
+
+ /**
+ * Looks through an gem's neighbours in a given direction
+ * @param vertical bool Check vertically or horizontally ?
+ * @param step int (-1 OR 1) Check on one direction or another (left/right, top/bottom)
+ * @return The streak array with the streaked gems in it
+ */
+ gem.parseNeighbours = function(vertical, step) {
+ var streak = [],
+ i = 0,
+ x = gem.x(),
+ y = gem.y(),
+ currentGem;
+
+ // We run through the gems in one direction. The step indicates if we go one way or another on the X or Y axis (the axis is defined by the 'vertical' parameter)
+ for (i = ((vertical ? y : x) + step); (step == -1) ? (i > -1) : (i < Game.GRID_SIZE); i += step) {
+ currentGem = vertical ? get('#tile' + i + '_' + x) : get('#tile' + y + '_' + i); // The current parsed gem
+ // If the current gem is equal to the source gem, we add it to the streak
+ if (streak.indexOf(currentGem) == -1 && gem.equals(currentGem) && currentGem.inStreak == false) {
+ streak = streak.concat(currentGem);
+ }else {
+ break;
+ }
+ };
+ return streak;
+ };
+
+
+
+ /**
+ * Checks for a streak in the gem's column
+ * @return An array containing the identical adjacent gems in the column
+ */
+ gem.checkColumn = function(top, bottom) {
+ if (top !== true && bottom !== true) {
+ return;
+ }
+
+ var column = [];
+ // Checking the gems on top (if the gem is at an extremity, don't check behind the border)
+ if (top && gem.y() > 0) {
+ column = column.concat(gem.parseNeighbours(true, -1));
+ }
+ // Checking the gems on bottom (if the gem is at an extremity, don't check behind the border)
+ if (bottom && gem.y() < (Game.GRID_SIZE - 1)) {
+ column = column.concat(gem.parseNeighbours(true, 1));
+ }
+ return column;
+ };
+
+ /**
+ * Checks for a streak in the gem's row
+ * @return An array containing the identical adjacent gems in the row
+ */
+ gem.checkRow = function(left, right) {
+ if (left !== true && right !== true) {
+ return;
+ }
+
+ var row = [];
+ // Checking the gems on the left
+ if (left && gem.x() > 0) {
+ row = row.concat(gem.parseNeighbours(false, -1));
+ }
+ // Checking the gems on the right
+ if (right && gem.x() < (Game.GRID_SIZE - 1)) {
+ row = row.concat(gem.parseNeighbours(false, 1));
+ }
+ return row;
+ };
+
+ /**
+ * Animates the explosion of an gem and removes it
+ * @param streak An array containing the gems that are in a streak
+ * @param fallAfter bool: Should the gms fall after this gem's explosion ? (true for the first destroyed gem)
+ */
+ gem.destroy = function(streak, fallAfter) {
+ var i = 0, loops = 3, timer;
+
+ function animateExplosion () {
+ if (i >= loops) {
+ clearInterval(gem.timer);
+ delete gem.timer;
+ if (gem.parentNode) {
+ gem.parentNode.removeChild(gem);
+ }
+ if (fallAfter === true) {
+ Game.onStreakRemoved(streak);
+ }
+ return;
+ }
+
+ gem.style.backgroundImage = 'url("./images/sprites/' + gem.value() + '_explosion' + (i % 2) + '.png")';
+ i++;
+ };
+
+ gem.timer = setInterval(function() {
+ animateExplosion();
+ }, 100);
+ };
+
+ /**
+ * Checks if a gem can be in a streak by a player's move
+ * @return {Array} The gems that the player has to swap in order to make a streak
+ */
+ gem.getPossibleMove = function() {
+ var row = gem.checkRow(true, true),
+ column = gem.checkColumn(true, true),
+ pair = [],
+ x = 0,
+ y = 0,
+ equalGem = null,
+ gemsToSwap = [];
+ y = gem.y();
+ x = gem.x();
+
+ if (row && row.length > 1 || column && column.length > 1) {
+ return [];
+ }
+
+ // The gem has to have one equal neighbour
+ if (row && row.length == 1) {
+ pair = gem.x() < row[0].x() ? [gem, row[0]] : [row[0], gem];
+ y = gem.y();
+
+ // If a move is possible with this row
+ if (pair[0].x() > 0 && gem.equals((equalGem = get('#tile' + (y - 1) + '_' + (pair[0].x() - 1)))) // Checking on left top
+ || pair[0].x() > 0 && gem.equals((equalGem = get('#tile' + (y + 1) + '_' + (pair[0].x() - 1)))) // Checking on left bottom
+ || pair[0].x() > 1 && gem.equals((equalGem = get('#tile' + (y) + '_' + (pair[0].x() - 2)))) // Checking on left, two gems further
+ || pair[1].x() < (Game.GRID_SIZE - 1) && gem.equals((equalGem = get('#tile' + (y - 1) + '_' + (pair[1].x() + 1)))) // Checking on right top
+ || pair[1].x() < (Game.GRID_SIZE - 1) && gem.equals((equalGem = get('#tile' + (y - 1) + '_' + (pair[1].x() + 1)))) // Checking on right bottom
+ || pair[1].x() < (Game.GRID_SIZE - 2) && gem.equals((equalGem = get('#tile' + (y) + '_' + (pair[1].x() + 2)))) // Checking on right, two gems further
+ ) {
+ gemsToSwap = [equalGem];
+ if (equalGem.x() > pair[1].x()) {
+ gemsToSwap.push(get('#tile' + y + '_' + (pair[1].x() + 1)));
+ }else {
+ gemsToSwap.push(get('#tile' + y + '_' + (pair[0].x() - 1)));
+ }
+ return gemsToSwap;
+ }
+ }else if (column && column.length == 1) {
+ pair = gem.y() < column[0].y() ? [gem, column[0]] : [column[0], gem];
+ x = gem.x();
+
+ // If a move is possible with this column
+ if (pair[0].y() > 0 && gem.equals((equalGem = get('#tile' + (pair[0].y() - 1) + '_' + (x - 1)))) // Checking on top left
+ || pair[0].y() > 0 && gem.equals((equalGem = get('#tile' + (pair[0].y() - 1) + '_' + (x + 1)))) // Checking on top right
+ || pair[0].y() > 1 && gem.equals((equalGem = get('#tile' + (pair[0].y() - 2) + '_' + (x)))) // Checking on top, two gems further
+ || pair[1].y() < (Game.GRID_SIZE - 1) && gem.equals((equalGem = get('#tile' + (pair[1].y() + 1) + '_' + (x - 1)))) // Checking on bottom left
+ || pair[1].y() < (Game.GRID_SIZE - 1) && gem.equals((equalGem = get('#tile' + (pair[1].y() + 1) + '_' + (x - 1)))) // Checking on bottom right
+ || pair[1].y() < (Game.GRID_SIZE - 2) && gem.equals((equalGem = get('#tile' + (pair[1].y() + 2) + '_' + (x)))) // Checking on bottom, two gems further
+ ) {
+ gemsToSwap = [equalGem];
+ // If the detected gem is below the column, the gem to swap it with will be below too
+ if (equalGem.y() > pair[1].y()) {
+ gemsToSwap.push(get('#tile' + (pair[1].y() + 1) + '_' + x));
+ }else { // Otherwise, the gem to swap it with will be on top too
+ gemsToSwap.push(get('#tile' + (pair[0].y() - 1) + '_' + x));
+ }
+ return gemsToSwap;
+ }
+ }
+ var equalVGem = null, equalHGem = null;
+ // If there are two equal gems with one space between them, and it is possible to make a streak by placing a surrouding gem between them (horizontally or vertically)
+ if (gem.equals(get('#tile' + y + '_' + (x + 2))) && (gem.equals((equalHGem = get('#tile' + (y + 1) + '_' + (x + 1)))) || gem.equals((equalHGem = get('#tile' + (y - 1) + '_' + (x + 1)))))
+ || gem.equals(get('#tile' + y + '_' + (x - 2))) && (gem.equals((equalHGem = get('#tile' + (y - 1) + '_' + (x - 1)))) || gem.equals((equalHGem = get('#tile' + (y + 1) + '_' + (x - 1)))))
+ || gem.equals(get('#tile' + (y + 2) + '_' + x)) && (gem.equals((equalVGem = get('#tile' + (y + 1) + '_' + (x - 1)))) || gem.equals((equalVGem = get('#tile' + (y + 1) + '_' + (x + 1)))))
+ || gem.equals(get('#tile' + (y - 2) + '_' + x)) && (gem.equals((equalVGem = get('#tile' + (y - 1) + '_' + (x - 1)))) || gem.equals((equalVGem = get('#tile' + (y - 1) + '_' + (x + 1)))))
+ ) {
+ equalGem = (equalHGem == null ? equalVGem : equalHGem);
+ x = equalGem.x();
+ y = equalGem.y();
+ return (equalVGem != null) ?
+ [equalGem, get('#tile' + y + '_' + gem.x())]
+ : [equalGem, get('#tile' + gem.y() + '_' + x)];
+ }
+ };
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/classes/Popup.js b/Games/Bejeweled/js/game/classes/Popup.js
new file mode 100644
index 0000000000..2103c3d900
--- /dev/null
+++ b/Games/Bejeweled/js/game/classes/Popup.js
@@ -0,0 +1,164 @@
+/**
+ * Displays a custom popup
+ * @param {object} args:
+ * args = {
+ * type: 'text' || 'html',
+ * content: 'string'
+ * buttons: [{
+ * text: 'string',
+ * callback: function
+ * }],
+ * background: CSS
+ * style: {
+ * CSS
+ * }
+ * }
+ */
+function Popup (args) {
+ if (args.type == undefined)
+ throw new Error('Missing property "type" in argument object');
+ if (args.content == undefined)
+ throw new Error('Missing property "content" in argument object');
+
+ var that = this;
+
+ // We create the popup container (background)
+ this.container = document.createElement('div');
+ this.container.className = 'popup';
+ this.container.style.position = 'absolute';
+ this.container.style.width = '100%';
+ this.container.style.height = '100%';
+ this.container.style.top = window.pageYOffset + 'px';
+ this.container.style.left = 0;
+ this.container.style.zIndex = '1000';
+ this.container.style.textAlign = 'center';
+ this.container.style.backgroundColor = args.background || 'rgba(255,255,255,0.1)';
+
+ // We create the popup box itself
+ var box = document.createElement('div');
+ box.style.position = 'absolute';
+ box.style.border = '1px solid #666';
+ box.style.background = '#FFFFFF';
+ box.style.width = '400px';
+ box.style.height = '150px';
+ box.style.boxShadow = '2px 2px 5px #333';
+ box.style.margin = 'auto';
+ box.style.paddingTop = '5px';
+ box.style.top = '50%';
+ box.style.left = '50%';
+
+ window.onscroll = function() {
+ that.container.style.top = window.pageYOffset + 'px';
+ }
+
+ // If style properties were passed, we set the popup CSS with them
+ if (args.style != undefined && args.style != null) {
+ box.style = args.style;
+ box.style.border = args.style.border || box.style.border;
+ box.style.background = args.style.background || box.style.background;
+ box.style.width = args.style.width || box.style.width;
+ box.style.height = args.style.height || box.style.height;
+ box.style.paddingTop = args.style.paddingTop || box.style.paddingTop;
+ box.style.top = args.style.top || box.style.top;
+ box.style.left = args.style.left || box.style.left;
+ }
+ // We center the box
+ var width = parseInt(box.style.width.substring(0, box.style.width.length - 2)),
+ height = parseInt(box.style.height.substring(0, box.style.height.length - 2));
+ box.style.margin = '-' + (height / 2) + 'px 0 0 -' + (width / 2) + 'px';
+
+
+ if (args.type === 'text') {
+ var p = document.createElement('p');
+ p.innerText = args.content;
+ box.appendChild(p);
+ }else if (args.type === 'html') {
+ box.innerHTML = args.content;
+ }else {
+ throw new Error('Error: "type" property in argument object should be string ("text" or "html"), ' + args.type + ':' + typeof args.type + ' given instead.');
+ }
+
+ // We create the given buttonss, and associate them with the corresponding callbacks
+ var btContainer = document.createElement('p');
+ for (var i = 0, button, currentBt; i < args.buttons.length; i++) {
+ button = document.createElement('button');
+ button.callback = args.buttons[i].callback;
+ button.innerHTML = args.buttons[i].text;
+ button.style.padding = '4px 12px 4px 12px';
+ button.style.fontSize = '14px';
+ button.style.fontWeight = 'bold';
+ button.style.color = '#555';
+ if (args.buttons[i].id != undefined)
+ button.id = args.buttons[i].id;
+
+ if (typeof button.callback === 'function') {
+ button.onclick = function(event) {
+ that.remove();
+ this.callback(event);
+ }
+ }else if (button.callback === 'remove') {
+ button.onclick = function(event) {
+ that.remove();
+ };
+ }
+ btContainer.appendChild(button);
+ };
+ box.appendChild(btContainer);
+
+ this.container.appendChild(box);
+}
+
+/**
+ * Displays the popup
+ */
+Popup.prototype.show = function() {
+ document.body.appendChild(this.container);
+};
+
+/**
+ * Removes the popup
+ */
+Popup.prototype.remove = function() {
+ document.body.removeChild(this.container);
+};
+
+/**
+ * A simple alert popup
+ */
+Popup.alert = function(text, callback) {
+ // We create a simple popup with a button that removes the popup
+ var popup = new Popup({
+ type: 'html',
+ content: '
' + text + '
',
+ buttons: [
+ {
+ text: 'Ok',
+ callback: callback || 'remove'
+ }
+ ]
+ });
+ popup.show();
+};
+
+/**
+ * A confirm popup with 'Yes' and 'No' options
+ */
+Popup.confirm = function(text, style, trueCallback, falseCallback) {
+ // We create a popup with two buttons
+ var popup = new Popup({
+ type: 'html',
+ content: '
' + text + '
',
+ buttons: [
+ {
+ text: 'Yes',
+ callback: trueCallback || 'remove'
+ },
+ {
+ text: 'No',
+ callback: falseCallback || 'remove'
+ }
+ ],
+ style: style || null
+ });
+ popup.show();
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/hint.js b/Games/Bejeweled/js/game/hint.js
new file mode 100644
index 0000000000..dfd73026df
--- /dev/null
+++ b/Games/Bejeweled/js/game/hint.js
@@ -0,0 +1,153 @@
+// Hint related functions (Possible streaks parsing, etc.)
+
+/**
+ * Checks if the player still has a possibility to make a streak
+ * @return {Array} An array containing the gems to swap if it is possible, null otherwise
+ */
+Game.checkHint = function() {
+ var gems = get('.gem'),
+ hint = null,
+ oneHint = false; // Is there at least one hint found
+
+ if (Game.hint != undefined) {
+ clearTimeout(Game.hint.timer);
+ delete Game.hint;
+ }
+
+ for (var i = gems.length - 1; i >= 0; i--) {
+ // If there is at least one gem can be moved to make a streak
+ if ((hint = gems[i].getPossibleMove()) != null) {
+ oneHint = true;
+ if (hint.length == 0) {
+ continue;
+ }
+ break;
+ }
+ };
+
+ // We set the hint
+ if (hint != null && hint.length > 0) {
+ Game.hint = {
+ gems: hint, // We keep the hint for the player
+ timer: setTimeout(Game.showHint, 15000), // We will show it in 15 seconds if the player is stuck
+ start: new Date()
+ };
+ }
+ return oneHint;
+};
+
+/**
+ * Displays an animation to give the player a hint on which gem to move if he is stuck
+ */
+Game.showHint = function() {
+ if (Game.hint == undefined || Game.hint.gems == undefined)
+ return;
+ if (Game.hint.timer) {
+ Game.removeHint(); // We remove the previous hint
+ }
+ var arrow = document.createElement('span'),
+ gems = Game.hint.gems,
+ left, top, width, height,
+ timer1, timer2;
+
+ // We make sure to put the first gem from the top, or the left as the first element of the array
+ if (gems[0].x() == gems[1].x() && gems[0].y() > gems[1].y() || gems[0].x() > gems[1].x()) {
+ gems.reverse();
+ }
+
+ left = parseInt(gems[0].left().substr(0, gems[0].left().length - 2)) - 2.5;
+ top = parseInt(gems[0].top().substr(0, gems[0].top().length - 2)) - 2.5;
+
+ // We place the arrow at the middle of the gems to swap
+ if (gems[0].x() == gems[1].x()) { // If the gems are in the same column.
+ width = 19;
+ height = 65;
+ arrow.style.backgroundImage = 'url("./images/sprites/v_arrow.png")';
+ arrow.style.left = (left + Game.TILE_SIZE / 2 - (width / 2)) + 'px';
+ arrow.style.top = (top + Game.TILE_SIZE / 2 - (height - Game.TILE_SIZE) / 2) + 'px';
+ }else {
+ width = 65;
+ height = 19;
+ arrow.style.backgroundImage = 'url("./images/sprites/h_arrow.png")';
+ arrow.style.left = (left + Game.TILE_SIZE / 2 - (width - Game.TILE_SIZE) / 2) + 'px';
+ arrow.style.top = (top + Game.TILE_SIZE / 2 - height / 2) + 'px';
+ }
+ arrow.id = 'hint_arrow';
+ arrow.style.position = 'absolute';
+ arrow.style.width = width + 'px';
+ arrow.style.height = height + 'px';
+
+
+ // We make the arrow blink
+ timer1 = setInterval(function() {
+ var blinks = 3, i = 0; // The arrow blinks 3 times
+ timer2 = setInterval(function() {
+ if (i == blinks * 2) {
+ clearInterval(timer2);
+ return;
+ }
+ // Once every two, we display and remove the arrow
+ if (i % 2 == 0) {
+ grid.appendChild(arrow);
+ }else {
+ remove(arrow);
+ }
+ i++;
+ }, 200);
+ }, 2000);
+
+ /**
+ * Removes the hint animation once the player has clicked on a gem
+ */
+ Game.removeHint = function(reset) {
+ if (Game.hint.timer != undefined) {
+ clearTimeout(Game.hint.timer);
+ delete Game.hint.timer;
+ }
+ if (timer1 != null) {
+ // We stop the blinking
+ clearInterval(timer2);
+ clearInterval(timer1);
+ timer2 = null;
+ timer1 = null;
+ remove(arrow); // We remove the arrow if it is displayed
+ }
+ };
+};
+
+/**
+ * Removes the hint animation once the player has clicked on a gem
+ */
+Game.removeHint = function() {
+ if (Game.hint != undefined) {
+ clearTimeout(Game.hint.timer);
+ delete Game.hint.timer;
+ }
+};
+
+/**
+ * Pauses the hint timeout, so that it doesn't show up if the user is on another tab from his browser
+ */
+Game.pauseHint = function() {
+ var remaningTime = 15000;
+ if (Game.hint != undefined) {
+ remaningTime -= (new Date() - Game.hint.start); // We calculate the hint's timer remaining time to resume it later
+ clearTimeout(Game.hint.timer);
+ delete Game.hint.timer;
+ }
+
+ /**
+ * Resumes the hint timeout
+ */
+ Game.resumeHint = function() {
+ if (remaningTime <= 0) { // If the hint timer was already finished, we look for another hint
+ Game.removeHint();
+ Game.checkGameOver();
+ }else if (Game.hint) {
+ Game.hint.start = new Date(); // We set a new starting moment
+ Game.hint.timer = setTimeout(Game.showHint, remaningTime); // We start the timer again, for the remaining time
+ }
+ };
+};
+
+Game.resumeHint = function() {};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/init.js b/Games/Bejeweled/js/game/init.js
new file mode 100644
index 0000000000..b69c2cc63e
--- /dev/null
+++ b/Games/Bejeweled/js/game/init.js
@@ -0,0 +1,88 @@
+// Game initialisation
+
+var Game = {}; // The game instance
+Game.GRID_SIZE = 8;
+Game.TILE_SIZE = 65;
+
+/**
+ * Initializes the game
+ */
+Game.init = function () {
+ Game.gemRange = 7; // The number of different gems on the grid
+ Game.level = 1;
+ Game.time = 0;
+ Game.gem = null; // The currently selected gem
+ Game.moving = false; // Are the gems moving or not ?
+ Game.score = {
+ goal: 5000,
+ current: 0
+ };
+ Game.bonus = {};
+ Game.pauses = false;
+ Game.initTimer();
+
+ // We initialize the UI
+ get('#level').innerHTML = Game.level;
+ get('#current_score').innerHTML = Game.score.current;
+ get('#goal_score').innerHTML = Game.score.goal;
+ get('#restart_bt').onclick = Game.confirmRestart;
+ get('#pause_bt').onclick = Game.pause;
+
+ Game.createGrid();
+};
+
+/**
+ * Created the game's grid
+ */
+Game.createGrid = function() {
+ var grid = get('#grid'), map = [], row, vGems = [], hGems = [], bg;
+
+ for (var i = 0, j = 0; i < Game.GRID_SIZE; i++) {
+ row = [];
+ map.push(row); // We create a row in the map
+
+ for (j = 0; j < Game.GRID_SIZE; j++) {
+ do {
+ gem = new Game.Gem(j, i, parseInt(Math.random() * Game.gemRange));
+ if (i > 0)
+ vGems = gem.parseNeighbours(true, -1);
+ if (j > 0)
+ hGems = gem.parseNeighbours(false, -1);
+ }while (vGems.length >= 2 || hGems.length >= 2);
+
+ gem.addEventListener('click', Game.onGemClick, false); // We add the mouse event listener
+ gem.pop(grid);
+ vGems = [];
+ hGems = [];
+ };
+ };
+
+ // We choose a random background
+ do {
+ bg = Math.floor(1 + Math.random() * 3);
+ get('#content').style.backgroundImage = 'url("./images/background_hover.png"), url("./images/background' + bg + '.jpg")';
+ } while (bg === Game.background);
+ Game.background = bg;
+ // We check if there is at least one possible move
+ Game.checkGameOver();
+
+ // If the player leaves the page, we stop the timer to display the hint after 15 seconds
+ window.onblur = function(){
+ Game.pauseHint();
+ }
+
+ // When he returns on the page, we resume the hint timer
+ window.onfocus = function(){
+ Game.resumeHint();
+ }
+};
+
+/**
+ * Removes all the items from the grid
+ */
+Game.emptyGrid = function() {
+ var items = get('.item'), grid = get('#grid');
+ for (var i = 0; i < items.length; i++) {
+ grid.removeChild(items[i]);
+ };
+}
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/level.js b/Games/Bejeweled/js/game/level.js
new file mode 100644
index 0000000000..e0460d8dc7
--- /dev/null
+++ b/Games/Bejeweled/js/game/level.js
@@ -0,0 +1,65 @@
+// Level related functions
+
+/**
+ * Displays a coinfirm popup to restart the game
+ */
+Game.confirmRestart = function() {
+ Popup.confirm('Do you want to restart ?', null, Game.restart);
+ Game.removeHint();
+};
+
+/**
+ * Restarts the game
+ */
+Game.restart = function() {
+ Game.removeHint();
+ Game.emptyGrid();
+ Game.resetTimer();
+ Game.init();
+};
+
+/**
+ * Notices the player of the end of the level
+ */
+Game.endLevel = function() {
+ Popup.alert('Level ' + Game.level + ' Ended !', Game.nextLevel);
+ Game.removeHint();
+}
+
+/**
+ * Goes to the next level
+ */
+Game.nextLevel = function() {
+ Game.level++;
+ if (Game.bonus.bomb != undefined) {
+ delete Game.bonus.bomb;
+ }
+
+ if (Game.level == 5) {
+ Game.gemRange++;
+ }
+
+ get('#current_gauge').style.height = '100%';
+ get('#level').innerHTML = Game.level;
+ Game.resetScore();
+ Game.resetTimer();
+ Game.emptyGrid();
+ Game.createGrid();
+};
+
+/**
+ * Checks if the game is over (no possibility to make a streak)
+ */
+Game.checkGameOver = function() {
+ if (!Game.checkHint()) {
+ Game.gameOver();
+ }
+};
+
+/**
+ * When the game is over : displays a popup to make the player restart
+ */
+Game.gameOver = function() {
+ Popup.confirm('There are no more possible moves. Do you want to start over ?', null, Game.restart);
+ Game.paused = true;
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/main.js b/Games/Bejeweled/js/game/main.js
new file mode 100644
index 0000000000..ac4a4ce41b
--- /dev/null
+++ b/Games/Bejeweled/js/game/main.js
@@ -0,0 +1,175 @@
+/**
+ * The game main loop
+ */
+Game.mainLoop = setInterval(function() {
+ // We consider the game in pause if there is a popup or if we are on another page
+ if (Game.paused || get('#game_content').style.display === 'none' || get('.popup') != null) {
+ return;
+ }
+ Game.updateTimer();
+
+ var gems = get('.gem');
+ for (var i = 0; i < gems.length; i++) {
+ if (gems[i].timer != undefined) { // If at least one gem is being animated
+ Game.moving = true; // The gems are moving, we prevent the player from clicking on the gems
+ return;
+ }
+ };
+
+ // If all the animations are over
+ if (Game.moving) {
+ Game.moving = false;
+ Game.onMoveComplete(gems);
+ }
+}, 50);
+
+/**
+ * Triggers when all the animations of a movement are done
+ */
+Game.onMoveComplete = function(gems) {
+ for (var i = 0; i < gems.length; i++) {
+ gems[i].removeEventListener('click', Game.onGemClick, false); // We remove all the previous listeners, just in case
+ gems[i].addEventListener('click', Game.onGemClick, false);
+ };
+
+ if (Game.combo >= 2 && Game.bonus.bomb == undefined) { // The player earns a bomb is he makes more than 1 combo
+ Game.winBomb();
+ }
+
+ // We activate the bomb's click
+ if (Game.bonus.bomb != undefined) {
+ Game.bonus.bomb.active = true;
+ }
+
+ if (Game.combo != undefined) {
+ delete Game.combo;
+ }
+
+ // If the goal has been reached, we go to the next level
+ if (Game.score.current >= Game.score.goal) {
+ Game.endLevel();
+ return;
+ }
+
+ // We check if the game is over (and get a hint for the player at the same time)
+ Game.checkGameOver();
+};
+
+/**
+ * Triggers when an gem is clicked (select it or proceed to the swap)
+ * @param {event} e The mouse event
+ */
+Game.onGemClick = function(e) {
+ var target = e.srcElement || e.target;
+
+ if (Game.gem == null) {
+ Game.selectGem(target);
+ }else {
+ if (target.isNeighbour(Game.gem)) { // If the clicked gem is adjacent to the first selected gem
+ Game.swapGems(Game.gem, target, true); // We can swap them
+ }else { // Otherwise
+ Game.selectGem(target); // We select the new one
+ }
+ }
+};
+
+/**
+ * Makes a given gem the game's selected gem
+ * @param {Gem} gem The gem to select
+ */
+Game.selectGem = function(gem) {
+ Game.deselectGem();
+ if (Game.gem == null || gem.id !== Game.gem.id) {
+ gem.style.backgroundImage += ', url("./images/sprites/gemSelected.gif")';
+ Game.gem = gem;
+ }
+};
+
+/**
+ * Deselects the game's selected gem
+ */
+Game.deselectGem = function() {
+ if (Game.gem != null) {
+ Game.gem.style.backgroundImage = 'url("./images/sprites/' + Game.gem.value() + '.png")';
+ Game.gem = null;
+ }
+};
+
+/**
+ * Swaps two gems
+ * @param {Gem} source The first gem to swap
+ * @param {Gem} dest The second gem to swap with the first one
+ * @param {bool} check Shall we look for a streak with the swapped gems ?
+ */
+Game.swapGems = function(source, dest, check) {
+ var sourceX = source.x(),
+ sourceY = source.y(),
+ destX = dest.x(),
+ destY = dest.y();
+
+ // We animate the gems to their new positions
+ if (source.left() != dest.left() || source.top() != dest.top()) {
+ var gems = get('.gem');
+ for (var i = 0; i < gems.length; i++) {
+ gems[i].removeEventListener('click', Game.onGemClick, false); // We prevent the player from clicking on the gems
+ };
+
+ if (source.left() != dest.left()) {
+ source.animate('left', source.left(), dest.left(), 9, check ? Game.checkStreak : null);
+ dest.animate('left', dest.left(), source.left(), 9, check ? Game.checkStreak : null);
+ }else if (source.top() != dest.top()) {
+ source.animate('top', source.top(), dest.top(), 9, check ? Game.checkStreak : null);
+ dest.animate('top', dest.top(), source.top(), 9, check ? Game.checkStreak : null);
+ }
+
+ // We swap the x and y properties
+ source.x(destX);
+ source.y(destY);
+ dest.x(sourceX);
+ dest.y(sourceY);
+ }
+};
+
+/**
+ * Pauses the game
+ */
+Game.pause = function() {
+ if (Game.moving) {
+ return;
+ }
+ var pause, items = get('.item');
+
+ /**
+ * Resumes the game
+ */
+ Game.resume = function() {
+ for (var i = 0; i < items.length; i++) {
+ items[i].style.visibility = 'visible';
+ };
+ remove(pause);
+ Game.paused = false;
+ Game.checkGameOver();
+ };
+
+ // We create a popup
+ pause = new Popup({
+ type: 'html',
+ content: '
Game Paused
',
+ buttons: [
+ {
+ text: 'Resume',
+ callback: Game.resume
+ }
+ ]
+ });
+
+ // We hide all the gems to avoid cheating
+ for (var i = 0; i < items.length; i++) {
+ items[i].style.visibility = 'hidden';
+ };
+ Game.removeHint();
+ pause.show();
+ Game.paused = true;
+};
+
+window.onload = Game.init();
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/score.js b/Games/Bejeweled/js/game/score.js
new file mode 100644
index 0000000000..253b8fa167
--- /dev/null
+++ b/Games/Bejeweled/js/game/score.js
@@ -0,0 +1,66 @@
+// Score related functions
+
+/**
+ * Updates the player's score after a match
+ * @param {Number} destroyedGems The number of gems that were destroyed
+ */
+Game.updateScore = function(destroyedGems) {
+ var perGem = 100 + (Game.combo == undefined ? 0 : (50 * Game.combo)), // Each combo makes the gems worth 50 points more
+ gain = destroyedGems * perGem,
+ gaugeSize = 0,
+ gainSpan = document.createElement('span'),
+ yOrigin = 215,
+ yShift = 5;
+
+ // For a streak bigger than 3, the player gets a bonus
+ if (destroyedGems > 3) {
+ for (var i = 0; i < destroyedGems - 3; i++) {
+ gain += 100 * (i+1);
+ };
+ }
+
+ Game.score.current += gain;
+
+ // If there is already a gain displayed, we sum the gains
+ var existingGain = get('.score_gain');
+ if (existingGain != null) {
+ if (existingGain.length == undefined) {
+ existingGain = [existingGain];
+ }
+ for (var i = 0; i < existingGain.length; i++) {
+ gain += parseInt(existingGain[i].innerHTML.substr(1));
+ get('#player_info').removeChild(existingGain[i]);
+ };
+ }
+
+ // We update the UI
+ gainSpan.className ='score_gain';
+ gainSpan.innerHTML = '+' + gain;
+ gainSpan.style.top = yOrigin + 'px';
+ get('#player_info').insertBefore(gainSpan, get('#total_score'));
+
+ // The gain animation
+ var gainMove = setInterval(function() {
+ var y = parseInt(gainSpan.style.top.substring(0, gainSpan.style.top.indexOf('px')));
+ if (y >= yOrigin + 35) {
+ clearInterval(gainMove);
+ if (gainSpan.parentNode) {
+ get('#player_info').removeChild(gainSpan);
+ }
+ return;
+ }
+ gainSpan.style.top = (y+yShift) + 'px';
+ }, 60);
+
+ get('#current_score').innerHTML = Game.score.current;
+};
+
+/**
+ * Resets the score between the levels
+ */
+Game.resetScore = function() {
+ Game.score.current = 0;
+ Game.score.goal *= 1.5;
+ get('#current_score').innerHTML = Game.score.current;
+ get('#goal_score').innerHTML = Game.score.goal;
+}
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/streak.js b/Games/Bejeweled/js/game/streak.js
new file mode 100644
index 0000000000..8d6804aa26
--- /dev/null
+++ b/Games/Bejeweled/js/game/streak.js
@@ -0,0 +1,130 @@
+// Gem streaks related functions (combo checking, etc.)
+
+/**
+ * Looks for the presence and removes a streak around an gem
+ * @param {Gem} gem The gem which neighbours will be checked for a streak
+ */
+Game.checkStreak = function(gem) {
+ var gems = get('.gem'),
+ streak = gem.getStreak(); // We look for a streak from the gem
+
+ for (var i = 0; i < gems.length; i++) {
+ gems[i].removeEventListener('click', Game.onGemClick, false); // We remove the mouse event listeners
+ };
+
+ if (Object.getLength(streak) > 0) {
+ // We calculate the number of combos
+ if (Game.gem == null) {
+ Game.combo = (Game.combo == undefined ? 1 : Game.combo + 1);
+ }
+
+ gem.inStreak = true;
+ Game.removeStreak(streak);
+ }else if (Game.gem != null && Game.gem.id !== gem.id && !Game.gem.inStreak) { // If there is a selected gem, and it is not in a streak, we will have to reverse the swap
+ Game.swapGems(gem, Game.gem, false); // We re-swap the gems to their respective original positions
+ Game.deselectGem();
+
+ for (var i = 0; i < gems.length; i++) {
+ gems[i].addEventListener('click', Game.onGemClick, false); // We add the mouse event listener
+ };
+ }
+};
+
+/**
+ * Removes all the gems that form a streak (column or row, or both)
+ * @param {Array} gemsToRemove An array containing the gems that are in a streak
+ */
+Game.removeStreak = function(gemsToRemove) {
+ Game.removeHint(); // We delete the gem hint
+ var totalGems = 0, nbGems = 0, fallAfter = false;
+
+ for (var column in gemsToRemove) {
+ for (var i = 0; i < gemsToRemove[column].length; i++) {
+ totalGems++;
+ };
+ };
+
+ for (var column in gemsToRemove) {
+ for (var i = 0; i < gemsToRemove[column].length; i++) {
+ nbGems++;
+ if (nbGems == totalGems) {
+ fallAfter = true; // We make the gems fall after the last one was destroyed
+ }
+ gemsToRemove[column][i].destroy(gemsToRemove, fallAfter);
+ };
+ };
+
+ Game.updateScore(totalGems);
+};
+
+/**
+ * Triggers when a streak is destroyed: starts the generation of new gems
+ * After that, makes the columns fall one by one
+ * @param {Array} streak An array containing the gems that are in a streak
+ */
+Game.onStreakRemoved = function(streak) { // We continue after the streak disappeared
+ var firstYToFall = Game.GRID_SIZE, // The Y of the first item that will fall
+ newGems = null,
+ currentItem = null,
+ fallHeight = 0,
+ fallStarted = false,
+ skip = false;
+
+ // We run through the gem columns
+ for (var column in streak) {
+ for (var i = Game.GRID_SIZE - 1; i >= 0; i--) {
+ currentItem = get('#tile' + i + '_' + column);
+ if (currentItem != null && currentItem.timer != undefined) { // If there is an item from another streak that is still animated, we pass this column
+ skip = true;
+ break;
+ }
+ };
+ if (skip) { // We will parse another column
+ skip = false;
+ continue;
+ }
+
+ firstYToFall = Game.GRID_SIZE;
+ for (i = Game.GRID_SIZE - 1; i >= 0; i--) { // We run through the column from bottom to top
+ currentItem = get('#tile' + i + '_' + column);
+ if (currentItem == null) { // Once we found a gem that was destroyed
+ for (var j = i - 1; j >= 0; j--) { // We run through the gems on top of it
+ currentItem = get('#tile' + j + '_' + column);
+ if (currentItem != null) { // Once we found a valid gem on top of the destroyed gems
+ firstYToFall = j; // It is the first that will fall on this column
+ break;
+ }
+ };
+ break;
+ }
+ };
+ if (firstYToFall >= 7) { // If there is no "hole" in the grid
+ firstYToFall = -1; // It means there is a hole from the top, the first gem to fall is on top the grid
+ }
+ Game.generateGems(column); // We generate the new gems
+ get('#tile' + firstYToFall + '_' + column).fallStreak(); // We make the first gem fall (the others will follow)
+ };
+ Game.deselectGem();
+};
+var check = true;
+/**
+ * Generates random gems above the grid after a streak disappeared
+ * @param {Array} streak An array containing the gems that are in a streak
+ * @return {Array} An array of the new generated gems
+ */
+Game.generateGems = function(x) {
+ var quantity = 0, y, value;
+ for (var i = Game.GRID_SIZE - 1; i >= 0; i--) {
+ currentGem = get('#tile' + i + '_' + x);
+ if (currentGem == null) {
+ quantity++;
+
+ value = parseInt(Math.random() * Game.gemRange);
+ if (check) value = 0;
+ y = -1 * quantity;
+ gem = new Game.Gem(parseInt(x), y, value);
+ grid.appendChild(gem);
+ }
+ };
+ check = false
+};
\ No newline at end of file
diff --git a/Games/Bejeweled/js/game/timer.js b/Games/Bejeweled/js/game/timer.js
new file mode 100644
index 0000000000..8fdce287a5
--- /dev/null
+++ b/Games/Bejeweled/js/game/timer.js
@@ -0,0 +1,33 @@
+/**
+ * Initializes the level timer
+ */
+Game.initTimer = function() {
+ var minutes = 3; // The timer is initialized at 3 minutes
+ Game.timer = minutes * 60 * 1000;
+ get('#current_gauge').style.height = '100%';
+
+ /**
+ * Updates the level timer
+ */
+ Game.updateTimer = function() {
+ Game.timer -= 50;
+
+ // Every second
+ if (Game.timer % 1000 == 0) {
+ get("#current_gauge").style.height = (Game.timer * 100 / (minutes * 60 * 1000)) + '%';
+ }
+
+ if (Game.timer <= 0) {
+ Game.timesUp();
+ }
+ };
+};
+Game.resetTimer = Game.initTimer;
+
+
+/**
+ * The player loses if the timer is finished
+ */
+Game.timesUp = function() {
+ Popup.confirm('Temps écoulé ! Voulez-vous recommencer ?', null, Game.restart);
+}
\ No newline at end of file
diff --git a/Games/Bejeweled/js/navigation.js b/Games/Bejeweled/js/navigation.js
new file mode 100644
index 0000000000..02677093fe
--- /dev/null
+++ b/Games/Bejeweled/js/navigation.js
@@ -0,0 +1,61 @@
+var Site = {}; // The Site namespace
+
+Site.initPage = function() {
+ var links = get('nav a');
+ for (var i = 0; i < links.length; i++) {
+ links[i].onclick = Site.onMenuClick;
+ };
+ Site.changePageFromHash();
+};
+
+/**
+ * Triggers on a click on a menu button
+ */
+Site.onMenuClick = function(event) {
+ // We set the clicked button as the current menu
+ var target = event.target || event.srcElement,
+ hash = window.location.hash;
+
+ Game.pauseHint(); // We pause the hint timer
+
+ if (target.getAttribute('href') == '#game')
+ window.location.hash = 'game';
+ else if (target.getAttribute('href') == '#rules')
+ window.location.hash = 'rules';
+ else
+ window.location.hash = '';
+
+ if (window.location.hash != hash)
+ Site.changePageFromHash();
+};
+
+/**
+ * Changes the site page from the hash given in url
+ */
+Site.changePageFromHash = function () {
+ var links = get('nav a');
+
+ get("#site_content").style.display = window.location.hash === '' ? 'block' : 'none';
+ get("#game_content").style.display = window.location.hash === '#game' ? 'block' : 'none';
+ get("#rules_content").style.display = window.location.hash === '#rules' ? 'block' : 'none';
+
+ if (window.location.hash === '#game') {
+ if (Game.paused == undefined) { // If the game hasn't been initialized yet, we do so
+ Game.init();
+ }else{
+ Game.resumeHint();
+ }
+ }
+
+ for (var i = 0; i < links.length; i++) {
+ links[i].setAttribute('class', '');
+ };
+
+ if (window.location.hash === '')
+ links[0].setAttribute('class', 'current_menu');
+ else
+ get('#bt_' + window.location.hash.substr(1)).setAttribute('class', 'current_menu');
+};
+
+// We initialize the menu buttons listeners
+window.onload = Site.initPage();
\ No newline at end of file
diff --git a/Games/Bejeweled/js/util.js b/Games/Bejeweled/js/util.js
new file mode 100644
index 0000000000..6b45eca890
--- /dev/null
+++ b/Games/Bejeweled/js/util.js
@@ -0,0 +1,46 @@
+/**
+ * Returns a DOM object or an array of DOM objects, depending on the argument (id, class, or tag name)
+ */
+function get(id) {
+ var elem = document.querySelectorAll(id);
+ if (elem.length < 2)
+ return elem[0] || document.querySelector(id);
+ return elem;
+}
+
+/**
+ * Removes an element from the page
+ */
+function remove (elem) {
+ if (elem.parentNode) {
+ elem.parentNode.removeChild(elem);
+ }
+}
+
+/**
+ * Returns the length of an object (associative array)
+ */
+Object.getLength = function(obj) {
+ var length = 0;
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ length++;
+ }
+ }
+ return length;
+};
+
+/**
+ * Checks if two arrays (one dimension) are equals
+ */
+Array.equals = function(arr1, arr2) {
+ if (!arr1 || !arr2 || arr1.length != arr2.length) {
+ return false;
+ }
+ for (var i = 0; i < arr1.length; i++) {
+ if (arr1[i] !== arr2[i]) {
+ return false;
+ }
+ };
+ return true;
+};
\ No newline at end of file
diff --git a/README.md b/README.md
index ad93e8faa9..d87168b240 100644
--- a/README.md
+++ b/README.md
@@ -1688,6 +1688,7 @@ This repository also provides one such platforms where contributers come over an
| [Airhockey_game](https://github.com/kunjgit/GameZone/tree/main/Games/Airhockey_game)|
| [Hole_And_Mole_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Hole_And_Mole_Game)|
|[Animal_Name_Guessing](https://github.com/kunjgit/GameZone/tree/main/Games/Animal_Name_Guessing)|
+|[Bejeweled](https://github.com/kunjgit/GameZone/tree/main/Games/Bejeweled)|
diff --git a/assets/images/Bejeweled.png b/assets/images/Bejeweled.png
new file mode 100644
index 0000000000..a07d103421
Binary files /dev/null and b/assets/images/Bejeweled.png differ