From 22ad8c8e571ef6d9b7bc0d5d638ba4902757d6d7 Mon Sep 17 00:00:00 2001 From: Sky Date: Wed, 24 May 2023 20:51:38 -0400 Subject: [PATCH] oops already had dino + added google analytics (god i wanna kms so badly i hope this doesnt break) --- 1v1lol/index.html | 11 + 2048/index.html | 10 + achieveunlocked/index.html | 11 + achieveunlocked2/index.html | 11 + adofai/index.html | 11 + avalanche/index.html | 11 + basketball-stars/index.html | 14 +- bookmarklets.html | 11 + btd3/index.html | 11 + cell-machine/index.html | 11 + championisland/index.html | 14 +- changelog.html | 13 +- chess/index.html | 11 + chibiknight/index.html | 11 + chrome-dino/1_hurdling.html | 135 - chrome-dino/2_gymnastics.html | 135 - chrome-dino/3_surfing.html | 135 - chrome-dino/4_swimming.html | 135 - chrome-dino/5_equestrian.html | 135 - chrome-dino/appmanifest.json | 37 - chrome-dino/favicon.ico | Bin 16174 -> 0 bytes chrome-dino/icons/icon-114.png | Bin 521 -> 0 bytes chrome-dino/icons/icon-128.png | Bin 550 -> 0 bytes chrome-dino/icons/icon-16.png | Bin 351 -> 0 bytes chrome-dino/icons/icon-256.png | Bin 996 -> 0 bytes chrome-dino/icons/icon-32.png | Bin 232 -> 0 bytes .../offline/100-disabled.png | Bin 382 -> 0 bytes .../offline/100-error-offline.png | Bin 196 -> 0 bytes .../offline/100-offline-sprite.png | Bin 3002 -> 0 bytes .../offline/100-olympic-firemedal-sprite.png | Bin 442 -> 0 bytes .../100-olympics-equestrian-sprite.png | Bin 1696 -> 0 bytes .../100-olympics-gymnastics-sprite.png | Bin 1815 -> 0 bytes .../offline/100-olympics-hurdling-sprite.png | Bin 2090 -> 0 bytes .../offline/100-olympics-surfing-sprite.png | Bin 2461 -> 0 bytes .../offline/100-olympics-swimming-sprite.png | Bin 1328 -> 0 bytes .../offline/200-disabled.png | Bin 479 -> 0 bytes .../offline/200-error-offline.png | Bin 269 -> 0 bytes .../offline/200-offline-sprite.png | Bin 4758 -> 0 bytes .../offline/200-olympic-firemedal-sprite.png | Bin 537 -> 0 bytes .../200-olympics-equestrian-sprite.png | Bin 2436 -> 0 bytes .../200-olympics-gymnastics-sprite.png | Bin 3189 -> 0 bytes .../offline/200-olympics-hurdling-sprite.png | Bin 2956 -> 0 bytes .../offline/200-olympics-surfing-sprite.png | Bin 3403 -> 0 bytes .../offline/200-olympics-swimming-sprite.png | Bin 1799 -> 0 bytes chrome-dino/index.html | 115 - chrome-dino/offline.js | 1 - chrome-dino/register-sw.js | 3 - chrome-dino/scripts/load_time_data.js | 215 - chrome-dino/scripts/neterror.slim.js | 39 - .../scripts/offline-sprite-definitions.js | 687 --- chrome-dino/scripts/offline.js | 4196 ----------------- chrome-dino/scripts/strings.js | 2 - chrome-dino/style.css | 209 - chrome-dino/styles/interstitial_common.css | 517 -- chrome-dino/styles/interstitial_core.css | 100 - chrome-dino/styles/neterror.css | 429 -- chrome-dino/sw.js | 399 -- circloo/index.html | 12 +- cluster-rush/index.html | 11 + cookieclicker/index.html | 11 + ctr/index.html | 14 +- dante/index.html | 15 +- death-run-3d/index.html | 14 +- dino/index.html | 12 +- doodlejump/index.html | 12 +- drift-boss/index.html | 10 + ducklife4/index.html | 11 + fireboywatergirl/index.html | 14 +- flappybird/index.html | 14 +- fluidsim/index.html | 11 + fridaynightfunkin/index.html | 20 +- gd/index.html | 11 + gunmayhem/index.html | 11 + gunmayhem2/index.html | 11 + gunmayhemredux/index.html | 11 + hardestgame/index.html | 11 + hexgl/index.html | 11 + index.html | 11 + iwbtc/index.html | 13 +- lowsadventures2/index.html | 11 + ltf/index.html | 11 + mario/index.html | 11 + offline-mc/index.html | 11 + offlineparadise/index.html | 14 +- osu/index.html | 11 + paperio/index.html | 12 +- resent-client/index.html | 11 + run2/index.html | 11 + sand/index.html | 11 + slope/index.html | 13 +- sm64/index.html | 13 +- stack/index.html | 12 +- superhot/index.html | 21 +- supermeatboy/index.html | 11 + tetris/index.html | 13 +- thisistheonlylevel/index.html | 11 + turbowarp-editor/index.html | 11 + turbowarp/index.html | 12 +- vex/index.html | 11 + vex2/index.html | 11 + vex3/index.html | 11 + vex4/index.html | 13 +- vex5/index.html | 13 +- vex6/index.html | 11 + wallsmash/index.html | 11 + weavesilk/index.html | 11 + wordle/index.html | 12 +- wordlebot/index.html | 11 + xx142-b2.exe/index.html | 11 + 109 files changed, 749 insertions(+), 7664 deletions(-) delete mode 100644 chrome-dino/1_hurdling.html delete mode 100644 chrome-dino/2_gymnastics.html delete mode 100644 chrome-dino/3_surfing.html delete mode 100644 chrome-dino/4_swimming.html delete mode 100644 chrome-dino/5_equestrian.html delete mode 100644 chrome-dino/appmanifest.json delete mode 100644 chrome-dino/favicon.ico delete mode 100644 chrome-dino/icons/icon-114.png delete mode 100644 chrome-dino/icons/icon-128.png delete mode 100644 chrome-dino/icons/icon-16.png delete mode 100644 chrome-dino/icons/icon-256.png delete mode 100644 chrome-dino/icons/icon-32.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-disabled.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-error-offline.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-offline-sprite.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-olympic-firemedal-sprite.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-olympics-equestrian-sprite.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-olympics-gymnastics-sprite.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-olympics-hurdling-sprite.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-olympics-surfing-sprite.png delete mode 100644 chrome-dino/images/default_100_percent/offline/100-olympics-swimming-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-disabled.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-error-offline.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-offline-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-olympic-firemedal-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-olympics-equestrian-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-olympics-gymnastics-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-olympics-hurdling-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-olympics-surfing-sprite.png delete mode 100644 chrome-dino/images/default_200_percent/offline/200-olympics-swimming-sprite.png delete mode 100644 chrome-dino/index.html delete mode 100644 chrome-dino/offline.js delete mode 100644 chrome-dino/register-sw.js delete mode 100644 chrome-dino/scripts/load_time_data.js delete mode 100644 chrome-dino/scripts/neterror.slim.js delete mode 100644 chrome-dino/scripts/offline-sprite-definitions.js delete mode 100644 chrome-dino/scripts/offline.js delete mode 100644 chrome-dino/scripts/strings.js delete mode 100644 chrome-dino/style.css delete mode 100644 chrome-dino/styles/interstitial_common.css delete mode 100644 chrome-dino/styles/interstitial_core.css delete mode 100644 chrome-dino/styles/neterror.css delete mode 100644 chrome-dino/sw.js diff --git a/1v1lol/index.html b/1v1lol/index.html index eb7d8858..55f1a559 100644 --- a/1v1lol/index.html +++ b/1v1lol/index.html @@ -1,6 +1,17 @@ + + + + + 1v1.LOL diff --git a/2048/index.html b/2048/index.html index bfd5590d..0581f1a7 100644 --- a/2048/index.html +++ b/2048/index.html @@ -1 +1,11 @@ + + + +

2048go

104
+24
1420
2
6
6
8
8
2
4
4
6
2
2
\ No newline at end of file diff --git a/achieveunlocked/index.html b/achieveunlocked/index.html index 5241690c..3697b630 100644 --- a/achieveunlocked/index.html +++ b/achieveunlocked/index.html @@ -1,5 +1,16 @@ + + + + + Achievement Unlocked diff --git a/achieveunlocked2/index.html b/achieveunlocked2/index.html index 89c4e0fc..a7791eb0 100644 --- a/achieveunlocked2/index.html +++ b/achieveunlocked2/index.html @@ -1,5 +1,16 @@ + + + + + Achievement Unlocked 2 diff --git a/adofai/index.html b/adofai/index.html index 5e113144..9a340563 100644 --- a/adofai/index.html +++ b/adofai/index.html @@ -1,6 +1,17 @@ + + + + + A Dance of Fire And Ice diff --git a/avalanche/index.html b/avalanche/index.html index 8115354e..f90979fc 100644 --- a/avalanche/index.html +++ b/avalanche/index.html @@ -1,5 +1,16 @@ + + + + + Avalanche diff --git a/basketball-stars/index.html b/basketball-stars/index.html index b6cdb67a..37b98c92 100644 --- a/basketball-stars/index.html +++ b/basketball-stars/index.html @@ -1,15 +1,25 @@ + + + + + - Basketball Stars | 3kh0 + Basketball Stars -
diff --git a/bookmarklets.html b/bookmarklets.html index 41e5e0d8..3231cc1c 100644 --- a/bookmarklets.html +++ b/bookmarklets.html @@ -1,6 +1,17 @@ + + + + + Bookmarklets | e-gamepass diff --git a/btd3/index.html b/btd3/index.html index 980e4481..95681744 100644 --- a/btd3/index.html +++ b/btd3/index.html @@ -1,5 +1,16 @@ + + + + + Bloons Tower Defense 3 diff --git a/cell-machine/index.html b/cell-machine/index.html index 50319ca9..b5bd1132 100644 --- a/cell-machine/index.html +++ b/cell-machine/index.html @@ -1,6 +1,17 @@ + + + + + Cell Machine diff --git a/championisland/index.html b/championisland/index.html index bd95bfdb..775ee623 100644 --- a/championisland/index.html +++ b/championisland/index.html @@ -1,6 +1,17 @@ + + + + + @@ -111,8 +122,7 @@ (function () { google.hs = {h: true, peh: true, sie: false}; })(); - Champion Island | 3kh0 - + Champion Island diff --git a/changelog.html b/changelog.html index b4c73a26..42162d3b 100644 --- a/changelog.html +++ b/changelog.html @@ -1,6 +1,17 @@ + + + + + Changelog | e-gamepass @@ -18,7 +29,7 @@

e-gamepass

v. 2023.05.24

-

Added 4 games (Slope, This is the only level, chrome dino, achievement unlocked 2)
Fixed web analytics (only affects me lol)

+

Added 4 games (Slope, This is the only level, achievement unlocked 2)
Fixed web analytics + google analytics (only affects me lol)

v. 2023.05.23

Major update?!
Added saves
Complete backend remake
Fixed flash pages to make them full screen automatically
Added 2 games (Cell Machine and Death Run 3D)
Added more bookmarklets
Tetris Update!

Back to home diff --git a/chess/index.html b/chess/index.html index 12752abd..9958bf1d 100644 --- a/chess/index.html +++ b/chess/index.html @@ -2,6 +2,17 @@ + + + + + diff --git a/chibiknight/index.html b/chibiknight/index.html index 39f16f33..10a3f7a8 100644 --- a/chibiknight/index.html +++ b/chibiknight/index.html @@ -1,5 +1,16 @@ + + + + + Chibi Knight diff --git a/chrome-dino/1_hurdling.html b/chrome-dino/1_hurdling.html deleted file mode 100644 index cfbb7e0b..00000000 --- a/chrome-dino/1_hurdling.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - Chrome Dino: Olympic Hurdling | 3kh0 - - - - - - - - - - -
-
- -
-

- Press space/up to play -

-

- Change theme: - -
- Refresh the page to change theme -

-
-
-
- - - Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape. When you hear an audio cue, press space to jump over obstacles. - -
-
-
- -
- -
-
- - - -
- - - - - \ No newline at end of file diff --git a/chrome-dino/2_gymnastics.html b/chrome-dino/2_gymnastics.html deleted file mode 100644 index 22648006..00000000 --- a/chrome-dino/2_gymnastics.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - Chrome Dino: Olympic Gymnastics | 3kh0 - - - - - - - - - - -
-
- -
-

- Press space/up to play -

-

- Change theme: - -
- Refresh the page to change theme -

-
-
-
- - - Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape. When you hear an audio cue, press space to jump over obstacles. - -
-
-
- -
- -
-
- - - -
- - - - - diff --git a/chrome-dino/3_surfing.html b/chrome-dino/3_surfing.html deleted file mode 100644 index dd8cf1d4..00000000 --- a/chrome-dino/3_surfing.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - Chrome Dino: Olympic Surfing | 3kh0 - - - - - - - - - - -
-
- -
-

- Press space/up to play -

-

- Change theme: - -
- Refresh the page to change theme -

-
-
-
- - - Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape. When you hear an audio cue, press space to jump over obstacles. - -
-
-
- -
- -
-
- - - -
- - - - - diff --git a/chrome-dino/4_swimming.html b/chrome-dino/4_swimming.html deleted file mode 100644 index 927a7c28..00000000 --- a/chrome-dino/4_swimming.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - Chrome Dino: Olympic Swimming | 3kh0 - - - - - - - - - - -
-
- -
-

- Press space/up to play -

-

- Change theme: - -
- Refresh the page to change theme -

-
-
-
- - - Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape. When you hear an audio cue, press space to jump over obstacles. - -
-
-
- -
- -
-
- - - -
- - - - - diff --git a/chrome-dino/5_equestrian.html b/chrome-dino/5_equestrian.html deleted file mode 100644 index ab237e45..00000000 --- a/chrome-dino/5_equestrian.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - Chrome Dino: Olympic Equestrian - - - - - - - - - - -
-
- -
-

- Press space/up to play -

-

- Change theme: - -
- Refresh the page to change theme -

