├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── Rakefile
├── css
├── gauntlet.css
└── normalize.css
├── images
├── backgrounds.png
├── booting.gif
├── entities.png
├── key.png
├── logo.jpg
├── mute.png
├── potion.png
└── splash.jpg
├── index.html
├── js
├── game.js
├── game
│ ├── base.js
│ ├── dom.js
│ ├── game.js
│ ├── key.js
│ ├── math.js
│ ├── pubsub.js
│ └── vendor
│ │ ├── animator.js
│ │ ├── audio-fx.js
│ │ ├── sizzle.js
│ │ ├── state-machine.js
│ │ └── stats.js
├── gauntlet.js
└── vendor.js
├── levels
├── level1.png
├── level10.png
├── level2.png
├── level3.png
├── level4.png
├── level5.png
├── level6.png
├── level7.png
├── level8.png
├── level9.png
├── reference.png
├── testlevel.png
├── trainer1.png
├── trainer2.png
├── trainer3.png
├── trainer4.png
├── trainer5.png
├── trainer6.png
└── trainer7.png
└── sounds
├── collectfood.mp3
├── collectfood.ogg
├── collectgold.mp3
├── collectgold.ogg
├── collectkey.mp3
├── collectkey.ogg
├── collectpotion.mp3
├── collectpotion.ogg
├── exitlevel.mp3
├── exitlevel.ogg
├── femalepain1.mp3
├── femalepain1.ogg
├── femalepain2.mp3
├── femalepain2.ogg
├── fireelf.mp3
├── fireelf.ogg
├── firevalkyrie.mp3
├── firevalkyrie.ogg
├── firewarrior.mp3
├── firewarrior.ogg
├── firewizard.mp3
├── firewizard.ogg
├── gameover.mp3
├── gameover.ogg
├── generatordeath.mp3
├── generatordeath.ogg
├── highscore.mp3
├── highscore.ogg
├── malepain1.mp3
├── malepain1.ogg
├── malepain2.mp3
├── malepain2.ogg
├── monsterdeath1.mp3
├── monsterdeath1.ogg
├── monsterdeath2.mp3
├── monsterdeath2.ogg
├── monsterdeath3.mp3
├── monsterdeath3.ogg
├── music.bloodyhalo.mp3
├── music.bloodyhalo.ogg
├── music.citrinitas.mp3
├── music.citrinitas.ogg
├── music.fleshandsteel.mp3
├── music.fleshandsteel.ogg
├── music.lostcorridors.mp3
├── music.lostcorridors.ogg
├── music.mountingassault.mp3
├── music.mountingassault.ogg
├── music.phantomdrone.mp3
├── music.phantomdrone.ogg
├── music.thebeginning.mp3
├── music.thebeginning.ogg
├── music.warbringer.mp3
├── music.warbringer.ogg
├── opendoor.mp3
├── opendoor.ogg
├── victory.mp3
├── victory.ogg
├── weak.mp3
└── weak.ogg
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 |
3 | # for running rake assets:server
4 | group :server do
5 | gem "unified-assets"
6 | gem "rack"
7 | gem "thin"
8 | end
9 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | daemons (1.1.9)
5 | eventmachine (1.0.0)
6 | rack (1.5.2)
7 | thin (1.5.0)
8 | daemons (>= 1.0.9)
9 | eventmachine (>= 0.12.6)
10 | rack (>= 1.0.0)
11 | unified-assets (0.0.1)
12 | rack
13 |
14 | PLATFORMS
15 | ruby
16 |
17 | DEPENDENCIES
18 | rack
19 | thin
20 | unified-assets
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013, 2014, 2015, 2016 Jake Gordon and contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Javascript Gauntlet
2 | ===================
3 |
4 | An HTML5 Gauntlet-style Game
5 |
6 | * [play the game](https://jakesgordon.com/games/gauntlet/)
7 | * read a [blog article](https://jakesgordon.com/writing/javascript-gauntlet/)
8 | * view the [source](https://github.com/jakesgordon/javascript-gauntlet)
9 |
10 | >> NOTE: single player only at this time, multiplayer coming later
11 |
12 | SUPPORTED BROWSERS
13 | ==================
14 |
15 | - Chrome 24+
16 | - Firefox 18+
17 | - IE 9+
18 |
19 | KNOWN ISSUES
20 | ============
21 |
22 | - No support for touch/mobile devices
23 | - Startup (and level transition) can be slow over a slow network connection (duh)
24 |
25 | TODO
26 | ====
27 |
28 | * POLISH - better scoreboard fonts
29 | * POLISH - let doors have corners (and choose more wisely between horz/vert when up against odd walls)
30 | * POLISH - render monsters top down with slight overlap for pseudo-3d
31 |
32 | DEVELOPMENT
33 | ===========
34 |
35 | The game is 100% client side javascript and css. It should run when served up by any web server.
36 |
37 | Any changes to the following files will be reflected immediately on refresh of the browser
38 |
39 | - js/gauntlet.js
40 | - css/gauntlet.css
41 | - images/
42 | - sounds/
43 | - levels/
44 |
45 | However, if you modify the js/game/ or js/vendor/ javascript files, the unified versions need to be regenerated:
46 |
47 | js/vendor.js # the unified 3rd party vendor scripts (sizzle, animator, audio-fx, stats, state-machine)
48 | js/game.js # the unified general purpose game engine
49 |
50 | If you have the Ruby language available, Rake tasks can be used to auto generate these unified files:
51 |
52 | rake assets:create # re-create unified javascript/css asset files on demand
53 | rake assets:server # run a simple rack server that automatically regenerates the unified files when the underlying source is modified
54 |
55 | Attributions
56 | =============
57 |
58 | All music is licensed, royalty-free, from [Lucky Lion Studios](http://luckylionstudios.com/) for this project only. If you re-use this
59 | project for your own purposes you must license your own music please.
60 |
61 | All sound effects are licensed, royalty-free from [Premium Beat](http://www.premiumbeat.com/sfx) for this project only. If you re-use this
62 | project for your own purposes you must license your own sound effects please.
63 |
64 | Background tilesets (walls, floors, doors) are provided by
65 |
66 | - [Ricardo Chirino](ricardochirino.com)
67 | - [Open Game Art](http://opengameart.org/content/gauntlet-like-tiles)
68 |
69 | Entity sprites (players, monsters, treasure, etc) are almost certainly ripped from an old (s)NES console ?
70 |
71 | - [Open Game Art](http://opengameart.org/forumtopic/request-for-tileset-spritesheet-similar-to-gauntlet-ii)
72 |
73 | License
74 | =======
75 |
76 | [MIT](http://en.wikipedia.org/wiki/MIT_License) license.
77 |
78 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/setup'
2 | require 'unified_assets/tasks'
3 |
4 | UnifiedAssets::Tasks.new do |t|
5 | t.minify = false
6 | t.assets = {
7 |
8 | "js/vendor.js" => [
9 | 'js/game/vendor/stats.js', # https://github.com/mrdoob/stats.js
10 | 'js/game/vendor/sizzle.js', # http://sizzlejs.com/
11 | 'js/game/vendor/animator.js', # http://berniesumption.com/software/animator/
12 | 'js/game/vendor/audio-fx.js', # https://github.com/jakesgordon/javascript-audio-fx
13 | 'js/game/vendor/state-machine.js', # https://github.com/jakesgordon/javascript-state-machine
14 | ],
15 |
16 | "js/game.js" => [
17 | 'js/game/base.js',
18 | 'js/game/game.js',
19 | 'js/game/pubsub.js',
20 | 'js/game/dom.js',
21 | 'js/game/key.js',
22 | 'js/game/math.js',
23 | ]
24 |
25 | }
26 | end
27 |
28 |
--------------------------------------------------------------------------------
/css/gauntlet.css:
--------------------------------------------------------------------------------
1 | /*******************************/
2 | /* global document body styles */
3 | /*******************************/
4 |
5 | body {
6 | background: black;
7 | color: #CCC;
8 | user-select: none;
9 | }
10 |
11 | /**************************/
12 | /* unique id-based styles */
13 | /**************************/
14 |
15 | #gauntlet { margin: 1em auto; position: relative; background: black; }
16 | #booting { position: absolute; width: 6%; left: 30%; top: 40%; }
17 | #splash { position: absolute; display: none; }
18 | #canvas { display: inline-block; vertical-align: top; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
19 | #help { position: absolute; width: 33%; left: 22%; top: 30%; text-align: center; font-size: 1.5em; font-weight: bold; background-color: rgba(255, 255, 255, 0.75); border: 6px solid black; color: black; padding: 1em; display: inline-block; border-radius: 0.5em; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
20 | #scoreboard { display: inline-block; vertical-align: top; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
21 | #stats { position: absolute; right: -90px; bottom: 0; color: white; }
22 | #instructions { font-style: italic; color: #999; font-size: 9pt; margin-left: 1em; margin-top: 0.5em; }
23 | #instructions b { color: #DDD; }
24 |
25 | #gauntlet.menu #splash { display: inline-block; }
26 | #gauntlet.help #canvas { opacity: 0.5; }
27 |
28 | /**************/
29 | /* scoreboard */
30 | /**************/
31 |
32 | #scoreboard { text-align: center; }
33 |
34 | #scoreboard #logo { width: 100%; }
35 | #scoreboard .level { margin-top: 0px; margin-bottom: 0px; }
36 | #scoreboard hr { width: 50%; margin: 1em auto; border: 1px solid #111; border-top: 0px; border-left: 0px; border-right: 0px; }
37 | #scoreboard .high { margin-bottom: 2em; font-size: 82.5%; opacity: 0.5; }
38 | #scoreboard .high .value { font-weight: bold; }
39 |
40 | #scoreboard .player { height: 6em; border: 2px solid #222; margin: 1em 0.5em; padding: 0.5em 0; opacity: 0.5; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
41 | #scoreboard .player .name { font-weight: bold; }
42 | #scoreboard .player .score { display: none; }
43 | #scoreboard .player .health { display: none; }
44 | #scoreboard .player .treasure { display: none; }
45 | #scoreboard .player .press { color: #999; margin-top: 1em; }
46 | #scoreboard .player .press b { color: white; }
47 | #scoreboard .player .multi { color: #666; font-style: italic; margin-top: 1em; font-size: 82.5%; }
48 |
49 | #scoreboard .player.active { opacity: 1.0; }
50 | #scoreboard .player.active .score { display: inline-block; text-align: right; margin-right: 1em; }
51 | #scoreboard .player.active .health { display: inline-block; text-align: left; margin-left: 1em; }
52 | #scoreboard .player.active .treasure { display: block; text-align: left; margin: 0 0.5em; margin-top: 0.25em; }
53 | #scoreboard .player.active .label { opacity: 0.7; font-size: smaller; }
54 | #scoreboard .player.active .value { opacity: 1.0; font-weight: bold; }
55 |
56 | #scoreboard .player .treasure img.key { margin-right: 2px; width: 1em; height: 1em; }
57 | #scoreboard .player .treasure img.potion { margin-left: 0px; float: right; width: 1em; height: 1em; }
58 |
59 | #scoreboard #warrior { color: #F90503; }
60 | #scoreboard #valkyrie { color: #08B4F0; }
61 | #scoreboard #wizard { color: #F5FC00; }
62 | #scoreboard #elf { color: #00FF03; }
63 |
64 | #scoreboard #warrior .press b { color: #F90503; }
65 | #scoreboard #valkyrie .press b { color: #08B4F0; }
66 | #scoreboard #wizard .press b { color: #F5FC00; }
67 | #scoreboard #elf .press b { color: #00FF03; }
68 |
69 | #scoreboard #warrior.active { border-color: #F90503; }
70 | #scoreboard #valkyrie.active { border-color: #08B4F0; }
71 | #scoreboard #wizard.active { border-color: #F5FC00; }
72 | #scoreboard #elf.active { border-color: #00FF03; }
73 |
74 | #scoreboard #warrior.weak { background-color: #F90503; }
75 | #scoreboard #valkyrie.weak { background-color: #08B4F0; }
76 | #scoreboard #wizard.weak { background-color: #F5FC00; }
77 | #scoreboard #elf.weak { background-color: #00FF03; }
78 |
79 | #scoreboard .high.warrior .value { color: #F90503; }
80 | #scoreboard .high.valkyrie .value { color: #08B4F0; }
81 | #scoreboard .high.wizard .value { color: #F5FC00; }
82 | #scoreboard .high.elf .value { color: #00FF03; }
83 |
84 | #gauntlet #scoreboard .player .press { display: none; }
85 | #gauntlet.menu #scoreboard .player .press { display: block; }
86 | #gauntlet #scoreboard .player .multi { display: block; }
87 | #gauntlet.booting #scoreboard .player .multi { display: none; }
88 | #gauntlet.menu #scoreboard .player .multi { display: none; }
89 | #gauntlet #scoreboard .player.active .multi { display: none; }
90 |
91 | #sound { width: 24px; height: 24px; background: url(../images/mute.png); display: inline-block; cursor: pointer; position: absolute; right: 1em; }
92 | #sound.on { background-position: 0 0; }
93 | #sound.off { background-position: -24px 0; }
94 |
95 | /*****************************/
96 | /* @media query based layout */
97 | /*****************************/
98 |
99 | @media screen and (min-width: 0px) and (min-height: 0px) { #gauntlet { width: 512px; height: 384px; font-size: 7pt; } #canvas { width: 384px; height: 384px; } #splash { width: 384px; height: 384px; } #scoreboard { width: /* 512 - 384 - 8 = */ 120px; height: 384px; } } /* block = 16 */
100 | @media screen and (min-width: 840px) and (min-height: 530px) { #gauntlet { width: 640px; height: 480px; font-size: 9pt; } #canvas { width: 480px; height: 480px; } #splash { width: 480px; height: 480px; } #scoreboard { width: /* 640 - 480 - 8 = */ 152px; height: 480px; } } /* block = 20 */
101 | @media screen and (min-width: 968px) and (min-height: 626px) { #gauntlet { width: 768px; height: 576px; font-size: 11pt; } #canvas { width: 576px; height: 576px; } #splash { width: 576px; height: 576px; } #scoreboard { width: /* 768 - 576 - 8 = */ 184px; height: 576px; } } /* block = 24 */
102 | @media screen and (min-width: 1096px) and (min-height: 722px) { #gauntlet { width: 896px; height: 672px; font-size: 13pt; } #canvas { width: 672px; height: 672px; } #splash { width: 672px; height: 672px; } #scoreboard { width: /* 896 - 672 - 8 = */ 216px; height: 672px; } } /* block = 28 */
103 | @media screen and (min-width: 1224px) and (min-height: 818px) { #gauntlet { width: 1024px; height: 768px; font-size: 15pt; } #canvas { width: 768px; height: 768px; } #splash { width: 768px; height: 768px; } #scoreboard { width: /* 1024 - 768 - 8 = */ 248px; height: 768px; } } /* block = 32 */
104 |
105 |
--------------------------------------------------------------------------------
/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css 2011-07-12T10:51 UTC · http://github.com/necolas/normalize.css */
2 |
3 | /* =============================================================================
4 | HTML5 element display
5 | ========================================================================== */
6 |
7 | /*
8 | * Corrects block display not defined in IE6/7/8/9 & FF3
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | nav,
20 | section {
21 | display: block;
22 | }
23 |
24 | /*
25 | * Corrects inline-block display not defined in IE6/7/8/9 & FF3
26 | * Known limitation: IE6 will not apply style for 'audio[controls]'
27 | */
28 |
29 | audio[controls],
30 | canvas,
31 | video {
32 | display: inline-block;
33 | *display: inline;
34 | *zoom: 1;
35 | }
36 |
37 |
38 | /* =============================================================================
39 | Base
40 | ========================================================================== */
41 |
42 | /*
43 | * 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units
44 | * http://clagnut.com/blog/348/#c790
45 | * 2. Keeps page centred in all browsers regardless of content height
46 | * 3. Removes Android and iOS tap highlight color to prevent entire container being highlighted
47 | * www.yuiblog.com/blog/2010/10/01/quick-tip-customizing-the-mobile-safari-tap-highlight-color/
48 | * 4. Prevents iOS text size adjust after orientation change, without disabling user zoom
49 | * www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/
50 | */
51 |
52 | html {
53 | font-size: 100%; /* 1 */
54 | overflow-y: scroll; /* 2 */
55 | -webkit-tap-highlight-color: rgba(0,0,0,0); /* 3 */
56 | -webkit-text-size-adjust: 100%; /* 4 */
57 | -ms-text-size-adjust: 100%; /* 4 */
58 | }
59 |
60 | /*
61 | * Addresses margins handled incorrectly in IE6/7
62 | */
63 |
64 | body {
65 | margin: 0;
66 | }
67 |
68 | /*
69 | * Addresses font-family inconsistency between 'textarea' and other form elements.
70 | */
71 |
72 | body,
73 | button,
74 | input,
75 | select,
76 | textarea {
77 | font-family: sans-serif;
78 | }
79 |
80 |
81 | /* =============================================================================
82 | Links
83 | ========================================================================== */
84 |
85 | a {
86 | color: #00e;
87 | }
88 |
89 | a:visited {
90 | color: #551a8b;
91 | }
92 |
93 | /*
94 | * Addresses outline displayed oddly in Chrome
95 | */
96 |
97 | a:focus {
98 | outline: thin dotted;
99 | }
100 |
101 | /*
102 | * Improves readability when focused and also mouse hovered in all browsers
103 | * people.opera.com/patrickl/experiments/keyboard/test
104 | */
105 |
106 | a:hover,
107 | a:active {
108 | outline: 0;
109 | }
110 |
111 |
112 | /* =============================================================================
113 | Typography
114 | ========================================================================== */
115 |
116 | /*
117 | * Addresses styling not present in IE7/8/9, S5, Chrome
118 | */
119 |
120 | abbr[title] {
121 | border-bottom: 1px dotted;
122 | }
123 |
124 | /*
125 | * Addresses style set to 'bolder' in FF3/4, S4/5, Chrome
126 | */
127 |
128 | b,
129 | strong {
130 | font-weight: bold;
131 | }
132 |
133 | blockquote {
134 | margin: 1em 40px;
135 | }
136 |
137 | /*
138 | * Addresses styling not present in S5, Chrome
139 | */
140 |
141 | dfn {
142 | font-style: italic;
143 | }
144 |
145 | /*
146 | * Addresses styling not present in IE6/7/8/9
147 | */
148 |
149 | mark {
150 | background: #ff0;
151 | color: #000;
152 | }
153 |
154 | /*
155 | * Corrects font family set oddly in IE6, S5, Chrome
156 | * en.wikipedia.org/wiki/User:Davidgothberg/Test59
157 | */
158 |
159 | pre,
160 | code,
161 | kbd,
162 | samp {
163 | font-family: monospace, monospace;
164 | _font-family: 'courier new', monospace;
165 | font-size: 1em;
166 | }
167 |
168 | /*
169 | * Improves readability of pre-formatted text in all browsers
170 | */
171 |
172 | pre {
173 | white-space: pre;
174 | white-space: pre-wrap;
175 | word-wrap: break-word;
176 | }
177 |
178 | /*
179 | * 1. Addresses CSS quotes not supported in IE6/7
180 | * 2. Addresses quote property not supported in S4
181 | */
182 |
183 | /* 1 */
184 |
185 | q {
186 | quotes: none;
187 | }
188 |
189 | /* 2 */
190 |
191 | q:before,
192 | q:after {
193 | content: '';
194 | content: none;
195 | }
196 |
197 | small {
198 | font-size: 75%;
199 | }
200 |
201 | /*
202 | * Prevents sub and sup affecting line-height in all browsers
203 | * gist.github.com/413930
204 | */
205 |
206 | sub,
207 | sup {
208 | font-size: 75%;
209 | line-height: 0;
210 | position: relative;
211 | vertical-align: baseline;
212 | }
213 |
214 | sup {
215 | top: -0.5em;
216 | }
217 |
218 | sub {
219 | bottom: -0.25em;
220 | }
221 |
222 |
223 | /* =============================================================================
224 | Lists
225 | ========================================================================== */
226 |
227 | ul,
228 | ol {
229 | margin: 1em 0;
230 | padding: 0 0 0 40px;
231 | }
232 |
233 | dd {
234 | margin: 0 0 0 40px;
235 | }
236 |
237 | nav ul,
238 | nav ol {
239 | list-style: none;
240 | }
241 |
242 |
243 | /* =============================================================================
244 | Embedded content
245 | ========================================================================== */
246 |
247 | /*
248 | * 1. Removes border when inside 'a' element in IE6/7/8/9
249 | * 2. Improves image quality when scaled in IE7
250 | * code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/
251 | */
252 |
253 | img {
254 | border: 0; /* 1 */
255 | -ms-interpolation-mode: bicubic; /* 2 */
256 | }
257 |
258 | /*
259 | * Corrects overflow displayed oddly in IE9
260 | */
261 |
262 | svg:not(:root) {
263 | overflow: hidden;
264 | }
265 |
266 |
267 | /* =============================================================================
268 | Figures
269 | ========================================================================== */
270 |
271 | /*
272 | * Addresses margin not present in IE6/7/8/9, S5, O11
273 | */
274 |
275 | figure {
276 | margin: 0;
277 | }
278 |
279 |
280 | /* =============================================================================
281 | Forms
282 | ========================================================================== */
283 |
284 | /*
285 | * Corrects margin displayed oddly in IE6/7
286 | */
287 |
288 | form {
289 | margin: 0;
290 | }
291 |
292 | /*
293 | * Define consistent margin and padding
294 | */
295 |
296 | fieldset {
297 | margin: 0 2px;
298 | padding: 0.35em 0.625em 0.75em;
299 | }
300 |
301 | /*
302 | * 1. Corrects color not being inherited in IE6/7/8/9
303 | * 2. Corrects alignment displayed oddly in IE6/7
304 | */
305 |
306 | legend {
307 | border: 0; /* 1 */
308 | *margin-left: -7px; /* 2 */
309 | }
310 |
311 | /*
312 | * 1. Corrects font size not being inherited in all browsers
313 | * 2. Addresses margins set differently in IE6/7, F3/4, S5, Chrome
314 | * 3. Improves appearance and consistency in all browsers
315 | */
316 |
317 | button,
318 | input,
319 | select,
320 | textarea {
321 | font-size: 100%; /* 1 */
322 | margin: 0; /* 2 */
323 | vertical-align: baseline; /* 3 */
324 | *vertical-align: middle; /* 3 */
325 | }
326 |
327 | /*
328 | * 1. Addresses FF3/4 setting line-height using !important in the UA stylesheet
329 | * 2. Corrects inner spacing displayed oddly in IE6/7
330 | */
331 |
332 | button,
333 | input {
334 | line-height: normal; /* 1 */
335 | *overflow: visible; /* 2 */
336 | }
337 |
338 | /*
339 | * Corrects overlap and whitespace issue for buttons and inputs in IE6/7
340 | * Known issue: reintroduces inner spacing
341 | */
342 |
343 | table button,
344 | table input {
345 | *overflow: auto;
346 | }
347 |
348 | /*
349 | * 1. Improves usability and consistency of cursor style between image-type 'input' and others
350 | * 2. Corrects inability to style clickable 'input' types in iOS
351 | */
352 |
353 | button,
354 | html input[type="button"],
355 | input[type="reset"],
356 | input[type="submit"] {
357 | cursor: pointer; /* 1 */
358 | -webkit-appearance: button; /* 2 */
359 | }
360 |
361 | /*
362 | * Addresses box sizing set to content-box in IE8/9
363 | */
364 |
365 | input[type="checkbox"],
366 | input[type="radio"] {
367 | box-sizing: border-box;
368 | }
369 |
370 | /*
371 | * 1. Addresses appearance set to searchfield in S5, Chrome
372 | * 2. Addresses box sizing set to border-box in S5, Chrome (include -moz to future-proof)
373 | */
374 |
375 | input[type="search"] {
376 | -webkit-appearance: textfield; /* 1 */
377 | -moz-box-sizing: content-box;
378 | -webkit-box-sizing: content-box; /* 2 */
379 | box-sizing: content-box;
380 | }
381 |
382 | /*
383 | * Corrects inner padding displayed oddly in S5, Chrome on OSX
384 | */
385 |
386 | input[type="search"]::-webkit-search-decoration {
387 | -webkit-appearance: none;
388 | }
389 |
390 | /*
391 | * Corrects inner padding and border displayed oddly in FF3/4
392 | * www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/
393 | */
394 |
395 | button::-moz-focus-inner,
396 | input::-moz-focus-inner {
397 | border: 0;
398 | padding: 0;
399 | }
400 |
401 | /*
402 | * 1. Removes default vertical scrollbar in IE6/7/8/9
403 | * 2. Improves readability and alignment in all browsers
404 | */
405 |
406 | textarea {
407 | overflow: auto; /* 1 */
408 | vertical-align: top; /* 2 */
409 | }
410 |
411 |
412 | /* =============================================================================
413 | Tables
414 | ========================================================================== */
415 |
416 | /*
417 | * Remove most spacing between table cells
418 | */
419 |
420 | table {
421 | border-collapse: collapse;
422 | border-spacing: 0;
423 | }
424 |
--------------------------------------------------------------------------------
/images/backgrounds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/backgrounds.png
--------------------------------------------------------------------------------
/images/booting.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/booting.gif
--------------------------------------------------------------------------------
/images/entities.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/entities.png
--------------------------------------------------------------------------------
/images/key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/key.png
--------------------------------------------------------------------------------
/images/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/logo.jpg
--------------------------------------------------------------------------------
/images/mute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/mute.png
--------------------------------------------------------------------------------
/images/potion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/potion.png
--------------------------------------------------------------------------------
/images/splash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/images/splash.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Javascript Gauntlet
9 |
10 |
11 |
12 |
13 |
14 |
15 |

16 |

17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |

29 |
30 |
31 |
32 |
33 |
34 |
High Score: 000000
35 |
36 |
37 |
Warrior
38 |
39 |
40 |
44 |
PRESS 1 TO START
45 |
multiplayer coming soon
46 |
47 |
48 |
49 |
Valkyrie
50 |
51 |
52 |
56 |
PRESS 2 TO START
57 |
multiplayer coming soon
58 |
59 |
60 |
61 |
Wizard
62 |
63 |
64 |
68 |
PRESS 3 TO START
69 |
multiplayer coming soon
70 |
71 |
72 |
73 |
Elf
74 |
75 |
76 |
80 |
PRESS 4 TO START
81 |
multiplayer coming soon
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | ARROW keys to move, HOLD SPACE to fire, ENTER to use potion
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/js/game.js:
--------------------------------------------------------------------------------
1 | //=============================================================================
2 | // feature detection
3 | //=============================================================================
4 | ua = function() {
5 |
6 | var ua = navigator.userAgent.toLowerCase(); // should avoid user agent sniffing... but sometimes you just gotta do what you gotta do
7 | var key = ((ua.indexOf("opera") > -1) ? "opera" : null);
8 | key = key || ((ua.indexOf("firefox") > -1) ? "firefox" : null);
9 | key = key || ((ua.indexOf("chrome") > -1) ? "chrome" : null);
10 | key = key || ((ua.indexOf("safari") > -1) ? "safari" : null);
11 | key = key || ((ua.indexOf("msie") > -1) ? "ie" : null);
12 |
13 | try {
14 | var re = (key == "ie") ? "msie ([\\d\\.]*)" : key + "\\/([\\d\\.]*)"
15 | var matches = ua.match(new RegExp(re, "i"));
16 | var version = matches ? matches[1] : null;
17 | } catch (e) {}
18 |
19 | return {
20 | full: ua,
21 | name: key + (version ? " " + version : ""),
22 | version: version,
23 | major: version ? parseInt(version) : null,
24 | is: {
25 | firefox: (key == "firefox"),
26 | chrome: (key == "chrome"),
27 | safari: (key == "safari"),
28 | opera: (key == "opera"),
29 | ie: (key == "ie")
30 | },
31 | has: {
32 | audio: AudioFX && AudioFX.enabled,
33 | canvas: (document.createElement('canvas').getContext),
34 | touch: ('ontouchstart' in window)
35 | }
36 | }
37 | }();
38 |
39 | //=============================================================================
40 | // type detection
41 | //=============================================================================
42 |
43 | is = {
44 | 'string': function(obj) { return (typeof obj === 'string'); },
45 | 'number': function(obj) { return (typeof obj === 'number'); },
46 | 'bool': function(obj) { return (typeof obj === 'boolean'); },
47 | 'array': function(obj) { return (obj instanceof Array); },
48 | 'undefined': function(obj) { return (typeof obj === 'undefined'); },
49 | 'func': function(obj) { return (typeof obj === 'function'); },
50 | 'null': function(obj) { return (obj === null); },
51 | 'notNull': function(obj) { return (obj !== null); },
52 | 'invalid': function(obj) { return ( is['null'](obj) || is.undefined(obj)); },
53 | 'valid': function(obj) { return (!is['null'](obj) && !is.undefined(obj)); },
54 | 'emptyString': function(obj) { return (is.string(obj) && (obj.length == 0)); },
55 | 'nonEmptyString': function(obj) { return (is.string(obj) && (obj.length > 0)); },
56 | 'emptyArray': function(obj) { return (is.array(obj) && (obj.length == 0)); },
57 | 'nonEmptyArray': function(obj) { return (is.array(obj) && (obj.length > 0)); },
58 | 'document': function(obj) { return (obj === document); },
59 | 'window': function(obj) { return (obj === window); },
60 | 'element': function(obj) { return (obj instanceof HTMLElement); },
61 | 'event': function(obj) { return (obj instanceof Event); },
62 | 'link': function(obj) { return (is.element(obj) && (obj.tagName == 'A')); }
63 | }
64 |
65 | //=============================================================================
66 | // type coersion
67 | //=============================================================================
68 |
69 | to = {
70 | 'bool': function(obj, def) { if (is.valid(obj)) return ((obj == 1) || (obj == true) || (obj == "1") || (obj == "y") || (obj == "Y") || (obj.toString().toLowerCase() == "true") || (obj.toString().toLowerCase() == 'yes')); else return (is.bool(def) ? def : false); },
71 | 'number': function(obj, def) { if (is.valid(obj)) { var x = parseFloat(obj); if (!isNaN(x)) return x; } return (is.number(def) ? def : 0); },
72 | 'string': function(obj, def) { if (is.valid(obj)) return obj.toString(); return (is.string(def) ? def : ''); }
73 | }
74 |
75 | //=============================================================================
76 | //
77 | // Compatibility for older browsers (compatibility: http://kangax.github.com/es5-compat-table/)
78 | //
79 | // Function.bind: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
80 | // Object.create: http://javascript.crockford.com/prototypal.html
81 | // Object.extend: (defacto standard like jquery $.extend or prototype's Object.extend)
82 | // Class.create: create a simple javascript 'class' (a constructor function with a prototype and optional class methods)
83 | //
84 | //=============================================================================
85 |
86 | if (!Function.prototype.bind) {
87 | Function.prototype.bind = function(obj) {
88 | var slice = [].slice,
89 | args = slice.call(arguments, 1),
90 | self = this,
91 | nop = function () {},
92 | bound = function () {
93 | return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
94 | };
95 | nop.prototype = self.prototype;
96 | bound.prototype = new nop();
97 | return bound;
98 | };
99 | }
100 |
101 | if (!Object.create) {
102 | Object.create = function(base) {
103 | function F() {};
104 | F.prototype = base;
105 | return new F();
106 | }
107 | }
108 |
109 | if (!Object.extend) {
110 | Object.extend = function(destination, source) {
111 | for (var property in source) {
112 | if (source.hasOwnProperty(property))
113 | destination[property] = source[property];
114 | }
115 | return destination;
116 | };
117 | }
118 |
119 | var Class = {
120 | create: function(prototype, extensions) {
121 | var ctor = function() { if (this.initialize) return this.initialize.apply(this, arguments); }
122 | ctor.prototype = prototype || {}; // instance methods
123 | Object.extend(ctor, extensions || {}); // class methods
124 | return ctor;
125 | }
126 | }
127 |
128 | if (!window.requestAnimationFrame) {// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
129 | window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
130 | window.mozRequestAnimationFrame ||
131 | window.oRequestAnimationFrame ||
132 | window.msRequestAnimationFrame ||
133 | function(callback, element) {
134 | window.setTimeout(callback, 1000 / 60);
135 | }
136 | }
137 |
138 | Game = {
139 |
140 | run: function(gameFactory, cfg) {
141 | document.addEventListener('DOMContentLoaded', function() {
142 | window.game = gameFactory();
143 | window.runner = new Game.Runner(window.game, cfg);
144 | }, false);
145 | },
146 |
147 | //===========================================================================
148 | // GAME RUNNER
149 | //===========================================================================
150 |
151 | Runner: Class.create({
152 |
153 | initialize: function(game, cfg) {
154 | this.game = game;
155 | this.cfg = (cfg = Object.extend(cfg || {}, (game.cfg && game.cfg.runner) || {}));
156 | this.fps = cfg.fps || 30;
157 | this.dstep = 1.0 / cfg.fps;
158 | this.frame = 0;
159 | this.canvas = $(cfg.id || 'canvas');
160 | this.bounds = this.canvas.getBoundingClientRect();
161 | this.width = cfg.width || this.canvas.offsetWidth;
162 | this.height = cfg.height || this.canvas.offsetHeight;
163 | this.canvas = this.canvas;
164 | this.canvas.width = this.width;
165 | this.canvas.height = this.height;
166 | this.ctx = this.canvas.getContext('2d');
167 | game.run(this);
168 | if (to.bool(this.cfg.start))
169 | this.start();
170 | return this.game;
171 | },
172 |
173 | timestamp: function() { return new Date().getTime(); },
174 |
175 | start: function() {
176 |
177 | this.addEvents();
178 | this.resetStats();
179 |
180 | if (this.cfg.manual)
181 | return this.draw();
182 |
183 | var timestamp = this.timestamp,
184 | start, middle, end, last = timestamp(),
185 | dt = 0.0, // time elapsed (seconds)
186 | stopping = false, // flag for stopping game loop
187 | self = this; // closure over this
188 |
189 | var step = function() {
190 | start = timestamp(); dt = self.update(dt + Math.min(1, (start - last)/1000.0)); // send dt as seconds, MAX of 1s (to avoid huge delta's when requestAnimationFrame put us in the background)
191 | middle = timestamp(); self.draw();
192 | end = timestamp();
193 | self.updateStats(middle - start, end - middle);
194 | last = start;
195 | if (!stopping)
196 | requestAnimationFrame(step);
197 | }
198 | this.stop = function() { stopping = true; }
199 | step();
200 |
201 | },
202 |
203 | update: function(dt) {
204 | while (dt >= this.dstep) {
205 | this.game.update(this.frame);
206 | this.frame = this.frame + 1;
207 | dt = dt - this.dstep;
208 | }
209 | return dt;
210 | },
211 |
212 | manual: function() {
213 | if (this.cfg.manual) {
214 | var start = this.timestamp(); this.update(this.dstep);
215 | var middle = this.timestamp(); this.draw();
216 | var end = this.timestamp();
217 | this.updateStats(middle - start, end - middle);
218 | }
219 | },
220 |
221 | draw: function() {
222 | this.ctx.save();
223 | this.game.draw(this.ctx, this.frame);
224 | this.ctx.restore();
225 | this.drawStats();
226 | },
227 |
228 | resetStats: function() {
229 | if (this.cfg.stats) {
230 | this.stats = new Stats();
231 | this.stats.extra = {
232 | update: 0,
233 | draw: 0
234 | };
235 | this.stats.domElement.id = 'stats';
236 | this.canvas.parentNode.appendChild(this.stats.domElement);
237 | this.stats.domElement.appendChild($({
238 | tag: 'div',
239 | 'class': 'extra',
240 | 'style': 'font-size: 8pt; position: absolute; top: -50px;',
241 | html: "
"
242 | }));
243 | this.stats.updateCounter = $(this.stats.domElement).down('div.extra span.update');
244 | this.stats.drawCounter = $(this.stats.domElement).down('div.extra span.draw');
245 | }
246 | },
247 |
248 | updateStats: function(update, draw) {
249 | if (this.cfg.stats) {
250 | this.stats.update();
251 | this.stats.extra.update = update ? Math.max(1, update) : this.stats.extra.update;
252 | this.stats.extra.draw = draw ? Math.max(1, draw) : this.stats.extra.draw;
253 | }
254 | },
255 |
256 | drawStats: function() {
257 | if (this.cfg.stats) {
258 | this.stats.updateCounter.update("update: " + Math.round(this.stats.extra.update) + "ms");
259 | this.stats.drawCounter.update( "draw: " + Math.round(this.stats.extra.draw) + "ms");
260 | }
261 | },
262 |
263 | addEvents: function() {
264 | var game = this.game;
265 |
266 | if (game.onfocus) {
267 | document.body.tabIndex = to.number(document.body.tabIndex, 0); // body needs tabIndex to recieve focus
268 | $(document.body).on('focus', function(ev) { game.onfocus(ev); });
269 | }
270 |
271 | if (game.onclick) {
272 | this.canvas.on('click', function(ev) { game.onclick(ev); });
273 | }
274 |
275 | if (game.onwheel) {
276 | this.canvas.on(ua.is.firefox ? "DOMMouseScroll" : "mousewheel", function(ev) { game.onwheel(Game.Event.mouseWheelDelta(ev), ev); });
277 | }
278 |
279 | },
280 |
281 | setSize: function(width, height) {
282 | this.width = this.canvas.width = width;
283 | this.height = this.canvas.height = height;
284 | }
285 |
286 | }),
287 |
288 | //===========================================================================
289 | // UTILS
290 | //===========================================================================
291 |
292 | qsValue: function(name, format) {
293 | var pattern = name + "=(" + (format || "\\w+") + ")",
294 | re = new RegExp(pattern),
295 | match = re.exec(location.search);
296 | return match ? match[1] : null;
297 | },
298 |
299 | qsNumber: function(name, def) {
300 | var value = this.qsValue(name);
301 | return value ? to.number(value, def) : null;
302 | },
303 |
304 | qsBool: function(name, def) {
305 | return to.bool(this.qsValue(name), def);
306 | },
307 |
308 | storage: function() {
309 | try {
310 | return this.localStorage = this.localStorage || window.localStorage || {};
311 | }
312 | catch(e) { // IE localStorage throws exceptions when using non-standard port (e.g. during development)
313 | return this.localStorage = {};
314 | }
315 | },
316 |
317 | createCanvas: function(width, height) {
318 | var canvas = document.createElement('canvas');
319 | canvas.width = width;
320 | canvas.height = height;
321 | return canvas;
322 | },
323 |
324 | renderToCanvas: function(width, height, render, canvas) { // http://kaioa.com/node/103
325 | canvas = canvas || this.createCanvas(width, height, canvas);
326 | render(canvas.getContext('2d'));
327 | return canvas;
328 | },
329 |
330 | parseImage: function(image, callback) {
331 | var tx, ty, index, pixel,
332 | tw = image.width,
333 | th = image.height,
334 | canvas = Game.renderToCanvas(tw, th, function(ctx) { ctx.drawImage(image, 0, 0); }),
335 | ctx = canvas.getContext('2d'),
336 | data = ctx.getImageData(0, 0, tw, th).data,
337 | helpers = {
338 | valid: function(tx,ty) { return (tx >= 0) && (tx < tw) && (ty >= 0) && (ty < th); },
339 | index: function(tx,ty) { return (tx + (ty*tw)) * 4; },
340 | pixel: function(tx,ty) { var i = this.index(tx,ty); return this.valid(tx,ty) ? (data[i]<<16)+(data[i+1]<<8)+(data[i+2]) : null; }
341 | }
342 |
343 | for(ty = 0 ; ty < th ; ty++)
344 | for(tx = 0 ; tx < tw ; tx++)
345 | callback(tx, ty, helpers.pixel(tx,ty), helpers);
346 | },
347 |
348 | createImage: function(url, options) {
349 | options = options || {};
350 | var image = $({tag: 'img'});
351 | if (options.onload)
352 | image.on('load', options.onload);
353 | image.src = url;
354 | return image;
355 | },
356 |
357 | loadResources: function(images, sounds, callback) { /* load multiple images and sounds and callback when ALL have finished loading */
358 | images = images || [];
359 | sounds = sounds || [];
360 | var count = images.length + sounds.length;
361 | var resources = { images: {}, sounds: {} };
362 | if (count == 0) {
363 | callback(resources);
364 | }
365 | else {
366 |
367 | var done = false;
368 | var loaded = function() {
369 | if (!done) {
370 | done = true; // ensure only called once, either by onload, or by setTimeout
371 | callback(resources);
372 | }
373 | }
374 |
375 | var onload = function() {
376 | if (--count == 0)
377 | loaded();
378 | };
379 |
380 | for(var n = 0 ; n < images.length ; n++) {
381 | var image = images[n];
382 | image = is.string(image) ? { id: image, url: image } : image;
383 | resources.images[image.id] = Game.createImage(image.url, { onload: onload });
384 | }
385 |
386 | for(var n = 0 ; n < sounds.length ; n++) {
387 | var sound = sounds[n];
388 | sound = is.string(sound) ? { id: sound, name: sound } : sound;
389 | resources.sounds[sound.id] = AudioFX(sound.name, sound, onload);
390 | }
391 |
392 | setTimeout(loaded, 15000); // need a timeout because HTML5 audio canplay event is VERY VERY FLAKEY (especially on slow connections)
393 |
394 | }
395 | }
396 |
397 | };
398 |
399 | Game.PubSub = {
400 |
401 | enable: function(cfg, on) {
402 |
403 | var n, max;
404 |
405 | on.subscribe = function(event, callback) {
406 | this.subscribers = this.subscribers || {};
407 | this.subscribers[event] = this.subscribers[event] || [];
408 | this.subscribers[event].push(callback);
409 | },
410 |
411 | on.publish = function(event) {
412 | if (this.subscribers && this.subscribers[event]) {
413 | var subs = this.subscribers[event],
414 | args = [].slice.call(arguments, 1),
415 | n, max;
416 | for(n = 0, max = subs.length ; n < max ; n++)
417 | subs[n].apply(on, args);
418 | }
419 | }
420 |
421 | if (cfg) {
422 | for(n = 0, max = cfg.length ; n < max ; n++)
423 | on.subscribe(cfg[n].event, cfg[n].action);
424 | }
425 |
426 | }
427 |
428 | }
429 | //=============================================================================
430 | // Minimal DOM Library ($)
431 | //=============================================================================
432 |
433 | Game.Element = function() {
434 |
435 | var query = function(selector, context) {
436 | if (is.array(context))
437 | return Sizzle.matches(selector, context);
438 | else
439 | return Sizzle(selector, context);
440 | };
441 |
442 | var extend = function(obj) {
443 | if (is.array(obj)) {
444 | for(var n = 0, l = obj.length ; n < l ; n++)
445 | obj[n] = extend(obj[n]);
446 | }
447 | else if (!obj._extended) {
448 | Object.extend(obj, Game.Element.instanceMethods);
449 | }
450 | return obj;
451 | };
452 |
453 | var on = function(ele, type, fn, capture) { ele.addEventListener(type, fn, capture); };
454 | var un = function(ele, type, fn, capture) { ele.removeEventListener(type, fn, capture); };
455 |
456 | var create = function(attributes) {
457 | var ele = document.createElement(attributes.tag);
458 | for (var name in attributes) {
459 | if (attributes.hasOwnProperty(name) && is.valid(attributes[name])) {
460 | switch(name) {
461 | case 'tag' : break;
462 | case 'html' : ele.innerHTML = attributes[name]; break;
463 | case 'text' : ele.appendChild(document.createTextNode(attributes[name])); break;
464 | case 'dom' :
465 | attributes[name] = is.array(attributes[name]) ? attributes[name] : [attributes[name]];
466 | for (var n = 0 ; n < attributes[name].length ; n++)
467 | ele.appendChild(attributes[name][n]);
468 | break;
469 | case 'class':
470 | case 'klass':
471 | case 'className':
472 | ele.className = attributes[name];
473 | break;
474 | case 'on':
475 | for(var ename in attributes[name])
476 | on(ele, ename, attributes[name][ename]);
477 | break;
478 | default:
479 | ele.setAttribute(name, attributes[name]);
480 | break;
481 | }
482 | }
483 | }
484 | return ele;
485 | };
486 |
487 | return {
488 |
489 | all: function(selector, context) {
490 | return extend(query(selector, context));
491 | },
492 |
493 | get: function(obj, context) {
494 | if (is.string(obj))
495 | return extend(query("#" + obj, context)[0]);
496 | else if (is.element(obj) || is.window(obj) || is.document(obj))
497 | return extend(obj);
498 | else if (is.event(obj))
499 | return extend(obj.target || obj.srcElement);
500 | else if ((typeof obj == 'object') && obj.tag)
501 | return extend(create(obj));
502 | else
503 | throw "not an appropriate type for DOM wrapping: " + ele;
504 | },
505 |
506 | instanceMethods: {
507 |
508 | _extended: true,
509 |
510 | on: function(type, fn, capture) { on(this, type, fn, capture); return this; },
511 | un: function(type, fn, capture) { un(this, type, fn, capture); return this; },
512 |
513 | showIf: function(on) { if (on) this.show(); else this.hide(); },
514 | show: function() { this.style.display = '' },
515 | hide: function() { this.style.display = 'none'; },
516 | visible: function() { return (this.style.display != 'none') && !this.fading; },
517 | fade: function(amount) { this.style.opacity = amount; },
518 |
519 | relations: function(property, includeSelf) {
520 | var result = includeSelf ? [this] : [], ele = this;
521 | while(ele = ele[property])
522 | if (ele.nodeType == 1)
523 | result.push(ele);
524 | return extend(result);
525 | },
526 |
527 | parent: function() { return extend(this.parentNode); },
528 | ancestors: function(includeSelf) { return this.relations('parentNode', includeSelf); },
529 | previousSiblings: function() { return this.relations('previousSibling'); },
530 | nextSiblings: function() { return this.relations('nextSibling'); },
531 |
532 | select: function(selector) { return Game.Element.all(selector, this); },
533 | down: function(selector) { return Game.Element.all(selector, this)[0]; },
534 | up: function(selector, includeSelf) { return Game.Element.all(selector, this.ancestors(includeSelf))[0]; },
535 | prev: function(selector) { return Game.Element.all(selector, this.previousSiblings())[0]; },
536 | next: function(selector) { return Game.Element.all(selector, this.nextSiblings())[0]; },
537 |
538 | remove: function() {
539 | if (this.parentNode)
540 | this.parentNode.removeChild(this);
541 | return this;
542 | },
543 |
544 | removeChildren: function() { // NOTE: can't use :clear because its in DOM level-1 and IE bitches if we try to provide our own
545 | while (this.childNodes.length > 0)
546 | this.removeChild(this.childNodes[0]);
547 | return this;
548 | },
549 |
550 | update: function(content) {
551 | this.innerHTML = "";
552 | this.append(content);
553 | return this;
554 | },
555 |
556 | append: function(content) {
557 | if (is.string(content))
558 | this.innerHTML += content;
559 | else if (is.array(content))
560 | for(var n = 0 ; n < content.length ; n++)
561 | this.append(content[n]);
562 | else
563 | this.appendChild(Game.Element.get(content));
564 | },
565 |
566 | setClassName: function(name) { this.className = name; },
567 | hasClassName: function(name) { return (new RegExp("(^|\s*)" + name + "(\s*|$)")).test(this.className) },
568 | addClassName: function(name) { this.toggleClassName(name, true); },
569 | removeClassName: function(name) { this.toggleClassName(name, false); },
570 | toggleClassName: function(name, on) {
571 | var classes = this.className.split(' ');
572 | var n = classes.indexOf(name);
573 | on = (typeof on == 'undefined') ? (n < 0) : on;
574 | if (on && (n < 0))
575 | classes.push(name);
576 | else if (!on && (n >= 0))
577 | classes.splice(n, 1);
578 | this.className = classes.join(' ');
579 | },
580 |
581 | fadeout: function(options) {
582 | options = options || {};
583 | this.cancelFade();
584 | this.fading = Animator.apply(this, 'opacity: 0', { duration: options.duration, onComplete: function() {
585 | this.hide();
586 | this.fading = null;
587 | this.style.opacity = is.ie ? 1 : null; /* clear opacity after hiding, but IE doesn't allow clear so set to 1.0 for IE */
588 | if (options.onComplete)
589 | options.onComplete();
590 | }.bind(this) });
591 | this.fading.play();
592 | },
593 |
594 | fadein: function(options) {
595 | options = options || {};
596 | this.cancelFade();
597 | this.style.opacity = 0;
598 | this.show();
599 | this.fading = Animator.apply(this, 'opacity: 1', { duration: options.duration, onComplete: function() {
600 | this.fading = null;
601 | this.style.opacity = is.ie ? 1 : null; /* clear opacity after hiding, but IE doesn't allow clear so set to 1.0 for IE */
602 | if (options.onComplete)
603 | options.onComplete();
604 | }.bind(this) });
605 | this.fading.play();
606 | },
607 |
608 | cancelFade: function() {
609 | if (this.fading) {
610 | this.fading.stop();
611 | delete this.fading;
612 | }
613 | }
614 |
615 | }
616 | };
617 |
618 | }();
619 |
620 | $ = Game.Element.get;
621 | $$ = Game.Element.all;
622 |
623 | Game.Event = {
624 |
625 | stop: function(ev) {
626 | ev.preventDefault();
627 | ev.cancelBubble = true;
628 | ev.returnValue = false;
629 | return false;
630 | },
631 |
632 | canvasPos: function(ev, canvas) {
633 | var bbox = canvas.getBoundingClientRect(),
634 | x = (ev.clientX - bbox.left) * (canvas.width / bbox.width),
635 | y = (ev.clientY - bbox.top) * (canvas.height / bbox.height);
636 | return { x: x, y: y };
637 | },
638 |
639 | mouseWheelDelta: function(ev) {
640 | if (is.valid(ev.wheelDelta))
641 | return ev.wheelDelta/120;
642 | else if (is.valid(ev.detail))
643 | return -ev.detail/3;
644 | else
645 | return 0;
646 | }
647 |
648 | }
649 |
650 | Game.Key = {
651 | BACKSPACE: 8,
652 | TAB: 9,
653 | RETURN: 13,
654 | ESC: 27,
655 | SPACE: 32,
656 | END: 35,
657 | HOME: 36,
658 | LEFT: 37,
659 | UP: 38,
660 | RIGHT: 39,
661 | DOWN: 40,
662 | PAGEUP: 33,
663 | PAGEDOWN: 34,
664 | INSERT: 45,
665 | DELETE: 46,
666 | ZERO: 48, ONE: 49, TWO: 50, THREE: 51, FOUR: 52, FIVE: 53, SIX: 54, SEVEN: 55, EIGHT: 56, NINE: 57,
667 | A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90,
668 | TILDA: 192,
669 |
670 | map: function(map, context, cfg) {
671 | cfg = cfg || {};
672 | var ele = $(cfg.ele || document);
673 | var onkey = function(ev, keyCode, mode) {
674 | var n, k, i;
675 | if ((ele === document) || ele.visible()) {
676 | for(n = 0 ; n < map.length ; ++n) {
677 | k = map[n];
678 | k.mode = k.mode || 'up';
679 | if (Game.Key.match(k, keyCode, mode, context)) {
680 | k.action.call(context, keyCode, ev.shiftKey);
681 | return Game.Event.stop(ev);
682 | }
683 | }
684 | }
685 | };
686 | ele.on('keydown', function(ev) { return onkey(ev, ev.keyCode, 'down'); });
687 | ele.on('keyup', function(ev) { return onkey(ev, ev.keyCode, 'up'); });
688 | },
689 |
690 | match: function(map, keyCode, mode, context) {
691 | if (map.mode === mode) {
692 | if (!map.state || !context || (map.state === context.current) || (is.array(map.state) && map.state.indexOf(context.current) >= 0)) {
693 | if ((map.key === keyCode) || (map.keys && (map.keys.indexOf(keyCode) >= 0))) {
694 | return true;
695 | }
696 | }
697 | }
698 | return false;
699 | }
700 |
701 | };
702 |
703 |
704 | Game.Math = {
705 |
706 | minmax: function(x, min, max) {
707 | return Math.max(min, Math.min(max, x));
708 | },
709 |
710 | random: function(min, max) {
711 | return (min + (Math.random() * (max - min)));
712 | },
713 |
714 | randomInt: function(min, max) {
715 | return Math.round(Game.Math.random(min, max));
716 | },
717 |
718 | randomChoice: function(choices) {
719 | return choices[Math.round(Game.Math.random(0, choices.length-1))];
720 | },
721 |
722 | randomBool: function() {
723 | return Game.randomChoice([true, false]);
724 | },
725 |
726 | between: function(x, from, to) {
727 | return (is.valid(x) && (from <= x) && (x <= to));
728 | },
729 |
730 | overlap: function(x1, y1, w1, h1, x2, y2, w2, h2) {
731 | return !(((x1 + w1 - 1) < x2) ||
732 | ((x2 + w2 - 1) < x1) ||
733 | ((y1 + h1 - 1) < y2) ||
734 | ((y2 + h2 - 1) < y1))
735 | }
736 |
737 | }
738 |
739 |
--------------------------------------------------------------------------------
/js/game/base.js:
--------------------------------------------------------------------------------
1 | //=============================================================================
2 | // feature detection
3 | //=============================================================================
4 | ua = function() {
5 |
6 | var ua = navigator.userAgent.toLowerCase(); // should avoid user agent sniffing... but sometimes you just gotta do what you gotta do
7 | var key = ((ua.indexOf("opera") > -1) ? "opera" : null);
8 | key = key || ((ua.indexOf("firefox") > -1) ? "firefox" : null);
9 | key = key || ((ua.indexOf("chrome") > -1) ? "chrome" : null);
10 | key = key || ((ua.indexOf("safari") > -1) ? "safari" : null);
11 | key = key || ((ua.indexOf("msie") > -1) ? "ie" : null);
12 |
13 | try {
14 | var re = (key == "ie") ? "msie ([\\d\\.]*)" : key + "\\/([\\d\\.]*)"
15 | var matches = ua.match(new RegExp(re, "i"));
16 | var version = matches ? matches[1] : null;
17 | } catch (e) {}
18 |
19 | return {
20 | full: ua,
21 | name: key + (version ? " " + version : ""),
22 | version: version,
23 | major: version ? parseInt(version) : null,
24 | is: {
25 | firefox: (key == "firefox"),
26 | chrome: (key == "chrome"),
27 | safari: (key == "safari"),
28 | opera: (key == "opera"),
29 | ie: (key == "ie")
30 | },
31 | has: {
32 | audio: AudioFX && AudioFX.enabled,
33 | canvas: (document.createElement('canvas').getContext),
34 | touch: ('ontouchstart' in window)
35 | }
36 | }
37 | }();
38 |
39 | //=============================================================================
40 | // type detection
41 | //=============================================================================
42 |
43 | is = {
44 | 'string': function(obj) { return (typeof obj === 'string'); },
45 | 'number': function(obj) { return (typeof obj === 'number'); },
46 | 'bool': function(obj) { return (typeof obj === 'boolean'); },
47 | 'array': function(obj) { return (obj instanceof Array); },
48 | 'undefined': function(obj) { return (typeof obj === 'undefined'); },
49 | 'func': function(obj) { return (typeof obj === 'function'); },
50 | 'null': function(obj) { return (obj === null); },
51 | 'notNull': function(obj) { return (obj !== null); },
52 | 'invalid': function(obj) { return ( is['null'](obj) || is.undefined(obj)); },
53 | 'valid': function(obj) { return (!is['null'](obj) && !is.undefined(obj)); },
54 | 'emptyString': function(obj) { return (is.string(obj) && (obj.length == 0)); },
55 | 'nonEmptyString': function(obj) { return (is.string(obj) && (obj.length > 0)); },
56 | 'emptyArray': function(obj) { return (is.array(obj) && (obj.length == 0)); },
57 | 'nonEmptyArray': function(obj) { return (is.array(obj) && (obj.length > 0)); },
58 | 'document': function(obj) { return (obj === document); },
59 | 'window': function(obj) { return (obj === window); },
60 | 'element': function(obj) { return (obj instanceof HTMLElement); },
61 | 'event': function(obj) { return (obj instanceof Event); },
62 | 'link': function(obj) { return (is.element(obj) && (obj.tagName == 'A')); }
63 | }
64 |
65 | //=============================================================================
66 | // type coersion
67 | //=============================================================================
68 |
69 | to = {
70 | 'bool': function(obj, def) { if (is.valid(obj)) return ((obj == 1) || (obj == true) || (obj == "1") || (obj == "y") || (obj == "Y") || (obj.toString().toLowerCase() == "true") || (obj.toString().toLowerCase() == 'yes')); else return (is.bool(def) ? def : false); },
71 | 'number': function(obj, def) { if (is.valid(obj)) { var x = parseFloat(obj); if (!isNaN(x)) return x; } return (is.number(def) ? def : 0); },
72 | 'string': function(obj, def) { if (is.valid(obj)) return obj.toString(); return (is.string(def) ? def : ''); }
73 | }
74 |
75 | //=============================================================================
76 | //
77 | // Compatibility for older browsers (compatibility: http://kangax.github.com/es5-compat-table/)
78 | //
79 | // Function.bind: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
80 | // Object.create: http://javascript.crockford.com/prototypal.html
81 | // Object.extend: (defacto standard like jquery $.extend or prototype's Object.extend)
82 | // Class.create: create a simple javascript 'class' (a constructor function with a prototype and optional class methods)
83 | //
84 | //=============================================================================
85 |
86 | if (!Function.prototype.bind) {
87 | Function.prototype.bind = function(obj) {
88 | var slice = [].slice,
89 | args = slice.call(arguments, 1),
90 | self = this,
91 | nop = function () {},
92 | bound = function () {
93 | return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
94 | };
95 | nop.prototype = self.prototype;
96 | bound.prototype = new nop();
97 | return bound;
98 | };
99 | }
100 |
101 | if (!Object.create) {
102 | Object.create = function(base) {
103 | function F() {};
104 | F.prototype = base;
105 | return new F();
106 | }
107 | }
108 |
109 | if (!Object.extend) {
110 | Object.extend = function(destination, source) {
111 | for (var property in source) {
112 | if (source.hasOwnProperty(property))
113 | destination[property] = source[property];
114 | }
115 | return destination;
116 | };
117 | }
118 |
119 | var Class = {
120 | create: function(prototype, extensions) {
121 | var ctor = function() { if (this.initialize) return this.initialize.apply(this, arguments); }
122 | ctor.prototype = prototype || {}; // instance methods
123 | Object.extend(ctor, extensions || {}); // class methods
124 | return ctor;
125 | }
126 | }
127 |
128 | if (!window.requestAnimationFrame) {// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
129 | window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
130 | window.mozRequestAnimationFrame ||
131 | window.oRequestAnimationFrame ||
132 | window.msRequestAnimationFrame ||
133 | function(callback, element) {
134 | window.setTimeout(callback, 1000 / 60);
135 | }
136 | }
137 |
138 |
--------------------------------------------------------------------------------
/js/game/dom.js:
--------------------------------------------------------------------------------
1 | //=============================================================================
2 | // Minimal DOM Library ($)
3 | //=============================================================================
4 |
5 | Game.Element = function() {
6 |
7 | var query = function(selector, context) {
8 | if (is.array(context))
9 | return Sizzle.matches(selector, context);
10 | else
11 | return Sizzle(selector, context);
12 | };
13 |
14 | var extend = function(obj) {
15 | if (is.array(obj)) {
16 | for(var n = 0, l = obj.length ; n < l ; n++)
17 | obj[n] = extend(obj[n]);
18 | }
19 | else if (!obj._extended) {
20 | Object.extend(obj, Game.Element.instanceMethods);
21 | }
22 | return obj;
23 | };
24 |
25 | var on = function(ele, type, fn, capture) { ele.addEventListener(type, fn, capture); };
26 | var un = function(ele, type, fn, capture) { ele.removeEventListener(type, fn, capture); };
27 |
28 | var create = function(attributes) {
29 | var ele = document.createElement(attributes.tag);
30 | for (var name in attributes) {
31 | if (attributes.hasOwnProperty(name) && is.valid(attributes[name])) {
32 | switch(name) {
33 | case 'tag' : break;
34 | case 'html' : ele.innerHTML = attributes[name]; break;
35 | case 'text' : ele.appendChild(document.createTextNode(attributes[name])); break;
36 | case 'dom' :
37 | attributes[name] = is.array(attributes[name]) ? attributes[name] : [attributes[name]];
38 | for (var n = 0 ; n < attributes[name].length ; n++)
39 | ele.appendChild(attributes[name][n]);
40 | break;
41 | case 'class':
42 | case 'klass':
43 | case 'className':
44 | ele.className = attributes[name];
45 | break;
46 | case 'on':
47 | for(var ename in attributes[name])
48 | on(ele, ename, attributes[name][ename]);
49 | break;
50 | default:
51 | ele.setAttribute(name, attributes[name]);
52 | break;
53 | }
54 | }
55 | }
56 | return ele;
57 | };
58 |
59 | return {
60 |
61 | all: function(selector, context) {
62 | return extend(query(selector, context));
63 | },
64 |
65 | get: function(obj, context) {
66 | if (is.string(obj))
67 | return extend(query("#" + obj, context)[0]);
68 | else if (is.element(obj) || is.window(obj) || is.document(obj))
69 | return extend(obj);
70 | else if (is.event(obj))
71 | return extend(obj.target || obj.srcElement);
72 | else if ((typeof obj == 'object') && obj.tag)
73 | return extend(create(obj));
74 | else
75 | throw "not an appropriate type for DOM wrapping: " + ele;
76 | },
77 |
78 | instanceMethods: {
79 |
80 | _extended: true,
81 |
82 | on: function(type, fn, capture) { on(this, type, fn, capture); return this; },
83 | un: function(type, fn, capture) { un(this, type, fn, capture); return this; },
84 |
85 | showIf: function(on) { if (on) this.show(); else this.hide(); },
86 | show: function() { this.style.display = '' },
87 | hide: function() { this.style.display = 'none'; },
88 | visible: function() { return (this.style.display != 'none') && !this.fading; },
89 | fade: function(amount) { this.style.opacity = amount; },
90 |
91 | relations: function(property, includeSelf) {
92 | var result = includeSelf ? [this] : [], ele = this;
93 | while(ele = ele[property])
94 | if (ele.nodeType == 1)
95 | result.push(ele);
96 | return extend(result);
97 | },
98 |
99 | parent: function() { return extend(this.parentNode); },
100 | ancestors: function(includeSelf) { return this.relations('parentNode', includeSelf); },
101 | previousSiblings: function() { return this.relations('previousSibling'); },
102 | nextSiblings: function() { return this.relations('nextSibling'); },
103 |
104 | select: function(selector) { return Game.Element.all(selector, this); },
105 | down: function(selector) { return Game.Element.all(selector, this)[0]; },
106 | up: function(selector, includeSelf) { return Game.Element.all(selector, this.ancestors(includeSelf))[0]; },
107 | prev: function(selector) { return Game.Element.all(selector, this.previousSiblings())[0]; },
108 | next: function(selector) { return Game.Element.all(selector, this.nextSiblings())[0]; },
109 |
110 | remove: function() {
111 | if (this.parentNode)
112 | this.parentNode.removeChild(this);
113 | return this;
114 | },
115 |
116 | removeChildren: function() { // NOTE: can't use :clear because its in DOM level-1 and IE bitches if we try to provide our own
117 | while (this.childNodes.length > 0)
118 | this.removeChild(this.childNodes[0]);
119 | return this;
120 | },
121 |
122 | update: function(content) {
123 | this.innerHTML = "";
124 | this.append(content);
125 | return this;
126 | },
127 |
128 | append: function(content) {
129 | if (is.string(content))
130 | this.innerHTML += content;
131 | else if (is.array(content))
132 | for(var n = 0 ; n < content.length ; n++)
133 | this.append(content[n]);
134 | else
135 | this.appendChild(Game.Element.get(content));
136 | },
137 |
138 | setClassName: function(name) { this.className = name; },
139 | hasClassName: function(name) { return (new RegExp("(^|\s*)" + name + "(\s*|$)")).test(this.className) },
140 | addClassName: function(name) { this.toggleClassName(name, true); },
141 | removeClassName: function(name) { this.toggleClassName(name, false); },
142 | toggleClassName: function(name, on) {
143 | var classes = this.className.split(' ');
144 | var n = classes.indexOf(name);
145 | on = (typeof on == 'undefined') ? (n < 0) : on;
146 | if (on && (n < 0))
147 | classes.push(name);
148 | else if (!on && (n >= 0))
149 | classes.splice(n, 1);
150 | this.className = classes.join(' ');
151 | },
152 |
153 | fadeout: function(options) {
154 | options = options || {};
155 | this.cancelFade();
156 | this.fading = Animator.apply(this, 'opacity: 0', { duration: options.duration, onComplete: function() {
157 | this.hide();
158 | this.fading = null;
159 | this.style.opacity = is.ie ? 1 : null; /* clear opacity after hiding, but IE doesn't allow clear so set to 1.0 for IE */
160 | if (options.onComplete)
161 | options.onComplete();
162 | }.bind(this) });
163 | this.fading.play();
164 | },
165 |
166 | fadein: function(options) {
167 | options = options || {};
168 | this.cancelFade();
169 | this.style.opacity = 0;
170 | this.show();
171 | this.fading = Animator.apply(this, 'opacity: 1', { duration: options.duration, onComplete: function() {
172 | this.fading = null;
173 | this.style.opacity = is.ie ? 1 : null; /* clear opacity after hiding, but IE doesn't allow clear so set to 1.0 for IE */
174 | if (options.onComplete)
175 | options.onComplete();
176 | }.bind(this) });
177 | this.fading.play();
178 | },
179 |
180 | cancelFade: function() {
181 | if (this.fading) {
182 | this.fading.stop();
183 | delete this.fading;
184 | }
185 | }
186 |
187 | }
188 | };
189 |
190 | }();
191 |
192 | $ = Game.Element.get;
193 | $$ = Game.Element.all;
194 |
195 | Game.Event = {
196 |
197 | stop: function(ev) {
198 | ev.preventDefault();
199 | ev.cancelBubble = true;
200 | ev.returnValue = false;
201 | return false;
202 | },
203 |
204 | canvasPos: function(ev, canvas) {
205 | var bbox = canvas.getBoundingClientRect(),
206 | x = (ev.clientX - bbox.left) * (canvas.width / bbox.width),
207 | y = (ev.clientY - bbox.top) * (canvas.height / bbox.height);
208 | return { x: x, y: y };
209 | },
210 |
211 | mouseWheelDelta: function(ev) {
212 | if (is.valid(ev.wheelDelta))
213 | return ev.wheelDelta/120;
214 | else if (is.valid(ev.detail))
215 | return -ev.detail/3;
216 | else
217 | return 0;
218 | }
219 |
220 | }
221 |
222 |
--------------------------------------------------------------------------------
/js/game/game.js:
--------------------------------------------------------------------------------
1 | Game = {
2 |
3 | run: function(gameFactory, cfg) {
4 | document.addEventListener('DOMContentLoaded', function() {
5 | window.game = gameFactory();
6 | window.runner = new Game.Runner(window.game, cfg);
7 | }, false);
8 | },
9 |
10 | //===========================================================================
11 | // GAME RUNNER
12 | //===========================================================================
13 |
14 | Runner: Class.create({
15 |
16 | initialize: function(game, cfg) {
17 | this.game = game;
18 | this.cfg = (cfg = Object.extend(cfg || {}, (game.cfg && game.cfg.runner) || {}));
19 | this.fps = cfg.fps || 30;
20 | this.dstep = 1.0 / cfg.fps;
21 | this.frame = 0;
22 | this.canvas = $(cfg.id || 'canvas');
23 | this.bounds = this.canvas.getBoundingClientRect();
24 | this.width = cfg.width || this.canvas.offsetWidth;
25 | this.height = cfg.height || this.canvas.offsetHeight;
26 | this.canvas = this.canvas;
27 | this.canvas.width = this.width;
28 | this.canvas.height = this.height;
29 | this.ctx = this.canvas.getContext('2d');
30 | game.run(this);
31 | if (to.bool(this.cfg.start))
32 | this.start();
33 | return this.game;
34 | },
35 |
36 | timestamp: function() { return new Date().getTime(); },
37 |
38 | start: function() {
39 |
40 | this.addEvents();
41 | this.resetStats();
42 |
43 | if (this.cfg.manual)
44 | return this.draw();
45 |
46 | var timestamp = this.timestamp,
47 | start, middle, end, last = timestamp(),
48 | dt = 0.0, // time elapsed (seconds)
49 | stopping = false, // flag for stopping game loop
50 | self = this; // closure over this
51 |
52 | var step = function() {
53 | start = timestamp(); dt = self.update(dt + Math.min(1, (start - last)/1000.0)); // send dt as seconds, MAX of 1s (to avoid huge delta's when requestAnimationFrame put us in the background)
54 | middle = timestamp(); self.draw();
55 | end = timestamp();
56 | self.updateStats(middle - start, end - middle);
57 | last = start;
58 | if (!stopping)
59 | requestAnimationFrame(step);
60 | }
61 | this.stop = function() { stopping = true; }
62 | step();
63 |
64 | },
65 |
66 | update: function(dt) {
67 | while (dt >= this.dstep) {
68 | this.game.update(this.frame);
69 | this.frame = this.frame + 1;
70 | dt = dt - this.dstep;
71 | }
72 | return dt;
73 | },
74 |
75 | manual: function() {
76 | if (this.cfg.manual) {
77 | var start = this.timestamp(); this.update(this.dstep);
78 | var middle = this.timestamp(); this.draw();
79 | var end = this.timestamp();
80 | this.updateStats(middle - start, end - middle);
81 | }
82 | },
83 |
84 | draw: function() {
85 | this.ctx.save();
86 | this.game.draw(this.ctx, this.frame);
87 | this.ctx.restore();
88 | this.drawStats();
89 | },
90 |
91 | resetStats: function() {
92 | if (this.cfg.stats) {
93 | this.stats = new Stats();
94 | this.stats.extra = {
95 | update: 0,
96 | draw: 0
97 | };
98 | this.stats.domElement.id = 'stats';
99 | this.canvas.parentNode.appendChild(this.stats.domElement);
100 | this.stats.domElement.appendChild($({
101 | tag: 'div',
102 | 'class': 'extra',
103 | 'style': 'font-size: 8pt; position: absolute; top: -50px;',
104 | html: "
"
105 | }));
106 | this.stats.updateCounter = $(this.stats.domElement).down('div.extra span.update');
107 | this.stats.drawCounter = $(this.stats.domElement).down('div.extra span.draw');
108 | }
109 | },
110 |
111 | updateStats: function(update, draw) {
112 | if (this.cfg.stats) {
113 | this.stats.update();
114 | this.stats.extra.update = update ? Math.max(1, update) : this.stats.extra.update;
115 | this.stats.extra.draw = draw ? Math.max(1, draw) : this.stats.extra.draw;
116 | }
117 | },
118 |
119 | drawStats: function() {
120 | if (this.cfg.stats) {
121 | this.stats.updateCounter.update("update: " + Math.round(this.stats.extra.update) + "ms");
122 | this.stats.drawCounter.update( "draw: " + Math.round(this.stats.extra.draw) + "ms");
123 | }
124 | },
125 |
126 | addEvents: function() {
127 | var game = this.game;
128 |
129 | if (game.onfocus) {
130 | document.body.tabIndex = to.number(document.body.tabIndex, 0); // body needs tabIndex to recieve focus
131 | $(document.body).on('focus', function(ev) { game.onfocus(ev); });
132 | }
133 |
134 | if (game.onclick) {
135 | this.canvas.on('click', function(ev) { game.onclick(ev); });
136 | }
137 |
138 | if (game.onwheel) {
139 | this.canvas.on(ua.is.firefox ? "DOMMouseScroll" : "mousewheel", function(ev) { game.onwheel(Game.Event.mouseWheelDelta(ev), ev); });
140 | }
141 |
142 | },
143 |
144 | setSize: function(width, height) {
145 | this.width = this.canvas.width = width;
146 | this.height = this.canvas.height = height;
147 | }
148 |
149 | }),
150 |
151 | //===========================================================================
152 | // UTILS
153 | //===========================================================================
154 |
155 | qsValue: function(name, format) {
156 | var pattern = name + "=(" + (format || "\\w+") + ")",
157 | re = new RegExp(pattern),
158 | match = re.exec(location.search);
159 | return match ? match[1] : null;
160 | },
161 |
162 | qsNumber: function(name, def) {
163 | var value = this.qsValue(name);
164 | return value ? to.number(value, def) : null;
165 | },
166 |
167 | qsBool: function(name, def) {
168 | return to.bool(this.qsValue(name), def);
169 | },
170 |
171 | storage: function() {
172 | try {
173 | return this.localStorage = this.localStorage || window.localStorage || {};
174 | }
175 | catch(e) { // IE localStorage throws exceptions when using non-standard port (e.g. during development)
176 | return this.localStorage = {};
177 | }
178 | },
179 |
180 | createCanvas: function(width, height) {
181 | var canvas = document.createElement('canvas');
182 | canvas.width = width;
183 | canvas.height = height;
184 | return canvas;
185 | },
186 |
187 | renderToCanvas: function(width, height, render, canvas) { // http://kaioa.com/node/103
188 | canvas = canvas || this.createCanvas(width, height, canvas);
189 | render(canvas.getContext('2d'));
190 | return canvas;
191 | },
192 |
193 | parseImage: function(image, callback) {
194 | var tx, ty, index, pixel,
195 | tw = image.width,
196 | th = image.height,
197 | canvas = Game.renderToCanvas(tw, th, function(ctx) { ctx.drawImage(image, 0, 0); }),
198 | ctx = canvas.getContext('2d'),
199 | data = ctx.getImageData(0, 0, tw, th).data,
200 | helpers = {
201 | valid: function(tx,ty) { return (tx >= 0) && (tx < tw) && (ty >= 0) && (ty < th); },
202 | index: function(tx,ty) { return (tx + (ty*tw)) * 4; },
203 | pixel: function(tx,ty) { var i = this.index(tx,ty); return this.valid(tx,ty) ? (data[i]<<16)+(data[i+1]<<8)+(data[i+2]) : null; }
204 | }
205 |
206 | for(ty = 0 ; ty < th ; ty++)
207 | for(tx = 0 ; tx < tw ; tx++)
208 | callback(tx, ty, helpers.pixel(tx,ty), helpers);
209 | },
210 |
211 | createImage: function(url, options) {
212 | options = options || {};
213 | var image = $({tag: 'img'});
214 | if (options.onload)
215 | image.on('load', options.onload);
216 | image.src = url;
217 | return image;
218 | },
219 |
220 | loadResources: function(images, sounds, callback) { /* load multiple images and sounds and callback when ALL have finished loading */
221 | images = images || [];
222 | sounds = sounds || [];
223 | var count = images.length + sounds.length;
224 | var resources = { images: {}, sounds: {} };
225 | if (count == 0) {
226 | callback(resources);
227 | }
228 | else {
229 |
230 | var done = false;
231 | var loaded = function() {
232 | if (!done) {
233 | done = true; // ensure only called once, either by onload, or by setTimeout
234 | callback(resources);
235 | }
236 | }
237 |
238 | var onload = function() {
239 | if (--count == 0)
240 | loaded();
241 | };
242 |
243 | for(var n = 0 ; n < images.length ; n++) {
244 | var image = images[n];
245 | image = is.string(image) ? { id: image, url: image } : image;
246 | resources.images[image.id] = Game.createImage(image.url, { onload: onload });
247 | }
248 |
249 | for(var n = 0 ; n < sounds.length ; n++) {
250 | var sound = sounds[n];
251 | sound = is.string(sound) ? { id: sound, name: sound } : sound;
252 | resources.sounds[sound.id] = AudioFX(sound.name, sound, onload);
253 | }
254 |
255 | setTimeout(loaded, 15000); // need a timeout because HTML5 audio canplay event is VERY VERY FLAKEY (especially on slow connections)
256 |
257 | }
258 | }
259 |
260 | };
261 |
262 |
--------------------------------------------------------------------------------
/js/game/key.js:
--------------------------------------------------------------------------------
1 | Game.Key = {
2 | BACKSPACE: 8,
3 | TAB: 9,
4 | RETURN: 13,
5 | ESC: 27,
6 | SPACE: 32,
7 | END: 35,
8 | HOME: 36,
9 | LEFT: 37,
10 | UP: 38,
11 | RIGHT: 39,
12 | DOWN: 40,
13 | PAGEUP: 33,
14 | PAGEDOWN: 34,
15 | INSERT: 45,
16 | DELETE: 46,
17 | ZERO: 48, ONE: 49, TWO: 50, THREE: 51, FOUR: 52, FIVE: 53, SIX: 54, SEVEN: 55, EIGHT: 56, NINE: 57,
18 | A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90,
19 | TILDA: 192,
20 |
21 | map: function(map, context, cfg) {
22 | cfg = cfg || {};
23 | var ele = $(cfg.ele || document);
24 | var onkey = function(ev, keyCode, mode) {
25 | var n, k, i;
26 | if ((ele === document) || ele.visible()) {
27 | for(n = 0 ; n < map.length ; ++n) {
28 | k = map[n];
29 | k.mode = k.mode || 'up';
30 | if (Game.Key.match(k, keyCode, mode, context)) {
31 | k.action.call(context, keyCode, ev.shiftKey);
32 | return Game.Event.stop(ev);
33 | }
34 | }
35 | }
36 | };
37 | ele.on('keydown', function(ev) { return onkey(ev, ev.keyCode, 'down'); });
38 | ele.on('keyup', function(ev) { return onkey(ev, ev.keyCode, 'up'); });
39 | },
40 |
41 | match: function(map, keyCode, mode, context) {
42 | if (map.mode === mode) {
43 | if (!map.state || !context || (map.state === context.current) || (is.array(map.state) && map.state.indexOf(context.current) >= 0)) {
44 | if ((map.key === keyCode) || (map.keys && (map.keys.indexOf(keyCode) >= 0))) {
45 | return true;
46 | }
47 | }
48 | }
49 | return false;
50 | }
51 |
52 | };
53 |
54 |
55 |
--------------------------------------------------------------------------------
/js/game/math.js:
--------------------------------------------------------------------------------
1 | Game.Math = {
2 |
3 | minmax: function(x, min, max) {
4 | return Math.max(min, Math.min(max, x));
5 | },
6 |
7 | random: function(min, max) {
8 | return (min + (Math.random() * (max - min)));
9 | },
10 |
11 | randomInt: function(min, max) {
12 | return Math.round(Game.Math.random(min, max));
13 | },
14 |
15 | randomChoice: function(choices) {
16 | return choices[Math.round(Game.Math.random(0, choices.length-1))];
17 | },
18 |
19 | randomBool: function() {
20 | return Game.randomChoice([true, false]);
21 | },
22 |
23 | between: function(x, from, to) {
24 | return (is.valid(x) && (from <= x) && (x <= to));
25 | },
26 |
27 | overlap: function(x1, y1, w1, h1, x2, y2, w2, h2) {
28 | return !(((x1 + w1 - 1) < x2) ||
29 | ((x2 + w2 - 1) < x1) ||
30 | ((y1 + h1 - 1) < y2) ||
31 | ((y2 + h2 - 1) < y1))
32 | }
33 |
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/js/game/pubsub.js:
--------------------------------------------------------------------------------
1 | Game.PubSub = {
2 |
3 | enable: function(cfg, on) {
4 |
5 | var n, max;
6 |
7 | on.subscribe = function(event, callback) {
8 | this.subscribers = this.subscribers || {};
9 | this.subscribers[event] = this.subscribers[event] || [];
10 | this.subscribers[event].push(callback);
11 | },
12 |
13 | on.publish = function(event) {
14 | if (this.subscribers && this.subscribers[event]) {
15 | var subs = this.subscribers[event],
16 | args = [].slice.call(arguments, 1),
17 | n, max;
18 | for(n = 0, max = subs.length ; n < max ; n++)
19 | subs[n].apply(on, args);
20 | }
21 | }
22 |
23 | if (cfg) {
24 | for(n = 0, max = cfg.length ; n < max ; n++)
25 | on.subscribe(cfg[n].event, cfg[n].action);
26 | }
27 |
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/js/game/vendor/animator.js:
--------------------------------------------------------------------------------
1 | /*
2 | Animator.js 1.1.11
3 |
4 | This library is released under the BSD license:
5 |
6 | Copyright (c) 2006, Bernard Sumption. All rights reserved.
7 |
8 | Redistribution and use in source and binary forms, with or without
9 | modification, are permitted provided that the following conditions are met:
10 |
11 | Redistributions of source code must retain the above copyright notice, this
12 | list of conditions and the following disclaimer. Redistributions in binary
13 | form must reproduce the above copyright notice, this list of conditions and
14 | the following disclaimer in the documentation and/or other materials
15 | provided with the distribution. Neither the name BernieCode nor
16 | the names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 | DAMAGE.
30 |
31 | */
32 |
33 |
34 | // Applies a sequence of numbers between 0 and 1 to a number of subjects
35 | // construct - see setOptions for parameters
36 | function Animator(options) {
37 | this.setOptions(options);
38 | var _this = this;
39 | this.timerDelegate = function(){_this.onTimerEvent()};
40 | this.subjects = [];
41 | this.target = 0;
42 | this.state = 0;
43 | this.lastTime = null;
44 | };
45 | Animator.prototype = {
46 | // apply defaults
47 | setOptions: function(options) {
48 | this.options = Animator.applyDefaults({
49 | repeat: false,
50 | interval: 20, // time between animation frames
51 | duration: 400, // length of animation
52 | onComplete: function(){},
53 | onStep: function(){},
54 | transition: Animator.tx.easeInOut
55 | }, options);
56 | },
57 | // animate from the current state to provided value
58 | seekTo: function(to) {
59 | this.seekFromTo(this.state, to);
60 | },
61 | // animate from the current state to provided value
62 | seekFromTo: function(from, to) {
63 | this.target = Math.max(0, Math.min(1, to));
64 | this.state = Math.max(0, Math.min(1, from));
65 | this.lastTime = new Date().getTime();
66 | if (!this.intervalId) {
67 | this.intervalId = window.setInterval(this.timerDelegate, this.options.interval);
68 | }
69 | },
70 | // animate from the current state to provided value
71 | jumpTo: function(to) {
72 | this.target = this.state = Math.max(0, Math.min(1, to));
73 | this.propagate();
74 | },
75 | // seek to the opposite of the current target
76 | toggle: function() {
77 | this.seekTo(1 - this.target);
78 | },
79 | // add a function or an object with a method setState(state) that will be called with a number
80 | // between 0 and 1 on each frame of the animation
81 | addSubject: function(subject) {
82 | this.subjects[this.subjects.length] = subject;
83 | return this;
84 | },
85 | // remove all subjects
86 | clearSubjects: function() {
87 | this.subjects = [];
88 | },
89 | // forward the current state to the animation subjects
90 | propagate: function() {
91 | var value = this.options.transition(this.state);
92 | for (var i=0; i= Math.abs(this.state - this.target)) {
107 | this.state = this.target;
108 | } else {
109 | this.state += movement;
110 | }
111 |
112 | try {
113 | this.propagate();
114 | } finally {
115 | this.options.onStep.call(this);
116 | if (this.target == this.state) {
117 | if (this.options.repeat === 'toggle')
118 | this.toggle();
119 | else if (this.options.repeat)
120 | this.play();
121 | else
122 | this.stop();
123 | }
124 | }
125 | },
126 | stop: function() {
127 | if (this.intervalId) {
128 | window.clearInterval(this.intervalId);
129 | this.intervalId = null;
130 | this.options.onComplete.call(this);
131 | }
132 | },
133 | // shortcuts
134 | play: function() {this.seekFromTo(0, 1)},
135 | reverse: function() {this.seekFromTo(1, 0)},
136 | // return a string describing this Animator, for debugging
137 | inspect: function() {
138 | var str = "#";
143 | return str;
144 | }
145 | }
146 | // merge the properties of two objects
147 | Animator.applyDefaults = function(defaults, prefs) {
148 | prefs = prefs || {};
149 | var prop, result = {};
150 | for (prop in defaults) result[prop] = prefs[prop] !== undefined ? prefs[prop] : defaults[prop];
151 | return result;
152 | }
153 | // make an array from any object
154 | Animator.makeArrayOfElements = function(o) {
155 | if (o == null) return [];
156 | if ("string" == typeof o) {
157 | return [document.getElementById(o)];
158 | }
159 | if (!o.length) return [o];
160 | var result = [];
161 | for (var i=0; i 20) return;
279 | }
280 | },
281 | getStyle: function(state) {
282 | state = this.from + ((this.to - this.from) * state);
283 | if (this.property == 'opacity') return state;
284 | return Math.round(state) + this.units;
285 | },
286 | inspect: function() {
287 | return "\t" + this.property + "(" + this.from + this.units + " to " + this.to + this.units + ")\n";
288 | }
289 | }
290 |
291 | // animates a colour based style property between two hex values
292 | function ColorStyleSubject(els, property, from, to) {
293 | this.els = Animator.makeArrayOfElements(els);
294 | this.property = Animator.camelize(property);
295 | this.to = this.expandColor(to);
296 | this.from = this.expandColor(from);
297 | this.origFrom = from;
298 | this.origTo = to;
299 | }
300 |
301 | ColorStyleSubject.prototype = {
302 | // parse "#FFFF00" to [256, 256, 0]
303 | expandColor: function(color) {
304 | var hexColor, red, green, blue;
305 | hexColor = ColorStyleSubject.parseColor(color);
306 | if (hexColor) {
307 | red = parseInt(hexColor.slice(1, 3), 16);
308 | green = parseInt(hexColor.slice(3, 5), 16);
309 | blue = parseInt(hexColor.slice(5, 7), 16);
310 | return [red,green,blue]
311 | }
312 | if (window.ANIMATOR_DEBUG) {
313 | alert("Invalid colour: '" + color + "'");
314 | }
315 | },
316 | getValueForState: function(color, state) {
317 | return Math.round(this.from[color] + ((this.to[color] - this.from[color]) * state));
318 | },
319 | setState: function(state) {
320 | var color = '#'
321 | + ColorStyleSubject.toColorPart(this.getValueForState(0, state))
322 | + ColorStyleSubject.toColorPart(this.getValueForState(1, state))
323 | + ColorStyleSubject.toColorPart(this.getValueForState(2, state));
324 | for (var i=0; i 255) number = 255;
358 | var digits = number.toString(16);
359 | if (number < 16) return '0' + digits;
360 | return digits;
361 | }
362 | ColorStyleSubject.parseColor.rgbRe = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
363 | ColorStyleSubject.parseColor.hexRe = /^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
364 |
365 | // Animates discrete styles, i.e. ones that do not scale but have discrete values
366 | // that can't be interpolated
367 | function DiscreteStyleSubject(els, property, from, to, threshold) {
368 | this.els = Animator.makeArrayOfElements(els);
369 | this.property = Animator.camelize(property);
370 | this.from = from;
371 | this.to = to;
372 | this.threshold = threshold || 0.5;
373 | }
374 |
375 | DiscreteStyleSubject.prototype = {
376 | setState: function(state) {
377 | var j=0;
378 | for (var i=0; i+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
10 | done = 0,
11 | toString = Object.prototype.toString,
12 | hasDuplicate = false,
13 | baseHasDuplicate = true,
14 | rBackslash = /\\/g,
15 | rNonWord = /\W/;
16 |
17 | // Here we check if the JavaScript engine is using some sort of
18 | // optimization where it does not always call our comparision
19 | // function. If that is the case, discard the hasDuplicate value.
20 | // Thus far that includes Google Chrome.
21 | [0, 0].sort(function() {
22 | baseHasDuplicate = false;
23 | return 0;
24 | });
25 |
26 | var Sizzle = function( selector, context, results, seed ) {
27 | results = results || [];
28 | context = context || document;
29 |
30 | var origContext = context;
31 |
32 | if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
33 | return [];
34 | }
35 |
36 | if ( !selector || typeof selector !== "string" ) {
37 | return results;
38 | }
39 |
40 | var m, set, checkSet, extra, ret, cur, pop, i,
41 | prune = true,
42 | contextXML = Sizzle.isXML( context ),
43 | parts = [],
44 | soFar = selector;
45 |
46 | // Reset the position of the chunker regexp (start from head)
47 | do {
48 | chunker.exec( "" );
49 | m = chunker.exec( soFar );
50 |
51 | if ( m ) {
52 | soFar = m[3];
53 |
54 | parts.push( m[1] );
55 |
56 | if ( m[2] ) {
57 | extra = m[3];
58 | break;
59 | }
60 | }
61 | } while ( m );
62 |
63 | if ( parts.length > 1 && origPOS.exec( selector ) ) {
64 |
65 | if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
66 | set = posProcess( parts[0] + parts[1], context );
67 |
68 | } else {
69 | set = Expr.relative[ parts[0] ] ?
70 | [ context ] :
71 | Sizzle( parts.shift(), context );
72 |
73 | while ( parts.length ) {
74 | selector = parts.shift();
75 |
76 | if ( Expr.relative[ selector ] ) {
77 | selector += parts.shift();
78 | }
79 |
80 | set = posProcess( selector, set );
81 | }
82 | }
83 |
84 | } else {
85 | // Take a shortcut and set the context if the root selector is an ID
86 | // (but not if it'll be faster if the inner selector is an ID)
87 | if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
88 | Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
89 |
90 | ret = Sizzle.find( parts.shift(), context, contextXML );
91 | context = ret.expr ?
92 | Sizzle.filter( ret.expr, ret.set )[0] :
93 | ret.set[0];
94 | }
95 |
96 | if ( context ) {
97 | ret = seed ?
98 | { expr: parts.pop(), set: makeArray(seed) } :
99 | Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
100 |
101 | set = ret.expr ?
102 | Sizzle.filter( ret.expr, ret.set ) :
103 | ret.set;
104 |
105 | if ( parts.length > 0 ) {
106 | checkSet = makeArray( set );
107 |
108 | } else {
109 | prune = false;
110 | }
111 |
112 | while ( parts.length ) {
113 | cur = parts.pop();
114 | pop = cur;
115 |
116 | if ( !Expr.relative[ cur ] ) {
117 | cur = "";
118 | } else {
119 | pop = parts.pop();
120 | }
121 |
122 | if ( pop == null ) {
123 | pop = context;
124 | }
125 |
126 | Expr.relative[ cur ]( checkSet, pop, contextXML );
127 | }
128 |
129 | } else {
130 | checkSet = parts = [];
131 | }
132 | }
133 |
134 | if ( !checkSet ) {
135 | checkSet = set;
136 | }
137 |
138 | if ( !checkSet ) {
139 | Sizzle.error( cur || selector );
140 | }
141 |
142 | if ( toString.call(checkSet) === "[object Array]" ) {
143 | if ( !prune ) {
144 | results.push.apply( results, checkSet );
145 |
146 | } else if ( context && context.nodeType === 1 ) {
147 | for ( i = 0; checkSet[i] != null; i++ ) {
148 | if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
149 | results.push( set[i] );
150 | }
151 | }
152 |
153 | } else {
154 | for ( i = 0; checkSet[i] != null; i++ ) {
155 | if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
156 | results.push( set[i] );
157 | }
158 | }
159 | }
160 |
161 | } else {
162 | makeArray( checkSet, results );
163 | }
164 |
165 | if ( extra ) {
166 | Sizzle( extra, origContext, results, seed );
167 | Sizzle.uniqueSort( results );
168 | }
169 |
170 | return results;
171 | };
172 |
173 | Sizzle.uniqueSort = function( results ) {
174 | if ( sortOrder ) {
175 | hasDuplicate = baseHasDuplicate;
176 | results.sort( sortOrder );
177 |
178 | if ( hasDuplicate ) {
179 | for ( var i = 1; i < results.length; i++ ) {
180 | if ( results[i] === results[ i - 1 ] ) {
181 | results.splice( i--, 1 );
182 | }
183 | }
184 | }
185 | }
186 |
187 | return results;
188 | };
189 |
190 | Sizzle.matches = function( expr, set ) {
191 | return Sizzle( expr, null, null, set );
192 | };
193 |
194 | Sizzle.matchesSelector = function( node, expr ) {
195 | return Sizzle( expr, null, null, [node] ).length > 0;
196 | };
197 |
198 | Sizzle.find = function( expr, context, isXML ) {
199 | var set;
200 |
201 | if ( !expr ) {
202 | return [];
203 | }
204 |
205 | for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
206 | var match,
207 | type = Expr.order[i];
208 |
209 | if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
210 | var left = match[1];
211 | match.splice( 1, 1 );
212 |
213 | if ( left.substr( left.length - 1 ) !== "\\" ) {
214 | match[1] = (match[1] || "").replace( rBackslash, "" );
215 | set = Expr.find[ type ]( match, context, isXML );
216 |
217 | if ( set != null ) {
218 | expr = expr.replace( Expr.match[ type ], "" );
219 | break;
220 | }
221 | }
222 | }
223 | }
224 |
225 | if ( !set ) {
226 | set = typeof context.getElementsByTagName !== "undefined" ?
227 | context.getElementsByTagName( "*" ) :
228 | [];
229 | }
230 |
231 | return { set: set, expr: expr };
232 | };
233 |
234 | Sizzle.filter = function( expr, set, inplace, not ) {
235 | var match, anyFound,
236 | old = expr,
237 | result = [],
238 | curLoop = set,
239 | isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
240 |
241 | while ( expr && set.length ) {
242 | for ( var type in Expr.filter ) {
243 | if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
244 | var found, item,
245 | filter = Expr.filter[ type ],
246 | left = match[1];
247 |
248 | anyFound = false;
249 |
250 | match.splice(1,1);
251 |
252 | if ( left.substr( left.length - 1 ) === "\\" ) {
253 | continue;
254 | }
255 |
256 | if ( curLoop === result ) {
257 | result = [];
258 | }
259 |
260 | if ( Expr.preFilter[ type ] ) {
261 | match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
262 |
263 | if ( !match ) {
264 | anyFound = found = true;
265 |
266 | } else if ( match === true ) {
267 | continue;
268 | }
269 | }
270 |
271 | if ( match ) {
272 | for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
273 | if ( item ) {
274 | found = filter( item, match, i, curLoop );
275 | var pass = not ^ !!found;
276 |
277 | if ( inplace && found != null ) {
278 | if ( pass ) {
279 | anyFound = true;
280 |
281 | } else {
282 | curLoop[i] = false;
283 | }
284 |
285 | } else if ( pass ) {
286 | result.push( item );
287 | anyFound = true;
288 | }
289 | }
290 | }
291 | }
292 |
293 | if ( found !== undefined ) {
294 | if ( !inplace ) {
295 | curLoop = result;
296 | }
297 |
298 | expr = expr.replace( Expr.match[ type ], "" );
299 |
300 | if ( !anyFound ) {
301 | return [];
302 | }
303 |
304 | break;
305 | }
306 | }
307 | }
308 |
309 | // Improper expression
310 | if ( expr === old ) {
311 | if ( anyFound == null ) {
312 | Sizzle.error( expr );
313 |
314 | } else {
315 | break;
316 | }
317 | }
318 |
319 | old = expr;
320 | }
321 |
322 | return curLoop;
323 | };
324 |
325 | Sizzle.error = function( msg ) {
326 | throw "Syntax error, unrecognized expression: " + msg;
327 | };
328 |
329 | var Expr = Sizzle.selectors = {
330 | order: [ "ID", "NAME", "TAG" ],
331 |
332 | match: {
333 | ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
334 | CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
335 | NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
336 | ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
337 | TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
338 | CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
339 | POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
340 | PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
341 | },
342 |
343 | leftMatch: {},
344 |
345 | attrMap: {
346 | "class": "className",
347 | "for": "htmlFor"
348 | },
349 |
350 | attrHandle: {
351 | href: function( elem ) {
352 | return elem.getAttribute( "href" );
353 | },
354 | type: function( elem ) {
355 | return elem.getAttribute( "type" );
356 | }
357 | },
358 |
359 | relative: {
360 | "+": function(checkSet, part){
361 | var isPartStr = typeof part === "string",
362 | isTag = isPartStr && !rNonWord.test( part ),
363 | isPartStrNotTag = isPartStr && !isTag;
364 |
365 | if ( isTag ) {
366 | part = part.toLowerCase();
367 | }
368 |
369 | for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
370 | if ( (elem = checkSet[i]) ) {
371 | while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
372 |
373 | checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
374 | elem || false :
375 | elem === part;
376 | }
377 | }
378 |
379 | if ( isPartStrNotTag ) {
380 | Sizzle.filter( part, checkSet, true );
381 | }
382 | },
383 |
384 | ">": function( checkSet, part ) {
385 | var elem,
386 | isPartStr = typeof part === "string",
387 | i = 0,
388 | l = checkSet.length;
389 |
390 | if ( isPartStr && !rNonWord.test( part ) ) {
391 | part = part.toLowerCase();
392 |
393 | for ( ; i < l; i++ ) {
394 | elem = checkSet[i];
395 |
396 | if ( elem ) {
397 | var parent = elem.parentNode;
398 | checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
399 | }
400 | }
401 |
402 | } else {
403 | for ( ; i < l; i++ ) {
404 | elem = checkSet[i];
405 |
406 | if ( elem ) {
407 | checkSet[i] = isPartStr ?
408 | elem.parentNode :
409 | elem.parentNode === part;
410 | }
411 | }
412 |
413 | if ( isPartStr ) {
414 | Sizzle.filter( part, checkSet, true );
415 | }
416 | }
417 | },
418 |
419 | "": function(checkSet, part, isXML){
420 | var nodeCheck,
421 | doneName = done++,
422 | checkFn = dirCheck;
423 |
424 | if ( typeof part === "string" && !rNonWord.test( part ) ) {
425 | part = part.toLowerCase();
426 | nodeCheck = part;
427 | checkFn = dirNodeCheck;
428 | }
429 |
430 | checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
431 | },
432 |
433 | "~": function( checkSet, part, isXML ) {
434 | var nodeCheck,
435 | doneName = done++,
436 | checkFn = dirCheck;
437 |
438 | if ( typeof part === "string" && !rNonWord.test( part ) ) {
439 | part = part.toLowerCase();
440 | nodeCheck = part;
441 | checkFn = dirNodeCheck;
442 | }
443 |
444 | checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
445 | }
446 | },
447 |
448 | find: {
449 | ID: function( match, context, isXML ) {
450 | if ( typeof context.getElementById !== "undefined" && !isXML ) {
451 | var m = context.getElementById(match[1]);
452 | // Check parentNode to catch when Blackberry 4.6 returns
453 | // nodes that are no longer in the document #6963
454 | return m && m.parentNode ? [m] : [];
455 | }
456 | },
457 |
458 | NAME: function( match, context ) {
459 | if ( typeof context.getElementsByName !== "undefined" ) {
460 | var ret = [],
461 | results = context.getElementsByName( match[1] );
462 |
463 | for ( var i = 0, l = results.length; i < l; i++ ) {
464 | if ( results[i].getAttribute("name") === match[1] ) {
465 | ret.push( results[i] );
466 | }
467 | }
468 |
469 | return ret.length === 0 ? null : ret;
470 | }
471 | },
472 |
473 | TAG: function( match, context ) {
474 | if ( typeof context.getElementsByTagName !== "undefined" ) {
475 | return context.getElementsByTagName( match[1] );
476 | }
477 | }
478 | },
479 | preFilter: {
480 | CLASS: function( match, curLoop, inplace, result, not, isXML ) {
481 | match = " " + match[1].replace( rBackslash, "" ) + " ";
482 |
483 | if ( isXML ) {
484 | return match;
485 | }
486 |
487 | for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
488 | if ( elem ) {
489 | if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
490 | if ( !inplace ) {
491 | result.push( elem );
492 | }
493 |
494 | } else if ( inplace ) {
495 | curLoop[i] = false;
496 | }
497 | }
498 | }
499 |
500 | return false;
501 | },
502 |
503 | ID: function( match ) {
504 | return match[1].replace( rBackslash, "" );
505 | },
506 |
507 | TAG: function( match, curLoop ) {
508 | return match[1].replace( rBackslash, "" ).toLowerCase();
509 | },
510 |
511 | CHILD: function( match ) {
512 | if ( match[1] === "nth" ) {
513 | if ( !match[2] ) {
514 | Sizzle.error( match[0] );
515 | }
516 |
517 | match[2] = match[2].replace(/^\+|\s*/g, '');
518 |
519 | // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
520 | var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
521 | match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
522 | !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
523 |
524 | // calculate the numbers (first)n+(last) including if they are negative
525 | match[2] = (test[1] + (test[2] || 1)) - 0;
526 | match[3] = test[3] - 0;
527 | }
528 | else if ( match[2] ) {
529 | Sizzle.error( match[0] );
530 | }
531 |
532 | // TODO: Move to normal caching system
533 | match[0] = done++;
534 |
535 | return match;
536 | },
537 |
538 | ATTR: function( match, curLoop, inplace, result, not, isXML ) {
539 | var name = match[1] = match[1].replace( rBackslash, "" );
540 |
541 | if ( !isXML && Expr.attrMap[name] ) {
542 | match[1] = Expr.attrMap[name];
543 | }
544 |
545 | // Handle if an un-quoted value was used
546 | match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
547 |
548 | if ( match[2] === "~=" ) {
549 | match[4] = " " + match[4] + " ";
550 | }
551 |
552 | return match;
553 | },
554 |
555 | PSEUDO: function( match, curLoop, inplace, result, not ) {
556 | if ( match[1] === "not" ) {
557 | // If we're dealing with a complex expression, or a simple one
558 | if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
559 | match[3] = Sizzle(match[3], null, null, curLoop);
560 |
561 | } else {
562 | var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
563 |
564 | if ( !inplace ) {
565 | result.push.apply( result, ret );
566 | }
567 |
568 | return false;
569 | }
570 |
571 | } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
572 | return true;
573 | }
574 |
575 | return match;
576 | },
577 |
578 | POS: function( match ) {
579 | match.unshift( true );
580 |
581 | return match;
582 | }
583 | },
584 |
585 | filters: {
586 | enabled: function( elem ) {
587 | return elem.disabled === false && elem.type !== "hidden";
588 | },
589 |
590 | disabled: function( elem ) {
591 | return elem.disabled === true;
592 | },
593 |
594 | checked: function( elem ) {
595 | return elem.checked === true;
596 | },
597 |
598 | selected: function( elem ) {
599 | // Accessing this property makes selected-by-default
600 | // options in Safari work properly
601 | if ( elem.parentNode ) {
602 | elem.parentNode.selectedIndex;
603 | }
604 |
605 | return elem.selected === true;
606 | },
607 |
608 | parent: function( elem ) {
609 | return !!elem.firstChild;
610 | },
611 |
612 | empty: function( elem ) {
613 | return !elem.firstChild;
614 | },
615 |
616 | has: function( elem, i, match ) {
617 | return !!Sizzle( match[3], elem ).length;
618 | },
619 |
620 | header: function( elem ) {
621 | return (/h\d/i).test( elem.nodeName );
622 | },
623 |
624 | text: function( elem ) {
625 | var attr = elem.getAttribute( "type" ), type = elem.type;
626 | // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
627 | // use getAttribute instead to test this case
628 | return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
629 | },
630 |
631 | radio: function( elem ) {
632 | return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
633 | },
634 |
635 | checkbox: function( elem ) {
636 | return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
637 | },
638 |
639 | file: function( elem ) {
640 | return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
641 | },
642 |
643 | password: function( elem ) {
644 | return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
645 | },
646 |
647 | submit: function( elem ) {
648 | var name = elem.nodeName.toLowerCase();
649 | return (name === "input" || name === "button") && "submit" === elem.type;
650 | },
651 |
652 | image: function( elem ) {
653 | return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
654 | },
655 |
656 | reset: function( elem ) {
657 | var name = elem.nodeName.toLowerCase();
658 | return (name === "input" || name === "button") && "reset" === elem.type;
659 | },
660 |
661 | button: function( elem ) {
662 | var name = elem.nodeName.toLowerCase();
663 | return name === "input" && "button" === elem.type || name === "button";
664 | },
665 |
666 | input: function( elem ) {
667 | return (/input|select|textarea|button/i).test( elem.nodeName );
668 | },
669 |
670 | focus: function( elem ) {
671 | return elem === elem.ownerDocument.activeElement;
672 | }
673 | },
674 | setFilters: {
675 | first: function( elem, i ) {
676 | return i === 0;
677 | },
678 |
679 | last: function( elem, i, match, array ) {
680 | return i === array.length - 1;
681 | },
682 |
683 | even: function( elem, i ) {
684 | return i % 2 === 0;
685 | },
686 |
687 | odd: function( elem, i ) {
688 | return i % 2 === 1;
689 | },
690 |
691 | lt: function( elem, i, match ) {
692 | return i < match[3] - 0;
693 | },
694 |
695 | gt: function( elem, i, match ) {
696 | return i > match[3] - 0;
697 | },
698 |
699 | nth: function( elem, i, match ) {
700 | return match[3] - 0 === i;
701 | },
702 |
703 | eq: function( elem, i, match ) {
704 | return match[3] - 0 === i;
705 | }
706 | },
707 | filter: {
708 | PSEUDO: function( elem, match, i, array ) {
709 | var name = match[1],
710 | filter = Expr.filters[ name ];
711 |
712 | if ( filter ) {
713 | return filter( elem, i, match, array );
714 |
715 | } else if ( name === "contains" ) {
716 | return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
717 |
718 | } else if ( name === "not" ) {
719 | var not = match[3];
720 |
721 | for ( var j = 0, l = not.length; j < l; j++ ) {
722 | if ( not[j] === elem ) {
723 | return false;
724 | }
725 | }
726 |
727 | return true;
728 |
729 | } else {
730 | Sizzle.error( name );
731 | }
732 | },
733 |
734 | CHILD: function( elem, match ) {
735 | var type = match[1],
736 | node = elem;
737 |
738 | switch ( type ) {
739 | case "only":
740 | case "first":
741 | while ( (node = node.previousSibling) ) {
742 | if ( node.nodeType === 1 ) {
743 | return false;
744 | }
745 | }
746 |
747 | if ( type === "first" ) {
748 | return true;
749 | }
750 |
751 | node = elem;
752 |
753 | case "last":
754 | while ( (node = node.nextSibling) ) {
755 | if ( node.nodeType === 1 ) {
756 | return false;
757 | }
758 | }
759 |
760 | return true;
761 |
762 | case "nth":
763 | var first = match[2],
764 | last = match[3];
765 |
766 | if ( first === 1 && last === 0 ) {
767 | return true;
768 | }
769 |
770 | var doneName = match[0],
771 | parent = elem.parentNode;
772 |
773 | if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
774 | var count = 0;
775 |
776 | for ( node = parent.firstChild; node; node = node.nextSibling ) {
777 | if ( node.nodeType === 1 ) {
778 | node.nodeIndex = ++count;
779 | }
780 | }
781 |
782 | parent.sizcache = doneName;
783 | }
784 |
785 | var diff = elem.nodeIndex - last;
786 |
787 | if ( first === 0 ) {
788 | return diff === 0;
789 |
790 | } else {
791 | return ( diff % first === 0 && diff / first >= 0 );
792 | }
793 | }
794 | },
795 |
796 | ID: function( elem, match ) {
797 | return elem.nodeType === 1 && elem.getAttribute("id") === match;
798 | },
799 |
800 | TAG: function( elem, match ) {
801 | return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
802 | },
803 |
804 | CLASS: function( elem, match ) {
805 | return (" " + (elem.className || elem.getAttribute("class")) + " ")
806 | .indexOf( match ) > -1;
807 | },
808 |
809 | ATTR: function( elem, match ) {
810 | var name = match[1],
811 | result = Expr.attrHandle[ name ] ?
812 | Expr.attrHandle[ name ]( elem ) :
813 | elem[ name ] != null ?
814 | elem[ name ] :
815 | elem.getAttribute( name ),
816 | value = result + "",
817 | type = match[2],
818 | check = match[4];
819 |
820 | return result == null ?
821 | type === "!=" :
822 | type === "=" ?
823 | value === check :
824 | type === "*=" ?
825 | value.indexOf(check) >= 0 :
826 | type === "~=" ?
827 | (" " + value + " ").indexOf(check) >= 0 :
828 | !check ?
829 | value && result !== false :
830 | type === "!=" ?
831 | value !== check :
832 | type === "^=" ?
833 | value.indexOf(check) === 0 :
834 | type === "$=" ?
835 | value.substr(value.length - check.length) === check :
836 | type === "|=" ?
837 | value === check || value.substr(0, check.length + 1) === check + "-" :
838 | false;
839 | },
840 |
841 | POS: function( elem, match, i, array ) {
842 | var name = match[2],
843 | filter = Expr.setFilters[ name ];
844 |
845 | if ( filter ) {
846 | return filter( elem, i, match, array );
847 | }
848 | }
849 | }
850 | };
851 |
852 | var origPOS = Expr.match.POS,
853 | fescape = function(all, num){
854 | return "\\" + (num - 0 + 1);
855 | };
856 |
857 | for ( var type in Expr.match ) {
858 | Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
859 | Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
860 | }
861 |
862 | var makeArray = function( array, results ) {
863 | array = Array.prototype.slice.call( array, 0 );
864 |
865 | if ( results ) {
866 | results.push.apply( results, array );
867 | return results;
868 | }
869 |
870 | return array;
871 | };
872 |
873 | // Perform a simple check to determine if the browser is capable of
874 | // converting a NodeList to an array using builtin methods.
875 | // Also verifies that the returned array holds DOM nodes
876 | // (which is not the case in the Blackberry browser)
877 | try {
878 | Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
879 |
880 | // Provide a fallback method if it does not work
881 | } catch( e ) {
882 | makeArray = function( array, results ) {
883 | var i = 0,
884 | ret = results || [];
885 |
886 | if ( toString.call(array) === "[object Array]" ) {
887 | Array.prototype.push.apply( ret, array );
888 |
889 | } else {
890 | if ( typeof array.length === "number" ) {
891 | for ( var l = array.length; i < l; i++ ) {
892 | ret.push( array[i] );
893 | }
894 |
895 | } else {
896 | for ( ; array[i]; i++ ) {
897 | ret.push( array[i] );
898 | }
899 | }
900 | }
901 |
902 | return ret;
903 | };
904 | }
905 |
906 | var sortOrder, siblingCheck;
907 |
908 | if ( document.documentElement.compareDocumentPosition ) {
909 | sortOrder = function( a, b ) {
910 | if ( a === b ) {
911 | hasDuplicate = true;
912 | return 0;
913 | }
914 |
915 | if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
916 | return a.compareDocumentPosition ? -1 : 1;
917 | }
918 |
919 | return a.compareDocumentPosition(b) & 4 ? -1 : 1;
920 | };
921 |
922 | } else {
923 | sortOrder = function( a, b ) {
924 | // The nodes are identical, we can exit early
925 | if ( a === b ) {
926 | hasDuplicate = true;
927 | return 0;
928 |
929 | // Fallback to using sourceIndex (in IE) if it's available on both nodes
930 | } else if ( a.sourceIndex && b.sourceIndex ) {
931 | return a.sourceIndex - b.sourceIndex;
932 | }
933 |
934 | var al, bl,
935 | ap = [],
936 | bp = [],
937 | aup = a.parentNode,
938 | bup = b.parentNode,
939 | cur = aup;
940 |
941 | // If the nodes are siblings (or identical) we can do a quick check
942 | if ( aup === bup ) {
943 | return siblingCheck( a, b );
944 |
945 | // If no parents were found then the nodes are disconnected
946 | } else if ( !aup ) {
947 | return -1;
948 |
949 | } else if ( !bup ) {
950 | return 1;
951 | }
952 |
953 | // Otherwise they're somewhere else in the tree so we need
954 | // to build up a full list of the parentNodes for comparison
955 | while ( cur ) {
956 | ap.unshift( cur );
957 | cur = cur.parentNode;
958 | }
959 |
960 | cur = bup;
961 |
962 | while ( cur ) {
963 | bp.unshift( cur );
964 | cur = cur.parentNode;
965 | }
966 |
967 | al = ap.length;
968 | bl = bp.length;
969 |
970 | // Start walking down the tree looking for a discrepancy
971 | for ( var i = 0; i < al && i < bl; i++ ) {
972 | if ( ap[i] !== bp[i] ) {
973 | return siblingCheck( ap[i], bp[i] );
974 | }
975 | }
976 |
977 | // We ended someplace up the tree so do a sibling check
978 | return i === al ?
979 | siblingCheck( a, bp[i], -1 ) :
980 | siblingCheck( ap[i], b, 1 );
981 | };
982 |
983 | siblingCheck = function( a, b, ret ) {
984 | if ( a === b ) {
985 | return ret;
986 | }
987 |
988 | var cur = a.nextSibling;
989 |
990 | while ( cur ) {
991 | if ( cur === b ) {
992 | return -1;
993 | }
994 |
995 | cur = cur.nextSibling;
996 | }
997 |
998 | return 1;
999 | };
1000 | }
1001 |
1002 | // Utility function for retreiving the text value of an array of DOM nodes
1003 | Sizzle.getText = function( elems ) {
1004 | var ret = "", elem;
1005 |
1006 | for ( var i = 0; elems[i]; i++ ) {
1007 | elem = elems[i];
1008 |
1009 | // Get the text from text nodes and CDATA nodes
1010 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
1011 | ret += elem.nodeValue;
1012 |
1013 | // Traverse everything else, except comment nodes
1014 | } else if ( elem.nodeType !== 8 ) {
1015 | ret += Sizzle.getText( elem.childNodes );
1016 | }
1017 | }
1018 |
1019 | return ret;
1020 | };
1021 |
1022 | // Check to see if the browser returns elements by name when
1023 | // querying by getElementById (and provide a workaround)
1024 | (function(){
1025 | // We're going to inject a fake input element with a specified name
1026 | var form = document.createElement("div"),
1027 | id = "script" + (new Date()).getTime(),
1028 | root = document.documentElement;
1029 |
1030 | form.innerHTML = "";
1031 |
1032 | // Inject it into the root element, check its status, and remove it quickly
1033 | root.insertBefore( form, root.firstChild );
1034 |
1035 | // The workaround has to do additional checks after a getElementById
1036 | // Which slows things down for other browsers (hence the branching)
1037 | if ( document.getElementById( id ) ) {
1038 | Expr.find.ID = function( match, context, isXML ) {
1039 | if ( typeof context.getElementById !== "undefined" && !isXML ) {
1040 | var m = context.getElementById(match[1]);
1041 |
1042 | return m ?
1043 | m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
1044 | [m] :
1045 | undefined :
1046 | [];
1047 | }
1048 | };
1049 |
1050 | Expr.filter.ID = function( elem, match ) {
1051 | var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
1052 |
1053 | return elem.nodeType === 1 && node && node.nodeValue === match;
1054 | };
1055 | }
1056 |
1057 | root.removeChild( form );
1058 |
1059 | // release memory in IE
1060 | root = form = null;
1061 | })();
1062 |
1063 | (function(){
1064 | // Check to see if the browser returns only elements
1065 | // when doing getElementsByTagName("*")
1066 |
1067 | // Create a fake element
1068 | var div = document.createElement("div");
1069 | div.appendChild( document.createComment("") );
1070 |
1071 | // Make sure no comments are found
1072 | if ( div.getElementsByTagName("*").length > 0 ) {
1073 | Expr.find.TAG = function( match, context ) {
1074 | var results = context.getElementsByTagName( match[1] );
1075 |
1076 | // Filter out possible comments
1077 | if ( match[1] === "*" ) {
1078 | var tmp = [];
1079 |
1080 | for ( var i = 0; results[i]; i++ ) {
1081 | if ( results[i].nodeType === 1 ) {
1082 | tmp.push( results[i] );
1083 | }
1084 | }
1085 |
1086 | results = tmp;
1087 | }
1088 |
1089 | return results;
1090 | };
1091 | }
1092 |
1093 | // Check to see if an attribute returns normalized href attributes
1094 | div.innerHTML = "";
1095 |
1096 | if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
1097 | div.firstChild.getAttribute("href") !== "#" ) {
1098 |
1099 | Expr.attrHandle.href = function( elem ) {
1100 | return elem.getAttribute( "href", 2 );
1101 | };
1102 | }
1103 |
1104 | // release memory in IE
1105 | div = null;
1106 | })();
1107 |
1108 | if ( document.querySelectorAll ) {
1109 | (function(){
1110 | var oldSizzle = Sizzle,
1111 | div = document.createElement("div"),
1112 | id = "__sizzle__";
1113 |
1114 | div.innerHTML = "";
1115 |
1116 | // Safari can't handle uppercase or unicode characters when
1117 | // in quirks mode.
1118 | if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
1119 | return;
1120 | }
1121 |
1122 | Sizzle = function( query, context, extra, seed ) {
1123 | context = context || document;
1124 |
1125 | // Only use querySelectorAll on non-XML documents
1126 | // (ID selectors don't work in non-HTML documents)
1127 | if ( !seed && !Sizzle.isXML(context) ) {
1128 | // See if we find a selector to speed up
1129 | var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
1130 |
1131 | if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
1132 | // Speed-up: Sizzle("TAG")
1133 | if ( match[1] ) {
1134 | return makeArray( context.getElementsByTagName( query ), extra );
1135 |
1136 | // Speed-up: Sizzle(".CLASS")
1137 | } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
1138 | return makeArray( context.getElementsByClassName( match[2] ), extra );
1139 | }
1140 | }
1141 |
1142 | if ( context.nodeType === 9 ) {
1143 | // Speed-up: Sizzle("body")
1144 | // The body element only exists once, optimize finding it
1145 | if ( query === "body" && context.body ) {
1146 | return makeArray( [ context.body ], extra );
1147 |
1148 | // Speed-up: Sizzle("#ID")
1149 | } else if ( match && match[3] ) {
1150 | var elem = context.getElementById( match[3] );
1151 |
1152 | // Check parentNode to catch when Blackberry 4.6 returns
1153 | // nodes that are no longer in the document #6963
1154 | if ( elem && elem.parentNode ) {
1155 | // Handle the case where IE and Opera return items
1156 | // by name instead of ID
1157 | if ( elem.id === match[3] ) {
1158 | return makeArray( [ elem ], extra );
1159 | }
1160 |
1161 | } else {
1162 | return makeArray( [], extra );
1163 | }
1164 | }
1165 |
1166 | try {
1167 | return makeArray( context.querySelectorAll(query), extra );
1168 | } catch(qsaError) {}
1169 |
1170 | // qSA works strangely on Element-rooted queries
1171 | // We can work around this by specifying an extra ID on the root
1172 | // and working up from there (Thanks to Andrew Dupont for the technique)
1173 | // IE 8 doesn't work on object elements
1174 | } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
1175 | var oldContext = context,
1176 | old = context.getAttribute( "id" ),
1177 | nid = old || id,
1178 | hasParent = context.parentNode,
1179 | relativeHierarchySelector = /^\s*[+~]/.test( query );
1180 |
1181 | if ( !old ) {
1182 | context.setAttribute( "id", nid );
1183 | } else {
1184 | nid = nid.replace( /'/g, "\\$&" );
1185 | }
1186 | if ( relativeHierarchySelector && hasParent ) {
1187 | context = context.parentNode;
1188 | }
1189 |
1190 | try {
1191 | if ( !relativeHierarchySelector || hasParent ) {
1192 | return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
1193 | }
1194 |
1195 | } catch(pseudoError) {
1196 | } finally {
1197 | if ( !old ) {
1198 | oldContext.removeAttribute( "id" );
1199 | }
1200 | }
1201 | }
1202 | }
1203 |
1204 | return oldSizzle(query, context, extra, seed);
1205 | };
1206 |
1207 | for ( var prop in oldSizzle ) {
1208 | Sizzle[ prop ] = oldSizzle[ prop ];
1209 | }
1210 |
1211 | // release memory in IE
1212 | div = null;
1213 | })();
1214 | }
1215 |
1216 | (function(){
1217 | var html = document.documentElement,
1218 | matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
1219 |
1220 | if ( matches ) {
1221 | // Check to see if it's possible to do matchesSelector
1222 | // on a disconnected node (IE 9 fails this)
1223 | var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
1224 | pseudoWorks = false;
1225 |
1226 | try {
1227 | // This should fail with an exception
1228 | // Gecko does not error, returns false instead
1229 | matches.call( document.documentElement, "[test!='']:sizzle" );
1230 |
1231 | } catch( pseudoError ) {
1232 | pseudoWorks = true;
1233 | }
1234 |
1235 | Sizzle.matchesSelector = function( node, expr ) {
1236 | // Make sure that attribute selectors are quoted
1237 | expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
1238 |
1239 | if ( !Sizzle.isXML( node ) ) {
1240 | try {
1241 | if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
1242 | var ret = matches.call( node, expr );
1243 |
1244 | // IE 9's matchesSelector returns false on disconnected nodes
1245 | if ( ret || !disconnectedMatch ||
1246 | // As well, disconnected nodes are said to be in a document
1247 | // fragment in IE 9, so check for that
1248 | node.document && node.document.nodeType !== 11 ) {
1249 | return ret;
1250 | }
1251 | }
1252 | } catch(e) {}
1253 | }
1254 |
1255 | return Sizzle(expr, null, null, [node]).length > 0;
1256 | };
1257 | }
1258 | })();
1259 |
1260 | (function(){
1261 | var div = document.createElement("div");
1262 |
1263 | div.innerHTML = "";
1264 |
1265 | // Opera can't find a second classname (in 9.6)
1266 | // Also, make sure that getElementsByClassName actually exists
1267 | if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
1268 | return;
1269 | }
1270 |
1271 | // Safari caches class attributes, doesn't catch changes (in 3.2)
1272 | div.lastChild.className = "e";
1273 |
1274 | if ( div.getElementsByClassName("e").length === 1 ) {
1275 | return;
1276 | }
1277 |
1278 | Expr.order.splice(1, 0, "CLASS");
1279 | Expr.find.CLASS = function( match, context, isXML ) {
1280 | if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
1281 | return context.getElementsByClassName(match[1]);
1282 | }
1283 | };
1284 |
1285 | // release memory in IE
1286 | div = null;
1287 | })();
1288 |
1289 | function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
1290 | for ( var i = 0, l = checkSet.length; i < l; i++ ) {
1291 | var elem = checkSet[i];
1292 |
1293 | if ( elem ) {
1294 | var match = false;
1295 |
1296 | elem = elem[dir];
1297 |
1298 | while ( elem ) {
1299 | if ( elem.sizcache === doneName ) {
1300 | match = checkSet[elem.sizset];
1301 | break;
1302 | }
1303 |
1304 | if ( elem.nodeType === 1 && !isXML ){
1305 | elem.sizcache = doneName;
1306 | elem.sizset = i;
1307 | }
1308 |
1309 | if ( elem.nodeName.toLowerCase() === cur ) {
1310 | match = elem;
1311 | break;
1312 | }
1313 |
1314 | elem = elem[dir];
1315 | }
1316 |
1317 | checkSet[i] = match;
1318 | }
1319 | }
1320 | }
1321 |
1322 | function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
1323 | for ( var i = 0, l = checkSet.length; i < l; i++ ) {
1324 | var elem = checkSet[i];
1325 |
1326 | if ( elem ) {
1327 | var match = false;
1328 |
1329 | elem = elem[dir];
1330 |
1331 | while ( elem ) {
1332 | if ( elem.sizcache === doneName ) {
1333 | match = checkSet[elem.sizset];
1334 | break;
1335 | }
1336 |
1337 | if ( elem.nodeType === 1 ) {
1338 | if ( !isXML ) {
1339 | elem.sizcache = doneName;
1340 | elem.sizset = i;
1341 | }
1342 |
1343 | if ( typeof cur !== "string" ) {
1344 | if ( elem === cur ) {
1345 | match = true;
1346 | break;
1347 | }
1348 |
1349 | } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
1350 | match = elem;
1351 | break;
1352 | }
1353 | }
1354 |
1355 | elem = elem[dir];
1356 | }
1357 |
1358 | checkSet[i] = match;
1359 | }
1360 | }
1361 | }
1362 |
1363 | if ( document.documentElement.contains ) {
1364 | Sizzle.contains = function( a, b ) {
1365 | return a !== b && (a.contains ? a.contains(b) : true);
1366 | };
1367 |
1368 | } else if ( document.documentElement.compareDocumentPosition ) {
1369 | Sizzle.contains = function( a, b ) {
1370 | return !!(a.compareDocumentPosition(b) & 16);
1371 | };
1372 |
1373 | } else {
1374 | Sizzle.contains = function() {
1375 | return false;
1376 | };
1377 | }
1378 |
1379 | Sizzle.isXML = function( elem ) {
1380 | // documentElement is verified for cases where it doesn't yet exist
1381 | // (such as loading iframes in IE - #4833)
1382 | var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
1383 |
1384 | return documentElement ? documentElement.nodeName !== "HTML" : false;
1385 | };
1386 |
1387 | var posProcess = function( selector, context ) {
1388 | var match,
1389 | tmpSet = [],
1390 | later = "",
1391 | root = context.nodeType ? [context] : context;
1392 |
1393 | // Position selectors must be done after the filter
1394 | // And so must :not(positional) so we move all PSEUDOs to the end
1395 | while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
1396 | later += match[0];
1397 | selector = selector.replace( Expr.match.PSEUDO, "" );
1398 | }
1399 |
1400 | selector = Expr.relative[selector] ? selector + "*" : selector;
1401 |
1402 | for ( var i = 0, l = root.length; i < l; i++ ) {
1403 | Sizzle( selector, root[i], tmpSet );
1404 | }
1405 |
1406 | return Sizzle.filter( later, tmpSet );
1407 | };
1408 |
1409 | // EXPOSE
1410 |
1411 | window.Sizzle = Sizzle;
1412 |
1413 | })();
1414 |
--------------------------------------------------------------------------------
/js/game/vendor/state-machine.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Javascript State Machine Library - https://github.com/jakesgordon/javascript-state-machine
4 |
5 | Copyright (c) 2012, 2013 Jake Gordon and contributors
6 | Released under the MIT license - https://github.com/jakesgordon/javascript-state-machine/blob/master/LICENSE
7 |
8 | */
9 |
10 | (function (window) {
11 |
12 | var StateMachine = {
13 |
14 | //---------------------------------------------------------------------------
15 |
16 | VERSION: "2.2.0",
17 |
18 | //---------------------------------------------------------------------------
19 |
20 | Result: {
21 | SUCCEEDED: 1, // the event transitioned successfully from one state to another
22 | NOTRANSITION: 2, // the event was successfull but no state transition was necessary
23 | CANCELLED: 3, // the event was cancelled by the caller in a beforeEvent callback
24 | PENDING: 4 // the event is asynchronous and the caller is in control of when the transition occurs
25 | },
26 |
27 | Error: {
28 | INVALID_TRANSITION: 100, // caller tried to fire an event that was innapropriate in the current state
29 | PENDING_TRANSITION: 200, // caller tried to fire an event while an async transition was still pending
30 | INVALID_CALLBACK: 300 // caller provided callback function threw an exception
31 | },
32 |
33 | WILDCARD: '*',
34 | ASYNC: 'async',
35 |
36 | //---------------------------------------------------------------------------
37 |
38 | create: function(cfg, target) {
39 |
40 | var initial = (typeof cfg.initial == 'string') ? { state: cfg.initial } : cfg.initial; // allow for a simple string, or an object with { state: 'foo', event: 'setup', defer: true|false }
41 | var terminal = cfg.terminal || cfg['final'];
42 | var fsm = target || cfg.target || {};
43 | var events = cfg.events || [];
44 | var callbacks = cfg.callbacks || {};
45 | var map = {};
46 |
47 | var add = function(e) {
48 | var from = (e.from instanceof Array) ? e.from : (e.from ? [e.from] : [StateMachine.WILDCARD]); // allow 'wildcard' transition if 'from' is not specified
49 | map[e.name] = map[e.name] || {};
50 | for (var n = 0 ; n < from.length ; n++)
51 | map[e.name][from[n]] = e.to || from[n]; // allow no-op transition if 'to' is not specified
52 | };
53 |
54 | if (initial) {
55 | initial.event = initial.event || 'startup';
56 | add({ name: initial.event, from: 'none', to: initial.state });
57 | }
58 |
59 | for(var n = 0 ; n < events.length ; n++)
60 | add(events[n]);
61 |
62 | for(var name in map) {
63 | if (map.hasOwnProperty(name))
64 | fsm[name] = StateMachine.buildEvent(name, map[name]);
65 | }
66 |
67 | for(var name in callbacks) {
68 | if (callbacks.hasOwnProperty(name))
69 | fsm[name] = callbacks[name]
70 | }
71 |
72 | fsm.current = 'none';
73 | fsm.is = function(state) { return (state instanceof Array) ? (state.indexOf(this.current) >= 0) : (this.current === state); };
74 | fsm.can = function(event) { return !this.transition && (map[event].hasOwnProperty(this.current) || map[event].hasOwnProperty(StateMachine.WILDCARD)); }
75 | fsm.cannot = function(event) { return !this.can(event); };
76 | fsm.error = cfg.error || function(name, from, to, args, error, msg, e) { throw e || msg; }; // default behavior when something unexpected happens is to throw an exception, but caller can override this behavior if desired (see github issue #3 and #17)
77 |
78 | fsm.isFinished = function() { return this.is(terminal); };
79 |
80 | if (initial && !initial.defer)
81 | fsm[initial.event]();
82 |
83 | return fsm;
84 |
85 | },
86 |
87 | //===========================================================================
88 |
89 | doCallback: function(fsm, func, name, from, to, args) {
90 | if (func) {
91 | try {
92 | return func.apply(fsm, [name, from, to].concat(args));
93 | }
94 | catch(e) {
95 | return fsm.error(name, from, to, args, StateMachine.Error.INVALID_CALLBACK, "an exception occurred in a caller-provided callback function", e);
96 | }
97 | }
98 | },
99 |
100 | beforeAnyEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onbeforeevent'], name, from, to, args); },
101 | afterAnyEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onafterevent'] || fsm['onevent'], name, from, to, args); },
102 | leaveAnyState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onleavestate'], name, from, to, args); },
103 | enterAnyState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onenterstate'] || fsm['onstate'], name, from, to, args); },
104 | changeState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onchangestate'], name, from, to, args); },
105 |
106 | beforeThisEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onbefore' + name], name, from, to, args); },
107 | afterThisEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onafter' + name] || fsm['on' + name], name, from, to, args); },
108 | leaveThisState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onleave' + from], name, from, to, args); },
109 | enterThisState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onenter' + to] || fsm['on' + to], name, from, to, args); },
110 |
111 | beforeEvent: function(fsm, name, from, to, args) {
112 | if ((false === StateMachine.beforeThisEvent(fsm, name, from, to, args)) ||
113 | (false === StateMachine.beforeAnyEvent( fsm, name, from, to, args)))
114 | return false;
115 | },
116 |
117 | afterEvent: function(fsm, name, from, to, args) {
118 | StateMachine.afterThisEvent(fsm, name, from, to, args);
119 | StateMachine.afterAnyEvent( fsm, name, from, to, args);
120 | },
121 |
122 | leaveState: function(fsm, name, from, to, args) {
123 | var specific = StateMachine.leaveThisState(fsm, name, from, to, args),
124 | general = StateMachine.leaveAnyState( fsm, name, from, to, args);
125 | if ((false === specific) || (false === general))
126 | return false;
127 | else if ((StateMachine.ASYNC === specific) || (StateMachine.ASYNC === general))
128 | return StateMachine.ASYNC;
129 | },
130 |
131 | enterState: function(fsm, name, from, to, args) {
132 | StateMachine.enterThisState(fsm, name, from, to, args);
133 | StateMachine.enterAnyState( fsm, name, from, to, args);
134 | },
135 |
136 | //===========================================================================
137 |
138 | buildEvent: function(name, map) {
139 | return function() {
140 |
141 | var from = this.current;
142 | var to = map[from] || map[StateMachine.WILDCARD] || from;
143 | var args = Array.prototype.slice.call(arguments); // turn arguments into pure array
144 |
145 | if (this.transition)
146 | return this.error(name, from, to, args, StateMachine.Error.PENDING_TRANSITION, "event " + name + " inappropriate because previous transition did not complete");
147 |
148 | if (this.cannot(name))
149 | return this.error(name, from, to, args, StateMachine.Error.INVALID_TRANSITION, "event " + name + " inappropriate in current state " + this.current);
150 |
151 | if (false === StateMachine.beforeEvent(this, name, from, to, args))
152 | return StateMachine.Result.CANCELLED;
153 |
154 | if (from === to) {
155 | StateMachine.afterEvent(this, name, from, to, args);
156 | return StateMachine.Result.NOTRANSITION;
157 | }
158 |
159 | // prepare a transition method for use EITHER lower down, or by caller if they want an async transition (indicated by an ASYNC return value from leaveState)
160 | var fsm = this;
161 | this.transition = function() {
162 | fsm.transition = null; // this method should only ever be called once
163 | fsm.current = to;
164 | StateMachine.enterState( fsm, name, from, to, args);
165 | StateMachine.changeState(fsm, name, from, to, args);
166 | StateMachine.afterEvent( fsm, name, from, to, args);
167 | return StateMachine.Result.SUCCEEDED;
168 | };
169 | this.transition.cancel = function() { // provide a way for caller to cancel async transition if desired (issue #22)
170 | fsm.transition = null;
171 | StateMachine.afterEvent(fsm, name, from, to, args);
172 | }
173 |
174 | var leave = StateMachine.leaveState(this, name, from, to, args);
175 | if (false === leave) {
176 | this.transition = null;
177 | return StateMachine.Result.CANCELLED;
178 | }
179 | else if (StateMachine.ASYNC === leave) {
180 | return StateMachine.Result.PENDING;
181 | }
182 | else {
183 | if (this.transition) // need to check in case user manually called transition() but forgot to return StateMachine.ASYNC
184 | return this.transition();
185 | }
186 |
187 | };
188 | }
189 |
190 | }; // StateMachine
191 |
192 | //===========================================================================
193 |
194 | if ("function" === typeof define) {
195 | define(function(require) { return StateMachine; });
196 | }
197 | else {
198 | window.StateMachine = StateMachine;
199 | }
200 |
201 | }(this));
202 |
203 |
--------------------------------------------------------------------------------
/js/game/vendor/stats.js:
--------------------------------------------------------------------------------
1 | // stats.js r6 - http://github.com/mrdoob/stats.js
2 | var Stats=function(){function s(a,g,d){var f,c,e;for(c=0;c<30;c++)for(f=0;f<73;f++)e=(f+c*74)*4,a[e]=a[e+4],a[e+1]=a[e+5],a[e+2]=a[e+6];for(c=0;c<30;c++)e=(73+c*74)*4,cFPS';k.appendChild(d);a=document.createElement("canvas");a.width=74;a.height=30;a.style.display="block";a.style.marginLeft="3px";k.appendChild(a);
5 | m=a.getContext("2d");m.fillStyle="rgb("+b.fps.bg.r+","+b.fps.bg.g+","+b.fps.bg.b+")";m.fillRect(0,0,a.width,a.height);y=m.getImageData(0,0,a.width,a.height);f=document.createElement("div");f.style.backgroundColor="rgb("+Math.floor(b.ms.bg.r/2)+","+Math.floor(b.ms.bg.g/2)+","+Math.floor(b.ms.bg.b/2)+")";f.style.padding="2px 0px 3px 0px";f.style.display="none";g.appendChild(f);c=document.createElement("div");c.style.fontFamily="Helvetica, Arial, sans-serif";c.style.textAlign="left";c.style.fontSize=
6 | "9px";c.style.color="rgb("+b.ms.fg.r+","+b.ms.fg.g+","+b.ms.fg.b+")";c.style.margin="0px 0px 1px 3px";c.innerHTML='MS';f.appendChild(c);a=document.createElement("canvas");a.width=74;a.height=30;a.style.display="block";a.style.marginLeft="3px";f.appendChild(a);o=a.getContext("2d");o.fillStyle="rgb("+b.ms.bg.r+","+b.ms.bg.g+","+b.ms.bg.b+")";o.fillRect(0,0,a.width,a.height);B=o.getImageData(0,0,a.width,a.height);try{performance&&performance.memory&&performance.memory.totalJSHeapSize&&
7 | (t=3)}catch(G){}h=document.createElement("div");h.style.backgroundColor="rgb("+Math.floor(b.mb.bg.r/2)+","+Math.floor(b.mb.bg.g/2)+","+Math.floor(b.mb.bg.b/2)+")";h.style.padding="2px 0px 3px 0px";h.style.display="none";g.appendChild(h);i=document.createElement("div");i.style.fontFamily="Helvetica, Arial, sans-serif";i.style.textAlign="left";i.style.fontSize="9px";i.style.color="rgb("+b.mb.fg.r+","+b.mb.fg.g+","+b.mb.fg.b+")";i.style.margin="0px 0px 1px 3px";i.innerHTML='MB';
8 | h.appendChild(i);a=document.createElement("canvas");a.width=74;a.height=30;a.style.display="block";a.style.marginLeft="3px";h.appendChild(a);q=a.getContext("2d");q.fillStyle="#301010";q.fillRect(0,0,a.width,a.height);E=q.getImageData(0,0,a.width,a.height);return{domElement:g,update:function(){u++;j=(new Date).getTime();n=j-F;z=Math.min(z,n);A=Math.max(A,n);s(B.data,Math.min(30,30-n/200*30),"ms");c.innerHTML=''+n+" MS ("+z+"-"+A+")";o.putImageData(B,0,0);F=j;if(j>
9 | v+1E3){l=Math.round(u*1E3/(j-v));w=Math.min(w,l);x=Math.max(x,l);s(y.data,Math.min(30,30-l/100*30),"fps");d.innerHTML=''+l+" FPS ("+w+"-"+x+")";m.putImageData(y,0,0);if(t==3)p=performance.memory.usedJSHeapSize*9.54E-7,C=Math.min(C,p),D=Math.max(D,p),s(E.data,Math.min(30,30-p/2),"mb"),i.innerHTML=''+Math.round(p)+" MB ("+Math.round(C)+"-"+Math.round(D)+")",q.putImageData(E,0,0);v=j;u=0}}}};
10 |
11 |
--------------------------------------------------------------------------------
/levels/level1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level1.png
--------------------------------------------------------------------------------
/levels/level10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level10.png
--------------------------------------------------------------------------------
/levels/level2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level2.png
--------------------------------------------------------------------------------
/levels/level3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level3.png
--------------------------------------------------------------------------------
/levels/level4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level4.png
--------------------------------------------------------------------------------
/levels/level5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level5.png
--------------------------------------------------------------------------------
/levels/level6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level6.png
--------------------------------------------------------------------------------
/levels/level7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level7.png
--------------------------------------------------------------------------------
/levels/level8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level8.png
--------------------------------------------------------------------------------
/levels/level9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/level9.png
--------------------------------------------------------------------------------
/levels/reference.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/reference.png
--------------------------------------------------------------------------------
/levels/testlevel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/testlevel.png
--------------------------------------------------------------------------------
/levels/trainer1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer1.png
--------------------------------------------------------------------------------
/levels/trainer2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer2.png
--------------------------------------------------------------------------------
/levels/trainer3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer3.png
--------------------------------------------------------------------------------
/levels/trainer4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer4.png
--------------------------------------------------------------------------------
/levels/trainer5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer5.png
--------------------------------------------------------------------------------
/levels/trainer6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer6.png
--------------------------------------------------------------------------------
/levels/trainer7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/levels/trainer7.png
--------------------------------------------------------------------------------
/sounds/collectfood.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectfood.mp3
--------------------------------------------------------------------------------
/sounds/collectfood.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectfood.ogg
--------------------------------------------------------------------------------
/sounds/collectgold.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectgold.mp3
--------------------------------------------------------------------------------
/sounds/collectgold.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectgold.ogg
--------------------------------------------------------------------------------
/sounds/collectkey.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectkey.mp3
--------------------------------------------------------------------------------
/sounds/collectkey.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectkey.ogg
--------------------------------------------------------------------------------
/sounds/collectpotion.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectpotion.mp3
--------------------------------------------------------------------------------
/sounds/collectpotion.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/collectpotion.ogg
--------------------------------------------------------------------------------
/sounds/exitlevel.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/exitlevel.mp3
--------------------------------------------------------------------------------
/sounds/exitlevel.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/exitlevel.ogg
--------------------------------------------------------------------------------
/sounds/femalepain1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/femalepain1.mp3
--------------------------------------------------------------------------------
/sounds/femalepain1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/femalepain1.ogg
--------------------------------------------------------------------------------
/sounds/femalepain2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/femalepain2.mp3
--------------------------------------------------------------------------------
/sounds/femalepain2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/femalepain2.ogg
--------------------------------------------------------------------------------
/sounds/fireelf.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/fireelf.mp3
--------------------------------------------------------------------------------
/sounds/fireelf.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/fireelf.ogg
--------------------------------------------------------------------------------
/sounds/firevalkyrie.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/firevalkyrie.mp3
--------------------------------------------------------------------------------
/sounds/firevalkyrie.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/firevalkyrie.ogg
--------------------------------------------------------------------------------
/sounds/firewarrior.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/firewarrior.mp3
--------------------------------------------------------------------------------
/sounds/firewarrior.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/firewarrior.ogg
--------------------------------------------------------------------------------
/sounds/firewizard.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/firewizard.mp3
--------------------------------------------------------------------------------
/sounds/firewizard.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/firewizard.ogg
--------------------------------------------------------------------------------
/sounds/gameover.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/gameover.mp3
--------------------------------------------------------------------------------
/sounds/gameover.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/gameover.ogg
--------------------------------------------------------------------------------
/sounds/generatordeath.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/generatordeath.mp3
--------------------------------------------------------------------------------
/sounds/generatordeath.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/generatordeath.ogg
--------------------------------------------------------------------------------
/sounds/highscore.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/highscore.mp3
--------------------------------------------------------------------------------
/sounds/highscore.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/highscore.ogg
--------------------------------------------------------------------------------
/sounds/malepain1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/malepain1.mp3
--------------------------------------------------------------------------------
/sounds/malepain1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/malepain1.ogg
--------------------------------------------------------------------------------
/sounds/malepain2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/malepain2.mp3
--------------------------------------------------------------------------------
/sounds/malepain2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/malepain2.ogg
--------------------------------------------------------------------------------
/sounds/monsterdeath1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/monsterdeath1.mp3
--------------------------------------------------------------------------------
/sounds/monsterdeath1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/monsterdeath1.ogg
--------------------------------------------------------------------------------
/sounds/monsterdeath2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/monsterdeath2.mp3
--------------------------------------------------------------------------------
/sounds/monsterdeath2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/monsterdeath2.ogg
--------------------------------------------------------------------------------
/sounds/monsterdeath3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/monsterdeath3.mp3
--------------------------------------------------------------------------------
/sounds/monsterdeath3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/monsterdeath3.ogg
--------------------------------------------------------------------------------
/sounds/music.bloodyhalo.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.bloodyhalo.mp3
--------------------------------------------------------------------------------
/sounds/music.bloodyhalo.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.bloodyhalo.ogg
--------------------------------------------------------------------------------
/sounds/music.citrinitas.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.citrinitas.mp3
--------------------------------------------------------------------------------
/sounds/music.citrinitas.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.citrinitas.ogg
--------------------------------------------------------------------------------
/sounds/music.fleshandsteel.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.fleshandsteel.mp3
--------------------------------------------------------------------------------
/sounds/music.fleshandsteel.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.fleshandsteel.ogg
--------------------------------------------------------------------------------
/sounds/music.lostcorridors.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.lostcorridors.mp3
--------------------------------------------------------------------------------
/sounds/music.lostcorridors.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.lostcorridors.ogg
--------------------------------------------------------------------------------
/sounds/music.mountingassault.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.mountingassault.mp3
--------------------------------------------------------------------------------
/sounds/music.mountingassault.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.mountingassault.ogg
--------------------------------------------------------------------------------
/sounds/music.phantomdrone.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.phantomdrone.mp3
--------------------------------------------------------------------------------
/sounds/music.phantomdrone.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.phantomdrone.ogg
--------------------------------------------------------------------------------
/sounds/music.thebeginning.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.thebeginning.mp3
--------------------------------------------------------------------------------
/sounds/music.thebeginning.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.thebeginning.ogg
--------------------------------------------------------------------------------
/sounds/music.warbringer.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.warbringer.mp3
--------------------------------------------------------------------------------
/sounds/music.warbringer.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/music.warbringer.ogg
--------------------------------------------------------------------------------
/sounds/opendoor.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/opendoor.mp3
--------------------------------------------------------------------------------
/sounds/opendoor.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/opendoor.ogg
--------------------------------------------------------------------------------
/sounds/victory.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/victory.mp3
--------------------------------------------------------------------------------
/sounds/victory.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/victory.ogg
--------------------------------------------------------------------------------
/sounds/weak.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/weak.mp3
--------------------------------------------------------------------------------
/sounds/weak.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakesgordon/javascript-gauntlet/2f9020dc642ba3bf98fd20a5251bed5c9d84d924/sounds/weak.ogg
--------------------------------------------------------------------------------