schooluk-2048 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/favicon.ico +0 -0
- package/index.html +126 -0
- package/js/animframe_polyfill.js +28 -0
- package/js/application.js +4 -0
- package/js/bind_polyfill.js +9 -0
- package/js/classlist_polyfill.js +71 -0
- package/js/game_manager.js +272 -0
- package/js/grid.js +117 -0
- package/js/html_actuator.js +139 -0
- package/js/keyboard_input_manager.js +144 -0
- package/js/local_storage_manager.js +63 -0
- package/js/tile.js +27 -0
- package/meta/apple-touch-icon.png +0 -0
- package/meta/apple-touch-startup-image-640x1096.png +0 -0
- package/meta/apple-touch-startup-image-640x920.png +0 -0
- package/package.json +13 -0
- package/style/fonts/ClearSans-Bold-webfont.eot +0 -0
- package/style/fonts/ClearSans-Bold-webfont.svg +640 -0
- package/style/fonts/ClearSans-Bold-webfont.woff +0 -0
- package/style/fonts/ClearSans-Bold-webfontd41d.eot +0 -0
- package/style/fonts/ClearSans-Light-webfont.eot +0 -0
- package/style/fonts/ClearSans-Light-webfont.svg +670 -0
- package/style/fonts/ClearSans-Light-webfont.woff +0 -0
- package/style/fonts/ClearSans-Light-webfontd41d.eot +0 -0
- package/style/fonts/ClearSans-Regular-webfont.eot +0 -0
- package/style/fonts/ClearSans-Regular-webfont.svg +669 -0
- package/style/fonts/ClearSans-Regular-webfont.woff +0 -0
- package/style/fonts/ClearSans-Regular-webfontd41d.eot +0 -0
- package/style/fonts/clear-sans.css +30 -0
- package/style/main.css +758 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
function HTMLActuator() {
|
|
2
|
+
this.tileContainer = document.querySelector(".tile-container");
|
|
3
|
+
this.scoreContainer = document.querySelector(".score-container");
|
|
4
|
+
this.bestContainer = document.querySelector(".best-container");
|
|
5
|
+
this.messageContainer = document.querySelector(".game-message");
|
|
6
|
+
|
|
7
|
+
this.score = 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
HTMLActuator.prototype.actuate = function (grid, metadata) {
|
|
11
|
+
var self = this;
|
|
12
|
+
|
|
13
|
+
window.requestAnimationFrame(function () {
|
|
14
|
+
self.clearContainer(self.tileContainer);
|
|
15
|
+
|
|
16
|
+
grid.cells.forEach(function (column) {
|
|
17
|
+
column.forEach(function (cell) {
|
|
18
|
+
if (cell) {
|
|
19
|
+
self.addTile(cell);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
self.updateScore(metadata.score);
|
|
25
|
+
self.updateBestScore(metadata.bestScore);
|
|
26
|
+
|
|
27
|
+
if (metadata.terminated) {
|
|
28
|
+
if (metadata.over) {
|
|
29
|
+
self.message(false); // You lose
|
|
30
|
+
} else if (metadata.won) {
|
|
31
|
+
self.message(true); // You win!
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Continues the game (both restart and keep playing)
|
|
39
|
+
HTMLActuator.prototype.continueGame = function () {
|
|
40
|
+
this.clearMessage();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
HTMLActuator.prototype.clearContainer = function (container) {
|
|
44
|
+
while (container.firstChild) {
|
|
45
|
+
container.removeChild(container.firstChild);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
HTMLActuator.prototype.addTile = function (tile) {
|
|
50
|
+
var self = this;
|
|
51
|
+
|
|
52
|
+
var wrapper = document.createElement("div");
|
|
53
|
+
var inner = document.createElement("div");
|
|
54
|
+
var position = tile.previousPosition || { x: tile.x, y: tile.y };
|
|
55
|
+
var positionClass = this.positionClass(position);
|
|
56
|
+
|
|
57
|
+
// We can't use classlist because it somehow glitches when replacing classes
|
|
58
|
+
var classes = ["tile", "tile-" + tile.value, positionClass];
|
|
59
|
+
|
|
60
|
+
if (tile.value > 2048) classes.push("tile-super");
|
|
61
|
+
|
|
62
|
+
this.applyClasses(wrapper, classes);
|
|
63
|
+
|
|
64
|
+
inner.classList.add("tile-inner");
|
|
65
|
+
inner.textContent = tile.value;
|
|
66
|
+
|
|
67
|
+
if (tile.previousPosition) {
|
|
68
|
+
// Make sure that the tile gets rendered in the previous position first
|
|
69
|
+
window.requestAnimationFrame(function () {
|
|
70
|
+
classes[2] = self.positionClass({ x: tile.x, y: tile.y });
|
|
71
|
+
self.applyClasses(wrapper, classes); // Update the position
|
|
72
|
+
});
|
|
73
|
+
} else if (tile.mergedFrom) {
|
|
74
|
+
classes.push("tile-merged");
|
|
75
|
+
this.applyClasses(wrapper, classes);
|
|
76
|
+
|
|
77
|
+
// Render the tiles that merged
|
|
78
|
+
tile.mergedFrom.forEach(function (merged) {
|
|
79
|
+
self.addTile(merged);
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
classes.push("tile-new");
|
|
83
|
+
this.applyClasses(wrapper, classes);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add the inner part of the tile to the wrapper
|
|
87
|
+
wrapper.appendChild(inner);
|
|
88
|
+
|
|
89
|
+
// Put the tile on the board
|
|
90
|
+
this.tileContainer.appendChild(wrapper);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
HTMLActuator.prototype.applyClasses = function (element, classes) {
|
|
94
|
+
element.setAttribute("class", classes.join(" "));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
HTMLActuator.prototype.normalizePosition = function (position) {
|
|
98
|
+
return { x: position.x + 1, y: position.y + 1 };
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
HTMLActuator.prototype.positionClass = function (position) {
|
|
102
|
+
position = this.normalizePosition(position);
|
|
103
|
+
return "tile-position-" + position.x + "-" + position.y;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
HTMLActuator.prototype.updateScore = function (score) {
|
|
107
|
+
this.clearContainer(this.scoreContainer);
|
|
108
|
+
|
|
109
|
+
var difference = score - this.score;
|
|
110
|
+
this.score = score;
|
|
111
|
+
|
|
112
|
+
this.scoreContainer.textContent = this.score;
|
|
113
|
+
|
|
114
|
+
if (difference > 0) {
|
|
115
|
+
var addition = document.createElement("div");
|
|
116
|
+
addition.classList.add("score-addition");
|
|
117
|
+
addition.textContent = "+" + difference;
|
|
118
|
+
|
|
119
|
+
this.scoreContainer.appendChild(addition);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
HTMLActuator.prototype.updateBestScore = function (bestScore) {
|
|
124
|
+
this.bestContainer.textContent = bestScore;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
HTMLActuator.prototype.message = function (won) {
|
|
128
|
+
var type = won ? "game-won" : "game-over";
|
|
129
|
+
var message = won ? "You win!" : "Game over!";
|
|
130
|
+
|
|
131
|
+
this.messageContainer.classList.add(type);
|
|
132
|
+
this.messageContainer.getElementsByTagName("p")[0].textContent = message;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
HTMLActuator.prototype.clearMessage = function () {
|
|
136
|
+
// IE only takes one value to remove at a time.
|
|
137
|
+
this.messageContainer.classList.remove("game-won");
|
|
138
|
+
this.messageContainer.classList.remove("game-over");
|
|
139
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
function KeyboardInputManager() {
|
|
2
|
+
this.events = {};
|
|
3
|
+
|
|
4
|
+
if (window.navigator.msPointerEnabled) {
|
|
5
|
+
//Internet Explorer 10 style
|
|
6
|
+
this.eventTouchstart = "MSPointerDown";
|
|
7
|
+
this.eventTouchmove = "MSPointerMove";
|
|
8
|
+
this.eventTouchend = "MSPointerUp";
|
|
9
|
+
} else {
|
|
10
|
+
this.eventTouchstart = "touchstart";
|
|
11
|
+
this.eventTouchmove = "touchmove";
|
|
12
|
+
this.eventTouchend = "touchend";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
this.listen();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
KeyboardInputManager.prototype.on = function (event, callback) {
|
|
19
|
+
if (!this.events[event]) {
|
|
20
|
+
this.events[event] = [];
|
|
21
|
+
}
|
|
22
|
+
this.events[event].push(callback);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
KeyboardInputManager.prototype.emit = function (event, data) {
|
|
26
|
+
var callbacks = this.events[event];
|
|
27
|
+
if (callbacks) {
|
|
28
|
+
callbacks.forEach(function (callback) {
|
|
29
|
+
callback(data);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
KeyboardInputManager.prototype.listen = function () {
|
|
35
|
+
var self = this;
|
|
36
|
+
|
|
37
|
+
var map = {
|
|
38
|
+
38: 0, // Up
|
|
39
|
+
39: 1, // Right
|
|
40
|
+
40: 2, // Down
|
|
41
|
+
37: 3, // Left
|
|
42
|
+
75: 0, // Vim up
|
|
43
|
+
76: 1, // Vim right
|
|
44
|
+
74: 2, // Vim down
|
|
45
|
+
72: 3, // Vim left
|
|
46
|
+
87: 0, // W
|
|
47
|
+
68: 1, // D
|
|
48
|
+
83: 2, // S
|
|
49
|
+
65: 3 // A
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Respond to direction keys
|
|
53
|
+
document.addEventListener("keydown", function (event) {
|
|
54
|
+
var modifiers = event.altKey || event.ctrlKey || event.metaKey ||
|
|
55
|
+
event.shiftKey;
|
|
56
|
+
var mapped = map[event.which];
|
|
57
|
+
|
|
58
|
+
if (!modifiers) {
|
|
59
|
+
if (mapped !== undefined) {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
self.emit("move", mapped);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// R key restarts the game
|
|
66
|
+
if (!modifiers && event.which === 82) {
|
|
67
|
+
self.restart.call(self, event);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Respond to button presses
|
|
72
|
+
this.bindButtonPress(".retry-button", this.restart);
|
|
73
|
+
this.bindButtonPress(".restart-button", this.restart);
|
|
74
|
+
this.bindButtonPress(".keep-playing-button", this.keepPlaying);
|
|
75
|
+
|
|
76
|
+
// Respond to swipe events
|
|
77
|
+
var touchStartClientX, touchStartClientY;
|
|
78
|
+
var gameContainer = document.getElementsByClassName("game-container")[0];
|
|
79
|
+
|
|
80
|
+
gameContainer.addEventListener(this.eventTouchstart, function (event) {
|
|
81
|
+
if ((!window.navigator.msPointerEnabled && event.touches.length > 1) ||
|
|
82
|
+
event.targetTouches.length > 1) {
|
|
83
|
+
return; // Ignore if touching with more than 1 finger
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (window.navigator.msPointerEnabled) {
|
|
87
|
+
touchStartClientX = event.pageX;
|
|
88
|
+
touchStartClientY = event.pageY;
|
|
89
|
+
} else {
|
|
90
|
+
touchStartClientX = event.touches[0].clientX;
|
|
91
|
+
touchStartClientY = event.touches[0].clientY;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
gameContainer.addEventListener(this.eventTouchmove, function (event) {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
gameContainer.addEventListener(this.eventTouchend, function (event) {
|
|
102
|
+
if ((!window.navigator.msPointerEnabled && event.touches.length > 0) ||
|
|
103
|
+
event.targetTouches.length > 0) {
|
|
104
|
+
return; // Ignore if still touching with one or more fingers
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
var touchEndClientX, touchEndClientY;
|
|
108
|
+
|
|
109
|
+
if (window.navigator.msPointerEnabled) {
|
|
110
|
+
touchEndClientX = event.pageX;
|
|
111
|
+
touchEndClientY = event.pageY;
|
|
112
|
+
} else {
|
|
113
|
+
touchEndClientX = event.changedTouches[0].clientX;
|
|
114
|
+
touchEndClientY = event.changedTouches[0].clientY;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
var dx = touchEndClientX - touchStartClientX;
|
|
118
|
+
var absDx = Math.abs(dx);
|
|
119
|
+
|
|
120
|
+
var dy = touchEndClientY - touchStartClientY;
|
|
121
|
+
var absDy = Math.abs(dy);
|
|
122
|
+
|
|
123
|
+
if (Math.max(absDx, absDy) > 10) {
|
|
124
|
+
// (right : left) : (down : up)
|
|
125
|
+
self.emit("move", absDx > absDy ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
KeyboardInputManager.prototype.restart = function (event) {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
this.emit("restart");
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
KeyboardInputManager.prototype.keepPlaying = function (event) {
|
|
136
|
+
event.preventDefault();
|
|
137
|
+
this.emit("keepPlaying");
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
KeyboardInputManager.prototype.bindButtonPress = function (selector, fn) {
|
|
141
|
+
var button = document.querySelector(selector);
|
|
142
|
+
button.addEventListener("click", fn.bind(this));
|
|
143
|
+
button.addEventListener(this.eventTouchend, fn.bind(this));
|
|
144
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
window.fakeStorage = {
|
|
2
|
+
_data: {},
|
|
3
|
+
|
|
4
|
+
setItem: function (id, val) {
|
|
5
|
+
return this._data[id] = String(val);
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
getItem: function (id) {
|
|
9
|
+
return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
removeItem: function (id) {
|
|
13
|
+
return delete this._data[id];
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
clear: function () {
|
|
17
|
+
return this._data = {};
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function LocalStorageManager() {
|
|
22
|
+
this.bestScoreKey = "bestScore";
|
|
23
|
+
this.gameStateKey = "gameState";
|
|
24
|
+
|
|
25
|
+
var supported = this.localStorageSupported();
|
|
26
|
+
this.storage = supported ? window.localStorage : window.fakeStorage;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
LocalStorageManager.prototype.localStorageSupported = function () {
|
|
30
|
+
var testKey = "test";
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
var storage = window.localStorage;
|
|
34
|
+
storage.setItem(testKey, "1");
|
|
35
|
+
storage.removeItem(testKey);
|
|
36
|
+
return true;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Best score getters/setters
|
|
43
|
+
LocalStorageManager.prototype.getBestScore = function () {
|
|
44
|
+
return this.storage.getItem(this.bestScoreKey) || 0;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
LocalStorageManager.prototype.setBestScore = function (score) {
|
|
48
|
+
this.storage.setItem(this.bestScoreKey, score);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Game state getters/setters and clearing
|
|
52
|
+
LocalStorageManager.prototype.getGameState = function () {
|
|
53
|
+
var stateJSON = this.storage.getItem(this.gameStateKey);
|
|
54
|
+
return stateJSON ? JSON.parse(stateJSON) : null;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
LocalStorageManager.prototype.setGameState = function (gameState) {
|
|
58
|
+
this.storage.setItem(this.gameStateKey, JSON.stringify(gameState));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
LocalStorageManager.prototype.clearGameState = function () {
|
|
62
|
+
this.storage.removeItem(this.gameStateKey);
|
|
63
|
+
};
|
package/js/tile.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
function Tile(position, value) {
|
|
2
|
+
this.x = position.x;
|
|
3
|
+
this.y = position.y;
|
|
4
|
+
this.value = value || 2;
|
|
5
|
+
|
|
6
|
+
this.previousPosition = null;
|
|
7
|
+
this.mergedFrom = null; // Tracks tiles that merged together
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Tile.prototype.savePosition = function () {
|
|
11
|
+
this.previousPosition = { x: this.x, y: this.y };
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
Tile.prototype.updatePosition = function (position) {
|
|
15
|
+
this.x = position.x;
|
|
16
|
+
this.y = position.y;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
Tile.prototype.serialize = function () {
|
|
20
|
+
return {
|
|
21
|
+
position: {
|
|
22
|
+
x: this.x,
|
|
23
|
+
y: this.y
|
|
24
|
+
},
|
|
25
|
+
value: this.value
|
|
26
|
+
};
|
|
27
|
+
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
Binary file
|