-
-
-
- - - Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape. When you hear an audio cue, press space to jump over obstacles. - -
-
-
- -
- -
-
- - - -
- - - - - diff --git a/chrome-dino/appmanifest.json b/chrome-dino/appmanifest.json deleted file mode 100644 index 9ce41e72..00000000 --- a/chrome-dino/appmanifest.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "Chrome Dino", - "short_name": "Chrome Dino", - "description": "Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape.", - "version": "2.0", - "start_url": "index.html", - "display": "fullscreen", - "orientation": "any", - "background_color": "#ffffff", - "icons": [ - { - "src": "icons/icon-16.png", - "sizes": "16x16", - "type": "image/png" - }, - { - "src": "icons/icon-32.png", - "sizes": "32x32", - "type": "image/png" - }, - { - "src": "icons/icon-114.png", - "sizes": "114x114", - "type": "image/png" - }, - { - "src": "icons/icon-128.png", - "sizes": "128x128", - "type": "image/png" - }, - { - "src": "icons/icon-256.png", - "sizes": "256x256", - "type": "image/png" - } - ] -} \ No newline at end of file diff --git a/chrome-dino/favicon.ico b/chrome-dino/favicon.ico deleted file mode 100644 index c520571a718a545fd15ed2865239d83ec3c1d8c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16174 zcmeI3&ud*p6vrn`OR!laph6V%1wpI|nhLELt9cfx;7+<1H$}ybo4N>v_iU{TH`(Yy zLA&Ute?vi%E^JZJAPROP1hWuy(HGKct8B)!i@y#wEznKNh3`Odj> z@4Ig@&$$t|+clfJBuCuLZs(qN&dtm?wWi&<2c3IMy6NfAo^^SG!D>vmFinepP^3;-h61#p${GQ*jSg1^U{>T{$WWtc>mT{R%+bofRmD=@i=tCDdDhSJg`lK@6lNa{ohK` zhhOVr9DZJpbm-p@-W9z6-hh%7{G|~OOZTPfZPhn~$I)2{{a;8P5aRrE9rs~lh57(s zwh=9<^W zo^it^nB$$;!lS~kLQ&r1xnR#)^K+2QiBx?T@dCb)jJcI<-?p_R&OC?VPVUR{&9ZCD zNA*e0@u+yb!({Q_c6B1LUkl_JSmRxC)c}TQ*^=!VNocW12!$;RUvjYl(ym+ zf7Z;V80Z3+L;C$;&|Khd_M3bSL!o%ae}9($z=`X?JIn!L5O;Ka{M8=%Toi-%;nT=0 z{%L)%Z%gt{9nWJ?I_5w>pgz>KB>!8=QOf>n)noqF<{q}S_9bI>QVDa&;t%gx;a~vk zjWx>swQ1krcFapWxMo@W?HUY{+y1^;+RrB9Y)~Xv5<^LnKpAxul z2d#P2?X>lCPJI6oc;|te@ywFn$)<3hr@Aa>dlCqw9pg^&S@RjckMTY-d-hjf%8XphGAZHT=_y>KxgMU z*q=y`pH;ihr;UxJ?8XKDXVvSXZaPW&JHm@*pgOVJg2jjD!BjtO?-LuVTmM$1#uERk z;NwZ+kND$d{Kzgh7wRYfgW&V0zPp5L!VLiq;EDh*xL`MEe3nk9es7lG2Va9g>|X@i zUxEXG4}I)Ok>^2Uvvj?6a+5`WP5zt^$2JZo;e}2A;0jvuc|>5%S*$EsIS0jT3X3IT z8z(sItzg>Tg?zET%y$`iRraOsg)$sd@yY)?0eRPgwRo@abQ%9eHuA&#iRagrWYJn= zPnl~lU*x+?zx)uNdH2kz3wA$n|D~@B^S@gtysvr=b1L#P4`LJP;K02OzcdGnKQ2AE zB_52=^AY+_OQv(c8vHrnc+9=(w&IY3bbRu$AiO8QjU3)m9`ZQLcN{;>g?gp;WXxrU z#3$$P3+c6Jc^LNm`F!|YVC`GXv^+2UFt0!N2y3jBfo6Qo>UN!hIsI~EwNM~Sb z>T>eT)Q5A)NYapg3~Xq3YGUQqppz`H-^V2%jZ7t6jgY_fYPj7h&Q;Oj7Pii+$Og&zXa zKKNaP=bGn3HXjSd@x1EH0Y9G#(|Kd*lTFiXROdGgZwriv*Sx?wDqUpX6c`VWivm1K zpSzJQ<5vZn2e~Z$?j_qh1@ZuY@QKiO%}P(EHXL3CcrtNSgr$}+*TrS6whgU%Lw66B I{oH>14F@{0H~;_u diff --git a/chrome-dino/icons/icon-114.png b/chrome-dino/icons/icon-114.png deleted file mode 100644 index 20244c69538e4684c5eb5ab10c12fe2ae0bb5d27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521 zcmeAS@N?(olHy`uVBq!ia0vp^MIg+<1|<9Mw$?H*FkbL%;VO zS48p-U&@Wx#4h$=nvU)r#Y|hvPC?fPXKU);dp`Ldcdw^mccIL2hpK%tOiGR_9V`=F znmhzJl{#4_DhL6YB#J4iFI{GS`TO7F|BrpGn(86YS$`w7{=++=(pd}BR4;AVocr$b zrnD?oA(vHQ*tJf}-M!mLG z1gf5OTi`}k>&`38AYXwruX^g9?SDR_N;Y%vil~ilN>|qaWn6T=l$dJ`>OWbWzHFu$}+r@~pV6+oqgw|7!YNea*Io zDJQ=NN7)=$7A8@)EbPbb?d$w2Ywv%58NK|>Po4(Lr8!??zO8(`G)hoV@IHHDGfV!r Sv`fG!X7F_Nb6Mw<&;$Tv&gVV= diff --git a/chrome-dino/icons/icon-128.png b/chrome-dino/icons/icon-128.png deleted file mode 100644 index f8f5686483e314e3d6b58ea56f68eb579dd07e67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 550 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV0`Q8;uunK>+M~~q9y~8hQv21 zXI*!2dFLIP8nNj?Tgm;6e%y)^_Pn=lkp22yHE`AX+WX)8Hte%uVRB&LU|>{WU}<0w zU|^z{vtWV9LqEUEAz$9FeVxSwH1@#dy-|N|?zog)cea{I_<*LuwE63gE%)UXW?)R< z%PW%E2EtmhglSEv8)I^;(gGUVclGYeG_l9I51pU@WcO@ z&D%x%V9VYxzT2D|yl{ zdEWCEe>0xuzFPcyj!13ulgkcC`iyJ$1jSBFVY={%CFAbym7z)XNlY^sD*L>{uSjLK z*IlpmoyRlVL5K1DeTlg<9n$mDXQ{;tyDt`g(4}9o=hf7h(`;*xBxf;AIBM<{-zc*! Sqj5R|5O})!xvX9^i7ha5|j`!?5;kHX8BSg+Syuh&L^>$=#s{d$d)$pnDyc1xCJ z04$eF9LITj2dyZI>Tm3HIs`#b?|!jZP?lvQA*Xv4MI`{4ip002ovPDHLkV1hNjmlgm3 diff --git a/chrome-dino/icons/icon-256.png b/chrome-dino/icons/icon-256.png deleted file mode 100644 index 855763f4e29808467d31e688c193bd49c9155a00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 996 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJFd)t)YnAr-gY-rZPu$U(s2 z;#BvIOt%(@I#n%T*y`fppnPOXvELiX+l@S%Ci-oEZvi`zumRt+D{;YYm(cUlU;lK#hQ#g^jU5Pmb%9PVoPH z0b%5jK5%Oduod0ZE!13EYMy$WmepUJ5z67ijUoTYxVQJ2T*i=2sXaYR&(@n%jWgJmaUs@x&qZ} zX_|6eS*c(D*E~Ltq76ND=&ab4&s-&-Is+1XCkpu+QY$z0gA6+$n6UWqDp5tK6x3;U zrAwz=`lUa!*7e2ZI=B=_vLPpE4WoSF-Z*8YM>Tf&pT0(ef_C1ci3#$2?anSPciw;B zaW_woLk+05sK=>RzV`m{mnB_^5=jzl;;g{PQ|P^$C;r&tm_?r+8xw;`&6l$uLT{#* zKl%N)ZtL|;Uw%K#JazkDv?(Jnb)T_d`oXbsqA)ZPEZ;HiU@4uu`Y&^c4=DNfF>ILO z-XFPkjlVC$0}fz(JdisO^n8Wx{IxYL+k9`{3m0avU;?T-reOdw4J6NS-)F7W^&RY+ zzP+@Y^BalQ$Ez6AGEBD?yw&#}Hr<`eaX*oahuR_A}fY&|7jR(3|HXX{_$p8eN Lu6{1-oD!M0}d_SE2w0h6Mo^qnHt$O6|#El1q>i$!8ajIUS-j%wC0GId|zW&FeW^+;!ilwRMPu&A1)tS3(uZ5EKTXWwES zxpDu?1Fsg%;<~gxQky+&0waU>w`tqsYlXAo_w!rui!iW#kctky^Ks%ruEff&sY<3j dCi841^MCK#Th|ybx)A7e22WQ%mvv4FO#lOpSU~^) diff --git a/chrome-dino/images/default_100_percent/offline/100-disabled.png b/chrome-dino/images/default_100_percent/offline/100-disabled.png deleted file mode 100644 index 526075e6d55359bb31f0ba3b1154524a04dea4cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 382 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 diff --git a/chrome-dino/images/default_100_percent/offline/100-olympic-firemedal-sprite.png b/chrome-dino/images/default_100_percent/offline/100-olympic-firemedal-sprite.png deleted file mode 100644 index d83fccf922fab6fb7393320a54fcbe13d7079fc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 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 ZWrAxM9}1^pI??|NA_au_i+qpg{u99uVxs^6 diff --git a/chrome-dino/images/default_100_percent/offline/100-olympics-hurdling-sprite.png b/chrome-dino/images/default_100_percent/offline/100-olympics-hurdling-sprite.png deleted file mode 100644 index 0314a6a5496f351e84437704ee3193b400e4713c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2090 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 diff --git a/chrome-dino/images/default_200_percent/offline/200-error-offline.png b/chrome-dino/images/default_200_percent/offline/200-error-offline.png deleted file mode 100644 index 79cae8411bbc1a0cc7e867e945b082dca37009a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^6F``S8Ays|{O<-*5&=FTu0T3CIM~riKhsM25i$LfsD(<_aPAt2kH^Q*)zxhCOR53jehw0tU5{I28NJ}-*HRl_Ik zwll{oc_tMXMkU3CTYft*d=2o)@V9mAkXy`Sdw;RO+H}YDjlKVEIT){4&E-F;G$ov$ z@ecDN57i?Y2N_gF9ZU?rXvXXeb^K}U-m$=YA#b1Ov;Q2kq@;lkW$<+Mb6Mw<&;$U4 CSzM?9 diff --git a/chrome-dino/images/default_200_percent/offline/200-offline-sprite.png b/chrome-dino/images/default_200_percent/offline/200-offline-sprite.png deleted file mode 100644 index 2270e89ae5e58e45305d6a8ac47df62d69fe0d57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4758 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 iSuTR59B8WL@d@->i4*dF$A zC^tZ$EL&XR`;Zj1RO_c>t@|%41uByQ(ZXRE+nAJA+I7m~*`)x{_5$Z4U0aC-Nia(D za6MOZES;IC5bI17$#70BNMcd2%q7}eDGDj)0rC^!9+oZc&ki??!3ElM#9!V%lX-Mv{l#rBv%bx$` z{sf|Yypoi!Szbv>u9Zs?Pg0VSPf)&XbFKWor6eUO*DNI|Nl8jle%4Zwl9Z$*B`HY> bnM~#rWphk^_rH_M00000NkvXXu0mjfeoyj2 diff --git a/chrome-dino/images/default_200_percent/offline/200-olympics-equestrian-sprite.png b/chrome-dino/images/default_200_percent/offline/200-olympics-equestrian-sprite.png deleted file mode 100644 index a05eac831d515e332303de6d1aa82ac2d2e9392b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2436 zcmXw5dmxkh8=uRZkeupFF1eJsL{pBCa*Aqd9M@&DH)}eg*F92F%A)!e2Q{{wh~-+% zs4RE7Vs6=F`-QZh%M?QyPRDHXdsogs-}imL&-Zyg&*$?z-}n2y>Em@+OG94+27_r~ zjvVrX!PG(My-rmH8n3&i?S>u*pWlz7S5{WKySt$gi^W27v{UGNr=03I=1$OuQoNrR z7UnYXJ`{2{#2j)xdR}RI_@$RAUjOwo1fRIC`!qU?Y-b6@1t(`sz9nm7$Gq*a z5(5$Y`G=t5PyQxtUq^;b4HwNF%9*z^Rc|@3Zyzjgje8;X}b%2vXQ)Q}|hNY>>JcIumGv759^N#R2DY|3csaQ%%(P7ce zTt$ITjn-Y+aeCmOS88j%Gnk7;xoQF&3Y}*wGR_i(t|^mGP#R|<(n)x5CeCGZpxz$N^r`!1 zibYT;1fuQMf}?EMAzcZ|K_@%&l405|2SybQ%F@+?lY%>{i|rFEBn*Z44rl;bi{#zA zb#U)n4nQUy77}b$0_Iy)Od%$68}M3LHYg;&CkyRZ@dMH@eJDT*Uz?Jzgh_ZOh^o#d zUVt+b2{sqm+|%+HvWE(N!tGx{sLF~pi`-5n+fbQzRV=BuPN=I;3JQTc$h0$=jFvRc zOgfIL;G9ex^gqN+n--ga5C@0?)eW?dt`R^E)vFF9rY*9^GX~#p1^!l}eIw&orhV;O^C$pje}?bm_C9*)dh;PSKxjw*uf5cHD3% z+woenWYwP)WqG!z+@Cyp0A14fFluYp!c?^CIFTT2aEp*rqgeX2nB21=nQuvcZ;@{jyo9?(P6fF6s4mjNv#-ap6(`9n=?3_=0D7Tjn)BH!QK%wnw z#dKJm;$J~tx<~0Zc465Sh485VRCgY+kxR#_zBJ0Dwu_E>+wUcdeL`r^ZEEvNiRB!X) zsewElR!w55G4~PadyV5uA;#@nIZJtVoX*~YbpDe&Luijx!q&=X9mj~q%q9E3eMM?q z3KSNEN-t^2a=`}A&vOTjNmKA2RnR?!B+8SxuI41|V^gkPKL z?f=lvtDKHIF{!~EQL?Q%+SA$k5DE-OVetOfN|bjIgY4WGQcT`DKwYst%!vMP24@bw zc%Er4^|b*$M_zB|2xxm-Dm(VfS-0dTmF6mbHylc(!^ze{TNjz9F%z1>a2k4$p}%b| z81oJY@Of~4!4bjy0874n>)Zb{3BrU(ZEw<)wSP5~swP0Ge0(}~jqX9RByS%u)U<(x zbQQT}=ROO)PxpaMMNK6E3m2|cbgvxi)^F;ID*mB^y<_thU~;cwZLygvqlri4>c^<9 zh);E<@(kwAXj{ljJUL>wCM0<21rqeXCILgJWzJ;236$9!5>DY=D_W5+R{{PHqT5ng z9iI~&YmIyT>g#q5YXAx{)PeiRKYyvQGCzC92x#ZtX3BzaJr^X(3T97`Xv!!XmffJI z%`aYvdTe#luwb7nqOfess%haJ9BD>0_w|h>X_|xXZI-SWE+;7m6P{cJU6utKVCkY^7F#PO|_nc|CH#R!IcGyT~v z1;D?$lDEKYbROTzN zhHwK5y2^*#6~bD7X*ta382}Brc~h|OL~jC#MI76JoKKp7u&951z_NA*4Lbi)_<=^} zICmY+Ha0bDL!>2NG5Q->VfPWaW!~i+Nli@{(XuPs*Z?w7RGIy5qH&PSpEKAmPn#Lp zR@ilB2|74Alz1LIkGQ3LZeAD^fKT^=x0BT%hbeP);6%iqd>g^CB_@T2gy$u-NPF>h z{OSwmukw=zpQ-fRxMH&QMaXfGz5M(bBE+kI&+yG|;j#~}ft`_@Odz;~DP78;y;aW9 zd2w7AY4xI_PuT~6xodvi2ZKy&tx&L1ICMXN!G3PflKwFd)6&VI^(l|C1%=XJa=m;n zomGGbf+Dg*ZaxVkt7*D>gejdx6NmFNd&S*2twQj^FG!Zu?-dK z3%HA3K3k|j;_5Fr+s+_oOUEhEh z*VfrN{U+(41@MMRCypM<^K<{p(p?V diff --git a/chrome-dino/images/default_200_percent/offline/200-olympics-gymnastics-sprite.png b/chrome-dino/images/default_200_percent/offline/200-olympics-gymnastics-sprite.png deleted file mode 100644 index 76e5c1884af5e320761a7a6269aa0f2da96d983c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3189 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 - - - - - - - - - - - - - Chrome Dino | 3kh0 - - - - - - - - - -
-
- -
-

- Press space/up to play -

-

- Change theme: - -
- Refresh the page to change theme -

-
-
-
- - - Dino game. A pixelated dinosaur dodges cacti and pterodactyls as it runs across a desolate landscape. When you hear an audio cue, press space to jump over obstacles. - -
-
-
-
- -
-
- - - -
- - - - - - diff --git a/chrome-dino/offline.js b/chrome-dino/offline.js deleted file mode 100644 index 6259ff9d..00000000 --- a/chrome-dino/offline.js +++ /dev/null @@ -1 +0,0 @@ -{"version":2.0,"fileList":["1_hurdling.html","2_gymnastics.html","3_surfing.html","4_swimming.html","5_equestrian.html","appmanifest.json","index.html","register-sw.js","sw.js","icons/icon-114.png","icons/icon-128.png","icons/icon-16.png","icons/icon-256.png","icons/icon-32.png","images/default_100_percent/offline/100-disabled.png","images/default_100_percent/offline/100-error-offline.png","images/default_100_percent/offline/100-offline-sprite.png","images/default_100_percent/offline/100-olympic-firemedal-sprite.png","images/default_100_percent/offline/100-olympics-equestrian-sprite.png","images/default_100_percent/offline/100-olympics-gymnastics-sprite.png","images/default_100_percent/offline/100-olympics-hurdling-sprite.png","images/default_100_percent/offline/100-olympics-surfing-sprite.png","images/default_100_percent/offline/100-olympics-swimming-sprite.png","images/default_200_percent/offline/200-disabled.png","images/default_200_percent/offline/200-error-offline.png","images/default_200_percent/offline/200-offline-sprite.png","images/default_200_percent/offline/200-olympic-firemedal-sprite.png","images/default_200_percent/offline/200-olympics-equestrian-sprite.png","images/default_200_percent/offline/200-olympics-gymnastics-sprite.png","images/default_200_percent/offline/200-olympics-hurdling-sprite.png","images/default_200_percent/offline/200-olympics-surfing-sprite.png","images/default_200_percent/offline/200-olympics-swimming-sprite.png","scripts/load_time_data.js","scripts/neterror.slim.js","scripts/offline.js","scripts/offline-sprite-definitions.js","scripts/strings.js","styles/interstitial_common.css","styles/interstitial_core.css","styles/neterror.css"]} \ No newline at end of file diff --git a/chrome-dino/register-sw.js b/chrome-dino/register-sw.js deleted file mode 100644 index 14397502..00000000 --- a/chrome-dino/register-sw.js +++ /dev/null @@ -1,3 +0,0 @@ -if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('sw.js'); -} \ No newline at end of file diff --git a/chrome-dino/scripts/load_time_data.js b/chrome-dino/scripts/load_time_data.js deleted file mode 100644 index a22baeaa..00000000 --- a/chrome-dino/scripts/load_time_data.js +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview This file defines a singleton which provides access to all data - * that is available as soon as the page's resources are loaded (before DOM - * content has finished loading). This data includes both localized strings and - * any data that is important to have ready from a very early stage (e.g. things - * that must be displayed right away). - * - * Note that loadTimeData is not guaranteed to be consistent between page - * refreshes (https://crbug.com/740629) and should not contain values that might - * change if the page is re-opened later. - */ - -/** @type {!LoadTimeData} */ -// eslint-disable-next-line no-var -var loadTimeData; - -class LoadTimeData { - constructor() { - /** @type {Object} */ - this.data_; - } - - /** - * Sets the backing object. - * - * Note that there is no getter for |data_| to discourage abuse of the form: - * - * var value = loadTimeData.data()['key']; - * - * @param {Object} value The de-serialized page data. - */ - set data(value) { - expect(!this.data_, 'Re-setting data.'); - this.data_ = value; - } - - /** - * @param {string} id An ID of a value that might exist. - * @return {boolean} True if |id| is a key in the dictionary. - */ - valueExists(id) { - return id in this.data_; - } - - /** - * Fetches a value, expecting that it exists. - * @param {string} id The key that identifies the desired value. - * @return {*} The corresponding value. - */ - getValue(id) { - expect(this.data_, 'No data. Did you remember to include strings.js?'); - const value = this.data_[id]; - expect(typeof value !== 'undefined', 'Could not find value for ' + id); - return value; - } - - /** - * As above, but also makes sure that the value is a string. - * @param {string} id The key that identifies the desired string. - * @return {string} The corresponding string value. - */ - getString(id) { - const value = this.getValue(id); - expectIsType(id, value, 'string'); - return /** @type {string} */ (value); - } - - /** - * Returns a formatted localized string where $1 to $9 are replaced by the - * second to the tenth argument. - * @param {string} id The ID of the string we want. - * @param {...(string|number)} var_args The extra values to include in the - * formatted output. - * @return {string} The formatted string. - */ - getStringF(id, var_args) { - const value = this.getString(id); - if (!value) { - return ''; - } - - const args = Array.prototype.slice.call(arguments); - args[0] = value; - return this.substituteString.apply(this, args); - } - - /** - * Returns a formatted localized string where $1 to $9 are replaced by the - * second to the tenth argument. Any standalone $ signs must be escaped as - * $$. - * @param {string} label The label to substitute through. - * This is not an resource ID. - * @param {...(string|number)} var_args The extra values to include in the - * formatted output. - * @return {string} The formatted string. - */ - substituteString(label, var_args) { - const varArgs = arguments; - return label.replace(/\$(.|$|\n)/g, function(m) { - expect(m.match(/\$[$1-9]/), 'Unescaped $ found in localized string.'); - return m === '$$' ? '$' : varArgs[m[1]]; - }); - } - - /** - * Returns a formatted string where $1 to $9 are replaced by the second to - * tenth argument, split apart into a list of pieces describing how the - * substitution was performed. Any standalone $ signs must be escaped as $$. - * @param {string} label A localized string to substitute through. - * This is not an resource ID. - * @param {...(string|number)} var_args The extra values to include in the - * formatted output. - * @return {!Array} The formatted - * string pieces. - */ - getSubstitutedStringPieces(label, var_args) { - const varArgs = arguments; - // Split the string by separately matching all occurrences of $1-9 and of - // non $1-9 pieces. - const pieces = (label.match(/(\$[1-9])|(([^$]|\$([^1-9]|$))+)/g) || - []).map(function(p) { - // Pieces that are not $1-9 should be returned after replacing $$ - // with $. - if (!p.match(/^\$[1-9]$/)) { - expect( - (p.match(/\$/g) || []).length % 2 === 0, - 'Unescaped $ found in localized string.'); - return {value: p.replace(/\$\$/g, '$'), arg: null}; - } - - // Otherwise, return the substitution value. - return {value: varArgs[p[1]], arg: p}; - }); - - return pieces; - } - - /** - * As above, but also makes sure that the value is a boolean. - * @param {string} id The key that identifies the desired boolean. - * @return {boolean} The corresponding boolean value. - */ - getBoolean(id) { - const value = this.getValue(id); - expectIsType(id, value, 'boolean'); - return /** @type {boolean} */ (value); - } - - /** - * As above, but also makes sure that the value is an integer. - * @param {string} id The key that identifies the desired number. - * @return {number} The corresponding number value. - */ - getInteger(id) { - const value = this.getValue(id); - expectIsType(id, value, 'number'); - expect(value === Math.floor(value), 'Number isn\'t integer: ' + value); - return /** @type {number} */ (value); - } - - /** - * Override values in loadTimeData with the values found in |replacements|. - * @param {Object} replacements The dictionary object of keys to replace. - */ - overrideValues(replacements) { - expect( - typeof replacements === 'object', - 'Replacements must be a dictionary object.'); - for (const key in replacements) { - this.data_[key] = replacements[key]; - } - } - - /** Reset loadTimeData's data to empty. Should only be used in tests. */ - resetForTesting() { - this.data_ = {}; - } -} - - /** - * Checks condition, throws error message if expectation fails. - * @param {*} condition The condition to check for truthiness. - * @param {string} message The message to display if the check fails. - */ - function expect(condition, message) { - if (!condition) { - throw new Error( - 'Unexpected condition on ' + document.location.href + ': ' + message); - } - } - - /** - * Checks that the given value has the given type. - * @param {string} id The id of the value (only used for error message). - * @param {*} value The value to check the type on. - * @param {string} type The type we expect |value| to be. - */ - function expectIsType(id, value, type) { - expect( - typeof value === type, '[' + value + '] (' + id + ') is not a ' + type); - } - - expect(!loadTimeData, 'should only include this file once'); - loadTimeData = new LoadTimeData; - - // Expose |loadTimeData| directly on |window|, since within a JS module the - // scope is local and not all files have been updated to import the exported - // |loadTimeData| explicitly. - window.loadTimeData = loadTimeData; - - // console.warn('crbug/1173575, non-JS module files deprecated.'); \ No newline at end of file diff --git a/chrome-dino/scripts/neterror.slim.js b/chrome-dino/scripts/neterror.slim.js deleted file mode 100644 index 5681dac2..00000000 --- a/chrome-dino/scripts/neterror.slim.js +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -const HIDDEN_CLASS = 'hidden'; - -// Subframes use a different layout but the same html file. This is to make it -// easier to support platforms that load the error page via different -// mechanisms (Currently just iOS). We also use the subframe style for portals -// as they are embedded like subframes and can't be interacted with by the user. -let isSubFrame = false; -if (window.top.location !== window.location || window.portalHost) { - document.documentElement.setAttribute('subframe', ''); - isSubFrame = true; -} - -// Adds an icon class to the list and removes classes previously set. -function updateIconClass(newClass) { - const frameSelector = isSubFrame ? '#sub-frame-error' : '#main-frame-error'; - const iconEl = document.querySelector(frameSelector + ' .icon'); - - if (iconEl.classList.contains(newClass)) { - return; - } - - iconEl.className = 'icon ' + newClass; -} - - -function onDocumentLoad() { - const iconClass = loadTimeData.valueExists('iconClass') && - loadTimeData.getValue('iconClass'); - updateIconClass(iconClass); - if (!isSubFrame && iconClass === 'icon-offline') { - document.documentElement.classList.add('offline'); - new Runner('.interstitial-wrapper'); - } -} -document.addEventListener('DOMContentLoaded', onDocumentLoad); \ No newline at end of file diff --git a/chrome-dino/scripts/offline-sprite-definitions.js b/chrome-dino/scripts/offline-sprite-definitions.js deleted file mode 100644 index 62073a43..00000000 --- a/chrome-dino/scripts/offline-sprite-definitions.js +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright (c) 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/* @const */ -const GAME_TYPE = ['type_1', 'type_2', 'type_3', 'type_4', 'type_5']; - -/** - * Obstacle definitions. - * minGap: minimum pixel space between obstacles. - * multipleSpeed: Speed at which multiples are allowed. - * speedOffset: speed faster / slower than the horizon. - * minSpeed: Minimum speed which the obstacle can make an appearance. - * - * @typedef {{ - * type: string, - * width: number, - * height: number, - * yPos: number, - * multipleSpeed: number, - * minGap: number, - * minSpeed: number, - * collisionBoxes: Array, - * }} - */ -let ObstacleType; - -/** - * T-Rex runner sprite definitions. - */ -Runner.spriteDefinitionByType = { - original: { - LDPI: { - BACKGROUND_EL: {x: 86, y: 2}, - CACTUS_LARGE: {x: 332, y: 2}, - CACTUS_SMALL: {x: 228, y: 2}, - OBSTACLE_2: {x: 332, y: 2}, - OBSTACLE: {x: 228, y: 2}, - CLOUD: {x: 86, y: 2}, - HORIZON: {x: 2, y: 54}, - MOON: {x: 484, y: 2}, - PTERODACTYL: {x: 134, y: 2}, - RESTART: {x: 2, y: 68}, - TEXT_SPRITE: {x: 655, y: 2}, - TREX: {x: 848, y: 2}, - STAR: {x: 645, y: 2}, - COLLECTABLE: {x: 2, y: 2}, - ALT_GAME_END: {x: 121, y: 2} - }, - HDPI: { - BACKGROUND_EL: {x: 166, y: 2}, - CACTUS_LARGE: {x: 652, y: 2}, - CACTUS_SMALL: {x: 446, y: 2}, - OBSTACLE_2: {x: 652, y: 2}, - OBSTACLE: {x: 446, y: 2}, - CLOUD: {x: 166, y: 2}, - HORIZON: {x: 2, y: 104}, - MOON: {x: 954, y: 2}, - PTERODACTYL: {x: 260, y: 2}, - RESTART: {x: 2, y: 130}, - TEXT_SPRITE: {x: 1294, y: 2}, - TREX: {x: 1678, y: 2}, - STAR: {x: 1276, y: 2}, - COLLECTABLE: {x: 4, y: 4}, - ALT_GAME_END: {x: 242, y: 4} - }, - MAX_GAP_COEFFICIENT: 1.5, - MAX_OBSTACLE_LENGTH: 3, - HAS_CLOUDS: 1, - BOTTOM_PAD: 10, - TREX: { - WAITING_1: {x: 44, w: 44, h: 47, xOffset: 0}, - WAITING_2: {x: 0, w: 44, h: 47, xOffset: 0}, - RUNNING_1: {x: 88, w: 44, h: 47, xOffset: 0}, - RUNNING_2: {x: 132, w: 44, h: 47, xOffset: 0}, - JUMPING: {x: 0, w: 44, h: 47, xOffset: 0}, - CRASHED: {x: 220, w: 44, h: 47, xOffset: 0}, - COLLISION_BOXES: [ - new CollisionBox(22, 0, 17, 16), new CollisionBox(1, 18, 30, 9), - new CollisionBox(10, 35, 14, 8), new CollisionBox(1, 24, 29, 5), - new CollisionBox(5, 30, 21, 4), new CollisionBox(9, 34, 15, 4) - ] - }, - /** @type {Array} */ - OBSTACLES: [ - { - type: 'CACTUS_SMALL', - width: 17, - height: 35, - yPos: 105, - multipleSpeed: 4, - minGap: 120, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(0, 7, 5, 27), new CollisionBox(4, 0, 6, 34), - new CollisionBox(10, 4, 7, 14) - ] - }, - { - type: 'CACTUS_LARGE', - width: 25, - height: 50, - yPos: 90, - multipleSpeed: 7, - minGap: 120, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(0, 12, 7, 38), new CollisionBox(8, 0, 7, 49), - new CollisionBox(13, 10, 10, 38) - ] - }, - { - type: 'PTERODACTYL', - width: 46, - height: 40, - yPos: [100, 75, 50], // Variable height. - yPosMobile: [100, 50], // Variable height mobile. - multipleSpeed: 999, - minSpeed: 8.5, - minGap: 150, - collisionBoxes: [ - new CollisionBox(15, 15, 16, 5), new CollisionBox(18, 21, 24, 6), - new CollisionBox(2, 14, 4, 3), new CollisionBox(6, 10, 4, 7), - new CollisionBox(10, 8, 6, 9) - ], - numFrames: 2, - frameRate: 1000 / 6, - speedOffset: .8 - }, - { - type: 'COLLECTABLE', - width: 12, - height: 38, - yPos: 90, - multipleSpeed: 999, - minGap: 999, - minSpeed: 0, - collisionBoxes: [new CollisionBox(0, 0, 12, 38)] - } - ], - BACKGROUND_EL: { - 'CLOUD': { - HEIGHT: 14, - MAX_CLOUD_GAP: 400, - MAX_SKY_LEVEL: 30, - MIN_CLOUD_GAP: 100, - MIN_SKY_LEVEL: 71, - OFFSET: 4, - WIDTH: 46, - X_POS: 1, - Y_POS: 120 - } - }, - BACKGROUND_EL_CONFIG: { - MAX_BG_ELS: 1, - MAX_GAP: 400, - MIN_GAP: 100, - POS: 0, - SPEED: 0.5, - Y_POS: 125 - }, - LINES: [ - {SOURCE_X: 2, SOURCE_Y: 52, WIDTH: 600, HEIGHT: 12, YPOS: 127}, - ], - ALT_GAME_END_CONFIG: { - WIDTH: 15, - HEIGHT: 17, - X_OFFSET: 0, - Y_OFFSET: 0, - }, - ALT_GAME_OVER_TEXT_CONFIG: { - TEXT_X: 14, - TEXT_Y: 2, - TEXT_WIDTH: 108, - TEXT_HEIGHT: 15, - FLASH_DURATION: 1500 - } - }, - type_1: { - LDPI: { - OBSTACLE_1: {x: 631, y: 2}, - OBSTACLE_2: {x: 656, y: 2}, - OBSTACLE_3: {x: 697, y: 2}, - OBSTACLE_4: {x: 754, y: 2}, - OBSTACLE_5: {x: 781, y: 2}, - OBSTACLE_6: {x: 826, y: 2}, - BACKGROUND_EL: {x: 0, y: 120}, - CLOUD: {x: 890, y: 2}, - HORIZON: {x: 2, y: 54}, - TREX: {x: 252, y: 2} - }, - HDPI: { - OBSTACLE_1: {x: 1262, y: 2}, - OBSTACLE_2: {x: 1312, y: 2}, - OBSTACLE_3: {x: 1394, y: 2}, - OBSTACLE_4: {x: 1508, y: 2}, - OBSTACLE_5: {x: 1562, y: 2}, - OBSTACLE_6: {x: 1652, y: 2}, - BACKGROUND_EL: {x: 0, y: 240}, - CLOUD: {x: 1780, y: 3}, - HORIZON: {x: 4, y: 108}, - TREX: {x: 504, y: 2} - }, - ALT_GAME_END_CONFIG: {WIDTH: 15, HEIGHT: 17, X_OFFSET: 19, Y_OFFSET: 17}, - MAX_GAP_COEFFICIENT: 0.56, - MAX_OBSTACLE_LENGTH: 1, - HAS_CLOUDS: 1, - BOTTOM_PAD: 10, - TREX: { - MAX_JUMP_HEIGHT: 50, - MIN_JUMP_HEIGHT: 50, - INITIAL_JUMP_VELOCITY: -10, - RUNNING_1: {x: 137, w: 44, h: 49, xOffset: 0}, - RUNNING_2: {x: 183, w: 44, h: 47, xOffset: 0}, - CRASHED: {x: 335, w: 44, h: 47, xOffset: 0}, - JUMPING: {x: 230, w: 59, h: 49, xOffset: 6}, - COLLISION_BOXES: [ - new CollisionBox(22, 0, 17, 16), new CollisionBox(0, 16, 32, 9), - new CollisionBox(3, 24, 27, 6), new CollisionBox(5, 30, 21, 4) - ] - }, - OBSTACLES: [ - { - type: 'OBSTACLE_1', - width: 24, - height: 60, - yPos: 106, - multipleSpeed: 4, - minGap: 70, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(0, 0, 3, 26), new CollisionBox(3, 5, 8, 31), - new CollisionBox(11, 24, 11, 10) - ] - }, - { - type: 'OBSTACLE_2', - width: 40, - height: 60, - yPos: 106, - multipleSpeed: 4, - minGap: 90, - minSpeed: 5, - collisionBoxes: [ - new CollisionBox(0, 0, 3, 26), new CollisionBox(3, 5, 24, 31), - new CollisionBox(27, 24, 11, 10) - ] - }, - { - type: 'OBSTACLE_3', - width: 57, - height: 60, - yPos: 106, - multipleSpeed: 4, - minGap: 100, - minSpeed: 7, - collisionBoxes: [ - new CollisionBox(0, 0, 3, 26), new CollisionBox(3, 5, 40, 31), - new CollisionBox(27, 43, 11, 10) - ] - }, - { - type: 'OBSTACLE_4', - width: 27, - height: 44, - yPos: 102, - multipleSpeed: 7, - minGap: 110, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(0, 0, 3, 26), new CollisionBox(3, 3, 8, 31), - new CollisionBox(11, 24, 11, 10) - ] - }, - { - type: 'OBSTACLE_5', - width: 45, - height: 44, - yPos: 102, - multipleSpeed: 7, - minGap: 120, - minSpeed: 7.5, - collisionBoxes: [ - new CollisionBox(0, 0, 4, 26), new CollisionBox(4, 3, 26, 31), - new CollisionBox(30, 30, 12, 11) - ] - }, - { - type: 'OBSTACLE_6', - width: 63, - height: 44, - yPos: 102, - multipleSpeed: 7, - minGap: 140, - minSpeed: 7.5, - collisionBoxes: [ - new CollisionBox(0, 0, 3, 26), new CollisionBox(4, 3, 44, 39), - new CollisionBox(48, 24, 12, 11) - ] - } - ], - BACKGROUND_EL: { - 'BACKGROUND_0': {HEIGHT: 93, WIDTH: 423, Y_POS: 120, X_POS: 1, OFFSET: 4} - }, - BACKGROUND_EL_CONFIG: { - MAX_BG_ELS: 1, - MAX_GAP: 600, - MIN_GAP: 600, - POS: 0, - SPEED: 0.2, - Y_POS: 125 - }, - LINES: [ - {SOURCE_X: 2, SOURCE_Y: 54, WIDTH: 600, HEIGHT: 12, YPOS: 125}, - {SOURCE_X: 2, SOURCE_Y: 84, WIDTH: 600, HEIGHT: 12, YPOS: 138} - ] - }, - type_2: { - LDPI: { - OBSTACLE_1: {x: 655, y: 2}, - BACKGROUND_EL: {x: 0, y: 60}, - CLOUD: {x: 963, y: 3}, - HORIZON: {x: 2, y: 54}, - TREX: {x: 252, y: 2} - }, - HDPI: { - OBSTACLE_1: {x: 1310, y: 2}, - BACKGROUND_EL: {x: 0, y: 120}, - CLOUD: {x: 1926, y: 3}, - HORIZON: {x: 4, y: 108}, - TREX: {x: 504, y: 2}, - }, - ALT_GAME_END_CONFIG: {WIDTH: 15, HEIGHT: 17, X_OFFSET: 19, Y_OFFSET: 18}, - MAX_GAP_COEFFICIENT: 0.56, - MAX_OBSTACLE_LENGTH: 1, - HAS_CLOUDS: 0, - BOTTOM_PAD: 10, - TREX: { - MAX_JUMP_HEIGHT: 30, - MIN_JUMP_HEIGHT: 30, - INITIAL_JUMP_VELOCITY: -19, - RUNNING_1: {x: 137, w: 44, h: 49, xOffset: 0}, - RUNNING_2: {x: 183, w: 44, h: 49, xOffset: 0}, - CRASHED: {x: 359, w: 44, h: 43, xOffset: 0}, - JUMPING: {x: 228, w: 43, h: 44, xOffset: 2.5}, - COLLISION_BOXES: [ - new CollisionBox(22, 0, 17, 16), new CollisionBox(17, 37, 7, 7), - new CollisionBox(10, 17, 19, 20) - ] - }, - OBSTACLES: [{ - type: 'OBSTACLE_1', - width: 54, - height: 54, - yPos: 90, - multipleSpeed: 4, - minGap: 70, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(0, 8, 20, 43), new CollisionBox(21, 6, 8, 42), - new CollisionBox(32, 2, 18, 49) - ] - }], - BACKGROUND_EL: { - 'BACKGROUND_0': {HEIGHT: 120, WIDTH: 89, Y_POS: 40, X_POS: 1, OFFSET: 4}, - 'BACKGROUND_1': - {HEIGHT: 108, WIDTH: 130, Y_POS: 40, X_POS: 92, OFFSET: 4}, - 'BACKGROUND_2': - {HEIGHT: 28, WIDTH: 204, Y_POS: 40, X_POS: 223, OFFSET: 4}, - }, - BACKGROUND_EL_CONFIG: { - MAX_BG_ELS: 2, - MAX_GAP: 550, - MIN_GAP: 400, - POS: 0, - SPEED: 0.5, - Y_POS: 125 - }, - LINES: [{SOURCE_X: 2, SOURCE_Y: 54, WIDTH: 600, HEIGHT: 5, YPOS: 125}] - }, - type_3: { - LDPI: { - OBSTACLE_1: {x: 611, y: 2}, - OBSTACLE_2: {x: 634, y: 2}, - OBSTACLE_3: {x: 671, y: 2}, - OBSTACLE_4: {x: 722, y: 2}, - OBSTACLE_5: {x: 762, y: 2}, - OBSTACLE_6: {x: 806, y: 2}, - BACKGROUND_EL: {x: 0, y: 65}, - CLOUD: {x: 888, y: 2}, - HORIZON: {x: 2, y: 58}, - TREX: {x: 252, y: 2} - }, - HDPI: { - OBSTACLE_1: {x: 1222, y: 2}, - OBSTACLE_2: {x: 1268, y: 2}, - OBSTACLE_3: {x: 1342, y: 2}, - OBSTACLE_4: {x: 1444, y: 2}, - OBSTACLE_5: {x: 1524, y: 2}, - OBSTACLE_6: {x: 1612, y: 2}, - BACKGROUND_EL: {x: 0, y: 130}, - CLOUD: {x: 1776, y: 3}, - HORIZON: {x: 4, y: 116}, - TREX: {x: 504, y: 2} - }, - ALT_GAME_END_CONFIG: {WIDTH: 15, HEIGHT: 17, X_OFFSET: 23, Y_OFFSET: 17}, - MAX_GAP_COEFFICIENT: 0.56, - MAX_OBSTACLE_LENGTH: 1, - BOTTOM_PAD: 10, - HAS_CLOUDS: 1, - TREX: { - MAX_JUMP_HEIGHT: 45, - MIN_JUMP_HEIGHT: 30, - INITIAL_JUMP_VELOCITY: -10, - RUNNING_1: {x: 104, w: 51, h: 57, xOffset: 0}, - RUNNING_2: {x: 156, w: 51, h: 57, xOffset: 0}, - CRASHED: {x: 309, w: 51, h: 57, xOffset: 0}, - JUMPING: {x: 208, w: 51, h: 57, xOffset: 0}, - COLLISION_BOXES: - [new CollisionBox(28, 35, 19, 11), new CollisionBox(3, 44, 26, 4)] - }, - OBSTACLES: [ - { - type: 'OBSTACLE_1', - width: 24, - height: 18, - yPos: 117, - multipleSpeed: 4, - minGap: 50, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(11, 2, 3, 2), new CollisionBox(7, 4, 11, 10), - new CollisionBox(2, 9, 5, 6) - ] - }, - { - type: 'OBSTACLE_2', - width: 40, - height: 22, - yPos: 117, - multipleSpeed: 4, - minGap: 60, - minSpeed: 4.5, - collisionBoxes: [ - new CollisionBox(11, 2, 3, 2), new CollisionBox(7, 5, 23, 10), - new CollisionBox(2, 9, 5, 6) - ] - }, - { - type: 'OBSTACLE_3', - width: 49, - height: 22, - yPos: 117, - multipleSpeed: 4, - minGap: 80, - minSpeed: 7, - collisionBoxes: [ - new CollisionBox(11, 2, 3, 2), new CollisionBox(8, 5, 39, 10), - new CollisionBox(2, 9, 5, 6) - ] - }, - { - type: 'OBSTACLE_4', - width: 37, - height: 26, - yPos: 113, - multipleSpeed: 7, - minGap: 120, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(4, 16, 5, 8), new CollisionBox(9, 12, 7, 12), - new CollisionBox(16, 5, 10, 19) - ] - }, - { - type: 'OBSTACLE_5', - width: 45, - height: 30, - yPos: 113, - multipleSpeed: 7, - minGap: 120, - minSpeed: 5.5, - collisionBoxes: [ - new CollisionBox(4, 16, 5, 8), new CollisionBox(9, 12, 7, 12), - new CollisionBox(16, 5, 10, 19), new CollisionBox(26, 14, 13, 11) - ] - }, - { - type: 'OBSTACLE_6', - width: 79, - height: 30, - yPos: 113, - multipleSpeed: 7, - minGap: 150, - minSpeed: 7, - collisionBoxes: [ - new CollisionBox(4, 16, 5, 8), new CollisionBox(9, 12, 7, 12), - new CollisionBox(16, 5, 10, 19), new CollisionBox(26, 14, 13, 11), - new CollisionBox(40, 18, 10, 6), new CollisionBox(50, 12, 10, 12), - new CollisionBox(57, 5, 10, 19) - ] - } - ], - BACKGROUND_EL: { - 'BACKGROUND_0': { - HEIGHT: 78, - OFFSET: 6, - WIDTH: 105, - X_POS: 425, - FIXED_X_POS: 0, - FIXED_Y_POS_1: 54, - FIXED_Y_POS_2: 51, - FIXED: true - } - }, - BACKGROUND_EL_CONFIG: { - MAX_BG_ELS: 1, - MAX_GAP: 550, - MIN_GAP: 400, - POS: 0, - SPEED: 0.2, - Y_POS: 125, - MS_PER_FRAME: 250 - }, - LINES: [ - {SOURCE_X: 2, SOURCE_Y: 58, WIDTH: 600, HEIGHT: 8, YPOS: 125}, - ] - }, - type_4: { - LDPI: { - OBSTACLE_1: {x: 514, y: 2}, - OBSTACLE_2: {x: 543, y: 2}, - OBSTACLE_3: {x: 599, y: 2}, - OBSTACLE_4: {x: 643, y: 2}, - BACKGROUND_EL: {x: 811, y: 2}, - CLOUD: {x: 888, y: 2}, - WALL: {x: 2, y: 54}, - HORIZON: {x: 2, y: 81}, - TREX: {x: 252, y: 2} - }, - HDPI: { - OBSTACLE_1: {x: 1028, y: 2}, - OBSTACLE_2: {x: 1086, y: 2}, - OBSTACLE_3: {x: 1198, y: 2}, - OBSTACLE_4: {x: 1286, y: 2}, - BACKGROUND_EL: {x: 1622, y: 4}, - CLOUD: {x: 1776, y: 3}, - WALL: {x: 2, y: 108}, - HORIZON: {x: 4, y: 162}, - TREX: {x: 504, y: 2} - }, - ALT_GAME_END_CONFIG: {WIDTH: 15, HEIGHT: 17, X_OFFSET: 38, Y_OFFSET: 16}, - MAX_GAP_COEFFICIENT: 0.56, - MAX_OBSTACLE_LENGTH: 1, - BOTTOM_PAD: 43, - HAS_CLOUDS: 0, - TREX: { - GRAVITY: 0.36, - MAX_JUMP_HEIGHT: 20, - MIN_JUMP_HEIGHT: 18, - INITIAL_JUMP_VELOCITY: -20, - INVERT_JUMP: 1, - RUNNING_1: {x: 0, w: 65, h: 30, xOffset: 0}, - RUNNING_2: {x: 67, w: 65, h: 30, xOffset: 0}, - CRASHED: {x: 196, w: 65, h: 30, xOffset: 0}, - JUMPING: {x: 133.5, w: 65, h: 30, xOffset: 0}, - COLLISION_BOXES: [ - new CollisionBox(17, 4, 49, 9), new CollisionBox(20, 17, 23, 4), - new CollisionBox(19, 20, 10, 7), new CollisionBox(17, 13, 42, 4) - ] - }, - OBSTACLES: [ - { - type: 'OBSTACLE_1', - width: 27, - height: 11, - yPos: 80, - multipleSpeed: 4, - minGap: 120, - minSpeed: 0, - collisionBoxes: [new CollisionBox(0, 2, 27, 8)] - }, - { - type: 'OBSTACLE_2', - width: 54, - height: 11, - yPos: 80, - multipleSpeed: 4, - minGap: 140, - minSpeed: 7, - collisionBoxes: [new CollisionBox(0, 2, 52, 8)] - }, - { - type: 'OBSTACLE_3', - width: 42, - height: 16, - yPos: 76, - multipleSpeed: 4, - minGap: 170, - minSpeed: 3, - collisionBoxes: [new CollisionBox(0, 2, 40, 14)] - } - ], - BACKGROUND_EL_CONFIG: { - SPEED: 0.5, - POS: 0, - MAX_BG_ELS: 3, - MIN_GAP: 100, - MAX_GAP: 400, - Y_POS: 100 - }, - BACKGROUND_EL: { - 'BACKGROUND_0': - {HEIGHT: 32, WIDTH: 30, Y_POS: 2, X_POS: 811, OFFSET: -65}, - 'BACKGROUND_1': - {HEIGHT: 37, WIDTH: 40, Y_POS: 2, X_POS: 842, OFFSET: -13}, - 'BACKGROUND_2': {HEIGHT: 33, WIDTH: 82, Y_POS: 2, X_POS: 727, OFFSET: -40} - }, - LINES: [ - {SOURCE_X: 2, SOURCE_Y: 81, WIDTH: 600, HEIGHT: 12, YPOS: 78}, - {SOURCE_X: 2, SOURCE_Y: 54, WIDTH: 600, HEIGHT: 12, YPOS: 56} - ] - }, - type_5: { - LDPI: { - OBSTACLE_1: {x: 458, y: 2}, - OBSTACLE_2: {x: 458, y: 2}, - BACKGROUND_EL: {x: 0, y: 0}, - CLOUD: {x: 482, y: 2}, - WALL: {x: 2, y: 54}, - HORIZON: {x: 2, y: 77}, - TREX: {x: 252, y: 2} - }, - HDPI: { - OBSTACLE_1: {x: 916, y: 2}, - OBSTACLE_2: {x: 916, y: 2}, - BACKGROUND_EL: {x: 0, y: 0}, - CLOUD: {x: 963, y: 3}, - WALL: {x: 2, y: 108}, - HORIZON: {x: 4, y: 154}, - TREX: {x: 504, y: 2} - }, - ALT_GAME_END_CONFIG: {WIDTH: 15, HEIGHT: 17, X_OFFSET: 24, Y_OFFSET: 23}, - MAX_GAP_COEFFICIENT: 2.5, - MAX_OBSTACLE_LENGTH: 1, - BOTTOM_PAD: 12, - HAS_CLOUDS: 1, - TREX: { - MAX_JUMP_HEIGHT: 30, - MIN_JUMP_HEIGHT: 30, - INITIAL_JUMP_VELOCITY: -10, - RUNNING_1: {x: 0, w: 51, h: 67, xOffset: 0}, - RUNNING_2: {x: 50, w: 51, h: 67, xOffset: 0}, - CRASHED: {x: 156, w: 51, h: 67, xOffset: 0}, - JUMPING: {x: 103, w: 54, h: 67, xOffset: 0}, - COLLISION_BOXES: [ - new CollisionBox(35, 30, 13, 9), new CollisionBox(19, 51, 22, 9), - new CollisionBox(9, 51, 9, 13), new CollisionBox(4, 27, 31, 28) - ] - }, - OBSTACLES: [{ - type: 'OBSTACLE_1', - width: 21, - height: 57, - yPos: 93, - multipleSpeed: 999, - minGap: 40, - minSpeed: 0, - collisionBoxes: [ - new CollisionBox(0, 0, 3, 41), new CollisionBox(3, 5, 14, 39), - new CollisionBox(16, 7, 4, 43) - ] - }], - BACKGROUND_EL_CONFIG: { - MAX_BG_ELS: 4, - MAX_GAP: 420, - MIN_GAP: 320, - POS: 0, - SPEED: 0.3, - Y_POS: 125 - }, - BACKGROUND_EL: { - 'BACKGROUND_0': {HEIGHT: 40, WIDTH: 170, Y_POS: 100, X_POS: 0, OFFSET: 10} - }, - LINES: [{SOURCE_X: 2, SOURCE_Y: 71, WIDTH: 600, HEIGHT: 12, YPOS: 123}] - } -}; \ No newline at end of file diff --git a/chrome-dino/scripts/offline.js b/chrome-dino/scripts/offline.js deleted file mode 100644 index a9978b3d..00000000 --- a/chrome-dino/scripts/offline.js +++ /dev/null @@ -1,4196 +0,0 @@ -function _isIpad() { - var isIpad = navigator.userAgent.toLowerCase().indexOf('ipad') !== -1; - - if (!isIpad && navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) { - return true; - } - - return isIpad; -} - -// Copyright (c) 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * T-Rex runner. - * @param {string} outerContainerId Outer containing element id. - * @param {!Object=} opt_config - * @constructor - * @implements {EventListener} - * @export - */ -function Runner(outerContainerId, opt_config) { - // Singleton - if (Runner.instance_) { - return Runner.instance_; - } - Runner.instance_ = this; - - this.outerContainerEl = document.querySelector(outerContainerId); - this.containerEl = null; - this.snackbarEl = null; - // A div to intercept touch events. Only set while (playing && useTouch). - this.touchController = null; - - this.config = opt_config || Object.assign(Runner.config, Runner.normalConfig); - // Logical dimensions of the container. - this.dimensions = Runner.defaultDimensions; - - this.gameType = null; - Runner.spriteDefinition = Runner.spriteDefinitionByType['original']; - - this.altGameImageSprite = null; - this.altGameModeActive = false; - this.altGameModeFlashTimer = null; - this.fadeInTimer = 0; - - this.canvas = null; - this.canvasCtx = null; - - this.tRex = null; - - this.distanceMeter = null; - this.distanceRan = 0; - - this.highestScore = window.localStorage.getItem("chrome-dino"); - this.syncHighestScore = false; - - this.time = 0; - this.runningTime = 0; - this.msPerFrame = 1000 / FPS; - this.currentSpeed = this.config.SPEED; - Runner.slowDown = false; - - this.obstacles = []; - - this.activated = false; // Whether the easter egg has been activated. - this.playing = false; // Whether the game is currently in play state. - this.crashed = false; - this.paused = false; - this.inverted = false; - this.invertTimer = 0; - this.resizeTimerId_ = null; - - this.playCount = 0; - - // Sound FX. - this.audioBuffer = null; - - /** @type {Object} */ - this.soundFx = {}; - this.generatedSoundFx = null; - - // Global web audio context for playing sounds. - this.audioContext = null; - - // Images. - this.images = {}; - this.imagesLoaded = 0; - - // Gamepad state. - this.pollingGamepads = false; - this.gamepadIndex = undefined; - this.previousGamepad = null; - - if (this.isDisabled()) { - this.setupDisabledRunner(); - } else { - if (Runner.isAltGameModeEnabled()) { - this.initAltGameType(); - Runner.gameType = this.gameType; - } - this.loadImages(); - - window['initializeEasterEggHighScore'] = - this.initializeHighScore.bind(this); - } -} - -/** - * Default game width. - * @const - */ -const DEFAULT_WIDTH = 600; - -/** - * Frames per second. - * @const - */ -const FPS = 60; - -/** @const */ -const IS_HIDPI = window.devicePixelRatio > 1; - -/** @const */ -const IS_IOS = !!window.navigator.userAgent.match(/iP(hone|ad|od)/i) && !!window.navigator.userAgent.match(/Safari/i) || - _isIpad() || /CriOS/.test(window.navigator.userAgent) || /FxiOS/.test(window.navigator.userAgent); - -/** @const */ -const IS_MOBILE = /Android/.test(window.navigator.userAgent) || IS_IOS; - -/** @const */ -const IS_RTL = document.querySelector('html').dir == 'rtl'; - -/** @const */ -const ARCADE_MODE_URL = 'chrome://dino/'; - -/** @const */ -const RESOURCE_POSTFIX = 'offline-resources-'; - -/** @const */ -const A11Y_STRINGS = { - ariaLabel: 'dinoGameA11yAriaLabel', - description: 'dinoGameA11yDescription', - gameOver: 'dinoGameA11yGameOver', - highScore: 'dinoGameA11yHighScore', - jump: 'dinoGameA11yJump', - started: 'dinoGameA11yStartGame', - speedLabel: 'dinoGameA11ySpeedToggle', -}; - -/** - * Default game configuration. - * Shared config for all versions of the game. Additional parameters are - * defined in Runner.normalConfig and Runner.slowConfig. - */ -Runner.config = { - AUDIOCUE_PROXIMITY_THRESHOLD: 190, - AUDIOCUE_PROXIMITY_THRESHOLD_MOBILE_A11Y: 250, - BG_CLOUD_SPEED: 0.2, - BOTTOM_PAD: 10, - // Scroll Y threshold at which the game can be activated. - CANVAS_IN_VIEW_OFFSET: -10, - CLEAR_TIME: 3000, - CLOUD_FREQUENCY: 0.5, - FADE_DURATION: 1, - FLASH_DURATION: 1000, - GAMEOVER_CLEAR_TIME: 1200, - INITIAL_JUMP_VELOCITY: 12, - INVERT_FADE_DURATION: 12000, - MAX_BLINK_COUNT: 3, - MAX_CLOUDS: 6, - MAX_OBSTACLE_LENGTH: 3, - MAX_OBSTACLE_DUPLICATION: 2, - RESOURCE_TEMPLATE_ID: 'audio-resources', - SPEED: 6, - SPEED_DROP_COEFFICIENT: 3, - ARCADE_MODE_INITIAL_TOP_POSITION: 35, - ARCADE_MODE_TOP_POSITION_PERCENT: 0.1, -}; - -Runner.normalConfig = { - ACCELERATION: 0.001, - AUDIOCUE_PROXIMITY_THRESHOLD: 190, - AUDIOCUE_PROXIMITY_THRESHOLD_MOBILE_A11Y: 250, - GAP_COEFFICIENT: 0.6, - INVERT_DISTANCE: 700, - MAX_SPEED: 13, - MOBILE_SPEED_COEFFICIENT: 1.2, - SPEED: 6, -}; - - -Runner.slowConfig = { - ACCELERATION: 0.0005, - AUDIOCUE_PROXIMITY_THRESHOLD: 170, - AUDIOCUE_PROXIMITY_THRESHOLD_MOBILE_A11Y: 220, - GAP_COEFFICIENT: 0.3, - INVERT_DISTANCE: 350, - MAX_SPEED: 9, - MOBILE_SPEED_COEFFICIENT: 1.5, - SPEED: 4.2, -}; - - -/** - * Default dimensions. - */ -Runner.defaultDimensions = { - WIDTH: DEFAULT_WIDTH, - HEIGHT: 150, -}; - - -/** - * CSS class names. - * @enum {string} - */ -Runner.classes = { - ARCADE_MODE: 'arcade-mode', - CANVAS: 'runner-canvas', - CONTAINER: 'runner-container', - CRASHED: 'crashed', - ICON: 'icon-offline', - INVERTED: 'inverted', - SNACKBAR: 'snackbar', - SNACKBAR_SHOW: 'snackbar-show', - TOUCH_CONTROLLER: 'controller', -}; - - -/** - * Sound FX. Reference to the ID of the audio tag on interstitial page. - * @enum {string} - */ -Runner.sounds = { - BUTTON_PRESS: 'offline-sound-press', - HIT: 'offline-sound-hit', - SCORE: 'offline-sound-reached', -}; - - -/** - * Key code mapping. - * @enum {Object} - */ -Runner.keycodes = { - JUMP: {'38': 1, '32': 1}, // Up, spacebar - DUCK: {'40': 1}, // Down - RESTART: {'13': 1}, // Enter -}; - - -/** - * Runner event names. - * @enum {string} - */ -Runner.events = { - ANIM_END: 'webkitAnimationEnd', - CLICK: 'click', - KEYDOWN: 'keydown', - KEYUP: 'keyup', - POINTERDOWN: 'pointerdown', - POINTERUP: 'pointerup', - RESIZE: 'resize', - TOUCHEND: 'touchend', - TOUCHSTART: 'touchstart', - VISIBILITY: 'visibilitychange', - BLUR: 'blur', - FOCUS: 'focus', - LOAD: 'load', - GAMEPADCONNECTED: 'gamepadconnected', -}; - -Runner.prototype = { - /** - * Initialize alternative game type. - */ - initAltGameType() { - if (GAME_TYPE.length > 0) { - this.gameType = loadTimeData && loadTimeData.valueExists('altGameType') ? - GAME_TYPE[parseInt(loadTimeData.getValue('altGameType'), 10) - 1] : - ''; - } - }, - - /** - * Whether the easter egg has been disabled. CrOS enterprise enrolled devices. - * @return {boolean} - */ - isDisabled() { - return loadTimeData && loadTimeData.valueExists('disabledEasterEgg'); - }, - - /** - * For disabled instances, set up a snackbar with the disabled message. - */ - setupDisabledRunner() { - this.containerEl = document.createElement('div'); - this.containerEl.className = Runner.classes.SNACKBAR; - this.containerEl.textContent = loadTimeData.getValue('disabledEasterEgg'); - this.outerContainerEl.appendChild(this.containerEl); - - // Show notification when the activation key is pressed. - document.addEventListener(Runner.events.KEYDOWN, function(e) { - if (Runner.keycodes.JUMP[e.keyCode]) { - this.containerEl.classList.add(Runner.classes.SNACKBAR_SHOW); - document.querySelector('.icon').classList.add('icon-disabled'); - } - }.bind(this)); - }, - - /** - * Setting individual settings for debugging. - * @param {string} setting - * @param {number|string} value - */ - updateConfigSetting(setting, value) { - if (setting in this.config && value !== undefined) { - this.config[setting] = value; - - switch (setting) { - case 'GRAVITY': - case 'MIN_JUMP_HEIGHT': - case 'SPEED_DROP_COEFFICIENT': - this.tRex.config[setting] = value; - break; - case 'INITIAL_JUMP_VELOCITY': - this.tRex.setJumpVelocity(value); - break; - case 'SPEED': - this.setSpeed(/** @type {number} */ (value)); - break; - } - } - }, - - /** - * Creates an on page image element from the base 64 encoded string source. - * @param {string} resourceName Name in data object, - * @return {HTMLImageElement} The created element. - */ - createImageElement(resourceName) { - const imgSrc = loadTimeData && loadTimeData.valueExists(resourceName) ? - loadTimeData.getString(resourceName) : - null; - - if (imgSrc) { - const el = - /** @type {HTMLImageElement} */ (document.createElement('img')); - el.id = resourceName; - el.src = imgSrc; - document.getElementById('offline-resources').appendChild(el); - return el; - } - return null; - }, - - /** - * Cache the appropriate image sprite from the page and get the sprite sheet - * definition. - */ - loadImages() { - let scale = '1x'; - this.spriteDef = Runner.spriteDefinition.LDPI; - if (IS_HIDPI) { - scale = '2x'; - this.spriteDef = Runner.spriteDefinition.HDPI; - } - - Runner.imageSprite = /** @type {HTMLImageElement} */ - (document.getElementById(RESOURCE_POSTFIX + scale)); - - if (this.gameType) { - Runner.altGameImageSprite = /** @type {HTMLImageElement} */ - (this.createImageElement('altGameSpecificImage' + scale)); - Runner.altCommonImageSprite = /** @type {HTMLImageElement} */ - (this.createImageElement('altGameCommonImage' + scale)); - } - Runner.origImageSprite = Runner.imageSprite; - - // Disable the alt game mode if the sprites can't be loaded. - if (!Runner.altGameImageSprite || !Runner.altCommonImageSprite) { - Runner.isAltGameModeEnabled = () => false; - this.altGameModeActive = false; - } - - if (Runner.imageSprite.complete) { - this.init(); - } else { - // If the images are not yet loaded, add a listener. - Runner.imageSprite.addEventListener(Runner.events.LOAD, - this.init.bind(this)); - } - }, - - /** - * Load and decode base 64 encoded sounds. - */ - loadSounds() { - if (!IS_IOS) { - this.audioContext = new AudioContext(); - - const resourceTemplate = - document.getElementById(this.config.RESOURCE_TEMPLATE_ID).content; - - for (const sound in Runner.sounds) { - let soundSrc = - resourceTemplate.getElementById(Runner.sounds[sound]).src; - soundSrc = soundSrc.substr(soundSrc.indexOf(',') + 1); - const buffer = decodeBase64ToArrayBuffer(soundSrc); - - // Async, so no guarantee of order in array. - this.audioContext.decodeAudioData(buffer, function(index, audioData) { - this.soundFx[index] = audioData; - }.bind(this, sound)); - } - } - }, - - /** - * Sets the game speed. Adjust the speed accordingly if on a smaller screen. - * @param {number=} opt_speed - */ - setSpeed(opt_speed) { - const speed = opt_speed || this.currentSpeed; - - // Reduce the speed on smaller mobile screens. - if (this.dimensions.WIDTH < DEFAULT_WIDTH) { - const mobileSpeed = Runner.slowDown ? speed : - speed * this.dimensions.WIDTH / - DEFAULT_WIDTH * this.config.MOBILE_SPEED_COEFFICIENT; - this.currentSpeed = mobileSpeed > speed ? speed : mobileSpeed; - } else if (opt_speed) { - this.currentSpeed = opt_speed; - } - }, - - /** - * Game initialiser. - */ - init() { - // Hide the static icon. - document.querySelector('.' + Runner.classes.ICON).style.visibility = - 'hidden'; - - this.adjustDimensions(); - this.setSpeed(); - - const ariaLabel = getA11yString(A11Y_STRINGS.ariaLabel); - this.containerEl = document.createElement('div'); - this.containerEl.setAttribute('role', IS_MOBILE ? 'button' : 'application'); - this.containerEl.setAttribute('tabindex', '0'); - this.containerEl.setAttribute('title', ariaLabel); - - this.containerEl.className = Runner.classes.CONTAINER; - - // Player canvas container. - this.canvas = createCanvas(this.containerEl, this.dimensions.WIDTH, - this.dimensions.HEIGHT); - - // Live region for game status updates. - this.a11yStatusEl = document.createElement('span'); - this.a11yStatusEl.className = 'offline-runner-live-region'; - this.a11yStatusEl.setAttribute('aria-live', 'assertive'); - this.a11yStatusEl.textContent = ''; - Runner.a11yStatusEl = this.a11yStatusEl; - - // Add checkbox to slow down the game. - this.slowSpeedCheckboxLabel = document.createElement('label'); - this.slowSpeedCheckboxLabel.className = 'slow-speed-option hidden'; - this.slowSpeedCheckboxLabel.textContent = - getA11yString(A11Y_STRINGS.speedLabel); - - this.slowSpeedCheckbox = document.createElement('input'); - this.slowSpeedCheckbox.setAttribute('type', 'checkbox'); - this.slowSpeedCheckbox.setAttribute( - 'title', getA11yString(A11Y_STRINGS.speedLabel)); - this.slowSpeedCheckbox.setAttribute('tabindex', '0'); - this.slowSpeedCheckbox.setAttribute('checked', 'checked'); - - this.slowSpeedToggleEl = document.createElement('span'); - this.slowSpeedToggleEl.className = 'slow-speed-toggle'; - - this.slowSpeedCheckboxLabel.appendChild(this.slowSpeedCheckbox); - this.slowSpeedCheckboxLabel.appendChild(this.slowSpeedToggleEl); - - if (IS_IOS) { - this.outerContainerEl.appendChild(this.a11yStatusEl); - } else { - this.containerEl.appendChild(this.a11yStatusEl); - } - - announcePhrase(getA11yString(A11Y_STRINGS.description)); - - this.generatedSoundFx = new GeneratedSoundFx(); - - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (this.canvas.getContext('2d')); - this.canvasCtx.fillStyle = '#f7f7f7'; - this.canvasCtx.fill(); - Runner.updateCanvasScaling(this.canvas); - - // Horizon contains clouds, obstacles and the ground. - this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions, - this.config.GAP_COEFFICIENT); - - // Distance meter - this.distanceMeter = new DistanceMeter(this.canvas, - this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH); - - // Draw t-rex - this.tRex = new Trex(this.canvas, this.spriteDef.TREX); - - this.outerContainerEl.appendChild(this.containerEl); - this.outerContainerEl.appendChild(this.slowSpeedCheckboxLabel); - - this.startListening(); - this.update(); - - window.addEventListener(Runner.events.RESIZE, - this.debounceResize.bind(this)); - - // Handle dark mode - const darkModeMediaQuery = - window.matchMedia('(prefers-color-scheme: dark)'); - this.isDarkMode = darkModeMediaQuery && darkModeMediaQuery.matches; - darkModeMediaQuery.addListener((e) => { - this.isDarkMode = e.matches; - }); - }, - - /** - * Create the touch controller. A div that covers whole screen. - */ - createTouchController() { - this.touchController = document.createElement('div'); - this.touchController.className = Runner.classes.TOUCH_CONTROLLER; - this.touchController.addEventListener(Runner.events.TOUCHSTART, this); - this.touchController.addEventListener(Runner.events.TOUCHEND, this); - this.outerContainerEl.appendChild(this.touchController); - }, - - /** - * Debounce the resize event. - */ - debounceResize() { - if (!this.resizeTimerId_) { - this.resizeTimerId_ = - setInterval(this.adjustDimensions.bind(this), 250); - } - }, - - /** - * Adjust game space dimensions on resize. - */ - adjustDimensions() { - clearInterval(this.resizeTimerId_); - this.resizeTimerId_ = null; - - const boxStyles = window.getComputedStyle(this.outerContainerEl); - const padding = Number(boxStyles.paddingLeft.substr(0, - boxStyles.paddingLeft.length - 2)); - - this.dimensions.WIDTH = this.outerContainerEl.offsetWidth - padding * 2; - if (this.isArcadeMode()) { - this.dimensions.WIDTH = Math.min(DEFAULT_WIDTH, this.dimensions.WIDTH); - if (this.activated) { - this.setArcadeModeContainerScale(); - } - } - - // Redraw the elements back onto the canvas. - if (this.canvas) { - this.canvas.width = this.dimensions.WIDTH; - this.canvas.height = this.dimensions.HEIGHT; - - Runner.updateCanvasScaling(this.canvas); - - this.distanceMeter.calcXPos(this.dimensions.WIDTH); - this.clearCanvas(); - this.horizon.update(0, 0, true); - this.tRex.update(0); - - // Outer container and distance meter. - if (this.playing || this.crashed || this.paused) { - this.containerEl.style.width = this.dimensions.WIDTH + 'px'; - this.containerEl.style.height = this.dimensions.HEIGHT + 'px'; - this.distanceMeter.update(0, Math.ceil(this.distanceRan)); - this.stop(); - } else { - this.tRex.draw(0, 0); - } - - // Game over panel. - if (this.crashed && this.gameOverPanel) { - this.gameOverPanel.updateDimensions(this.dimensions.WIDTH); - this.gameOverPanel.draw(this.altGameModeActive, this.tRex); - } - } - }, - - /** - * Play the game intro. - * Canvas container width expands out to the full width. - */ - playIntro() { - if (!this.activated && !this.crashed) { - this.playingIntro = true; - this.tRex.playingIntro = true; - if (window.localStorage.getItem("chrome-dino") !== null) { - this.distanceMeter.setHighScore(window.localStorage.getItem("chrome-dino")); - } - - // CSS animation definition. - const keyframes = '@-webkit-keyframes intro { ' + - 'from { width:' + Trex.config.WIDTH + 'px }' + - 'to { width: ' + this.dimensions.WIDTH + 'px }' + - '}'; - // document.styleSheets[0].insertRule(keyframes, 0); - - // create a style sheet to put the keyframe rule in - // and then place the style sheet in the html head - var sheet = document.createElement('style'); - sheet.innerHTML = keyframes; - document.head.appendChild(sheet); - - this.containerEl.addEventListener(Runner.events.ANIM_END, - this.startGame.bind(this)); - - this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both'; - this.containerEl.style.width = this.dimensions.WIDTH + 'px'; - - this.setPlayStatus(true); - this.activated = true; - } else if (this.crashed) { - this.restart(); - } - }, - - - /** - * Update the game status to started. - */ - startGame() { - if (this.isArcadeMode()) { - this.setArcadeMode(); - } - this.toggleSpeed(); - this.runningTime = 0; - this.playingIntro = false; - this.tRex.playingIntro = false; - this.containerEl.style.webkitAnimation = ''; - this.playCount++; - this.generatedSoundFx.background(); - announcePhrase(getA11yString(A11Y_STRINGS.started)); - - if (Runner.audioCues) { - this.containerEl.setAttribute('title', getA11yString(A11Y_STRINGS.jump)); - } - - // Handle tabbing off the page. Pause the current game. - document.addEventListener(Runner.events.VISIBILITY, - this.onVisibilityChange.bind(this)); - - window.addEventListener(Runner.events.BLUR, - this.onVisibilityChange.bind(this)); - - window.addEventListener(Runner.events.FOCUS, - this.onVisibilityChange.bind(this)); - }, - - clearCanvas() { - this.canvasCtx.clearRect(0, 0, this.dimensions.WIDTH, - this.dimensions.HEIGHT); - }, - - /** - * Checks whether the canvas area is in the viewport of the browser - * through the current scroll position. - * @return boolean. - */ - isCanvasInView() { - return this.containerEl.getBoundingClientRect().top > - Runner.config.CANVAS_IN_VIEW_OFFSET; - }, - - /** - * Enable the alt game mode. Switching out the sprites. - */ - enableAltGameMode() { - Runner.imageSprite = Runner.altGameImageSprite; - Runner.spriteDefinition = Runner.spriteDefinitionByType[Runner.gameType]; - - if (IS_HIDPI) { - this.spriteDef = Runner.spriteDefinition.HDPI; - } else { - this.spriteDef = Runner.spriteDefinition.LDPI; - } - - this.altGameModeActive = true; - this.tRex.enableAltGameMode(this.spriteDef.TREX); - this.horizon.enableAltGameMode(this.spriteDef); - this.generatedSoundFx.background(); - }, - - /** - * Update the game frame and schedules the next one. - */ - update() { - this.updatePending = false; - - const now = getTimeStamp(); - let deltaTime = now - (this.time || now); - - // Flashing when switching game modes. - if (this.altGameModeFlashTimer < 0 || this.altGameModeFlashTimer === 0) { - this.altGameModeFlashTimer = null; - this.tRex.setFlashing(false); - this.enableAltGameMode(); - } else if (this.altGameModeFlashTimer > 0) { - this.altGameModeFlashTimer -= deltaTime; - this.tRex.update(deltaTime); - deltaTime = 0; - } - - this.time = now; - - if (this.playing) { - this.clearCanvas(); - - // Additional fade in - Prevents jump when switching sprites - if (this.altGameModeActive && - this.fadeInTimer <= this.config.FADE_DURATION) { - this.fadeInTimer += deltaTime / 1000; - this.canvasCtx.globalAlpha = this.fadeInTimer; - } else { - this.canvasCtx.globalAlpha = 1; - } - - if (this.tRex.jumping) { - this.tRex.updateJump(deltaTime); - } - - this.runningTime += deltaTime; - const hasObstacles = this.runningTime > this.config.CLEAR_TIME; - - // First jump triggers the intro. - if (this.tRex.jumpCount === 1 && !this.playingIntro) { - this.playIntro(); - } - - // The horizon doesn't move until the intro is over. - if (this.playingIntro) { - this.horizon.update(0, this.currentSpeed, hasObstacles); - } else if (!this.crashed) { - const showNightMode = this.isDarkMode ^ this.inverted; - deltaTime = !this.activated ? 0 : deltaTime; - this.horizon.update( - deltaTime, this.currentSpeed, hasObstacles, showNightMode); - } - - // Check for collisions. - let collision = hasObstacles && - checkForCollision(this.horizon.obstacles[0], this.tRex); - - // For a11y, audio cues. - if (Runner.audioCues && hasObstacles) { - const jumpObstacle = - this.horizon.obstacles[0].typeConfig.type != 'COLLECTABLE'; - - if (!this.horizon.obstacles[0].jumpAlerted) { - const threshold = Runner.isMobileMouseInput ? - Runner.config.AUDIOCUE_PROXIMITY_THRESHOLD_MOBILE_A11Y : - Runner.config.AUDIOCUE_PROXIMITY_THRESHOLD; - const adjProximityThreshold = threshold + - (threshold * Math.log10(this.currentSpeed / Runner.config.SPEED)); - - if (this.horizon.obstacles[0].xPos < adjProximityThreshold) { - if (jumpObstacle) { - this.generatedSoundFx.jump(); - } - this.horizon.obstacles[0].jumpAlerted = true; - } - } - } - - // Activated alt game mode. - if (Runner.isAltGameModeEnabled() && collision && - this.horizon.obstacles[0].typeConfig.type == 'COLLECTABLE') { - this.horizon.removeFirstObstacle(); - this.tRex.setFlashing(true); - collision = false; - this.altGameModeFlashTimer = this.config.FLASH_DURATION; - this.runningTime = 0; - this.generatedSoundFx.collect(); - } - - if (!collision) { - this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame; - - if (this.currentSpeed < this.config.MAX_SPEED) { - this.currentSpeed += this.config.ACCELERATION; - } - } else { - this.gameOver(); - } - - const playAchievementSound = this.distanceMeter.update(deltaTime, - Math.ceil(this.distanceRan)); - - if (!Runner.audioCues && playAchievementSound) { - this.playSound(this.soundFx.SCORE); - } - - // Night mode. - if (!Runner.isAltGameModeEnabled()) { - if (this.invertTimer > this.config.INVERT_FADE_DURATION) { - this.invertTimer = 0; - this.invertTrigger = false; - this.invert(false); - } else if (this.invertTimer) { - this.invertTimer += deltaTime; - } else { - const actualDistance = - this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan)); - - if (actualDistance > 0) { - this.invertTrigger = - !(actualDistance % this.config.INVERT_DISTANCE); - - if (this.invertTrigger && this.invertTimer === 0) { - this.invertTimer += deltaTime; - this.invert(false); - } - } - } - } - } - - if (this.playing || (!this.activated && - this.tRex.blinkCount < Runner.config.MAX_BLINK_COUNT)) { - this.tRex.update(deltaTime); - this.scheduleNextUpdate(); - } - }, - - /** - * Event handler. - * @param {Event} e - */ - handleEvent(e) { - return (function(evtType, events) { - switch (evtType) { - case events.KEYDOWN: - case events.TOUCHSTART: - case events.POINTERDOWN: - this.onKeyDown(e); - break; - case events.KEYUP: - case events.TOUCHEND: - case events.POINTERUP: - this.onKeyUp(e); - break; - case events.GAMEPADCONNECTED: - this.onGamepadConnected(e); - break; - } - }.bind(this))(e.type, Runner.events); - }, - - /** - * Initialize audio cues if activated by focus on the canvas element. - * @param {Event} e - */ - handleCanvasKeyPress(e) { - if (!this.activated && !Runner.audioCues) { - this.toggleSpeed(); - Runner.audioCues = true; - this.generatedSoundFx.init(); - Runner.generatedSoundFx = this.generatedSoundFx; - Runner.config.CLEAR_TIME *= 1.2; - } else if (e.keyCode && Runner.keycodes.JUMP[e.keyCode]) { - this.onKeyDown(e); - } - }, - - /** - * Prevent space key press from scrolling. - * @param {Event} e - */ - preventScrolling(e) { - if (e.keyCode === 32) { - e.preventDefault(); - } - }, - - /** - * Toggle speed setting if toggle is shown. - */ - toggleSpeed() { - if (Runner.audioCues) { - const speedChange = Runner.slowDown != this.slowSpeedCheckbox.checked; - - if (speedChange) { - Runner.slowDown = this.slowSpeedCheckbox.checked; - const updatedConfig = - Runner.slowDown ? Runner.slowConfig : Runner.normalConfig; - - Runner.config = Object.assign(Runner.config, updatedConfig); - this.currentSpeed = updatedConfig.SPEED; - this.tRex.enableSlowConfig(); - this.horizon.adjustObstacleSpeed(); - } - if (this.playing) { - this.disableSpeedToggle(true); - } - } - }, - - /** - * Show the speed toggle. - * From focus event or when audio cues are activated. - * @param {Event=} e - */ - showSpeedToggle(e) { - const isFocusEvent = e && e.type == 'focus'; - if (Runner.audioCues || isFocusEvent) { - this.slowSpeedCheckboxLabel.classList.toggle( - HIDDEN_CLASS, isFocusEvent ? false : !this.crashed); - } - }, - - /** - * Disable the speed toggle. - * @param {boolean} disable - */ - disableSpeedToggle(disable) { - if (disable) { - this.slowSpeedCheckbox.setAttribute('disabled', 'disabled'); - } else { - this.slowSpeedCheckbox.removeAttribute('disabled'); - } - }, - - /** - * Bind relevant key / mouse / touch listeners. - */ - startListening() { - // A11y keyboard / screen reader activation. - this.containerEl.addEventListener( - Runner.events.KEYDOWN, this.handleCanvasKeyPress.bind(this)); - if (!IS_MOBILE) { - this.containerEl.addEventListener( - Runner.events.FOCUS, this.showSpeedToggle.bind(this)); - } - this.canvas.addEventListener( - Runner.events.KEYDOWN, this.preventScrolling.bind(this)); - this.canvas.addEventListener( - Runner.events.KEYUP, this.preventScrolling.bind(this)); - - // Keys. - document.addEventListener(Runner.events.KEYDOWN, this); - document.addEventListener(Runner.events.KEYUP, this); - - // Touch / pointer. - this.containerEl.addEventListener(Runner.events.TOUCHSTART, this); - document.addEventListener(Runner.events.POINTERDOWN, this); - document.addEventListener(Runner.events.POINTERUP, this); - - if (this.isArcadeMode()) { - // Gamepad - window.addEventListener(Runner.events.GAMEPADCONNECTED, this); - } - }, - - /** - * Remove all listeners. - */ - stopListening() { - document.removeEventListener(Runner.events.KEYDOWN, this); - document.removeEventListener(Runner.events.KEYUP, this); - - if (this.touchController) { - this.touchController.removeEventListener(Runner.events.TOUCHSTART, this); - this.touchController.removeEventListener(Runner.events.TOUCHEND, this); - } - - this.containerEl.removeEventListener(Runner.events.TOUCHSTART, this); - document.removeEventListener(Runner.events.POINTERDOWN, this); - document.removeEventListener(Runner.events.POINTERUP, this); - - if (this.isArcadeMode()) { - window.removeEventListener(Runner.events.GAMEPADCONNECTED, this); - } - }, - - /** - * Process keydown. - * @param {Event} e - */ - onKeyDown(e) { - // Prevent native page scrolling whilst tapping on mobile. - if (IS_MOBILE && this.playing) { - e.preventDefault(); - } - - if (this.isCanvasInView()) { - // Allow toggling of speed toggle. - if (Runner.keycodes.JUMP[e.keyCode] && - e.target == this.slowSpeedCheckbox) { - return; - } - - if (!this.crashed && !this.paused) { - // For a11y, screen reader activation. - const isMobileMouseInput = IS_MOBILE && - e.type === Runner.events.POINTERDOWN && - e.pointerType == 'mouse' && e.target == this.containerEl || - (IS_IOS && e.pointerType == 'touch' && - document.activeElement == this.containerEl); - - if (Runner.keycodes.JUMP[e.keyCode] || - e.type === Runner.events.TOUCHSTART || isMobileMouseInput || - (Runner.keycodes.DUCK[e.keyCode] && this.altGameModeActive)) { - e.preventDefault(); - // Starting the game for the first time. - if (!this.playing) { - // Started by touch so create a touch controller. - if (!this.touchController && e.type === Runner.events.TOUCHSTART) { - this.createTouchController(); - } - - if (isMobileMouseInput) { - this.handleCanvasKeyPress(e); - } - this.loadSounds(); - this.setPlayStatus(true); - this.update(); - if (window.errorPageController) { - errorPageController.trackEasterEgg(); - } - } - // Start jump. - if (!this.tRex.jumping && !this.tRex.ducking) { - if (Runner.audioCues) { - this.generatedSoundFx.cancelFootSteps(); - } else { - this.playSound(this.soundFx.BUTTON_PRESS); - } - this.tRex.startJump(this.currentSpeed); - } - // Ducking is disabled on alt game modes. - } else if ( - !this.altGameModeActive && this.playing && - Runner.keycodes.DUCK[e.keyCode]) { - e.preventDefault(); - if (this.tRex.jumping) { - // Speed drop, activated only when jump key is not pressed. - this.tRex.setSpeedDrop(); - } else if (!this.tRex.jumping && !this.tRex.ducking) { - // Duck. - this.tRex.setDuck(true); - } - } - } - } - }, - - /** - * Process key up. - * @param {Event} e - */ - onKeyUp(e) { - const keyCode = String(e.keyCode); - const isjumpKey = Runner.keycodes.JUMP[keyCode] || - e.type === Runner.events.TOUCHEND || e.type === Runner.events.POINTERUP; - - if (this.isRunning() && isjumpKey) { - this.tRex.endJump(); - } else if (Runner.keycodes.DUCK[keyCode]) { - this.tRex.speedDrop = false; - this.tRex.setDuck(false); - } else if (this.crashed) { - // Check that enough time has elapsed before allowing jump key to restart. - const deltaTime = getTimeStamp() - this.time; - - if (this.isCanvasInView() && - (Runner.keycodes.RESTART[keyCode] || this.isLeftClickOnCanvas(e) || - (deltaTime >= this.config.GAMEOVER_CLEAR_TIME && - Runner.keycodes.JUMP[keyCode]))) { - this.handleGameOverClicks(e); - } - } else if (this.paused && isjumpKey) { - // Reset the jump state - this.tRex.reset(); - this.play(); - } - }, - - /** - * Process gamepad connected event. - * @param {Event} e - */ - onGamepadConnected(e) { - if (!this.pollingGamepads) { - this.pollGamepadState(); - } - }, - - /** - * rAF loop for gamepad polling. - */ - pollGamepadState() { - const gamepads = navigator.getGamepads(); - this.pollActiveGamepad(gamepads); - - this.pollingGamepads = true; - requestAnimationFrame(this.pollGamepadState.bind(this)); - }, - - /** - * Polls for a gamepad with the jump button pressed. If one is found this - * becomes the "active" gamepad and all others are ignored. - * @param {!Array} gamepads - */ - pollForActiveGamepad(gamepads) { - for (let i = 0; i < gamepads.length; ++i) { - if (gamepads[i] && gamepads[i].buttons.length > 0 && - gamepads[i].buttons[0].pressed) { - this.gamepadIndex = i; - this.pollActiveGamepad(gamepads); - return; - } - } - }, - - /** - * Polls the chosen gamepad for button presses and generates KeyboardEvents - * to integrate with the rest of the game logic. - * @param {!Array} gamepads - */ - pollActiveGamepad(gamepads) { - if (this.gamepadIndex === undefined) { - this.pollForActiveGamepad(gamepads); - return; - } - - const gamepad = gamepads[this.gamepadIndex]; - if (!gamepad) { - this.gamepadIndex = undefined; - this.pollForActiveGamepad(gamepads); - return; - } - - // The gamepad specification defines the typical mapping of physical buttons - // to button indicies: https://w3c.github.io/gamepad/#remapping - this.pollGamepadButton(gamepad, 0, 38); // Jump - if (gamepad.buttons.length >= 2) { - this.pollGamepadButton(gamepad, 1, 40); // Duck - } - if (gamepad.buttons.length >= 10) { - this.pollGamepadButton(gamepad, 9, 13); // Restart - } - - this.previousGamepad = gamepad; - }, - - /** - * Generates a key event based on a gamepad button. - * @param {!Gamepad} gamepad - * @param {number} buttonIndex - * @param {number} keyCode - */ - pollGamepadButton(gamepad, buttonIndex, keyCode) { - const state = gamepad.buttons[buttonIndex].pressed; - let previousState = false; - if (this.previousGamepad) { - previousState = this.previousGamepad.buttons[buttonIndex].pressed; - } - // Generate key events on the rising and falling edge of a button press. - if (state !== previousState) { - const e = new KeyboardEvent(state ? Runner.events.KEYDOWN - : Runner.events.KEYUP, - { keyCode: keyCode }); - document.dispatchEvent(e); - } - }, - - /** - * Handle interactions on the game over screen state. - * A user is able to tap the high score twice to reset it. - * @param {Event} e - */ - handleGameOverClicks(e) { - if (e.target != this.slowSpeedCheckbox) { - e.preventDefault(); - if (this.distanceMeter.hasClickedOnHighScore(e) && this.highestScore) { - if (this.distanceMeter.isHighScoreFlashing()) { - // Subsequent click, reset the high score. - this.saveHighScore(0, true); - this.distanceMeter.resetHighScore(); - this.distanceMeter.setHighScore(window.localStorage.removeItem('chrome-dino')); - } else { - // First click, flash the high score. - this.distanceMeter.startHighScoreFlashing(); - } - } else { - this.distanceMeter.cancelHighScoreFlashing(); - this.restart(); - } - } - }, - - /** - * Returns whether the event was a left click on canvas. - * On Windows right click is registered as a click. - * @param {Event} e - * @return {boolean} - */ - isLeftClickOnCanvas(e) { - return e.button != null && e.button < 2 && - e.type === Runner.events.POINTERUP && - (e.target === this.canvas || - (IS_MOBILE && Runner.audioCues && e.target === this.containerEl)); - }, - - /** - * RequestAnimationFrame wrapper. - */ - scheduleNextUpdate() { - if (!this.updatePending) { - this.updatePending = true; - this.raqId = requestAnimationFrame(this.update.bind(this)); - } - }, - - /** - * Whether the game is running. - * @return {boolean} - */ - isRunning() { - return !!this.raqId; - }, - - /** - * Set the initial high score as stored in the user's profile. - * @param {number} highScore - */ - initializeHighScore(highScore) { - this.syncHighestScore = true; - highScore = Math.ceil(highScore); - if (highScore < this.highestScore) { - if (window.errorPageController) { - errorPageController.updateEasterEggHighScore(this.highestScore); - } - return; - } - this.highestScore = highScore; - this.distanceMeter.setHighScore(this.highestScore); - }, - - /** - * Sets the current high score and saves to the profile if available. - * @param {number} distanceRan Total distance ran. - * @param {boolean=} opt_resetScore Whether to reset the score. - */ - saveHighScore(distanceRan, opt_resetScore) { - this.highestScore = Math.ceil(distanceRan); - this.distanceMeter.setHighScore(this.highestScore); - - // Store the new high score in the profile. - if (this.syncHighestScore && window.errorPageController) { - if (opt_resetScore) { - errorPageController.resetEasterEggHighScore(); - } else { - errorPageController.updateEasterEggHighScore(this.highestScore); - } - } - }, - - /** - * Game over state. - */ - gameOver() { - this.playSound(this.soundFx.HIT); - vibrate(200); - - this.stop(); - this.crashed = true; - this.distanceMeter.achievement = false; - - this.tRex.update(100, Trex.status.CRASHED); - - // Game over panel. - if (!this.gameOverPanel) { - const origSpriteDef = IS_HIDPI ? - Runner.spriteDefinitionByType.original.HDPI : - Runner.spriteDefinitionByType.original.LDPI; - - if (this.canvas) { - if (Runner.isAltGameModeEnabled) { - this.gameOverPanel = new GameOverPanel( - this.canvas, origSpriteDef.TEXT_SPRITE, origSpriteDef.RESTART, - this.dimensions, origSpriteDef.ALT_GAME_END, - this.altGameModeActive); - } else { - this.gameOverPanel = new GameOverPanel( - this.canvas, origSpriteDef.TEXT_SPRITE, origSpriteDef.RESTART, - this.dimensions); - } - } - } - - this.gameOverPanel.draw(this.altGameModeActive, this.tRex); - - // Update the high score. - if (this.distanceRan > this.highestScore) { - this.saveHighScore(this.distanceRan); - window.localStorage.setItem('chrome-dino', this.distanceRan); - } - - // Reset the time clock. - this.time = getTimeStamp(); - - if (Runner.audioCues) { - this.generatedSoundFx.stopAll(); - announcePhrase( - getA11yString(A11Y_STRINGS.gameOver) - .replace( - '$1', - this.distanceMeter.getActualDistance(this.distanceRan) - .toString()) + - ' ' + - getA11yString(A11Y_STRINGS.highScore) - .replace( - '$1', - - this.distanceMeter.getActualDistance(this.highestScore) - .toString())); - this.containerEl.setAttribute( - 'title', getA11yString(A11Y_STRINGS.ariaLabel)); - } - this.showSpeedToggle(); - this.disableSpeedToggle(false); - }, - - stop() { - this.setPlayStatus(false); - this.paused = true; - cancelAnimationFrame(this.raqId); - this.raqId = 0; - this.generatedSoundFx.stopAll(); - }, - - play() { - if (!this.crashed) { - this.setPlayStatus(true); - this.paused = false; - this.tRex.update(0, Trex.status.RUNNING); - this.time = getTimeStamp(); - this.update(); - this.generatedSoundFx.background(); - } - }, - - restart() { - if (!this.raqId) { - this.playCount++; - this.runningTime = 0; - this.setPlayStatus(true); - this.toggleSpeed(); - this.paused = false; - this.crashed = false; - this.distanceRan = 0; - this.setSpeed(this.config.SPEED); - this.time = getTimeStamp(); - this.containerEl.classList.remove(Runner.classes.CRASHED); - this.clearCanvas(); - this.distanceMeter.reset(); - this.horizon.reset(); - this.tRex.reset(); - this.playSound(this.soundFx.BUTTON_PRESS); - this.invert(true); - this.flashTimer = null; - this.update(); - this.gameOverPanel.reset(); - this.generatedSoundFx.background(); - this.containerEl.setAttribute('title', getA11yString(A11Y_STRINGS.jump)); - announcePhrase(getA11yString(A11Y_STRINGS.started)); - } - }, - - setPlayStatus(isPlaying) { - if (this.touchController) { - this.touchController.classList.toggle(HIDDEN_CLASS, !isPlaying); - } - this.playing = isPlaying; - }, - - /** - * Whether the game should go into arcade mode. - * @return {boolean} - */ - isArcadeMode() { - // In RTL languages the title is wrapped with the left to right mark - // control characters ‪ and ‬ but are invisible. - /* return IS_RTL ? document.title.indexOf(ARCADE_MODE_URL) == 1 : - document.title === ARCADE_MODE_URL; */ - return true; - }, - - /** - * Hides offline messaging for a fullscreen game only experience. - */ - setArcadeMode() { - document.body.classList.add(Runner.classes.ARCADE_MODE); - this.setArcadeModeContainerScale(); - }, - - /** - * Sets the scaling for arcade mode. - */ - setArcadeModeContainerScale() { - const windowHeight = window.innerHeight; - const scaleHeight = windowHeight / this.dimensions.HEIGHT; - const scaleWidth = window.innerWidth / this.dimensions.WIDTH; - const scale = Math.max(1, Math.min(scaleHeight, scaleWidth)); - const scaledCanvasHeight = this.dimensions.HEIGHT * scale; - // Positions the game container at 10% of the available vertical window - // height minus the game container height. - const translateY = Math.ceil(Math.max(0, (windowHeight - scaledCanvasHeight - - Runner.config.ARCADE_MODE_INITIAL_TOP_POSITION) * - Runner.config.ARCADE_MODE_TOP_POSITION_PERCENT)) * - window.devicePixelRatio; - - const cssScale = IS_RTL ? -scale + ',' + scale : scale; - this.containerEl.style.transform = - 'scale(' + cssScale + ') translateY(' + translateY + 'px)'; - }, - - /** - * Pause the game if the tab is not in focus. - */ - onVisibilityChange(e) { - if (document.hidden || document.webkitHidden || e.type === 'blur' || - document.visibilityState !== 'visible') { - this.stop(); - } else if (!this.crashed) { - this.tRex.reset(); - this.play(); - } - }, - - /** - * Play a sound. - * @param {AudioBuffer} soundBuffer - */ - playSound(soundBuffer) { - if (soundBuffer) { - const sourceNode = this.audioContext.createBufferSource(); - sourceNode.buffer = soundBuffer; - sourceNode.connect(this.audioContext.destination); - sourceNode.start(0); - } - }, - - /** - * Inverts the current page / canvas colors. - * @param {boolean} reset Whether to reset colors. - */ - invert(reset) { - const htmlEl = document.firstElementChild; - - if (reset) { - htmlEl.classList.toggle(Runner.classes.INVERTED, - false); - this.invertTimer = 0; - this.inverted = false; - } else { - this.inverted = htmlEl.classList.toggle( - Runner.classes.INVERTED, this.invertTrigger); - } - }, -}; - - -/** - * Updates the canvas size taking into - * account the backing store pixel ratio and - * the device pixel ratio. - * - * See article by Paul Lewis: - * http://www.html5rocks.com/en/tutorials/canvas/hidpi/ - * - * @param {HTMLCanvasElement} canvas - * @param {number=} opt_width - * @param {number=} opt_height - * @return {boolean} Whether the canvas was scaled. - */ -Runner.updateCanvasScaling = function(canvas, opt_width, opt_height) { - const context = - /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - - // Query the various pixel ratios - const devicePixelRatio = Math.floor(window.devicePixelRatio) || 1; - /** @suppress {missingProperties} */ - const backingStoreRatio = - Math.floor(context.webkitBackingStorePixelRatio) || 1; - const ratio = devicePixelRatio / backingStoreRatio; - - // Upscale the canvas if the two ratios don't match - if (devicePixelRatio !== backingStoreRatio) { - const oldWidth = opt_width || canvas.width; - const oldHeight = opt_height || canvas.height; - - canvas.width = oldWidth * ratio; - canvas.height = oldHeight * ratio; - - canvas.style.width = oldWidth + 'px'; - canvas.style.height = oldHeight + 'px'; - - // Scale the context to counter the fact that we've manually scaled - // our canvas element. - context.scale(ratio, ratio); - return true; - } else if (devicePixelRatio === 1) { - // Reset the canvas width / height. Fixes scaling bug when the page is - // zoomed and the devicePixelRatio changes accordingly. - canvas.style.width = canvas.width + 'px'; - canvas.style.height = canvas.height + 'px'; - } - return false; -}; - - -/** - * Whether events are enabled. - * @return {boolean} - */ -Runner.isAltGameModeEnabled = function() { - return loadTimeData && loadTimeData.valueExists('enableAltGameMode'); -}; - - -/** - * Generated sound FX class for audio cues. - * @constructor - */ -function GeneratedSoundFx() { - this.audioCues = false; - this.context = null; - this.panner = null; -} - -GeneratedSoundFx.prototype = { - init() { - this.audioCues = true; - if (!this.context) { - // iOS only supports the webkit version. - this.context = window.webkitAudioContext ? new webkitAudioContext() : - new AudioContext(); - if (IS_IOS) { - this.context.onstatechange = (function() { - if (this.context.state != 'running') { - this.context.resume(); - } - }).bind(this); - this.context.resume(); - } - this.panner = this.context.createStereoPanner ? - this.context.createStereoPanner() : - null; - } - }, - - stopAll() { - this.cancelFootSteps(); - }, - - /** - * Play oscillators at certain frequency and for a certain time. - * @param {number} frequency - * @param {number} startTime - * @param {number} duration - * @param {?number=} opt_vol - * @param {number=} opt_pan - */ - playNote(frequency, startTime, duration, opt_vol, opt_pan) { - const osc1 = this.context.createOscillator(); - const osc2 = this.context.createOscillator(); - const volume = this.context.createGain(); - - // Set oscillator wave type - osc1.type = 'triangle'; - osc2.type = 'triangle'; - volume.gain.value = 0.1; - - // Set up node routing - if (this.panner) { - this.panner.pan.value = opt_pan || 0; - osc1.connect(volume).connect(this.panner); - osc2.connect(volume).connect(this.panner); - this.panner.connect(this.context.destination); - } else { - osc1.connect(volume); - osc2.connect(volume); - volume.connect(this.context.destination); - } - - // Detune oscillators for chorus effect - osc1.frequency.value = frequency + 1; - osc2.frequency.value = frequency - 2; - - // Fade out - volume.gain.setValueAtTime(opt_vol || 0.01, startTime + duration - 0.05); - volume.gain.linearRampToValueAtTime(0.00001, startTime + duration); - - // Start oscillators - osc1.start(startTime); - osc2.start(startTime); - // Stop oscillators - osc1.stop(startTime + duration); - osc2.stop(startTime + duration); - }, - - background() { - if (this.audioCues) { - const now = this.context.currentTime; - this.playNote(493.883, now, 0.116); - this.playNote(659.255, now + 0.116, 0.232); - this.loopFootSteps(); - } - }, - - loopFootSteps() { - if (this.audioCues && !this.bgSoundIntervalId) { - this.bgSoundIntervalId = setInterval(function() { - this.playNote(73.42, this.context.currentTime, 0.05, 0.16); - this.playNote(69.30, this.context.currentTime + 0.116, 0.116, 0.16); - }.bind(this), 280); - } - }, - - cancelFootSteps() { - if (this.audioCues && this.bgSoundIntervalId) { - clearInterval(this.bgSoundIntervalId); - this.bgSoundIntervalId = null; - this.playNote(103.83, this.context.currentTime, 0.232, 0.02); - this.playNote(116.54, this.context.currentTime + 0.116, 0.232, 0.02); - } - }, - - collect() { - if (this.audioCues) { - this.cancelFootSteps(); - const now = this.context.currentTime; - this.playNote(830.61, now, 0.116); - this.playNote(1318.51, now + 0.116, 0.232); - } - }, - - jump() { - if (this.audioCues) { - const now = this.context.currentTime; - this.playNote(659.25, now, 0.116, 0.3, -0.6); - this.playNote(880, now + 0.116, 0.232, 0.3, -0.6); - } - }, -}; - - -/** - * Speak a phrase using Speech Synthesis API for a11y. - * @param {string} phrase Sentence to speak. - */ -function speakPhrase(phrase) { - if ('speechSynthesis' in window) { - const msg = new SpeechSynthesisUtterance(phrase); - const voices = window.speechSynthesis.getVoices(); - msg.text = phrase; - speechSynthesis.speak(msg); - } -} - - -/** - * For screen readers make an announcement to the live region. - * @param {string} phrase Sentence to speak. - */ -function announcePhrase(phrase) { - if (Runner.a11yStatusEl) { - Runner.a11yStatusEl.textContent = ''; - Runner.a11yStatusEl.textContent = phrase; - } -} - - -/** - * Returns a string from loadTimeData data object. - * @param {string} stringName - * @return {string} - */ -function getA11yString(stringName) { - return loadTimeData && loadTimeData.valueExists(stringName) ? - loadTimeData.getString(stringName) : - ''; -} - - -/** - * Get random number. - * @param {number} min - * @param {number} max - */ -function getRandomNum(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - - -/** - * Vibrate on mobile devices. - * @param {number} duration Duration of the vibration in milliseconds. - */ -function vibrate(duration) { - if (IS_MOBILE && window.navigator.vibrate) { - window.navigator.vibrate(duration); - } -} - - -/** - * Create canvas element. - * @param {Element} container Element to append canvas to. - * @param {number} width - * @param {number} height - * @param {string=} opt_classname - * @return {HTMLCanvasElement} - */ -function createCanvas(container, width, height, opt_classname) { - const canvas = - /** @type {!HTMLCanvasElement} */ (document.createElement('canvas')); - canvas.className = opt_classname ? Runner.classes.CANVAS + ' ' + - opt_classname : Runner.classes.CANVAS; - canvas.width = width; - canvas.height = height; - container.appendChild(canvas); - - return canvas; -} - - -/** - * Decodes the base 64 audio to ArrayBuffer used by Web Audio. - * @param {string} base64String - */ -function decodeBase64ToArrayBuffer(base64String) { - const len = (base64String.length / 4) * 3; - const str = atob(base64String); - const arrayBuffer = new ArrayBuffer(len); - const bytes = new Uint8Array(arrayBuffer); - - for (let i = 0; i < len; i++) { - bytes[i] = str.charCodeAt(i); - } - return bytes.buffer; -} - - -/** - * Return the current timestamp. - * @return {number} - */ -function getTimeStamp() { - return IS_IOS ? new Date().getTime() : performance.now(); -} - - -//****************************************************************************** - - -/** - * Game over panel. - * @param {!HTMLCanvasElement} canvas - * @param {Object} textImgPos - * @param {Object} restartImgPos - * @param {!Object} dimensions Canvas dimensions. - * @param {Object=} opt_altGameEndImgPos - * @param {boolean=} opt_altGameActive - * @constructor - */ -function GameOverPanel( - canvas, textImgPos, restartImgPos, dimensions, opt_altGameEndImgPos, - opt_altGameActive) { - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - this.canvasDimensions = dimensions; - this.textImgPos = textImgPos; - this.restartImgPos = restartImgPos; - this.altGameEndImgPos = opt_altGameEndImgPos; - this.altGameModeActive = opt_altGameActive; - - // Retry animation. - this.frameTimeStamp = 0; - this.animTimer = 0; - this.currentFrame = 0; - - this.gameOverRafId = null; - - this.flashTimer = 0; - this.flashCounter = 0; - this.originalText = true; -} - -GameOverPanel.RESTART_ANIM_DURATION = 875; -GameOverPanel.LOGO_PAUSE_DURATION = 875; -GameOverPanel.FLASH_ITERATIONS = 5; - -/** - * Animation frames spec. - */ -GameOverPanel.animConfig = { - frames: [0, 36, 72, 108, 144, 180, 216, 252], - msPerFrame: GameOverPanel.RESTART_ANIM_DURATION / 8, -}; - -/** - * Dimensions used in the panel. - * @enum {number} - */ -GameOverPanel.dimensions = { - TEXT_X: 0, - TEXT_Y: 13, - TEXT_WIDTH: 191, - TEXT_HEIGHT: 11, - RESTART_WIDTH: 36, - RESTART_HEIGHT: 32, -}; - - -GameOverPanel.prototype = { - /** - * Update the panel dimensions. - * @param {number} width New canvas width. - * @param {number} opt_height Optional new canvas height. - */ - updateDimensions(width, opt_height) { - this.canvasDimensions.WIDTH = width; - if (opt_height) { - this.canvasDimensions.HEIGHT = opt_height; - } - this.currentFrame = GameOverPanel.animConfig.frames.length - 1; - }, - - drawGameOverText(dimensions, opt_useAltText) { - const centerX = this.canvasDimensions.WIDTH / 2; - let textSourceX = dimensions.TEXT_X; - let textSourceY = dimensions.TEXT_Y; - let textSourceWidth = dimensions.TEXT_WIDTH; - let textSourceHeight = dimensions.TEXT_HEIGHT; - - const textTargetX = Math.round(centerX - (dimensions.TEXT_WIDTH / 2)); - const textTargetY = Math.round((this.canvasDimensions.HEIGHT - 25) / 3); - const textTargetWidth = dimensions.TEXT_WIDTH; - const textTargetHeight = dimensions.TEXT_HEIGHT; - - if (IS_HIDPI) { - textSourceY *= 2; - textSourceX *= 2; - textSourceWidth *= 2; - textSourceHeight *= 2; - } - - if (!opt_useAltText) { - textSourceX += this.textImgPos.x; - textSourceY += this.textImgPos.y; - } - - const spriteSource = - opt_useAltText ? Runner.altCommonImageSprite : Runner.origImageSprite; - - this.canvasCtx.save(); - - if (IS_RTL) { - this.canvasCtx.translate(this.canvasDimensions.WIDTH, 0); - this.canvasCtx.scale(-1, 1); - } - - // Game over text from sprite. - this.canvasCtx.drawImage( - spriteSource, textSourceX, textSourceY, textSourceWidth, - textSourceHeight, textTargetX, textTargetY, textTargetWidth, - textTargetHeight); - - this.canvasCtx.restore(); - }, - - /** - * Draw additional adornments for alternative game types. - */ - drawAltGameElements(tRex) { - // Additional adornments. - if (this.altGameModeActive && Runner.spriteDefinition.ALT_GAME_END_CONFIG) { - const altGameEndConfig = Runner.spriteDefinition.ALT_GAME_END_CONFIG; - - let altGameEndSourceWidth = altGameEndConfig.WIDTH; - let altGameEndSourceHeight = altGameEndConfig.HEIGHT; - const altGameEndTargetX = tRex.xPos + altGameEndConfig.X_OFFSET; - const altGameEndTargetY = tRex.yPos + altGameEndConfig.Y_OFFSET; - - if (IS_HIDPI) { - altGameEndSourceWidth *= 2; - altGameEndSourceHeight *= 2; - } - - this.canvasCtx.drawImage( - Runner.altCommonImageSprite, this.altGameEndImgPos.x, - this.altGameEndImgPos.y, altGameEndSourceWidth, - altGameEndSourceHeight, altGameEndTargetX, altGameEndTargetY, - altGameEndConfig.WIDTH, altGameEndConfig.HEIGHT); - } - }, - - /** - * Draw restart button. - */ - drawRestartButton() { - const dimensions = GameOverPanel.dimensions; - let framePosX = GameOverPanel.animConfig.frames[this.currentFrame]; - let restartSourceWidth = dimensions.RESTART_WIDTH; - let restartSourceHeight = dimensions.RESTART_HEIGHT; - const restartTargetX = - (this.canvasDimensions.WIDTH / 2) - (dimensions.RESTART_WIDTH / 2); - const restartTargetY = this.canvasDimensions.HEIGHT / 2; - - if (IS_HIDPI) { - restartSourceWidth *= 2; - restartSourceHeight *= 2; - framePosX *= 2; - } - - this.canvasCtx.save(); - - if (IS_RTL) { - this.canvasCtx.translate(this.canvasDimensions.WIDTH, 0); - this.canvasCtx.scale(-1, 1); - } - - this.canvasCtx.drawImage( - Runner.origImageSprite, this.restartImgPos.x + framePosX, - this.restartImgPos.y, restartSourceWidth, restartSourceHeight, - restartTargetX, restartTargetY, dimensions.RESTART_WIDTH, - dimensions.RESTART_HEIGHT); - this.canvasCtx.restore(); - }, - - - /** - * Draw the panel. - * @param {boolean} opt_altGameModeActive - * @param {!Trex} opt_tRex - */ - draw(opt_altGameModeActive, opt_tRex) { - if (opt_altGameModeActive) { - this.altGameModeActive = opt_altGameModeActive; - } - - this.drawGameOverText(GameOverPanel.dimensions, false); - this.drawRestartButton(); - this.drawAltGameElements(opt_tRex); - this.update(); - }, - - /** - * Update animation frames. - */ - update() { - const now = getTimeStamp(); - const deltaTime = now - (this.frameTimeStamp || now); - - this.frameTimeStamp = now; - this.animTimer += deltaTime; - this.flashTimer += deltaTime; - - // Restart Button - if (this.currentFrame == 0 && - this.animTimer > GameOverPanel.LOGO_PAUSE_DURATION) { - this.animTimer = 0; - this.currentFrame++; - this.drawRestartButton(); - } else if ( - this.currentFrame > 0 && - this.currentFrame < GameOverPanel.animConfig.frames.length) { - if (this.animTimer >= GameOverPanel.animConfig.msPerFrame) { - this.currentFrame++; - this.drawRestartButton(); - } - } else if ( - !this.altGameModeActive && - this.currentFrame == GameOverPanel.animConfig.frames.length) { - this.reset(); - return; - } - - // Game over text - if (this.altGameModeActive && - Runner.spriteDefinitionByType.original.ALT_GAME_OVER_TEXT_CONFIG) { - const altTextConfig = - Runner.spriteDefinitionByType.original.ALT_GAME_OVER_TEXT_CONFIG; - - if (this.flashCounter < GameOverPanel.FLASH_ITERATIONS && - this.flashTimer > altTextConfig.FLASH_DURATION) { - this.flashTimer = 0; - this.originalText = !this.originalText; - - this.clearGameOverTextBounds(); - if (this.originalText) { - this.drawGameOverText(GameOverPanel.dimensions, false); - this.flashCounter++; - } else { - this.drawGameOverText(altTextConfig, true); - } - } else if (this.flashCounter >= GameOverPanel.FLASH_ITERATIONS) { - this.reset(); - return; - } - } - - this.gameOverRafId = requestAnimationFrame(this.update.bind(this)); - }, - - /** - * Clear game over text. - */ - clearGameOverTextBounds() { - this.canvasCtx.save(); - - this.canvasCtx.clearRect( - Math.round( - this.canvasDimensions.WIDTH / 2 - - (GameOverPanel.dimensions.TEXT_WIDTH / 2)), - Math.round((this.canvasDimensions.HEIGHT - 25) / 3), - GameOverPanel.dimensions.TEXT_WIDTH, - GameOverPanel.dimensions.TEXT_HEIGHT + 4); - this.canvasCtx.restore(); - }, - - reset() { - if (this.gameOverRafId) { - cancelAnimationFrame(this.gameOverRafId); - this.gameOverRafId = null; - } - this.animTimer = 0; - this.frameTimeStamp = 0; - this.currentFrame = 0; - this.flashTimer = 0; - this.flashCounter = 0; - this.originalText = true; - }, -}; - - -//****************************************************************************** - -/** - * Check for a collision. - * @param {!Obstacle} obstacle - * @param {!Trex} tRex T-rex object. - * @param {CanvasRenderingContext2D=} opt_canvasCtx Optional canvas context for - * drawing collision boxes. - * @return {Array|undefined} - */ -function checkForCollision(obstacle, tRex, opt_canvasCtx) { - const obstacleBoxXPos = Runner.defaultDimensions.WIDTH + obstacle.xPos; - - // Adjustments are made to the bounding box as there is a 1 pixel white - // border around the t-rex and obstacles. - const tRexBox = new CollisionBox( - tRex.xPos + 1, - tRex.yPos + 1, - tRex.config.WIDTH - 2, - tRex.config.HEIGHT - 2); - - const obstacleBox = new CollisionBox( - obstacle.xPos + 1, - obstacle.yPos + 1, - obstacle.typeConfig.width * obstacle.size - 2, - obstacle.typeConfig.height - 2); - - // Debug outer box - if (opt_canvasCtx) { - drawCollisionBoxes(opt_canvasCtx, tRexBox, obstacleBox); - } - - // Simple outer bounds check. - if (boxCompare(tRexBox, obstacleBox)) { - const collisionBoxes = obstacle.collisionBoxes; - let tRexCollisionBoxes = []; - - if (Runner.isAltGameModeEnabled()) { - tRexCollisionBoxes = Runner.spriteDefinition.TREX.COLLISION_BOXES; - } else { - tRexCollisionBoxes = tRex.ducking ? Trex.collisionBoxes.DUCKING : - Trex.collisionBoxes.RUNNING; - } - - // Detailed axis aligned box check. - for (let t = 0; t < tRexCollisionBoxes.length; t++) { - for (let i = 0; i < collisionBoxes.length; i++) { - // Adjust the box to actual positions. - const adjTrexBox = - createAdjustedCollisionBox(tRexCollisionBoxes[t], tRexBox); - const adjObstacleBox = - createAdjustedCollisionBox(collisionBoxes[i], obstacleBox); - const crashed = boxCompare(adjTrexBox, adjObstacleBox); - - // Draw boxes for debug. - if (opt_canvasCtx) { - drawCollisionBoxes(opt_canvasCtx, adjTrexBox, adjObstacleBox); - } - - if (crashed) { - return [adjTrexBox, adjObstacleBox]; - } - } - } - } -} - - -/** - * Adjust the collision box. - * @param {!CollisionBox} box The original box. - * @param {!CollisionBox} adjustment Adjustment box. - * @return {CollisionBox} The adjusted collision box object. - */ -function createAdjustedCollisionBox(box, adjustment) { - return new CollisionBox( - box.x + adjustment.x, - box.y + adjustment.y, - box.width, - box.height); -} - - -/** - * Draw the collision boxes for debug. - */ -function drawCollisionBoxes(canvasCtx, tRexBox, obstacleBox) { - canvasCtx.save(); - canvasCtx.strokeStyle = '#f00'; - canvasCtx.strokeRect(tRexBox.x, tRexBox.y, tRexBox.width, tRexBox.height); - - canvasCtx.strokeStyle = '#0f0'; - canvasCtx.strokeRect(obstacleBox.x, obstacleBox.y, - obstacleBox.width, obstacleBox.height); - canvasCtx.restore(); -} - - -/** - * Compare two collision boxes for a collision. - * @param {CollisionBox} tRexBox - * @param {CollisionBox} obstacleBox - * @return {boolean} Whether the boxes intersected. - */ -function boxCompare(tRexBox, obstacleBox) { - let crashed = false; - const tRexBoxX = tRexBox.x; - const tRexBoxY = tRexBox.y; - - const obstacleBoxX = obstacleBox.x; - const obstacleBoxY = obstacleBox.y; - - // Axis-Aligned Bounding Box method. - if (tRexBox.x < obstacleBoxX + obstacleBox.width && - tRexBox.x + tRexBox.width > obstacleBoxX && - tRexBox.y < obstacleBox.y + obstacleBox.height && - tRexBox.height + tRexBox.y > obstacleBox.y) { - crashed = true; - } - - return crashed; -} - - -//****************************************************************************** - -/** - * Collision box object. - * @param {number} x X position. - * @param {number} y Y Position. - * @param {number} w Width. - * @param {number} h Height. - * @constructor - */ -function CollisionBox(x, y, w, h) { - this.x = x; - this.y = y; - this.width = w; - this.height = h; -} - - -//****************************************************************************** - -/** - * Obstacle. - * @param {CanvasRenderingContext2D} canvasCtx - * @param {ObstacleType} type - * @param {Object} spriteImgPos Obstacle position in sprite. - * @param {Object} dimensions - * @param {number} gapCoefficient Mutipler in determining the gap. - * @param {number} speed - * @param {number=} opt_xOffset - * @param {boolean=} opt_isAltGameMode - * @constructor - */ -function Obstacle( - canvasCtx, type, spriteImgPos, dimensions, gapCoefficient, speed, - opt_xOffset, opt_isAltGameMode) { - this.canvasCtx = canvasCtx; - this.spritePos = spriteImgPos; - this.typeConfig = type; - this.gapCoefficient = Runner.slowDown ? gapCoefficient * 2 : gapCoefficient; - this.size = getRandomNum(1, Obstacle.MAX_OBSTACLE_LENGTH); - this.dimensions = dimensions; - this.remove = false; - this.xPos = dimensions.WIDTH + (opt_xOffset || 0); - this.yPos = 0; - this.width = 0; - this.collisionBoxes = []; - this.gap = 0; - this.speedOffset = 0; - this.altGameModeActive = opt_isAltGameMode; - this.imageSprite = this.typeConfig.type == 'COLLECTABLE' ? - Runner.altCommonImageSprite : - this.altGameModeActive ? Runner.altGameImageSprite : Runner.imageSprite; - - // For animated obstacles. - this.currentFrame = 0; - this.timer = 0; - - this.init(speed); -} - -/** - * Coefficient for calculating the maximum gap. - */ -Obstacle.MAX_GAP_COEFFICIENT = 1.5; - -/** - * Maximum obstacle grouping count. - */ -Obstacle.MAX_OBSTACLE_LENGTH = 3; - - -Obstacle.prototype = { - /** - * Initialise the DOM for the obstacle. - * @param {number} speed - */ - init(speed) { - this.cloneCollisionBoxes(); - - // Only allow sizing if we're at the right speed. - if (this.size > 1 && this.typeConfig.multipleSpeed > speed) { - this.size = 1; - } - - this.width = this.typeConfig.width * this.size; - - // Check if obstacle can be positioned at various heights. - if (Array.isArray(this.typeConfig.yPos)) { - const yPosConfig = - IS_MOBILE ? this.typeConfig.yPosMobile : this.typeConfig.yPos; - this.yPos = yPosConfig[getRandomNum(0, yPosConfig.length - 1)]; - } else { - this.yPos = this.typeConfig.yPos; - } - - this.draw(); - - // Make collision box adjustments, - // Central box is adjusted to the size as one box. - // ____ ______ ________ - // _| |-| _| |-| _| |-| - // | |<->| | | |<--->| | | |<----->| | - // | | 1 | | | | 2 | | | | 3 | | - // |_|___|_| |_|_____|_| |_|_______|_| - // - if (this.size > 1) { - this.collisionBoxes[1].width = this.width - this.collisionBoxes[0].width - - this.collisionBoxes[2].width; - this.collisionBoxes[2].x = this.width - this.collisionBoxes[2].width; - } - - // For obstacles that go at a different speed from the horizon. - if (this.typeConfig.speedOffset) { - this.speedOffset = Math.random() > 0.5 ? this.typeConfig.speedOffset : - -this.typeConfig.speedOffset; - } - - this.gap = this.getGap(this.gapCoefficient, speed); - - // Increase gap for audio cues enabled. - if (Runner.audioCues) { - this.gap *= 2; - } - }, - - /** - * Draw and crop based on size. - */ - draw() { - let sourceWidth = this.typeConfig.width; - let sourceHeight = this.typeConfig.height; - - if (IS_HIDPI) { - sourceWidth = sourceWidth * 2; - sourceHeight = sourceHeight * 2; - } - - // X position in sprite. - let sourceX = - (sourceWidth * this.size) * (0.5 * (this.size - 1)) + this.spritePos.x; - - // Animation frames. - if (this.currentFrame > 0) { - sourceX += sourceWidth * this.currentFrame; - } - - this.canvasCtx.drawImage( - this.imageSprite, sourceX, this.spritePos.y, sourceWidth * this.size, - sourceHeight, this.xPos, this.yPos, this.typeConfig.width * this.size, - this.typeConfig.height); - }, - - /** - * Obstacle frame update. - * @param {number} deltaTime - * @param {number} speed - */ - update(deltaTime, speed) { - if (!this.remove) { - if (this.typeConfig.speedOffset) { - speed += this.speedOffset; - } - this.xPos -= Math.floor((speed * FPS / 1000) * deltaTime); - - // Update frame - if (this.typeConfig.numFrames) { - this.timer += deltaTime; - if (this.timer >= this.typeConfig.frameRate) { - this.currentFrame = - this.currentFrame === this.typeConfig.numFrames - 1 ? - 0 : - this.currentFrame + 1; - this.timer = 0; - } - } - this.draw(); - - if (!this.isVisible()) { - this.remove = true; - } - } - }, - - /** - * Calculate a random gap size. - * - Minimum gap gets wider as speed increses - * @param {number} gapCoefficient - * @param {number} speed - * @return {number} The gap size. - */ - getGap(gapCoefficient, speed) { - const minGap = Math.round( - this.width * speed + this.typeConfig.minGap * gapCoefficient); - const maxGap = Math.round(minGap * Obstacle.MAX_GAP_COEFFICIENT); - return getRandomNum(minGap, maxGap); - }, - - /** - * Check if obstacle is visible. - * @return {boolean} Whether the obstacle is in the game area. - */ - isVisible() { - return this.xPos + this.width > 0; - }, - - /** - * Make a copy of the collision boxes, since these will change based on - * obstacle type and size. - */ - cloneCollisionBoxes() { - const collisionBoxes = this.typeConfig.collisionBoxes; - - for (let i = collisionBoxes.length - 1; i >= 0; i--) { - this.collisionBoxes[i] = new CollisionBox( - collisionBoxes[i].x, collisionBoxes[i].y, collisionBoxes[i].width, - collisionBoxes[i].height); - } - }, -}; - - -//****************************************************************************** -/** - * T-rex game character. - * @param {HTMLCanvasElement} canvas - * @param {Object} spritePos Positioning within image sprite. - * @constructor - */ -function Trex(canvas, spritePos) { - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - this.spritePos = spritePos; - this.xPos = 0; - this.yPos = 0; - this.xInitialPos = 0; - // Position when on the ground. - this.groundYPos = 0; - this.currentFrame = 0; - this.currentAnimFrames = []; - this.blinkDelay = 0; - this.blinkCount = 0; - this.animStartTime = 0; - this.timer = 0; - this.msPerFrame = 1000 / FPS; - this.config = Object.assign(Trex.config, Trex.normalJumpConfig); - // Current status. - this.status = Trex.status.WAITING; - this.jumping = false; - this.ducking = false; - this.jumpVelocity = 0; - this.reachedMinHeight = false; - this.speedDrop = false; - this.jumpCount = 0; - this.jumpspotX = 0; - this.altGameModeEnabled = false; - this.flashing = false; - - this.init(); -} - - -/** - * T-rex player config. - */ -Trex.config = { - DROP_VELOCITY: -5, - FLASH_OFF: 175, - FLASH_ON: 100, - HEIGHT: 47, - HEIGHT_DUCK: 25, - INTRO_DURATION: 1500, - SPEED_DROP_COEFFICIENT: 3, - SPRITE_WIDTH: 262, - START_X_POS: 50, - WIDTH: 44, - WIDTH_DUCK: 59, -}; - -Trex.slowJumpConfig = { - GRAVITY: 0.25, - MAX_JUMP_HEIGHT: 50, - MIN_JUMP_HEIGHT: 45, - INITIAL_JUMP_VELOCITY: -20, -}; - -Trex.normalJumpConfig = { - GRAVITY: 0.6, - MAX_JUMP_HEIGHT: 30, - MIN_JUMP_HEIGHT: 30, - INITIAL_JUMP_VELOCITY: -10, -}; - -/** - * Used in collision detection. - * @enum {Array} - */ -Trex.collisionBoxes = { - DUCKING: [new CollisionBox(1, 18, 55, 25)], - RUNNING: [ - new CollisionBox(22, 0, 17, 16), - new CollisionBox(1, 18, 30, 9), - new CollisionBox(10, 35, 14, 8), - new CollisionBox(1, 24, 29, 5), - new CollisionBox(5, 30, 21, 4), - new CollisionBox(9, 34, 15, 4), - ], -}; - - -/** - * Animation states. - * @enum {string} - */ -Trex.status = { - CRASHED: 'CRASHED', - DUCKING: 'DUCKING', - JUMPING: 'JUMPING', - RUNNING: 'RUNNING', - WAITING: 'WAITING', -}; - -/** - * Blinking coefficient. - * @const - */ -Trex.BLINK_TIMING = 7000; - - -/** - * Animation config for different states. - * @enum {Object} - */ -Trex.animFrames = { - WAITING: { - frames: [44, 0], - msPerFrame: 1000 / 3, - }, - RUNNING: { - frames: [88, 132], - msPerFrame: 1000 / 12, - }, - CRASHED: { - frames: [220], - msPerFrame: 1000 / 60, - }, - JUMPING: { - frames: [0], - msPerFrame: 1000 / 60, - }, - DUCKING: { - frames: [264, 323], - msPerFrame: 1000 / 8, - }, -}; - - -Trex.prototype = { - /** - * T-rex player initaliser. - * Sets the t-rex to blink at random intervals. - */ - init() { - this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT - - Runner.config.BOTTOM_PAD; - this.yPos = this.groundYPos; - this.minJumpHeight = this.groundYPos - this.config.MIN_JUMP_HEIGHT; - - this.draw(0, 0); - this.update(0, Trex.status.WAITING); - }, - - /** - * Assign the appropriate jump parameters based on the game speed. - */ - enableSlowConfig: function() { - const jumpConfig = - Runner.slowDown ? Trex.slowJumpConfig : Trex.normalJumpConfig; - Trex.config = Object.assign(Trex.config, jumpConfig); - - this.adjustAltGameConfigForSlowSpeed(); - }, - - /** - * Enables the alternative game. Redefines the dino config. - * @param {Object} spritePos New positioning within image sprite. - */ - enableAltGameMode: function(spritePos) { - this.altGameModeEnabled = true; - this.spritePos = spritePos; - const spriteDefinition = Runner.spriteDefinition['TREX']; - - // Update animation frames. - Trex.animFrames.RUNNING.frames = - [spriteDefinition.RUNNING_1.x, spriteDefinition.RUNNING_2.x]; - Trex.animFrames.CRASHED.frames = [spriteDefinition.CRASHED.x]; - - if (typeof spriteDefinition.JUMPING.x == 'object') { - Trex.animFrames.JUMPING.frames = spriteDefinition.JUMPING.x; - } else { - Trex.animFrames.JUMPING.frames = [spriteDefinition.JUMPING.x]; - } - - Trex.animFrames.DUCKING.frames = - [spriteDefinition.RUNNING_1.x, spriteDefinition.RUNNING_2.x]; - - // Update Trex config - Trex.config.GRAVITY = spriteDefinition.GRAVITY || Trex.config.GRAVITY; - Trex.config.HEIGHT = spriteDefinition.RUNNING_1.h, - Trex.config.INITIAL_JUMP_VELOCITY = spriteDefinition.INITIAL_JUMP_VELOCITY; - Trex.config.MAX_JUMP_HEIGHT = spriteDefinition.MAX_JUMP_HEIGHT; - Trex.config.MIN_JUMP_HEIGHT = spriteDefinition.MIN_JUMP_HEIGHT; - Trex.config.WIDTH = spriteDefinition.RUNNING_1.w; - Trex.config.WIDTH_JUMP = spriteDefinition.JUMPING.w; - Trex.config.INVERT_JUMP = spriteDefinition.INVERT_JUMP; - - this.adjustAltGameConfigForSlowSpeed(spriteDefinition.GRAVITY); - this.config = Trex.config; - - // Adjust bottom horizon placement. - this.groundYPos = Runner.defaultDimensions.HEIGHT - this.config.HEIGHT - - Runner.spriteDefinition['BOTTOM_PAD']; - this.yPos = this.groundYPos; - this.reset(); - }, - - /** - * Slow speeds adjustments for the alt game modes. - * @param {number=} opt_gravityValue - */ - adjustAltGameConfigForSlowSpeed: function(opt_gravityValue) { - if (Runner.slowDown) { - if (opt_gravityValue) { - Trex.config.GRAVITY = opt_gravityValue / 1.5; - } - Trex.config.MIN_JUMP_HEIGHT *= 1.5; - Trex.config.MAX_JUMP_HEIGHT *= 1.5; - Trex.config.INITIAL_JUMP_VELOCITY = - Trex.config.INITIAL_JUMP_VELOCITY * 1.5; - } - }, - - /** - * Setter whether dino is flashing. - * @param {boolean} status - */ - setFlashing: function(status) { - this.flashing = status; - }, - - /** - * Setter for the jump velocity. - * The approriate drop velocity is also set. - * @param {number} setting - */ - setJumpVelocity(setting) { - this.config.INITIAL_JUMP_VELOCITY = -setting; - this.config.DROP_VELOCITY = -setting / 2; - }, - - /** - * Set the animation status. - * @param {!number} deltaTime - * @param {Trex.status=} opt_status Optional status to switch to. - */ - update(deltaTime, opt_status) { - this.timer += deltaTime; - - // Update the status. - if (opt_status) { - this.status = opt_status; - this.currentFrame = 0; - this.msPerFrame = Trex.animFrames[opt_status].msPerFrame; - this.currentAnimFrames = Trex.animFrames[opt_status].frames; - - if (opt_status === Trex.status.WAITING) { - this.animStartTime = getTimeStamp(); - this.setBlinkDelay(); - } - } - // Game intro animation, T-rex moves in from the left. - if (this.playingIntro && this.xPos < this.config.START_X_POS) { - this.xPos += Math.round((this.config.START_X_POS / - this.config.INTRO_DURATION) * deltaTime); - this.xInitialPos = this.xPos; - } - - if (this.status === Trex.status.WAITING) { - this.blink(getTimeStamp()); - } else { - this.draw(this.currentAnimFrames[this.currentFrame], 0); - } - - // Update the frame position. - if (!this.flashing && this.timer >= this.msPerFrame) { - this.currentFrame = this.currentFrame == - this.currentAnimFrames.length - 1 ? 0 : this.currentFrame + 1; - this.timer = 0; - } - - if (!this.altGameModeEnabled) { - // Speed drop becomes duck if the down key is still being pressed. - if (this.speedDrop && this.yPos === this.groundYPos) { - this.speedDrop = false; - this.setDuck(true); - } - } - }, - - /** - * Draw the t-rex to a particular position. - * @param {number} x - * @param {number} y - */ - draw(x, y) { - let sourceX = x; - let sourceY = y; - let sourceWidth = this.ducking && this.status !== Trex.status.CRASHED ? - this.config.WIDTH_DUCK : - this.config.WIDTH; - let sourceHeight = this.config.HEIGHT; - const outputHeight = sourceHeight; - - let jumpOffset = Runner.spriteDefinition.TREX.JUMPING.xOffset; - - // Width of sprite changes on jump. - if (this.altGameModeEnabled && this.jumping && - this.status !== Trex.status.CRASHED) { - sourceWidth = this.config.WIDTH_JUMP; - } - - if (IS_HIDPI) { - sourceX *= 2; - sourceY *= 2; - sourceWidth *= 2; - sourceHeight *= 2; - jumpOffset *= 2; - } - - // Adjustments for sprite sheet position. - sourceX += this.spritePos.x; - sourceY += this.spritePos.y; - - // Flashing. - if (this.flashing) { - if (this.timer < this.config.FLASH_ON) { - this.canvasCtx.globalAlpha = 0.5; - } else if (this.timer > this.config.FLASH_OFF) { - this.timer = 0; - } - } - - // Ducking. - if (!this.altGameModeEnabled && this.ducking && - this.status !== Trex.status.CRASHED) { - this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, - sourceWidth, sourceHeight, - this.xPos, this.yPos, - this.config.WIDTH_DUCK, outputHeight); - } else if ( - this.altGameModeEnabled && this.jumping && - this.status !== Trex.status.CRASHED) { - // Jumping with adjustments. - this.canvasCtx.drawImage( - Runner.imageSprite, sourceX, sourceY, sourceWidth, sourceHeight, - this.xPos - jumpOffset, this.yPos, this.config.WIDTH_JUMP, - outputHeight); - } else { - // Crashed whilst ducking. Trex is standing up so needs adjustment. - if (this.ducking && this.status === Trex.status.CRASHED) { - this.xPos++; - } - // Standing / running - this.canvasCtx.drawImage(Runner.imageSprite, sourceX, sourceY, - sourceWidth, sourceHeight, - this.xPos, this.yPos, - this.config.WIDTH, outputHeight); - } - this.canvasCtx.globalAlpha = 1; - }, - - /** - * Sets a random time for the blink to happen. - */ - setBlinkDelay() { - this.blinkDelay = Math.ceil(Math.random() * Trex.BLINK_TIMING); - }, - - /** - * Make t-rex blink at random intervals. - * @param {number} time Current time in milliseconds. - */ - blink(time) { - const deltaTime = time - this.animStartTime; - - if (deltaTime >= this.blinkDelay) { - this.draw(this.currentAnimFrames[this.currentFrame], 0); - - if (this.currentFrame === 1) { - // Set new random delay to blink. - this.setBlinkDelay(); - this.animStartTime = time; - this.blinkCount++; - } - } - }, - - /** - * Initialise a jump. - * @param {number} speed - */ - startJump(speed) { - if (!this.jumping) { - this.update(0, Trex.status.JUMPING); - // Tweak the jump velocity based on the speed. - this.jumpVelocity = this.config.INITIAL_JUMP_VELOCITY - (speed / 10); - this.jumping = true; - this.reachedMinHeight = false; - this.speedDrop = false; - - if (this.config.INVERT_JUMP) { - this.minJumpHeight = this.groundYPos + this.config.MIN_JUMP_HEIGHT; - } - } - }, - - /** - * Jump is complete, falling down. - */ - endJump() { - if (this.reachedMinHeight && - this.jumpVelocity < this.config.DROP_VELOCITY) { - this.jumpVelocity = this.config.DROP_VELOCITY; - } - }, - - /** - * Update frame for a jump. - * @param {number} deltaTime - */ - updateJump(deltaTime) { - const msPerFrame = Trex.animFrames[this.status].msPerFrame; - const framesElapsed = deltaTime / msPerFrame; - - // Speed drop makes Trex fall faster. - if (this.speedDrop) { - this.yPos += Math.round(this.jumpVelocity * - this.config.SPEED_DROP_COEFFICIENT * framesElapsed); - } else if (this.config.INVERT_JUMP) { - this.yPos -= Math.round(this.jumpVelocity * framesElapsed); - } else { - this.yPos += Math.round(this.jumpVelocity * framesElapsed); - } - - this.jumpVelocity += this.config.GRAVITY * framesElapsed; - - // Minimum height has been reached. - if (this.config.INVERT_JUMP && (this.yPos > this.minJumpHeight) || - !this.config.INVERT_JUMP && (this.yPos < this.minJumpHeight) || - this.speedDrop) { - this.reachedMinHeight = true; - } - - // Reached max height. - if (this.config.INVERT_JUMP && (this.yPos > -this.config.MAX_JUMP_HEIGHT) || - !this.config.INVERT_JUMP && (this.yPos < this.config.MAX_JUMP_HEIGHT) || - this.speedDrop) { - this.endJump(); - } - - // Back down at ground level. Jump completed. - if ((this.config.INVERT_JUMP && this.yPos) < this.groundYPos || - (!this.config.INVERT_JUMP && this.yPos) > this.groundYPos) { - this.reset(); - this.jumpCount++; - - if (Runner.audioCues) { - Runner.generatedSoundFx.loopFootSteps(); - } - } - }, - - /** - * Set the speed drop. Immediately cancels the current jump. - */ - setSpeedDrop() { - this.speedDrop = true; - this.jumpVelocity = 1; - }, - - /** - * @param {boolean} isDucking - */ - setDuck(isDucking) { - if (isDucking && this.status !== Trex.status.DUCKING) { - this.update(0, Trex.status.DUCKING); - this.ducking = true; - } else if (this.status === Trex.status.DUCKING) { - this.update(0, Trex.status.RUNNING); - this.ducking = false; - } - }, - - /** - * Reset the t-rex to running at start of game. - */ - reset() { - this.xPos = this.xInitialPos; - this.yPos = this.groundYPos; - this.jumpVelocity = 0; - this.jumping = false; - this.ducking = false; - this.update(0, Trex.status.RUNNING); - this.midair = false; - this.speedDrop = false; - this.jumpCount = 0; - }, -}; - - -//****************************************************************************** - -/** - * Handles displaying the distance meter. - * @param {!HTMLCanvasElement} canvas - * @param {Object} spritePos Image position in sprite. - * @param {number} canvasWidth - * @constructor - */ -function DistanceMeter(canvas, spritePos, canvasWidth) { - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - this.image = Runner.imageSprite; - this.spritePos = spritePos; - this.x = 0; - this.y = 5; - - this.currentDistance = 0; - this.maxScore = 0; - this.highScore = '0'; - this.container = null; - - this.digits = []; - this.achievement = false; - this.defaultString = ''; - this.flashTimer = 0; - this.flashIterations = 0; - this.invertTrigger = false; - this.flashingRafId = null; - this.highScoreBounds = {}; - this.highScoreFlashing = false; - - this.config = DistanceMeter.config; - this.maxScoreUnits = this.config.MAX_DISTANCE_UNITS; - this.canvasWidth = canvasWidth; - this.init(canvasWidth); -} - - -/** - * @enum {number} - */ -DistanceMeter.dimensions = { - WIDTH: 10, - HEIGHT: 13, - DEST_WIDTH: 11, -}; - - -/** - * Y positioning of the digits in the sprite sheet. - * X position is always 0. - * @type {Array} - */ -DistanceMeter.yPos = [0, 13, 27, 40, 53, 67, 80, 93, 107, 120]; - - -/** - * Distance meter config. - * @enum {number} - */ -DistanceMeter.config = { - // Number of digits. - MAX_DISTANCE_UNITS: 5, - - // Distance that causes achievement animation. - ACHIEVEMENT_DISTANCE: 100, - - // Used for conversion from pixel distance to a scaled unit. - COEFFICIENT: 0.025, - - // Flash duration in milliseconds. - FLASH_DURATION: 1000 / 4, - - // Flash iterations for achievement animation. - FLASH_ITERATIONS: 3, - - // Padding around the high score hit area. - HIGH_SCORE_HIT_AREA_PADDING: 4, -}; - - -DistanceMeter.prototype = { - /** - * Initialise the distance meter to '00000'. - * @param {number} width Canvas width in px. - */ - init(width) { - let maxDistanceStr = ''; - - this.calcXPos(width); - this.maxScore = this.maxScoreUnits; - for (let i = 0; i < this.maxScoreUnits; i++) { - this.draw(i, 0); - this.defaultString += '0'; - maxDistanceStr += '9'; - } - - this.maxScore = parseInt(maxDistanceStr, 10); - }, - - /** - * Calculate the xPos in the canvas. - * @param {number} canvasWidth - */ - calcXPos(canvasWidth) { - this.x = canvasWidth - (DistanceMeter.dimensions.DEST_WIDTH * - (this.maxScoreUnits + 1)); - }, - - /** - * Draw a digit to canvas. - * @param {number} digitPos Position of the digit. - * @param {number} value Digit value 0-9. - * @param {boolean=} opt_highScore Whether drawing the high score. - */ - draw(digitPos, value, opt_highScore) { - let sourceWidth = DistanceMeter.dimensions.WIDTH; - let sourceHeight = DistanceMeter.dimensions.HEIGHT; - let sourceX = DistanceMeter.dimensions.WIDTH * value; - let sourceY = 0; - - const targetX = digitPos * DistanceMeter.dimensions.DEST_WIDTH; - const targetY = this.y; - const targetWidth = DistanceMeter.dimensions.WIDTH; - const targetHeight = DistanceMeter.dimensions.HEIGHT; - - // For high DPI we 2x source values. - if (IS_HIDPI) { - sourceWidth *= 2; - sourceHeight *= 2; - sourceX *= 2; - } - - sourceX += this.spritePos.x; - sourceY += this.spritePos.y; - - this.canvasCtx.save(); - - if (IS_RTL) { - if (opt_highScore) { - this.canvasCtx.translate( - this.canvasWidth - - (DistanceMeter.dimensions.WIDTH * (this.maxScoreUnits + 3)), - this.y); - } else { - this.canvasCtx.translate( - this.canvasWidth - DistanceMeter.dimensions.WIDTH, this.y); - } - this.canvasCtx.scale(-1, 1); - } else { - const highScoreX = - this.x - (this.maxScoreUnits * 2) * DistanceMeter.dimensions.WIDTH; - if (opt_highScore) { - this.canvasCtx.translate(highScoreX, this.y); - } else { - this.canvasCtx.translate(this.x, this.y); - } - } - - this.canvasCtx.drawImage( - this.image, - sourceX, - sourceY, - sourceWidth, - sourceHeight, - targetX, - targetY, - targetWidth, - targetHeight, - ); - - this.canvasCtx.restore(); - }, - - /** - * Covert pixel distance to a 'real' distance. - * @param {number} distance Pixel distance ran. - * @return {number} The 'real' distance ran. - */ - getActualDistance(distance) { - return distance ? Math.round(distance * this.config.COEFFICIENT) : 0; - }, - - /** - * Update the distance meter. - * @param {number} distance - * @param {number} deltaTime - * @return {boolean} Whether the acheivement sound fx should be played. - */ - update(deltaTime, distance) { - let paint = true; - let playSound = false; - - if (!this.achievement) { - distance = this.getActualDistance(distance); - // Score has gone beyond the initial digit count. - if (distance > this.maxScore && this.maxScoreUnits == - this.config.MAX_DISTANCE_UNITS) { - this.maxScoreUnits++; - this.maxScore = parseInt(this.maxScore + '9', 10); - } else { - this.distance = 0; - } - - if (distance > 0) { - // Achievement unlocked. - if (distance % this.config.ACHIEVEMENT_DISTANCE === 0) { - // Flash score and play sound. - this.achievement = true; - this.flashTimer = 0; - playSound = true; - } - - // Create a string representation of the distance with leading 0. - const distanceStr = (this.defaultString + - distance).substr(-this.maxScoreUnits); - this.digits = distanceStr.split(''); - } else { - this.digits = this.defaultString.split(''); - } - } else { - // Control flashing of the score on reaching acheivement. - if (this.flashIterations <= this.config.FLASH_ITERATIONS) { - this.flashTimer += deltaTime; - - if (this.flashTimer < this.config.FLASH_DURATION) { - paint = false; - } else if (this.flashTimer > this.config.FLASH_DURATION * 2) { - this.flashTimer = 0; - this.flashIterations++; - } - } else { - this.achievement = false; - this.flashIterations = 0; - this.flashTimer = 0; - } - } - - // Draw the digits if not flashing. - if (paint) { - for (let i = this.digits.length - 1; i >= 0; i--) { - this.draw(i, parseInt(this.digits[i], 10)); - } - } - - this.drawHighScore(); - return playSound; - }, - - /** - * Draw the high score. - */ - drawHighScore() { - if (parseInt(this.highScore, 10) > 0) { - this.canvasCtx.save(); - this.canvasCtx.globalAlpha = .8; - for (let i = this.highScore.length - 1; i >= 0; i--) { - this.draw(i, parseInt(this.highScore[i], 10), true); - } - this.canvasCtx.restore(); - } - }, - - /** - * Set the highscore as a array string. - * Position of char in the sprite: H - 10, I - 11. - * @param {number} distance Distance ran in pixels. - */ - setHighScore(distance) { - distance = this.getActualDistance(distance); - const highScoreStr = (this.defaultString + - distance).substr(-this.maxScoreUnits); - - this.highScore = ['10', '11', ''].concat(highScoreStr.split('')); - }, - - - /** - * Whether a clicked is in the high score area. - * @param {Event} e Event object. - * @return {boolean} Whether the click was in the high score bounds. - */ - hasClickedOnHighScore(e) { - let x = 0; - let y = 0; - - if (e.touches) { - // Bounds for touch differ from pointer. - const canvasBounds = this.canvas.getBoundingClientRect(); - x = e.touches[0].clientX - canvasBounds.left; - y = e.touches[0].clientY - canvasBounds.top; - } else { - x = e.offsetX; - y = e.offsetY; - } - - this.highScoreBounds = this.getHighScoreBounds(); - return x >= this.highScoreBounds.x && x <= - this.highScoreBounds.x + this.highScoreBounds.width && - y >= this.highScoreBounds.y && y <= - this.highScoreBounds.y + this.highScoreBounds.height; - }, - - /** - * Get the bounding box for the high score. - * @return {Object} Object with x, y, width and height properties. - */ - getHighScoreBounds() { - return { - x: (this.x - (this.maxScoreUnits * 2) * DistanceMeter.dimensions.WIDTH) - - DistanceMeter.config.HIGH_SCORE_HIT_AREA_PADDING, - y: this.y, - width: DistanceMeter.dimensions.WIDTH * (this.highScore.length + 1) + - DistanceMeter.config.HIGH_SCORE_HIT_AREA_PADDING, - height: DistanceMeter.dimensions.HEIGHT + - (DistanceMeter.config.HIGH_SCORE_HIT_AREA_PADDING * 2), - }; - }, - - /** - * Animate flashing the high score to indicate ready for resetting. - * The flashing stops following this.config.FLASH_ITERATIONS x 2 flashes. - */ - flashHighScore() { - const now = getTimeStamp(); - const deltaTime = now - (this.frameTimeStamp || now); - let paint = true; - this.frameTimeStamp = now; - - // Reached the max number of flashes. - if (this.flashIterations > this.config.FLASH_ITERATIONS * 2) { - this.cancelHighScoreFlashing(); - return; - } - - this.flashTimer += deltaTime; - - if (this.flashTimer < this.config.FLASH_DURATION) { - paint = false; - } else if (this.flashTimer > this.config.FLASH_DURATION * 2) { - this.flashTimer = 0; - this.flashIterations++; - } - - if (paint) { - this.drawHighScore(); - } else { - this.clearHighScoreBounds(); - } - // Frame update. - this.flashingRafId = - requestAnimationFrame(this.flashHighScore.bind(this)); - }, - - /** - * Draw empty rectangle over high score. - */ - clearHighScoreBounds() { - this.canvasCtx.save(); - this.canvasCtx.fillStyle = '#fff'; - this.canvasCtx.rect(this.highScoreBounds.x, this.highScoreBounds.y, - this.highScoreBounds.width, this.highScoreBounds.height); - this.canvasCtx.fill(); - this.canvasCtx.restore(); - }, - - /** - * Starts the flashing of the high score. - */ - startHighScoreFlashing() { - this.highScoreFlashing = true; - this.flashHighScore(); - }, - - /** - * Whether high score is flashing. - * @return {boolean} - */ - isHighScoreFlashing() { - return this.highScoreFlashing; - }, - - /** - * Stop flashing the high score. - */ - cancelHighScoreFlashing() { - if (this.flashingRafId) { - cancelAnimationFrame(this.flashingRafId); - } - this.flashIterations = 0; - this.flashTimer = 0; - this.highScoreFlashing = false; - this.clearHighScoreBounds(); - this.drawHighScore(); - }, - - /** - * Clear the high score. - */ - resetHighScore() { - this.setHighScore(0); - this.cancelHighScoreFlashing(); - }, - - /** - * Reset the distance meter back to '00000'. - */ - reset() { - this.update(0, 0); - this.achievement = false; - }, -}; - - -//****************************************************************************** - -/** - * Cloud background item. - * Similar to an obstacle object but without collision boxes. - * @param {HTMLCanvasElement} canvas Canvas element. - * @param {Object} spritePos Position of image in sprite. - * @param {number} containerWidth - * @constructor - */ -function Cloud(canvas, spritePos, containerWidth) { - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (this.canvas.getContext('2d')); - this.spritePos = spritePos; - this.containerWidth = containerWidth; - this.xPos = containerWidth; - this.yPos = 0; - this.remove = false; - this.gap = - getRandomNum(Cloud.config.MIN_CLOUD_GAP, Cloud.config.MAX_CLOUD_GAP); - - this.init(); -} - - -/** - * Cloud object config. - * @enum {number} - */ -Cloud.config = { - HEIGHT: 14, - MAX_CLOUD_GAP: 400, - MAX_SKY_LEVEL: 30, - MIN_CLOUD_GAP: 100, - MIN_SKY_LEVEL: 71, - WIDTH: 46, -}; - - -Cloud.prototype = { - /** - * Initialise the cloud. Sets the Cloud height. - */ - init() { - this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL, - Cloud.config.MIN_SKY_LEVEL); - this.draw(); - }, - - /** - * Draw the cloud. - */ - draw() { - this.canvasCtx.save(); - let sourceWidth = Cloud.config.WIDTH; - let sourceHeight = Cloud.config.HEIGHT; - const outputWidth = sourceWidth; - const outputHeight = sourceHeight; - if (IS_HIDPI) { - sourceWidth = sourceWidth * 2; - sourceHeight = sourceHeight * 2; - } - - this.canvasCtx.drawImage(Runner.imageSprite, this.spritePos.x, - this.spritePos.y, - sourceWidth, sourceHeight, - this.xPos, this.yPos, - outputWidth, outputHeight); - - this.canvasCtx.restore(); - }, - - /** - * Update the cloud position. - * @param {number} speed - */ - update(speed) { - if (!this.remove) { - this.xPos -= Math.ceil(speed); - this.draw(); - - // Mark as removeable if no longer in the canvas. - if (!this.isVisible()) { - this.remove = true; - } - } - }, - - /** - * Check if the cloud is visible on the stage. - * @return {boolean} - */ - isVisible() { - return this.xPos + Cloud.config.WIDTH > 0; - }, -}; - - -/** - * Background item. - * Similar to cloud, without random y position. - * @param {HTMLCanvasElement} canvas Canvas element. - * @param {Object} spritePos Position of image in sprite. - * @param {number} containerWidth - * @param {string} type Element type. - * @constructor - */ -function BackgroundEl(canvas, spritePos, containerWidth, type) { - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (this.canvas.getContext('2d')); - this.spritePos = spritePos; - this.containerWidth = containerWidth; - this.xPos = containerWidth; - this.yPos = 0; - this.remove = false; - this.type = type; - this.gap = - getRandomNum(BackgroundEl.config.MIN_GAP, BackgroundEl.config.MAX_GAP); - this.animTimer = 0; - this.switchFrames = false; - - this.spriteConfig = {}; - this.init(); -} - -/** - * Background element object config. - * Real values assigned when game type changes. - * @enum {number} - */ -BackgroundEl.config = { - MAX_BG_ELS: 0, - MAX_GAP: 0, - MIN_GAP: 0, - POS: 0, - SPEED: 0, - Y_POS: 0, - MS_PER_FRAME: 0, // only needed when BACKGROUND_EL.FIXED is true -}; - - -BackgroundEl.prototype = { - /** - * Initialise the element setting the y position. - */ - init() { - this.spriteConfig = Runner.spriteDefinition.BACKGROUND_EL[this.type]; - if (this.spriteConfig.FIXED) { - this.xPos = this.spriteConfig.FIXED_X_POS; - } - this.yPos = BackgroundEl.config.Y_POS - this.spriteConfig.HEIGHT + - this.spriteConfig.OFFSET; - this.draw(); - }, - - /** - * Draw the element. - */ - draw() { - this.canvasCtx.save(); - let sourceWidth = this.spriteConfig.WIDTH; - let sourceHeight = this.spriteConfig.HEIGHT; - let sourceX = this.spriteConfig.X_POS; - const outputWidth = sourceWidth; - const outputHeight = sourceHeight; - - if (IS_HIDPI) { - sourceWidth *= 2; - sourceHeight *= 2; - sourceX *= 2; - } - - this.canvasCtx.drawImage( - Runner.imageSprite, sourceX, this.spritePos.y, sourceWidth, - sourceHeight, this.xPos, this.yPos, outputWidth, outputHeight); - - this.canvasCtx.restore(); - }, - - /** - * Update the background element position. - * @param {number} speed - */ - update(speed) { - if (!this.remove) { - if (this.spriteConfig.FIXED) { - this.animTimer += speed; - if (this.animTimer > BackgroundEl.config.MS_PER_FRAME) { - this.animTimer = 0; - this.switchFrames = !this.switchFrames; - } - - if (this.spriteConfig.FIXED_Y_POS_1 && - this.spriteConfig.FIXED_Y_POS_2) { - this.yPos = this.switchFrames ? this.spriteConfig.FIXED_Y_POS_1 : - this.spriteConfig.FIXED_Y_POS_2; - } - } else { - // Fixed speed, regardless of actual game speed. - this.xPos -= BackgroundEl.config.SPEED; - } - this.draw(); - - // Mark as removable if no longer in the canvas. - if (!this.isVisible()) { - this.remove = true; - } - } - }, - - /** - * Check if the element is visible on the stage. - * @return {boolean} - */ - isVisible() { - return this.xPos + this.spriteConfig.WIDTH > 0; - }, -}; - - - -//****************************************************************************** - -/** - * Nightmode shows a moon and stars on the horizon. - * @param {HTMLCanvasElement} canvas - * @param {number} spritePos - * @param {number} containerWidth - * @constructor - */ -function NightMode(canvas, spritePos, containerWidth) { - this.spritePos = spritePos; - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - this.xPos = containerWidth - 50; - this.yPos = 30; - this.currentPhase = 0; - this.opacity = 0; - this.containerWidth = containerWidth; - this.stars = []; - this.drawStars = false; - this.placeStars(); -} - -/** - * @enum {number} - */ -NightMode.config = { - FADE_SPEED: 0.035, - HEIGHT: 40, - MOON_SPEED: 0.25, - NUM_STARS: 2, - STAR_SIZE: 9, - STAR_SPEED: 0.3, - STAR_MAX_Y: 70, - WIDTH: 20, -}; - -NightMode.phases = [140, 120, 100, 60, 40, 20, 0]; - -NightMode.prototype = { - /** - * Update moving moon, changing phases. - * @param {boolean} activated Whether night mode is activated. - */ - update(activated) { - // Moon phase. - if (activated && this.opacity === 0) { - this.currentPhase++; - - if (this.currentPhase >= NightMode.phases.length) { - this.currentPhase = 0; - } - } - - // Fade in / out. - if (activated && (this.opacity < 1 || this.opacity === 0)) { - this.opacity += NightMode.config.FADE_SPEED; - } else if (this.opacity > 0) { - this.opacity -= NightMode.config.FADE_SPEED; - } - - // Set moon positioning. - if (this.opacity > 0) { - this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED); - - // Update stars. - if (this.drawStars) { - for (let i = 0; i < NightMode.config.NUM_STARS; i++) { - this.stars[i].x = - this.updateXPos(this.stars[i].x, NightMode.config.STAR_SPEED); - } - } - this.draw(); - } else { - this.opacity = 0; - this.placeStars(); - } - this.drawStars = true; - }, - - updateXPos(currentPos, speed) { - if (currentPos < -NightMode.config.WIDTH) { - currentPos = this.containerWidth; - } else { - currentPos -= speed; - } - return currentPos; - }, - - draw() { - let moonSourceWidth = this.currentPhase === 3 ? NightMode.config.WIDTH * 2 : - NightMode.config.WIDTH; - let moonSourceHeight = NightMode.config.HEIGHT; - let moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase]; - const moonOutputWidth = moonSourceWidth; - let starSize = NightMode.config.STAR_SIZE; - let starSourceX = Runner.spriteDefinitionByType.original.LDPI.STAR.x; - - if (IS_HIDPI) { - moonSourceWidth *= 2; - moonSourceHeight *= 2; - moonSourceX = this.spritePos.x + - (NightMode.phases[this.currentPhase] * 2); - starSize *= 2; - starSourceX = Runner.spriteDefinitionByType.original.HDPI.STAR.x; - } - - this.canvasCtx.save(); - this.canvasCtx.globalAlpha = this.opacity; - - // Stars. - if (this.drawStars) { - for (let i = 0; i < NightMode.config.NUM_STARS; i++) { - this.canvasCtx.drawImage( - Runner.origImageSprite, starSourceX, this.stars[i].sourceY, - starSize, starSize, Math.round(this.stars[i].x), this.stars[i].y, - NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE); - } - } - - // Moon. - this.canvasCtx.drawImage( - Runner.origImageSprite, moonSourceX, this.spritePos.y, moonSourceWidth, - moonSourceHeight, Math.round(this.xPos), this.yPos, moonOutputWidth, - NightMode.config.HEIGHT); - - this.canvasCtx.globalAlpha = 1; - this.canvasCtx.restore(); - }, - - // Do star placement. - placeStars() { - const segmentSize = Math.round(this.containerWidth / - NightMode.config.NUM_STARS); - - for (let i = 0; i < NightMode.config.NUM_STARS; i++) { - this.stars[i] = {}; - this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1)); - this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y); - - if (IS_HIDPI) { - this.stars[i].sourceY = - Runner.spriteDefinitionByType.original.HDPI.STAR.y + - NightMode.config.STAR_SIZE * 2 * i; - } else { - this.stars[i].sourceY = - Runner.spriteDefinitionByType.original.LDPI.STAR.y + - NightMode.config.STAR_SIZE * i; - } - } - }, - - reset() { - this.currentPhase = 0; - this.opacity = 0; - this.update(false); - }, - -}; - - -//****************************************************************************** - -/** - * Horizon Line. - * Consists of two connecting lines. Randomly assigns a flat / bumpy horizon. - * @param {HTMLCanvasElement} canvas - * @param {Object} lineConfig Configuration object. - * @constructor - */ -function HorizonLine(canvas, lineConfig) { - let sourceX = lineConfig.SOURCE_X; - let sourceY = lineConfig.SOURCE_Y; - - if (IS_HIDPI) { - sourceX *= 2; - sourceY *= 2; - } - - this.spritePos = {x: sourceX, y: sourceY}; - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - this.sourceDimensions = {}; - this.dimensions = lineConfig; - - this.sourceXPos = [this.spritePos.x, this.spritePos.x + - this.dimensions.WIDTH]; - this.xPos = []; - this.yPos = 0; - this.bumpThreshold = 0.5; - - this.setSourceDimensions(lineConfig); - this.draw(); -} - - -/** - * Horizon line dimensions. - * @enum {number} - */ -HorizonLine.dimensions = { - WIDTH: 600, - HEIGHT: 12, - YPOS: 127, -}; - - -HorizonLine.prototype = { - /** - * Set the source dimensions of the horizon line. - */ - setSourceDimensions(newDimensions) { - for (const dimension in newDimensions) { - if (dimension !== 'SOURCE_X' && dimension !== 'SOURCE_Y') { - if (IS_HIDPI) { - if (dimension !== 'YPOS') { - this.sourceDimensions[dimension] = newDimensions[dimension] * 2; - } - } else { - this.sourceDimensions[dimension] = newDimensions[dimension]; - } - this.dimensions[dimension] = newDimensions[dimension]; - } - } - - this.xPos = [0, newDimensions.WIDTH]; - this.yPos = newDimensions.YPOS; - }, - - /** - * Return the crop x position of a type. - */ - getRandomType() { - return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0; - }, - - /** - * Draw the horizon line. - */ - draw() { - this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[0], - this.spritePos.y, - this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT, - this.xPos[0], this.yPos, - this.dimensions.WIDTH, this.dimensions.HEIGHT); - - this.canvasCtx.drawImage(Runner.imageSprite, this.sourceXPos[1], - this.spritePos.y, - this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT, - this.xPos[1], this.yPos, - this.dimensions.WIDTH, this.dimensions.HEIGHT); - }, - - /** - * Update the x position of an indivdual piece of the line. - * @param {number} pos Line position. - * @param {number} increment - */ - updateXPos(pos, increment) { - const line1 = pos; - const line2 = pos === 0 ? 1 : 0; - - this.xPos[line1] -= increment; - this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH; - - if (this.xPos[line1] <= -this.dimensions.WIDTH) { - this.xPos[line1] += this.dimensions.WIDTH * 2; - this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH; - this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x; - } - }, - - /** - * Update the horizon line. - * @param {number} deltaTime - * @param {number} speed - */ - update(deltaTime, speed) { - const increment = Math.floor(speed * (FPS / 1000) * deltaTime); - - if (this.xPos[0] <= 0) { - this.updateXPos(0, increment); - } else { - this.updateXPos(1, increment); - } - this.draw(); - }, - - /** - * Reset horizon to the starting position. - */ - reset() { - this.xPos[0] = 0; - this.xPos[1] = this.dimensions.WIDTH; - }, -}; - - -//****************************************************************************** - -/** - * Horizon background class. - * @param {HTMLCanvasElement} canvas - * @param {Object} spritePos Sprite positioning. - * @param {Object} dimensions Canvas dimensions. - * @param {number} gapCoefficient - * @constructor - */ -function Horizon(canvas, spritePos, dimensions, gapCoefficient) { - this.canvas = canvas; - this.canvasCtx = - /** @type {CanvasRenderingContext2D} */ (this.canvas.getContext('2d')); - this.config = Horizon.config; - this.dimensions = dimensions; - this.gapCoefficient = gapCoefficient; - this.obstacles = []; - this.obstacleHistory = []; - this.horizonOffsets = [0, 0]; - this.cloudFrequency = this.config.CLOUD_FREQUENCY; - this.spritePos = spritePos; - this.nightMode = null; - this.altGameModeActive = false; - - // Cloud - this.clouds = []; - this.cloudSpeed = this.config.BG_CLOUD_SPEED; - - // Background elements - this.backgroundEls = []; - this.lastEl = null; - this.backgroundSpeed = this.config.BG_CLOUD_SPEED; - - // Horizon - this.horizonLine = null; - this.horizonLines = []; - this.init(); -} - - -/** - * Horizon config. - * @enum {number} - */ -Horizon.config = { - BG_CLOUD_SPEED: 0.2, - BUMPY_THRESHOLD: .3, - CLOUD_FREQUENCY: .5, - HORIZON_HEIGHT: 16, - MAX_CLOUDS: 6, -}; - - -Horizon.prototype = { - /** - * Initialise the horizon. Just add the line and a cloud. No obstacles. - */ - init() { - Obstacle.types = Runner.spriteDefinitionByType.original.OBSTACLES; - this.addCloud(); - // Multiple Horizon lines - for (let i = 0; i < Runner.spriteDefinition.LINES.length; i++) { - this.horizonLines.push( - new HorizonLine(this.canvas, Runner.spriteDefinition.LINES[i])); - } - - this.nightMode = new NightMode(this.canvas, this.spritePos.MOON, - this.dimensions.WIDTH); - }, - - /** - * Update obstacle definitions based on the speed of the game. - */ - adjustObstacleSpeed: function() { - for (let i = 0; i < Obstacle.types.length; i++) { - if (Runner.slowDown) { - Obstacle.types[i].multipleSpeed = Obstacle.types[i].multipleSpeed / 2; - Obstacle.types[i].minGap *= 1.5; - Obstacle.types[i].minSpeed = Obstacle.types[i].minSpeed / 2; - - // Convert variable y position obstacles to fixed. - if (typeof (Obstacle.types[i].yPos) == 'object') { - Obstacle.types[i].yPos = Obstacle.types[i].yPos[0]; - Obstacle.types[i].yPosMobile = Obstacle.types[i].yPos[0]; - } - } - } - }, - - /** - * Update sprites to correspond to change in sprite sheet. - * @param {number} spritePos - */ - enableAltGameMode: function(spritePos) { - // Clear existing horizon objects. - this.clouds = []; - this.backgroundEls = []; - - this.altGameModeActive = true; - this.spritePos = spritePos; - - Obstacle.types = Runner.spriteDefinition.OBSTACLES; - this.adjustObstacleSpeed(); - - Obstacle.MAX_GAP_COEFFICIENT = Runner.spriteDefinition.MAX_GAP_COEFFICIENT; - Obstacle.MAX_OBSTACLE_LENGTH = Runner.spriteDefinition.MAX_OBSTACLE_LENGTH; - - BackgroundEl.config = Runner.spriteDefinition.BACKGROUND_EL_CONFIG; - - this.horizonLines = []; - for (let i = 0; i < Runner.spriteDefinition.LINES.length; i++) { - this.horizonLines.push( - new HorizonLine(this.canvas, Runner.spriteDefinition.LINES[i])); - } - this.reset(); - }, - - /** - * @param {number} deltaTime - * @param {number} currentSpeed - * @param {boolean} updateObstacles Used as an override to prevent - * the obstacles from being updated / added. This happens in the - * ease in section. - * @param {boolean} showNightMode Night mode activated. - */ - update(deltaTime, currentSpeed, updateObstacles, showNightMode) { - this.runningTime += deltaTime; - - if (this.altGameModeActive) { - this.updateBackgroundEls(deltaTime, currentSpeed); - } - - for (let i = 0; i < this.horizonLines.length; i++) { - this.horizonLines[i].update(deltaTime, currentSpeed); - } - - if (!this.altGameModeActive || Runner.spriteDefinition.HAS_CLOUDS) { - this.nightMode.update(showNightMode); - this.updateClouds(deltaTime, currentSpeed); - } - - if (updateObstacles) { - this.updateObstacles(deltaTime, currentSpeed); - } - }, - - /** - * Update background element positions. Also handles creating new elements. - * @param {number} elSpeed - * @param {Array} bgElArray - * @param {number} maxBgEl - * @param {Function} bgElAddFunction - * @param {number} frequency - */ - updateBackgroundEl(elSpeed, bgElArray, maxBgEl, bgElAddFunction, frequency) { - const numElements = bgElArray.length; - - if (numElements) { - for (let i = numElements - 1; i >= 0; i--) { - bgElArray[i].update(elSpeed); - } - - const lastEl = bgElArray[numElements - 1]; - - // Check for adding a new element. - if (numElements < maxBgEl && - (this.dimensions.WIDTH - lastEl.xPos) > lastEl.gap && - frequency > Math.random()) { - bgElAddFunction(); - } - } else { - bgElAddFunction(); - } - }, - - /** - * Update the cloud positions. - * @param {number} deltaTime - * @param {number} speed - */ - updateClouds(deltaTime, speed) { - const elSpeed = this.cloudSpeed / 1000 * deltaTime * speed; - this.updateBackgroundEl( - elSpeed, this.clouds, this.config.MAX_CLOUDS, this.addCloud.bind(this), - this.cloudFrequency); - - // Remove expired elements. - this.clouds = this.clouds.filter((obj) => !obj.remove); - }, - - /** - * Update the background element positions. - * @param {number} deltaTime - * @param {number} speed - */ - updateBackgroundEls(deltaTime, speed) { - this.updateBackgroundEl( - deltaTime, this.backgroundEls, BackgroundEl.config.MAX_BG_ELS, - this.addBackgroundEl.bind(this), this.cloudFrequency); - - // Remove expired elements. - this.backgroundEls = this.backgroundEls.filter((obj) => !obj.remove); - }, - - /** - * Update the obstacle positions. - * @param {number} deltaTime - * @param {number} currentSpeed - */ - updateObstacles(deltaTime, currentSpeed) { - const updatedObstacles = this.obstacles.slice(0); - - for (let i = 0; i < this.obstacles.length; i++) { - const obstacle = this.obstacles[i]; - obstacle.update(deltaTime, currentSpeed); - - // Clean up existing obstacles. - if (obstacle.remove) { - updatedObstacles.shift(); - } - } - this.obstacles = updatedObstacles; - - if (this.obstacles.length > 0) { - const lastObstacle = this.obstacles[this.obstacles.length - 1]; - - if (lastObstacle && !lastObstacle.followingObstacleCreated && - lastObstacle.isVisible() && - (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) < - this.dimensions.WIDTH) { - this.addNewObstacle(currentSpeed); - lastObstacle.followingObstacleCreated = true; - } - } else { - // Create new obstacles. - this.addNewObstacle(currentSpeed); - } - }, - - removeFirstObstacle() { - this.obstacles.shift(); - }, - - /** - * Add a new obstacle. - * @param {number} currentSpeed - */ - addNewObstacle(currentSpeed) { - const obstacleCount = - Obstacle.types[Obstacle.types.length - 1].type != 'COLLECTABLE' || - (Runner.isAltGameModeEnabled() && !this.altGameModeActive || - this.altGameModeActive) ? - Obstacle.types.length - 1 : - Obstacle.types.length - 2; - const obstacleTypeIndex = - obstacleCount > 0 ? getRandomNum(0, obstacleCount) : 0; - const obstacleType = Obstacle.types[obstacleTypeIndex]; - - // Check for multiples of the same type of obstacle. - // Also check obstacle is available at current speed. - if ((obstacleCount > 0 && this.duplicateObstacleCheck(obstacleType.type)) || - currentSpeed < obstacleType.minSpeed) { - this.addNewObstacle(currentSpeed); - } else { - const obstacleSpritePos = this.spritePos[obstacleType.type]; - - this.obstacles.push(new Obstacle( - this.canvasCtx, obstacleType, obstacleSpritePos, this.dimensions, - this.gapCoefficient, currentSpeed, obstacleType.width, - this.altGameModeActive)); - - this.obstacleHistory.unshift(obstacleType.type); - - if (this.obstacleHistory.length > 1) { - this.obstacleHistory.splice(Runner.config.MAX_OBSTACLE_DUPLICATION); - } - } - }, - - /** - * Returns whether the previous two obstacles are the same as the next one. - * Maximum duplication is set in config value MAX_OBSTACLE_DUPLICATION. - * @return {boolean} - */ - duplicateObstacleCheck(nextObstacleType) { - let duplicateCount = 0; - - for (let i = 0; i < this.obstacleHistory.length; i++) { - duplicateCount = - this.obstacleHistory[i] === nextObstacleType ? duplicateCount + 1 : 0; - } - return duplicateCount >= Runner.config.MAX_OBSTACLE_DUPLICATION; - }, - - /** - * Reset the horizon layer. - * Remove existing obstacles and reposition the horizon line. - */ - reset() { - this.obstacles = []; - for (let l = 0; l < this.horizonLines.length; l++) { - this.horizonLines[l].reset(); - } - - this.nightMode.reset(); - }, - - /** - * Update the canvas width and scaling. - * @param {number} width Canvas width. - * @param {number} height Canvas height. - */ - resize(width, height) { - this.canvas.width = width; - this.canvas.height = height; - }, - - /** - * Add a new cloud to the horizon. - */ - addCloud() { - this.clouds.push(new Cloud(this.canvas, this.spritePos.CLOUD, - this.dimensions.WIDTH)); - }, - - /** - * Add a random background element to the horizon. - */ - addBackgroundEl() { - const backgroundElTypes = - Object.keys(Runner.spriteDefinition.BACKGROUND_EL); - - if (backgroundElTypes.length > 0) { - let index = getRandomNum(0, backgroundElTypes.length - 1); - let type = backgroundElTypes[index]; - - // Add variation if available. - while (type == this.lastEl && backgroundElTypes.length > 1) { - index = getRandomNum(0, backgroundElTypes.length - 1); - type = backgroundElTypes[index]; - } - - this.lastEl = type; - this.backgroundEls.push(new BackgroundEl( - this.canvas, this.spritePos.BACKGROUND_EL, this.dimensions.WIDTH, - type)); - } - }, -}; diff --git a/chrome-dino/scripts/strings.js b/chrome-dino/scripts/strings.js deleted file mode 100644 index d8326761..00000000 --- a/chrome-dino/scripts/strings.js +++ /dev/null @@ -1,2 +0,0 @@ -var pageData = {"altGameCommonImage1x":"images/default_100_percent/offline/100-olympic-firemedal-sprites.png","altGameCommonImage2x":"images/default_200_percent/offline/200-olympic-firemedal-sprites.png","dinoGameA11yAriaLabel":"","dinoGameA11yGameOver":"Game over, your score is $1.","dinoGameA11yHighScore":"Your highest score is $1.","dinoGameA11yJump":"Jump!","dinoGameA11ySpeedToggle":"Start slower","dinoGameA11yStartGame":"Game started.","enableAltGameMode":false,"errorCode":"","fontfamily":"'Segoe UI', Tahoma, sans-serif","fontsize":"75%","heading":{"hostName":"dino","msg":"Press space to play"},"iconClass":"icon-offline","language":"en","textdirection":"ltr","title":"chrome://dino/"}; -loadTimeData.data = pageData; \ No newline at end of file diff --git a/chrome-dino/style.css b/chrome-dino/style.css deleted file mode 100644 index 579e7261..00000000 --- a/chrome-dino/style.css +++ /dev/null @@ -1,209 +0,0 @@ -/* Copyright 2013 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -html, body { - padding: 0; - margin: 0; - width: 100%; - height: 100%; -} - -.icon { - -webkit-user-select: none; - user-select: none; - display: inline-block; -} - - -.hidden { - display: none; -} - - -/* Offline page */ -.offline { - transition: filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1), - background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); -} - -.offline body { - transition: background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); -} -.offline.inverted { - background-color: #000; - filter: invert(1); -} - -.offline.inverted body { - background-color: #fff; -} - -.offline .interstitial-wrapper { - color: #2b2b2b; - font-size: 0.8em; - line-height: 1.55; - margin: 0 auto; - max-width: 600px; - padding-top: 150px; - width: 100%; -} - -.offline .runner-container { - direction: ltr; - height: 150px; - max-width: 600px; - overflow: hidden; - position: absolute; - top: 35px; - width: 44px; -} - -.offline .runner-canvas { - height: 150px; - max-width: 600px; - opacity: 1; - overflow: hidden; - position: absolute; - top: 0; - z-index: 10; -} - -.offline .controller { - background: rgba(247, 247, 247, .1); - height: 100vh; - left: 0; - position: absolute; - top: 0; - width: 100vw; - z-index: 1; -} - -#offline-resources { - display: none; -} - - -.arcade-mode, -.arcade-mode .runner-container, -.arcade-mode .runner-canvas { - image-rendering: -moz-crisp-edges; - image-rendering: -webkit-crisp-edges; - image-rendering: pixelated; - image-rendering: crisp-edges; - max-width: 100%; - overflow: hidden; -} - -.arcade-mode #buttons, -.arcade-mode #main-content { - opacity: 0; - overflow: hidden; -} - -.arcade-mode .interstitial-wrapper { - height: 100vh; - max-width: 100%; - overflow: hidden; -} - -.arcade-mode .runner-container { - image-rendering: -moz-crisp-edges; - image-rendering: -webkit-crisp-edges; - image-rendering: pixelated; - image-rendering: crisp-edges; - left: 0; - margin: auto; - right: 0; - transform-origin: top center; - transition: transform 250ms cubic-bezier(0.4, 0, 1, 1) 400ms; - z-index: 2; -} - - -@media (prefers-color-scheme: dark) { - - body { - background-color: #000; - } - - .offline .runner-canvas { - filter: invert(1); - } - - .offline.inverted { - background-color: #fff; - filter: invert(1); - } - - .offline.inverted body { - background-color: #fff; - } - - h1{filter: invert(1);} -} - -@media (max-width: 420px) { - .suggested-left > #control-buttons, .suggested-right > #control-buttons { - float: none; - } - .snackbar { - left: 0; - bottom: 0; - width: 100%; - border-radius: 0; - } -} - -@media (max-height: 350px) { - h1 { - margin: 0 0 15px; - } - .icon-offline { - margin: 0 0 10px; - } - .interstitial-wrapper { - margin-top: 5%; - } - .nav-wrapper { - margin-top: 30px; - } -} - -@media (min-width: 600px) and (max-width: 736px) and (orientation: landscape) { - .offline .interstitial-wrapper { - margin-left: 0; - margin-right: 0; - } -} - -@media (min-width: 420px) and (max-width: 736px) and (min-height: 240px) and (max-height: 420px) and (orientation:landscape) { - .interstitial-wrapper { - margin-bottom: 100px; - } -} - -@media (min-height: 240px) and (orientation: landscape) { - .offline .interstitial-wrapper { - margin-bottom: 90px; - } - .icon-offline { - margin-bottom: 20px; - } -} - -@media (max-height: 320px) and (orientation: landscape) { - .icon-offline { - margin-bottom: 0; - } - .offline .runner-container { - top: 10px; - } -} - -@media (max-width: 240px) { - .interstitial-wrapper { - overflow: inherit; - padding: 0 8px; - } -} \ No newline at end of file diff --git a/chrome-dino/styles/interstitial_common.css b/chrome-dino/styles/interstitial_common.css deleted file mode 100644 index 38b07598..00000000 --- a/chrome-dino/styles/interstitial_common.css +++ /dev/null @@ -1,517 +0,0 @@ -/* Copyright 2014 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. */ - -button { - border: 0; - border-radius: 4px; - box-sizing: border-box; - color: var(--primary-button-text-color); - cursor: pointer; - float: right; - font-size: .875em; - margin: 0; - padding: 8px 16px; - transition: box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1); - user-select: none; -} - -[dir='rtl'] button { - float: left; -} - -.bad-clock button, -.captive-portal button, -.https-only button, -.insecure-form button, -.lookalike-url button, -.main-frame-blocked button, -.neterror button, -.pdf button, -.ssl button, -.safe-browsing-billing button { - background: var(--primary-button-fill-color); -} - -button:active { - background: var(--primary-button-fill-color-active); - outline: 0; -} - -#debugging { - display: inline; - overflow: auto; -} - -.debugging-content { - line-height: 1em; - margin-bottom: 0; - margin-top: 1em; -} - -.debugging-content-fixed-width { - display: block; - font-family: monospace; - font-size: 1.2em; - margin-top: 0.5em; -} - -.debugging-title { - font-weight: bold; -} - -#details { - margin: 0 0 50px; -} - -#details p:not(:first-of-type) { - margin-top: 20px; -} - -.secondary-button:active { - border-color: white; - box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .3), - 0 2px 6px 2px rgba(60, 64, 67, .15); -} - -.secondary-button:hover { - background: var(--secondary-button-hover-fill-color); - border-color: var(--secondary-button-hover-border-color); - text-decoration: none; -} - -.error-code { - color: var(--error-code-color); - font-size: .8em; - margin-top: 12px; - text-transform: uppercase; -} - -#error-debugging-info { - font-size: 0.8em; -} - -h1 { - color: var(--heading-color); - font-size: 1.6em; - font-weight: normal; - line-height: 1.25em; - margin-bottom: 16px; -} - -h2 { - font-size: 1.2em; - font-weight: normal; -} - -.icon { - height: 72px; - margin: 0 0 40px; - width: 72px; -} - -input[type=checkbox] { - opacity: 0; -} - -input[type=checkbox]:focus ~ .checkbox::after { - outline: -webkit-focus-ring-color auto 5px; -} - -.interstitial-wrapper { - box-sizing: border-box; - font-size: 1em; - line-height: 1.6em; - margin: 14vh auto 0; - max-width: 600px; - width: 100%; -} - -#main-message > p { - display: inline; -} - -#extended-reporting-opt-in { - font-size: .875em; - margin-top: 32px; -} - -#extended-reporting-opt-in label { - display: grid; - grid-template-columns: 1.8em 1fr; - position: relative; -} - -#enhanced-protection-message { - border-radius: 4px; - font-size: 1em; - margin-top: 32px; - padding: 10px 5px; -} - -#enhanced-protection-message label { - display: grid; - grid-template-columns: 2.5em 1fr; - position: relative; -} - -#enhanced-protection-message div { - margin: 0.5em; -} - -#enhanced-protection-message .icon { - height: 1.5em; - vertical-align: middle; - width: 1.5em; -} - -.nav-wrapper { - margin-top: 51px; -} - -.nav-wrapper::after { - clear: both; - content: ''; - display: table; - width: 100%; -} - -.small-link { - color: var(--small-link-color); - font-size: .875em; -} - -.checkboxes { - flex: 0 0 24px; -} - -.checkbox { - --padding: .9em; - background: transparent; - display: block; - height: 1em; - left: -1em; - padding-inline-start: var(--padding); - position: absolute; - right: 0; - top: -.5em; - width: 1em; -} - -.checkbox::after { - border: 1px solid white; - border-radius: 2px; - content: ''; - height: 1em; - left: var(--padding); - position: absolute; - top: var(--padding); - width: 1em; -} - -.checkbox::before { - background: transparent; - border: 2px solid white; - border-inline-end-width: 0; - border-top-width: 0; - content: ''; - height: .2em; - left: calc(.3em + var(--padding)); - opacity: 0; - position: absolute; - top: calc(.3em + var(--padding)); - transform: rotate(-45deg); - width: .5em; -} - -input[type=checkbox]:checked ~ .checkbox::before { - opacity: 1; -} - -#recurrent-error-message { - background: #ededed; - border-radius: 4px; - margin-bottom: 16px; - margin-top: 12px; - padding: 12px 16px; -} - -.showing-recurrent-error-message #extended-reporting-opt-in { - margin-top: 16px; -} - -.showing-recurrent-error-message #enhanced-protection-message { - margin-top: 16px; -} - -@media (max-width: 700px) { - .interstitial-wrapper { - padding: 0 10%; - } - - #error-debugging-info { - overflow: auto; - } -} - -@media (max-width: 420px) { - button, - [dir='rtl'] button, - .small-link { - float: none; - font-size: .825em; - font-weight: 500; - margin: 0; - width: 100%; - } - - button { - padding: 16px 24px; - } - - #details { - margin: 20px 0 20px 0; - } - - #details p:not(:first-of-type) { - margin-top: 10px; - } - - .secondary-button:not(.hidden) { - display: block; - margin-top: 20px; - text-align: center; - width: 100%; - } - - .interstitial-wrapper { - padding: 0 5%; - } - - #extended-reporting-opt-in { - margin-top: 24px; - } - - #enhanced-protection-message { - margin-top: 24px; - } - - .nav-wrapper { - margin-top: 30px; - } -} - -/** - * Mobile specific styling. - * Navigation buttons are anchored to the bottom of the screen. - * Details message replaces the top content in its own scrollable area. - */ - -@media (max-width: 420px) { - .nav-wrapper .secondary-button { - border: 0; - margin: 16px 0 0; - margin-inline-end: 0; - padding-bottom: 16px; - padding-top: 16px; - } -} - -/* Fixed nav. */ -@media (min-width: 240px) and (max-width: 420px) and - (min-height: 401px), - (min-width: 421px) and (min-height: 240px) and - (max-height: 560px) { - body .nav-wrapper { - background: var(--background-color); - bottom: 0; - box-shadow: 0 -12px 24px var(--background-color); - left: 0; - margin: 0 auto; - max-width: 736px; - padding-inline-end: 24px; - padding-inline-start: 24px; - position: fixed; - right: 0; - width: 100%; - z-index: 2; - } - - .interstitial-wrapper { - max-width: 736px; - } - - #details, - #main-content { - padding-bottom: 40px; - } - - #details { - padding-top: 5.5vh; - } - - button.small-link { - color: var(--google-blue-600); - } -} - -@media (max-width: 420px) and (orientation: portrait), - (max-height: 560px) { - body { - margin: 0 auto; - } - - button, - [dir='rtl'] button, - button.small-link, - .nav-wrapper .secondary-button { - font-family: Roboto-Regular,Helvetica; - font-size: .933em; - margin: 6px 0; - transform: translatez(0); - } - - .nav-wrapper { - box-sizing: border-box; - padding-bottom: 8px; - width: 100%; - } - - #details { - box-sizing: border-box; - height: auto; - margin: 0; - opacity: 1; - transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1); - } - - #details.hidden, - #main-content.hidden { - height: 0; - opacity: 0; - overflow: hidden; - padding-bottom: 0; - transition: none; - } - - h1 { - font-size: 1.5em; - margin-bottom: 8px; - } - - .icon { - margin-bottom: 5.69vh; - } - - .interstitial-wrapper { - box-sizing: border-box; - margin: 7vh auto 12px; - padding: 0 24px; - position: relative; - } - - .interstitial-wrapper p { - font-size: .95em; - line-height: 1.61em; - margin-top: 8px; - } - - #main-content { - margin: 0; - transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1); - } - - .small-link { - border: 0; - } - - .suggested-left > #control-buttons, - .suggested-right > #control-buttons { - float: none; - margin: 0; - } -} - -@media (min-width: 421px) and (min-height: 500px) and (max-height: 560px) { - .interstitial-wrapper { - margin-top: 10vh; - } -} - -@media (min-height: 400px) and (orientation:portrait) { - .interstitial-wrapper { - margin-bottom: 145px; - } -} - -@media (min-height: 299px) { - .nav-wrapper { - padding-bottom: 16px; - } -} - -@media (max-height: 560px) and (min-height: 240px) and (orientation:landscape) { - .extended-reporting-has-checkbox #details { - padding-bottom: 80px; - } -} - -@media (min-height: 500px) and (max-height: 650px) and (max-width: 414px) and - (orientation: portrait) { - .interstitial-wrapper { - margin-top: 7vh; - } -} - -@media (min-height: 650px) and (max-width: 414px) and (orientation: portrait) { - .interstitial-wrapper { - margin-top: 10vh; - } -} - -/* Small mobile screens. No fixed nav. */ -@media (max-height: 400px) and (orientation: portrait), - (max-height: 239px) and (orientation: landscape), - (max-width: 419px) and (max-height: 399px) { - .interstitial-wrapper { - display: flex; - flex-direction: column; - margin-bottom: 0; - } - - #details { - flex: 1 1 auto; - order: 0; - } - - #main-content { - flex: 1 1 auto; - order: 0; - } - - .nav-wrapper { - flex: 0 1 auto; - margin-top: 8px; - order: 1; - padding-inline-end: 0; - padding-inline-start: 0; - position: relative; - width: 100%; - } - - button, - .nav-wrapper .secondary-button { - padding: 16px 24px; - } - - button.small-link { - color: var(--google-blue-600); - } -} - -@media (max-width: 239px) and (orientation: portrait) { - .nav-wrapper { - padding-inline-end: 0; - padding-inline-start: 0; - } -} diff --git a/chrome-dino/styles/interstitial_core.css b/chrome-dino/styles/interstitial_core.css deleted file mode 100644 index cd249d1a..00000000 --- a/chrome-dino/styles/interstitial_core.css +++ /dev/null @@ -1,100 +0,0 @@ -:root { - --background-color: #fff; - --google-blue-100: rgb(210, 227, 252); - --google-blue-300: rgb(138, 180, 248); - --google-blue-600: rgb(26, 115, 232); - --google-blue-700: rgb(25, 103, 210); - --google-gray-100: rgb(241, 243, 244); - --google-gray-300: rgb(218, 220, 224); - --google-gray-500: rgb(154, 160, 166); - --google-gray-50: rgb(248, 249, 250); - --google-gray-600: rgb(128, 134, 139); - --google-gray-700: rgb(95, 99, 104); - --google-gray-800: rgb(60, 64, 67); - --google-gray-900: rgb(32, 33, 36); -} - - -/* Copyright 2017 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -a { - color: var(--link-color); -} - -body { - --background-color: #fff; - --error-code-color: var(--google-gray-700); - --google-blue-100: rgb(210, 227, 252); - --google-blue-300: rgb(138, 180, 248); - --google-blue-600: rgb(26, 115, 232); - --google-blue-700: rgb(25, 103, 210); - --google-gray-100: rgb(241, 243, 244); - --google-gray-300: rgb(218, 220, 224); - --google-gray-500: rgb(154, 160, 166); - --google-gray-50: rgb(248, 249, 250); - --google-gray-600: rgb(128, 134, 139); - --google-gray-700: rgb(95, 99, 104); - --google-gray-800: rgb(60, 64, 67); - --google-gray-900: rgb(32, 33, 36); - --heading-color: var(--google-gray-900); - --link-color: rgb(88, 88, 88); - --popup-container-background-color: rgba(0,0,0,.65); - --primary-button-fill-color-active: var(--google-blue-700); - --primary-button-fill-color: var(--google-blue-600); - --primary-button-text-color: #fff; - --quiet-background-color: rgb(247, 247, 247); - --secondary-button-border-color: var(--google-gray-500); - --secondary-button-fill-color: #fff; - --secondary-button-hover-border-color: var(--google-gray-600); - --secondary-button-hover-fill-color: var(--google-gray-50); - --secondary-button-text-color: var(--google-gray-700); - --small-link-color: var(--google-gray-700); - --text-color: var(--google-gray-700); - background: var(--background-color); - color: var(--text-color); - word-wrap: break-word; -} - -.nav-wrapper .secondary-button { - background: var(--secondary-button-fill-color); - border: 1px solid var(--secondary-button-border-color); - color: var(--secondary-button-text-color); - float: none; - margin: 0; - padding: 8px 16px; -} - -.hidden { - display: none; -} - -html { - -webkit-text-size-adjust: 100%; - font-size: 125%; -} - -.icon { - background-repeat: no-repeat; - background-size: 100%; -} - -@media (prefers-color-scheme: dark) { - body { - --background-color: var(--google-gray-900); - --error-code-color: var(--google-gray-500); - --heading-color: var(--google-gray-500); - --link-color: var(--google-blue-300); - --primary-button-fill-color-active: rgb(129, 162, 208); - --primary-button-fill-color: var(--google-blue-300); - --primary-button-text-color: var(--google-gray-900); - --quiet-background-color: var(--background-color); - --secondary-button-border-color: var(--google-gray-700); - --secondary-button-fill-color: var(--google-gray-900); - --secondary-button-hover-fill-color: rgb(48, 51, 57); - --secondary-button-text-color: var(--google-blue-300); - --small-link-color: var(--google-blue-300); - --text-color: var(--google-gray-500); - } -} diff --git a/chrome-dino/styles/neterror.css b/chrome-dino/styles/neterror.css deleted file mode 100644 index 78d08300..00000000 --- a/chrome-dino/styles/neterror.css +++ /dev/null @@ -1,429 +0,0 @@ -* { - font-family: system-ui, 'Segoe UI', Tahoma, sans-serif; -} - -/* Copyright 2013 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -/* Don't use the main frame div when the error is in a subframe. */ -html[subframe] #main-frame-error { - display: none; -} - -/* Don't use the subframe error div when the error is in a main frame. */ -html:not([subframe]) #sub-frame-error { - display: none; -} - -h1 { - margin-top: 0; - word-wrap: break-word; -} - -h1 span { - font-weight: 500; -} - -a { - text-decoration: none; -} - -.icon { - -webkit-user-select: none; - display: inline-block; -} - -.icon-offline { - content: -webkit-image-set( - url(../images/default_100_percent/offline/100-error-offline.png) 1x, - url(../images/default_200_percent/offline/200-error-offline.png) 2x); - position: relative; -} - -.hidden { - display: none; -} - -/* Offline page */ -html[dir='rtl'] .runner-container, -html[dir='rtl'].offline .icon-offline { - transform: scaleX(-1); -} - -.offline { - transition: filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1), - background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); -} - -.offline body { - transition: background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); -} - -.offline #main-message > p { - display: none; -} - -.offline.inverted { - background-color: #dfdedb; /* --google-gray-900 inverted value */ - filter: invert(1); -} - -.offline.inverted body { - background-color: #dfdedb; -} - -.offline .interstitial-wrapper { - color: var(--text-color); - font-size: 1em; - line-height: 1.55; - margin: 0 auto; - max-width: 600px; - padding-top: 100px; - position: relative; - width: 100%; -} - -.offline .runner-container { - direction: ltr; - height: 150px; - max-width: 600px; - overflow: hidden; - position: absolute; - top: 35px; - width: 44px; -} - -.offline .runner-container:focus { - outline: none; -} - -.offline .runner-container:focus-visible { - /* outline: 3px solid var(--google-blue-300); */ - outline: none; -} - -.offline .runner-canvas { - height: 150px; - max-width: 600px; - opacity: 1; - overflow: hidden; - position: absolute; - top: 0; - z-index: 10; -} - -.offline .controller { - height: 100vh; - left: 0; - position: absolute; - top: 0; - width: 100vw; - z-index: 9; -} - -#offline-resources { - display: none; -} - -#offline-instruction { - image-rendering: -moz-crisp-edges; - image-rendering: -webkit-crisp-edges; - image-rendering: pixelated; - image-rendering: crisp-edges; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 60px; - width: fit-content; -} - -.offline-runner-live-region { - bottom: 0; - clip-path: polygon(0 0, 0 0, 0 0); - color: var(--background-color); - display: block; - font-size: xx-small; - overflow: hidden; - position: absolute; - text-align: center; - transition: color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); - user-select: none; -} - -/* Custom toggle */ -.slow-speed-option { - align-items: center; - background: var(--google-gray-50); - border-radius: 24px/50%; - bottom: 0; - color: var(--error-code-color); - /*display: inline-flex;*/ - display: none; - font-size: 1em; - left: 0; - line-height: 1.1em; - margin: 5px auto; - padding: 2px 12px 3px 20px; - position: absolute; - right: 0; - width: max-content; - z-index: 999; -} - -.slow-speed-option.hidden { - display: none; -} - -.slow-speed-option [type=checkbox] { - opacity: 0; - pointer-events: none; - position: absolute; -} - -.slow-speed-option .slow-speed-toggle { - cursor: pointer; - margin-inline-start: 8px; - padding: 8px 4px; - position: relative; -} - -.slow-speed-option [type=checkbox]:disabled ~ .slow-speed-toggle { - cursor: default; -} - -.slow-speed-option-label [type=checkbox] { - opacity: 0; - pointer-events: none; - position: absolute; -} - -.slow-speed-option .slow-speed-toggle::before, -.slow-speed-option .slow-speed-toggle::after { - content: ''; - display: block; - margin: 0 3px; - transition: all 100ms cubic-bezier(0.4, 0, 1, 1); -} - -.slow-speed-option .slow-speed-toggle::before { - background: rgb(189,193,198); - border-radius: 0.65em; - height: 0.9em; - width: 2em; -} - -.slow-speed-option .slow-speed-toggle::after { - background: #fff; - border-radius: 50%; - box-shadow: 0 1px 3px 0 rgb(0 0 0 / 40%); - height: 1.2em; - position: absolute; - top: 51%; - transform: translate(-20%, -50%); - width: 1.1em; -} - -.slow-speed-option [type=checkbox]:focus + .slow-speed-toggle { - box-shadow: 0 0 8px rgb(94, 158, 214); - outline: 1px solid rgb(93, 157, 213); -} - -.slow-speed-option [type=checkbox]:checked + .slow-speed-toggle::before { - background: var(--google-blue-600); - opacity: 0.5; -} - -.slow-speed-option [type=checkbox]:checked + .slow-speed-toggle::after { - background: var(--google-blue-600); - transform: translate(calc(2em - 90%), -50%); -} - -.slow-speed-option [type=checkbox]:checked:disabled + - .slow-speed-toggle::before { - background: rgb(189,193,198); -} - -.slow-speed-option [type=checkbox]:checked:disabled + - .slow-speed-toggle::after { - background: var(--google-gray-50); -} - -@media (max-width: 420px) { - #download-button { - padding-bottom: 12px; - padding-top: 12px; - } - - .suggested-left > #control-buttons, - .suggested-right > #control-buttons { - float: none; - } - - .snackbar { - border-radius: 0; - bottom: 0; - left: 0; - width: 100%; - } -} - -@media (max-height: 350px) { - h1 { - margin: 0 0 15px; - } - - .icon-offline { - margin: 0 0 10px; - } - - .interstitial-wrapper { - margin-top: 5%; - } - - .nav-wrapper { - margin-top: 30px; - } -} - -@media (min-width: 420px) and (max-width: 736px) and - (min-height: 240px) and (max-height: 420px) and - (orientation:landscape) { - .interstitial-wrapper { - margin-bottom: 100px; - } -} - -@media (max-width: 360px) and (max-height: 480px) { - .offline .interstitial-wrapper { - padding-top: 60px; - } - - .offline .runner-container { - top: 8px; - } -} - -@media (min-height: 240px) and (orientation: landscape) { - .offline .interstitial-wrapper { - margin-bottom: 90px; - } - - .icon-offline { - margin-bottom: 20px; - } -} - -@media (max-height: 320px) and (orientation: landscape) { - .icon-offline { - margin-bottom: 0; - } - - .offline .runner-container { - top: 10px; - } -} - -@media (max-width: 240px) { - button { - padding-inline-end: 12px; - padding-inline-start: 12px; - } - - .interstitial-wrapper { - overflow: inherit; - padding: 0 8px; - } -} - -@media (max-width: 120px) { - button { - width: auto; - } -} - -.arcade-mode, -.arcade-mode .runner-container, -.arcade-mode .runner-canvas { - image-rendering: -moz-crisp-edges; - image-rendering: -webkit-crisp-edges; - image-rendering: pixelated; - image-rendering: crisp-edges; - max-width: 100%; - overflow: hidden; -} - -.arcade-mode #buttons, -.arcade-mode #main-content { - opacity: 0; - overflow: hidden; -} - -.arcade-mode .interstitial-wrapper { - height: 100vh; - max-width: 100%; - overflow: hidden; -} - -.arcade-mode .runner-container { - left: 0; - margin: auto; - right: 0; - transform-origin: top center; - transition: transform 250ms cubic-bezier(0.4, 0, 1, 1) 400ms; - z-index: 2; -} - -@media (prefers-color-scheme: dark) { - .icon { - filter: invert(1); - } - - .offline .runner-canvas { - filter: invert(1); - transition: filter 1.5s cubic-bezier(0.65, 0.05, 0.36, 1), - background-color 1.5s cubic-bezier(0.65, 0.05, 0.36, 1); - } - - .offline.inverted .runner-canvas { - filter: invert(0); - } - - .offline.inverted { - background-color: var(--background-color); - filter: invert(0); - } - - .offline.inverted body { - background-color: #fff; - } - - .offline.inverted .offline-runner-live-region { - color: #fff; - } - - #suggestions-list a { - color: var(--link-color); - } - - #error-information-button { - filter: invert(0.6); - } - - .slow-speed-option { - background: var(--google-gray-800); - color: var(--google-gray-100); - } - - .slow-speed-option .slow-speed-toggle::before, - .slow-speed-option [type=checkbox]:checked:disabled + - .slow-speed-toggle::before { - background: rgb(189,193,198); - } - - .slow-speed-option [type=checkbox]:checked + .slow-speed-toggle::after, - .slow-speed-option [type=checkbox]:checked + .slow-speed-toggle::before { - background: var(--google-blue-300); - } -} diff --git a/chrome-dino/sw.js b/chrome-dino/sw.js deleted file mode 100644 index bf4ccad0..00000000 --- a/chrome-dino/sw.js +++ /dev/null @@ -1,399 +0,0 @@ -"use strict"; - -const OFFLINE_DATA_FILE = "offline.js"; -const CACHE_NAME_PREFIX = "chrome-dino"; -const BROADCASTCHANNEL_NAME = "offline"; -const CONSOLE_PREFIX = "[SW] "; -const LAZYLOAD_KEYNAME = ""; - -// Create a BroadcastChannel if supported. -const broadcastChannel = (typeof BroadcastChannel === "undefined" ? null : new BroadcastChannel(BROADCASTCHANNEL_NAME)); - -////////////////////////////////////// -// Utility methods -function PostBroadcastMessage(o) -{ - if (!broadcastChannel) - return; // not supported - - // Impose artificial (and arbitrary!) delay of 3 seconds to make sure client is listening by the time the message is sent. - // Note we could remove the delay on some messages, but then we create a race condition where sometimes messages can arrive - // in the wrong order (e.g. "update ready" arrives before "started downloading update"). So to keep the consistent ordering, - // delay all messages by the same amount. - setTimeout(() => broadcastChannel.postMessage(o), 3000); -}; - -function Broadcast(type) -{ - PostBroadcastMessage({ - "type": type - }); -}; - -function BroadcastDownloadingUpdate(version) -{ - PostBroadcastMessage({ - "type": "downloading-update", - "version": version - }); -} - -function BroadcastUpdateReady(version) -{ - PostBroadcastMessage({ - "type": "update-ready", - "version": version - }); -} - -function IsUrlInLazyLoadList(url, lazyLoadList) -{ - if (!lazyLoadList) - return false; // presumably lazy load list failed to load - - try { - for (const lazyLoadRegex of lazyLoadList) - { - if (new RegExp(lazyLoadRegex).test(url)) - return true; - } - } - catch (err) - { - console.error(CONSOLE_PREFIX + "Error matching in lazy-load list: ", err); - } - - return false; -}; - -function WriteLazyLoadListToStorage(lazyLoadList) -{ - if (typeof localforage === "undefined") - return Promise.resolve(); // bypass if localforage not imported - else - return localforage.setItem(LAZYLOAD_KEYNAME, lazyLoadList) -}; - -function ReadLazyLoadListFromStorage() -{ - if (typeof localforage === "undefined") - return Promise.resolve([]); // bypass if localforage not imported - else - return localforage.getItem(LAZYLOAD_KEYNAME); -}; - -function GetCacheBaseName() -{ - // Include the scope to avoid name collisions with any other SWs on the same origin. - // e.g. "c3offline-https://example.com/foo/" (won't collide with anything under bar/) - return CACHE_NAME_PREFIX + "-" + self.registration.scope; -}; - -function GetCacheVersionName(version) -{ - // Append the version number to the cache name. - // e.g. "c3offline-https://example.com/foo/-v2" - return GetCacheBaseName() + "-v" + version; -}; - -// Return caches.keys() filtered down to just caches we're interested in (with the right base name). -// This filters out caches from unrelated scopes. -async function GetAvailableCacheNames() -{ - const cacheNames = await caches.keys(); - const cacheBaseName = GetCacheBaseName(); - return cacheNames.filter(n => n.startsWith(cacheBaseName)); -}; - -// Identify if an update is pending, which is the case when we have 2 or more available caches. -// One must be an update that is waiting, since the next navigate that does an upgrade will -// delete all the old caches leaving just one currently-in-use cache. -async function IsUpdatePending() -{ - const availableCacheNames = await GetAvailableCacheNames(); - return (availableCacheNames.length >= 2); -}; - -// Automatically deduce the main page URL (e.g. index.html or main.aspx) from the available browser windows. -// This prevents having to hard-code an index page in the file list, implicitly caching it like AppCache did. -async function GetMainPageUrl() -{ - const allClients = await clients.matchAll({ - includeUncontrolled: true, - type: "window" - }); - - for (const c of allClients) - { - // Parse off the scope from the full client URL, e.g. https://example.com/index.html -> index.html - let url = c.url; - if (url.startsWith(self.registration.scope)) - url = url.substring(self.registration.scope.length); - - if (url && url !== "/") // ./ is also implicitly cached so don't bother returning that - { - // If the URL is solely a search string, prefix it with / to ensure it caches correctly. - // e.g. https://example.com/?foo=bar needs to cache as /?foo=bar, not just ?foo=bar. - if (url.startsWith("?")) - url = "/" + url; - - return url; - } - } - - return ""; // no main page URL could be identified -}; - -// Fetch optionally bypassing HTTP cache using fetch cache options -function fetchWithBypass(request, bypassCache) -{ - if (typeof request === "string") - request = new Request(request); - - if (bypassCache) - { - return fetch(request.url, { - method: 'GET', - headers: request.headers, - mode: request.mode == 'navigate' ? 'cors' : request.mode, - credentials: request.credentials, - redirect: request.redirect - }); - } - else - { - // bypass disabled: perform normal fetch which is allowed to return from HTTP cache - return fetch(request); - } -}; - -// Effectively a cache.addAll() that only creates the cache on all requests being successful (as a weak attempt at making it atomic) -// and can optionally cache-bypass with fetchWithBypass in every request -async function CreateCacheFromFileList(cacheName, fileList, bypassCache) -{ - // Kick off all requests and wait for them all to complete - const responses = await Promise.all(fileList.map(url => fetchWithBypass(url, bypassCache))); - - // Check if any request failed. If so don't move on to opening the cache. - // This makes sure we only open a cache if all requests succeeded. - let allOk = true; - - for (const response of responses) - { - if (!response.ok) - { - allOk = false; - console.error(CONSOLE_PREFIX + "Error fetching '" + response.url + "' (" + response.status + " " + response.statusText + ")"); - } - } - - if (!allOk) - throw new Error("not all resources were fetched successfully"); - - // Can now assume all responses are OK. Open a cache and write all responses there. - // TODO: ideally we can do this transactionally to ensure a complete cache is written as one atomic operation. - // This needs either new transactional features in the spec, or at the very least a way to rename a cache - // (so we can write to a temporary name that won't be returned by GetAvailableCacheNames() and then rename it when ready). - const cache = await caches.open(cacheName); - - try { - return await Promise.all(responses.map( - (response, i) => cache.put(fileList[i], response) - )); - } - catch (err) - { - // Not sure why cache.put() would fail (maybe if storage quota exceeded?) but in case it does, - // clean up the cache to try to avoid leaving behind an incomplete cache. - console.error(CONSOLE_PREFIX + "Error writing cache entries: ", err); - caches.delete(cacheName); - throw err; - } -}; - -async function UpdateCheck(isFirst) -{ - try { - // Always bypass cache when requesting offline.js to make sure we find out about new versions. - const response = await fetchWithBypass(OFFLINE_DATA_FILE, true); - - if (!response.ok) - throw new Error(OFFLINE_DATA_FILE + " responded with " + response.status + " " + response.statusText); - - const data = await response.json(); - - const version = data.version; - const fileList = data.fileList; - const lazyLoadList = data.lazyLoad; - const currentCacheName = GetCacheVersionName(version); - - const cacheExists = await caches.has(currentCacheName); - - // Don't recache if there is already a cache that exists for this version. Assume it is complete. - if (cacheExists) - { - // Log whether we are up-to-date or pending an update. - const isUpdatePending = await IsUpdatePending(); - if (isUpdatePending) - { - console.log(CONSOLE_PREFIX + "Update pending"); - Broadcast("update-pending"); - } - else - { - console.log(CONSOLE_PREFIX + "Up to date"); - Broadcast("up-to-date"); - } - return; - } - - // Implicitly add the main page URL to the file list, e.g. "index.html", so we don't have to assume a specific name. - const mainPageUrl = await GetMainPageUrl(); - - // Prepend the main page URL to the file list if we found one and it is not already in the list. - // Also make sure we request the base / which should serve the main page. - fileList.unshift("./"); - - if (mainPageUrl && fileList.indexOf(mainPageUrl) === -1) - fileList.unshift(mainPageUrl); - - console.log(CONSOLE_PREFIX + "Caching " + fileList.length + " files for offline use"); - - if (isFirst) - Broadcast("downloading"); - else - BroadcastDownloadingUpdate(version); - - // Note we don't bypass the cache on the first update check. This is because SW installation and the following - // update check caching will race with the normal page load requests. For any normal loading fetches that have already - // completed or are in-flight, it is pointless and wasteful to cache-bust the request for offline caching, since that - // forces a second network request to be issued when a response from the browser HTTP cache would be fine. - if (lazyLoadList) - await WriteLazyLoadListToStorage(lazyLoadList); // dump lazy load list to local storage# - - await CreateCacheFromFileList(currentCacheName, fileList, !isFirst); - const isUpdatePending = await IsUpdatePending(); - - if (isUpdatePending) - { - console.log(CONSOLE_PREFIX + "All resources saved, update ready"); - BroadcastUpdateReady(version); - } - else - { - console.log(CONSOLE_PREFIX + "All resources saved, offline support ready"); - Broadcast("offline-ready"); - } - } - catch (err) - { - // Update check fetches fail when we're offline, but in case there's any other kind of problem with it, log a warning. - console.warn(CONSOLE_PREFIX + "Update check failed: ", err); - } -}; - -self.addEventListener("install", event => -{ - // On install kick off an update check to cache files on first use. - // If it fails we can still complete the install event and leave the SW running, we'll just - // retry on the next navigate. - event.waitUntil( - UpdateCheck(true) // first update - .catch(() => null) - ); -}); - -async function GetCacheNameToUse(availableCacheNames, doUpdateCheck) -{ - // Prefer the oldest cache available. This avoids mixed-version responses by ensuring that if a new cache - // is created and filled due to an update check while the page is running, we keep returning resources - // from the original (oldest) cache only. - if (availableCacheNames.length === 1 || !doUpdateCheck) - return availableCacheNames[0]; - - // We are making a navigate request with more than one cache available. Check if we can expire any old ones. - const allClients = await clients.matchAll(); - - // If there are other clients open, don't expire anything yet. We don't want to delete any caches they - // might be using, which could cause mixed-version responses. - if (allClients.length > 1) - return availableCacheNames[0]; - - // Identify newest cache to use. Delete all the others. - const latestCacheName = availableCacheNames[availableCacheNames.length - 1]; - console.log(CONSOLE_PREFIX + "Updating to new version"); - - await Promise.all( - availableCacheNames.slice(0, -1) - .map(c => caches.delete(c)) - ); - - return latestCacheName; -}; - -async function HandleFetch(event, doUpdateCheck) -{ - const availableCacheNames = await GetAvailableCacheNames(); - - // No caches available: go to network - if (!availableCacheNames.length) - return fetch(event.request); - - const useCacheName = await GetCacheNameToUse(availableCacheNames, doUpdateCheck); - const cache = await caches.open(useCacheName); - const cachedResponse = await cache.match(event.request); - - if (cachedResponse) - return cachedResponse; // use cached response - - // We need to check if this request is to be lazy-cached. Send the request and load the lazy-load list - // from storage simultaneously. - const result = await Promise.all([fetch(event.request), ReadLazyLoadListFromStorage()]); - const fetchResponse = result[0]; - const lazyLoadList = result[1]; - - if (IsUrlInLazyLoadList(event.request.url, lazyLoadList)) - { - // Handle failure writing to the cache. This can happen if the storage quota is exceeded, which is particularly - // likely in Safari 11.1, which appears to have very tight storage limits. Make sure even in the event of an error - // we continue to return the response from the fetch. - try { - // Note clone response since we also respond with it - await cache.put(event.request, fetchResponse.clone()); - } - catch (err) - { - console.warn(CONSOLE_PREFIX + "Error caching '" + event.request.url + "': ", err); - } - } - - return fetchResponse; -}; - -self.addEventListener("fetch", event => -{ - /** NOTE (iain) - * This check is to prevent a bug with XMLHttpRequest where if its - * proxied with "FetchEvent.prototype.respondWith" no upload progress - * events are triggered. By returning we allow the default action to - * occur instead. Currently all cross-origin requests fall back to default. - */ - if (new URL(event.request.url).origin !== location.origin) - return; - - // Check for an update on navigate requests - const doUpdateCheck = (event.request.mode === "navigate"); - - const responsePromise = HandleFetch(event, doUpdateCheck); - - if (doUpdateCheck) - { - // allow the main request to complete, then check for updates - event.waitUntil( - responsePromise - .then(() => UpdateCheck(false)) // not first check - ); - } - - event.respondWith(responsePromise); -}); \ No newline at end of file diff --git a/circloo/index.html b/circloo/index.html index c401adcd..a140d4fa 100644 --- a/circloo/index.html +++ b/circloo/index.html @@ -1,4 +1,14 @@ -

Your browser doesn't support HTML5 canvas.

+ +

Your browser doesn't support HTML5 canvas.

+ + Cluster Rush diff --git a/cookieclicker/index.html b/cookieclicker/index.html index 5eef730d..e1a4a178 100644 --- a/cookieclicker/index.html +++ b/cookieclicker/index.html @@ -3,6 +3,17 @@ + + + + + diff --git a/ctr/index.html b/ctr/index.html index 53cbf0e9..83aeed5e 100644 --- a/ctr/index.html +++ b/ctr/index.html @@ -1,9 +1,20 @@ + + + + + - Cut the Rope | 3kh0 + Cut the Rope @@ -16,7 +27,6 @@ -