diff options
| author | Michele Bini <michele.bini@gmail.com> | 2014-12-24 12:22:27 (GMT) |
|---|---|---|
| committer | Michele Bini <michele.bini@gmail.com> | 2014-12-24 12:22:27 (GMT) |
| commit | ad5b4e6d85e7b6c9f5c009f12c17c27659043816 (patch) | |
| tree | c2ff652d8f8be0c2a9987f1ef2d54f7961bc9eaa | |
| parent | 365daa50c441dc905a348f72eaf9da606a34c0fb (diff) | |
Pruned unused files
34 files changed, 0 insertions, 13792 deletions
diff --git a/Evil-bubble.png b/Evil-bubble.png Binary files differdeleted file mode 100644 index e56b72c..0000000 --- a/Evil-bubble.png +++ /dev/null diff --git a/Grumpy-bubble.png b/Grumpy-bubble.png Binary files differdeleted file mode 100644 index 2053221..0000000 --- a/Grumpy-bubble.png +++ /dev/null diff --git a/Happy-oxygen-bubble-v2.png b/Happy-oxygen-bubble-v2.png Binary files differdeleted file mode 100644 index 7190965..0000000 --- a/Happy-oxygen-bubble-v2.png +++ /dev/null diff --git a/Happy-oxygen-bubble.png b/Happy-oxygen-bubble.png Binary files differdeleted file mode 100644 index 7529157..0000000 --- a/Happy-oxygen-bubble.png +++ /dev/null diff --git a/Stilla-the-starfish.png b/Stilla-the-starfish.png Binary files differdeleted file mode 100644 index 7d1dae5..0000000 --- a/Stilla-the-starfish.png +++ /dev/null diff --git a/SunkenMoon.appcache b/SunkenMoon.appcache deleted file mode 100644 index d2e3191..0000000 --- a/SunkenMoon.appcache +++ /dev/null @@ -1,904 +0,0 @@ -CACHE MANIFEST - -CACHE: -SunkenMoon.html - -NETWORK: -/ -/remote/* -* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SunkenMoon.html b/SunkenMoon.html deleted file mode 100644 index e16f73d..0000000 --- a/SunkenMoon.html +++ /dev/null @@ -1,1395 +0,0 @@ -<!DOCTYPE html> -<html lang="en" manifest="SunkenMoon.appcache" style="height:100%"><head><meta charset="utf-8"><meta name="viewport" content="width=480, user-scalable=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="mobile-web-app-capable" content="yes"><link rel="shortcut icon" href="data:image/x-icon;base64,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zNPUy9LUydPUys3PxMXIv7zHvLTBtq+8sKO0r5SfoW9+gDpOUDNBRDpFST1HTMnJvNHUwszRxMzPxc/Nxc7Kxc/NxNHTwNTVwtnSytnUzNLVy9LUytLTytHSydHSyM/Rx8zPxczNw8rNw8THvr3EurS+s6+8sJmtpIOckniJhl1zcDNQVjdLU0pTVlBYW9DCvdLHws3Iwc3HwMrGv8rFvsbFvcbJtsfKuMfMwcfMwsrPxc3Rx87Rx8nNw8fMwr7Jv7zHvcPHvcLGvLrAtbm+tbm8t6O1qo6lmnuWinmOhlx1ZzdcVjhVU01dW1pmZse8tsq+uL6/t7y/tr6/t8C9tLu9tbnCtbvEtrzEuL7GvMPIv8XKwMbJwb3FwbzHvbfGu7rDuMPFv8LJv8fPwsTKwsPJxMDJvbG/tJu1qqi+sLvKvsPRxDNXTVFsaWh5eMK1r8G0rru2rrW3rrS1rbaurbCxrLG1rrG2rrW6tLjAtsXKwMzQxdDUz8bY0cjczcXXztHU0sbGyb7Fw7fEv7O+urW+u7i+vsLHxczU0tXg3eTs4s3Vy1Rxam+FgYeUkbqvqbWqpbetprOqpaqmnqujnpyemp2gnaKmpKqwsL7Ev97g1fb17v/7+f////j499Ha2rO/v6y2uq+3u6u0tqmxtKSrrp6jppKbmYqXlY6bmJKfnI2bmIKRjomZlaStq6ylnqihmZ+blKCXlpOPiHx8dG50dH2Bg5KcnKOursPJxuDn2+317PHw8NnS07y4u6Kkp5+ipqCprJ2prKGtr6+wtLGwtKenrKClpJObmY+VlYqUk4uVk5GamKyvrt/Y2KGhmZGRkJOOjXFubFxaVVpVU1ddX3t8fpqgoL25usTBwbnBwLG4t6SfpJWQlZKLkY2LkI6PlJSanZymqaaqrayssKSrrqClqJmfnpGdmI+fmZSgm6Glo7m8uNrU0uDY15yUk4uIhnp1dENCQUpDRFdKUFJXV35+gJqZm6CfoZ2dnoCHiYGBhoJ9gn56gHt4fXN5fXN+goCJipWanLCusr26tra8tqywsqyusLW0sby6tsC/u8zGxMnNx8bKx8bMyJWKiIl3eFdPTjc1NEE4OUk/Q0Y+RGBbYnh7gXd8gHmChHd+gnh3fHBxdWNnaWBiZl5iZmRoanZ0eYiDjJyVnqulpaahoJ6WoaGZpK2iq7WorcK0tsS8u8DAvrq7uru8upOAgHZkZjMtLS8rKzYxMj03OjkxOE1CSm5vcm1zeGh0d2lxdWptcGNmalhcYFFWWVBVWFVaXmdobHh0foaFj5OOl5KPloqHkI2IkpWPmKCUnqmepKympq6trKqurKuwrY96e2RYVyMiISooJzQvMTUwNDEtMTcvN0RBR2hsb2hzdWhwdGZrbWBkaFFZWkhSVEVPUkpQUlNYXWJkbXV3gIKCioKBiYKAiYCAiIWEjY2KlJaTmJyamaOhoKKfpaSiqIR1dmRYWCIiIjk1NEpCQ0lCREA6PjkzOjEsMkA/RHZ5fG12eWtwcl9kaFJbXkdRU0RPUEdNT0VOVlhdZm5weX58hYF+hn18hIB9hYOCioiGj4+LlZGOlpWRmpuTnZmSm3ZxcnFrbC8mKEU2Ol9NVGdXX2FYW1BUVjlDRzhESG13emp5enF4e291d25ucGNmaVJYWlJNVkdKUk9WXmJkbHNye317gnt5gXx5goB8hIeDjIuGj5CKk5GKkomEjIF9hXNvcXdvcDQoKzMjKk1AR2JVW3BiaG5rbm5wdGhxdGx2eXh7fYeDhZGDh2ZTWDgsMTYvNVVPV01KVE5RWFVbZGdqcHNze3h3fnt6gX58g4J+hoOAh4aCiYF9hXd1fHZ0fHNvcH1ydTw0NiIaHzsvNlRIT2FYXWxnanBvcW9ydXZ2ent7fXVvcjQzNhULEAoAAA4IBzEtMlBOVktPVlFYX2Jka21tdW5wdnV0fHZ1fHV0e3d2fnV0e3Nzem5vdm5udnFucHlwcVFERhsTGCkgJTs1O0RCR1FPU1pVWGFbX21kaHVqbkNCRiInKRAOEQUEBAYHBhwjI05GUElETlBOVltXYHdud3htd3FpcXFpcXVxeXNzenNyeXBxeW5vdmxtdW1rbHBubl5XWA8PEhgVFiUfIy4sMTk3Oz47P0hBRldPVHVqbm9naiUrLRUUGRMRFRQUHTA0O0lBSz48RERFTVhWX2dhamZeZ11ZYmxkbXFudWttdGxtdGlrcWRpbmZqb2ppaHBubnNubg8bGxAPDBUPEh4XHSQiJy8lKjcrLzo2OlBJTWxiZnBnaVhUWEVBSkRGTk5GTz43QTk0PT4+R0tJUVtUXVNKU1FNVGZdZGpiam1kbW1mbmpkbGlja2dkbG1rbG1sbHNycjs8PgoMDRALDxINEhQSGBsUGxgVHR4cICwpLUA5PUpDSFBGTlNIU0Y+STgxPDAsNTAsNjw4QUtFTUlASj49RkNDS1RSWlhXXl1aYl9bY2BcZF9cZFxcZG9tbWtqanRwcXFnaQ8RFgsJDhALDg8LEwsOGg0PGhYTGh8UHyUVIiUcJiEhKickLSEgKSMhKickLSwpMzUwOjw3PzYzOzc1PTo7QkRHTlBQV1FSWFRUWlVUW1hXXlpWYHZydHBtbnFucH10dltVWgANDwYMDwgMFAkLGAgQGhQTGx0THR8QHB4WIBoaIxscJRwdJh0dJh8gKCgmLy4rNCUlLSUoMDAsNTEvODQ7QUBDS0NGTkZIUEZIT0hKUU1KU3ZzdHBtbnBubnFwb3NxcUdHTA4SFggOEQgPEQgOEgsQFg8RHBESHBMWIBcaIxgaIxsbIxwcJR4eJyMjKyknMCAgKSAhKCgmLysqMicuNC42Ozg4Pz0+RD4+RkNCTkJBT3t3eHRwcnBub3FwbnNxcXRxclJVWA8XGggOEggOEQ0QFg8SHBETHRETHRQWIBQWIBgcIxwdJBwdJB4fJx4fJhscIx0fJh0eJiAgKCUjKy8rNDYxOjk0PTg0OzM4PjM3PoJ3eXx4eHZycnNwcHNwb3Nwb3NxcGBiYhEgHgcVFgsTGRASHRIUHRMVIBcYIhocIxsdJRweJRweJRsdJBseJSAfKCAgKB0eJh0eJiAfJyMhKDEtNjk0PDcxOjMxOS4tNoN5en57fHl0enZxdHRxcXRxcHRycHl1dm1qbi04OBEaHg8SHBETHRQWIBocIxwdJBweJhwfJh0gJx4gKB8gKB8gKB8gKB8gKB8gJx4fJh8gKCkoMDEuNzAuNiIsMiErMYV7fIN9foR9fnx4eXh1dndzdHZzcnl1dHhzdnZzclRUVxseJxMWIBYYIhgcIxwdJR0eJR0gJx8gKCAhKSEiKSEiKSEiKCEiKCEiKSEiKSEiKSEiKicmLycnLyAoLR8oLX10dn55e4N9fXp1dnh0dXh0dXdzcXVycnVxcnRubnBqcmFgaUFETSImLRocIxweJhwfJR4fKB4gKBwdJhweJh0fJyIkKyIkKiEjKiEjKh8hKCAiKCMkKiIkKiQlLCQlLGpkZW5tbHVycXRxcW9tbXBubm1samlpaGtpamxnaWtjbWZjbGFgaVxVXkI8RCQkLB8gKB8gKR8hKBweJh0eJx0fJxweJh8hKCEiKh8hKRwdJRwdJh8gKB4gKCIjKiIjKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"><title>Vilma, the happy Vaquita - Sunken Moon</title></head><body style="margin:0;border:0;padding:0;height:100%;width:100%;background:black"><div style="visibility:hidden;position:absolute"><img id="pixyvaquita" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAFWElEQVRYw+2WX0gcVxTGv7l3Zmd2drs6aHaNW7Jd88cYDaQYI1jsQ5GSh1BCAhUsNQ/JWxT6lLKExISCQkBECoW+6IMJEknaIEKQbKXJ5iFY6mKsEhOkk6qbRTe67szs7M7szvQhydKWhibEbQn1B/M2557z3e/ecy6wxRZbbLHFFv8hZ86ccYVCIe/rxkUiEXckEnFvVh3M6wZMTk7un56eHkkkEnsJIdA0DZRSuFwumKbZqSjKRHd399zL4sfHx9l4PL7TMIw5AGR4eLh0YmJi402FkFf9cXZ2tv7SpUv2zZs378fj8b2WZcE0TRDybAnTNKHr+tcOh2N2ZGTk4Iu4vr6+P22Wqqo1uVzuQSwWI4uLi5AkqfJlOdva2pj+/v7tFy5cIJviiKIort7eXlUURei6DgAwDAMOhwPZbBZPnz5FSUkJBEGAruvgOA48z8Plcj14/Pjx516vVxcEQVlZWflsx44d3clkEplMBrZtI5PJHPR4PFlK6dK5c+eSf8z76NGjytHR0eXa2tomlmUftLS0rL+RkBs3bhycn5//CQCSySSOHDkCv98Py7JAKYWu68hkMlhYWMDDhw8Rj8fhcrlg2zYMw/hZkiSNYRhKCPlAVdWCYJZlQSlFPp/v4jhOIYT8ePbs2WgikRB7enp2+ny++06nE36/v/7YsWNTb+RIOByWotHoGsuyiMViOH36NAAgn8+jo6MDtm2jv78fDMNA0zRYloV79+4hlUpBUZRnSRgGhmGgpKQEAGDbNhiGQT6fRz6fh2VZ4DgOlFIQQpDNZpFOpwEAHo8HlmXBtm2Iong1n89fDYVC3/+1TvYV7saHuVwO2WwW5eXlAACHw4HW1lbcuXMHPM/j1KlTGBoawp49exCNRtHc3AyPx4PBwUGkUinkcjmwLItkMlkQJkkSqqurwTAM5ubmkMlkYJomTNOEYRiglKK6uhqGYUCSJDyvoRXARwC+f+3LnkqlPgUAXddx9OhRiKIIlmVhWRbKysrgdrsLDk1PT0MQBFBKwXEcgsEgGOaZ6S922TAMAEB9fT14nocoimhsbIQoiuB5HoIgYNu2bdi1a1e/0+n8ZPfu3R9TSn1ra2s+TdPe6+jo+NtW/4+OpNNpORaLoaurC6ZpYnBwEMePH8fly5fR3t4OALh+/ToAgFIKy7KgqipUVUVjYyMURcHU1FTh6NTW1sLr9SKdToNhmMJXUVGBRCIBp9MJSmnHtWvXvr1y5Qr8fn9uU+ZIZ2enPT4+DgCQZRkcx2FmZgYcxxXa7vLyMu7evVvoZsFgEA0NDSCEYGNjA4QQyLIM27YLR0sQhMIMMgwDhJBveJ7/cnJy0urp6Ulv+kA8fPjwFwsLC30Mw2BoaAiBQAC6rmN0dBTr6+sQRRGlpaU4cOAATNPErVu3CgUTQlBTUwOv1wtKKTRNW2ZZNg7gt1QqtSxJ0jwh5LtcLsd7vd4Vl8uVraqqyhVlssuyLLa3t2vz8/M4f/48kskkDMMAx3EIBAJwOp3w+XyFbmOaJm7fvo2ysjJwHAfTNFFXVwdd139YXV39KhgMZgkhv3IcpzY3N2v/+hPl4sWL72ua1pvNZgcAlFNK4Xa76wghw5ZlCYqirFZWVpY8efLEcfLkybFIJAJVVV9M/4YTJ078UlFRkSnWm48pxqLhcHg/y7L3l5aWsLi4CAAIhUIMiggpxqItLS0zPp8PoiiC4zgYhoGBgQHurRMCADzPv7tv3z4cOnQI8Xh8ezgctt9KIVVVVctjY2PvpFKpMlmW12VZLqqQouP3+5m2tjbS1NREi5mHLbaQQCCA52683Y5s8X/ld5pkXmGj97LmAAAAAElFTkSuQmCC"><img id="pixyvaquita_twist_l" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAE7UlEQVRYR+1XSUikRxitdtd2R5S4xCUQ4zLg0KMectFDlIAQRFAMRMhFERUGEcUEFwRPKokHifEQBQ2BVqOI4EpQkaDEBZdRW6LIgCLuW9vunfcq9jAHm14Sh2SwoKj+ra+q3vveq69QId6TpnhPeIgnIv81JZ8UeVLkkTLwZC1DYouKipT4raytrd0xN9nNzc1OSqVSkZ6erjN3jak4ixUZHR19trKyoj44OPjExsZGaLVaYWtrKwBMXF9fF+zt7f1WV1e3ZOxgtVpte3x8HHt5ealobW19NTk5eWIKpDnzZhOZn59X9ff3T+l0OkEC7Hq9Xtzc3AiFQiEcHByEYS48PDw2MzNz6h4Az9AbwHR2doZtbm4ug/zt8PCwqqura9kIUEVlZaUb5s4w3pkiYxaR7e1tZWNj45mLi4sEy3Z1dSXBI7Nif39feHh4CCcnJzlvb28vHB0dqdLKxsbGV15eXjr8PoWKX4eFhaWB/DNnZ2cxNzf3wtvb+xJAF9E90Y8MgJEkm/b2dgXOdN/d3dWdnp7+joTpCwoKVA+RMotIR0fHi7W1tT+4wdHRkUhJSREBAQHi7u5O2orgLy4uBGLE6uqqAHFpNSoGwtOenp5aKIhQ209hP0kSwOQaJKOiurq6Cv0zfO/l5uZqYM0Psf+Ou7u7PxKl5rnY6zI/P/+5MWVMEunt7fVaXl4+sLOzE1tbWyIvL0/udXt7K7CxBFtfXy/txftCchMTE+Lk5ESCZeMcFaRq96BEUFCQjIVicqSK7FT4/Pxc+Pv7y2TgPpHsa5D/HAlah2oXVilSU1PzBazQTeA8KCMjQ1qK49jYmMxufHy8wMUVISEhYnZ2VqqEbApUJ0mI94iJ4N9JKiIiQs4bigUSJRMTHBwsyZ6dncl4EnJ1dWVCVnD2l4hfzMnJubaKSFVV1c/chJtnZWUJ+F3uk5qaKgiALSoqSrS0tEgAJMkMw05iaGhIoEiwmkmgbHFxcXKOJBjHdnh4KBPCb9oUKqvx3YK9WBRc8PsW4w4sfWi1tUpLS6thqW8qKiokoO7ubpGWliazS2JsqERyZAYJhoQMwFCZxMzMjFSTa5KSkt7YjTH8G+1D68Gar6DejyjRPyQkJNyZU60MxEzeEVQJ/cDAgIynnwloYWFBjmwkh3IqxsfH5TcBhYaGitjYWJl1guTItbxPtBarm+H9YTzmv0fct9PT09dNTU0PWseYEmYTSU5Ofrm+vv4dAfAe0MeUv6enR1qCJZlWiYmJkaRoJwJmIwHeB19fX5l5eH0TdlGi8rXBohoo9yv2dcSeOxqN5gIv/d/+s6KZVGRqasqlsLBQi4NEeXm5LL/MIhUhKb4Hfn5+EjQrDtoR7OTJeV5ojiQYHR1Ny82AeBnehVHYUmsFXqNLTBIxrCwrK3uOOl8HEj/Bxz70NypKNOZ/QXeCOruBgYEeJSUlQ7jgfw4ODn5EEvevvQpWsoHvFxMTEx8sn/+UlNlELDloaWlJ09bW9jErHMuvm5ubqri4eMaSPSyNfRQiDQ0NWygAH/BesOziYVP5+PgsGHsDLAX9UPyjEOFB2dnZV7CaPcqxChVrC31vZGTk5t8A/U6J8DC8A66RkZEOeOG1fX19JGF1VTKVgEdT5K2DeYah/6+JkNPbCXvzv4mpLFsy/y4UsQSP1bFPRKxO3SMt/AuU53c9zBm5QgAAAABJRU5ErkJggg=="><img id="pixyvaquita_twist_r" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAFFklEQVRYR+1XfSiuZxy+fX+85vtj5jstyrGZM/OfUco/1pKVrOYvoZBNdHLKZ+2PxTrJWl7TGJ2thpFMRCtG1N7DIp/FxOGIfHvx+tx13bynkxjve9A6uevped7nuX+/+7p+1/W7714D8YYMgzeEh7gn8n9T8l6Re0VuqQL31tIWNisrS4FnRUlJyZIuxR4ZGTGtq6s7LCgoONYl7rK5OivS1dUVOD4+/uvq6qq/oaGhUKvVwsjISCgUCnFwcJC+sLDwR1lZ2ehlCwK4Mb4Zu7q62hkbGxtUVlau9fX17b4umWsTGRoaetjW1qba3d0VJMDr5OREHB4eCgMDA2Fqaiq03/z8/ELi4+NVZ+C4xokWaE1NjWJzc9MxMjIyZGVl5TMHB4c5f3//7PNE5ubmLPhucnLSvre3NxbrWBQVFX3zWoosLi4qysvLty0tLSVYjv39fQleo9EIABI2NjbC3NxcfjcxMRFmZmZUaXxmZuYLOzu7XTxvQcV3PD09P/L29k4bGxt7d3l5man6kDcJ9+dQa50vSMLd3f0oJSXl4+Pj4wh7e/uvkE9ZWFj4GEXbuYjMtRSpr6//cGpq6i8mWF9fF9HR0cLNzU1gEWkrgt/b2xOYwwoKEJdWo2Ig/MzW1lYNBY3Cw8N/BrFHo6OjnojRIN4MxWhGDiUAPktISCDIt/DbhZaFegOwnxqFOXJ2do6Pior6E+pt6UWkpaXFDtVbRUIB/4vU1FSZ5+joSKSlpUmwpaWl0l5cnOT6+/sF7CO2tk7X5DcqSJWoGufxN99TZcaAUA1/c521tbUE5uV7a2vrVaT4PTg4OBP2NkIfbkC5vfNkrlSkuLj4U/izicBpmbi4OGkp3ru7uyW40NBQUVtbK2AZMTg4KFUCAFFVVSUJsY8IkH1FsLyYy8LCYpiAOTY2NgK5hta2zOHl5SXm5+dJ9gXsVY9vs4iZxDxVenr6wqtkriSCBnuKRT/f3t4WkF7A7zI+JiZGQCn5HBAQIKqrqwXnkCQrCTuJjo4OgSpyN5MKcpAQiWDXGqYqLi4ubmj45wMDA++RKEmzOOi5UkdHRyXU+QTvInE9RG4NLDsN8j8kJyf/pBORnJycr2Gpx/n5+RJQU1OTiI2NlVUnMY6GhgZ539nZkSRIiGD43NnZKQBSKsAYDqoFoMNOTk6B/I3nRSj39pnFGJ+GvlSipwRsdJiZmfk+SDv4+vraI88civIiIyNjVicikPCkvb1dxqBRJaDh4WF55yA5yt/T0/PSFj4+PiIkJERaCZaRd8ZqbURFtL1CclQGc77H9QjEjysqKi7cmV4Ffv75Smthp/hyenr6CRdnH9C33KWam5vZlLJZaaOgoCBJinbSAtb2RFhYmFQDVZ+HUosAMYvdbx42nUD1f0NuM+Rdamxs1FCB/wJ82bcriahUKktIq56YmBB5eXly+2UFqQhJofnoc1l1nikks7S0JDw8PARObGkvXhgnmBcBtTQ4P/6BitvZ2dlqfUBfFHMlEW1Qbm7uBzgrvgWJH3E5koCVldUDfP8FlznUWcb7CqhxjApbopkfREREHLS2tlLBQ24SONBO/XgL49pErrN2UlLSU5CIBJm/4fMoxmDXmoJaezwA0TfB18mjz5wbJZKYmJgA0N/xlFYqlRH6ANI35kaJEMQZGQco8kRfUPrE3TgRfUDcRMxdEOEa2uv0eL+FcVdEtNBf/i+5aS53QeSmMV+Y757InZRZh0X+BYR+fj33yDXSAAAAAElFTkSuQmCC"><img id="happybubble0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAlElEQVQYV32QzQ2DMAyFn2GUCvFzyC54jF67AAsgboxBdskhUDFLSfOjJBSJ+mRbn56fHyGWNk8UqNJ8YEdHs5vJL1czWkCiXkSC3qxwoEdLL4JTKKECQCpBMAIO/EAQNjOhWXZoZnRy8JDuh9BbcOMqQz8qUe8KrfzIp2zXSpmVzp4iGIHk6d931rSLIUTgzd7n9AU5+Eu6k4Zb7QAAAABJRU5ErkJggg=="><img id="grumpybubble0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAACiklEQVQoU52Tz0tUURTHP2/em9EZf4yOM+r4K00xrSwcN4ZWtIuSjJCiIKj+gFYWaBC1DoQgiGgRVCRJbYJyVVRouhpJKw2l/K0z4+9y1Jn3fJ03masW0d3ce+6553u+53zPVcBUQDGRVUX8kIFWHwPvBjjEYYhDTYKYA+ZU9K4B7O+tt1ac+K2gpcw47ool8GRjeAuJFLnQXQqqbmJoUbToBL7xMOpcBizYWR4aIGMxEVyBecAGTQvwMcDM6DmCZRorKXbsRpy4qpO++oTASBB/sQf2b8KzIZQexaIaRksXFM8pBjeO0L9uoBs6RoLcCqvrNmxKMoWZ79jl7KTUKnHeh/5D2Y3ZqmFMFhAerWW8wkvUnUW0T2F5EeyOQYpSbRjuSiYzf5K63EH17CTZJTpqvlKG2RZgOnSCvkgLx28IarcdM/k8fe1Cj0cELsimybmylXuXUikobKcm/yu5mUoh5p06JhZO0hW5wtlmoW/Rsl0k2CzE9YcE2uTCK3dKC3cbPWRUPudgXi8FaUoR5u1aZlbO0D2zRnTcgXNexbQZ/FbCWmKbYm/GWMty4ip6Sp2/F396InM9Y0uNfAgF2WN0sO+oZGz4Eyh7VIU3l3l9K5VhVzru8u3MVs17mYk0EQxd51iDjlItNH0S5NoCiAqFOQ0zeI0H95PI8W/XvNXtKen29wH8r6za5PFLAdCts+hvSrNOWPLcpPOqdDs0i684hpqX0DmClibOrNN8Wq8itNnDjrJVUkSieOwwI8MwOOdkp6eLUuUF5WsCHM1B3/jnCXtMzbc+ct0yYfnC5u1nlC9/ne0SwvlOYsnCWibNdKyRtD6Fb2IadUqaMaUSGusnd9WS9T9/FfwCFcIToaBH7usAAAAASUVORK5CYII="><img id="evilbubble0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAACS0lEQVRIS8WXv08UURDHvw8NmGiBRuIVcIAdGIkVzRI7KQAL/wKohUCCf4cmGq398Q/YAAUl2W2oCATo+HFSYELUQihI4Dlf3u3d7vPt7fPY814z92PmfWbmzbydVYBWuFpKG5m3wgy9seo+efZVmgE3gqZBvQg+0LQDuKC8BG5QHiGaTSMbO1Lz8jF+3d1C98+6sQGOYHSGchOdn31iGcH5tNFf/2T03Q5kpCfUJQRTNDuGWvYB2jol6EljHy254A5wqIcRvNmBetUM0LYZhn69g2jRhlvgYqGxEy54Ahzq+wien0BJaopfTH0y7e0Gh/oRRqe30fml+FjrO7LiTbWPqWrEdfA4Kk9XUV5rhQNOsF11vdAVwqWZI8ou6Ft76HjhcqgfekUiuMn/5FYZovwG1ed2nvdDIuKsRo+NH+LyKz/bcEL5+yHUhF+WauBQ8xo8gpprZNhXzYBEUk7qlaEP+L0CNeADlky+F70BydD/B/NevwKXEbwTjxd8PL6ujmTobQXRfHvBPmd83Uhje55xLdW+YDGSx+9f60wK846vYwkwTUyJZxnLuewLsScLIJv9lsHgxK+yPfuYUNlw0CcitlY+3AJz0rCnDEbyL2mkc2JzKDb9Lkef4HxmA+sfUzeXDW4GmiggJ9wBNufMcafZUSfvKO5BP/uBaDWupdTzuE1gE3WR81achSHol7uIZCyud07Lhz0XlA61bLzlmRKQPNcHOL79HaXTFLiogZ6Vy41N23CZ9BJKmQDnvTu17BUmD2w3SjEvbX8AewkfzHulUU8AAAAASUVORK5CYII="><img id="stilla0" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAEdklEQVRYR+1Wf0yUdRx+3ve9H9zvnxz3cpwd5ICFDUxdK41hW5ut5fQPg+XGNFcsN0uba26KtlYUsNFsrbZ+LR1DDJubbhot0nTCEowiQVG0A+LXwRHcvRy8d/fet/dlqzk8uLu9q9nG/fl+n+/zPO/z/Xyfeyk8YD/qAfODZUOJTmQ5of8kod4XniS2lXbYq87ITlw2gfTGN58rIqxNC9PxVtl8sgkGvjpK5r78FDZWC2ZrOcxle2RxytospdP/UQ0JfduAdNYC5erHYdr9vixOWZslQ966IyR6+TvYXUZAZ4W5+oQsTlmbJUN3D+8l3JWLyMjLgpLMwvpJiyxOWZslQ7f3vULQ9yvshTkQxoZg/+ySLE5Zm+dnaP8u4u9ogzJK4FjlRMSagayqxvt4xz/c9zwB2U4IKaWCwwhO+vFw3Q/34eIa6m8+1ciEpqD0jdySRDMqKg8vVmi3X9xIaCWNtAwbVOEAaBHIewqQZs0GEwkjGhoFGe0HMdCITQZBpkOIqSjQBhbOqvrkDHmbm8jsiTpYtUYQixEqkwuMx4loUABlywRhRNkZDqGrVzHV3YUYUYLNMYMSACrdg6iWR+zGLdACAVY4wA36QFMaWMxqzI77kNn086Ins+jCb2XrCeMbB63XgeNC0FrN4HkBKkaFND4EfnYOxnUFiMUojLR2gw6HodJoYVlXBMNaN+iOmxi+3gedMx3MRABQ8kCWC66Pzy85JglnqHvbM8Tn7YNKb5kXnwlxUIsB2U1mvDsyiGMNp+E9tAfhgIBwWACLKN4ITM4/9x89gtiAD0TBwH2uPaGWNBZJgSTgT6WbiP/6DcxGIlCIx5hXmIv9Zy9CHwrgvSfWIKpIAx8TB9vtxktnzs8/r31qDRROD9jPv0laJ2ng30P9444yMni5DbSOQiQQQno2i+LSEvhbOjE0NA1nfiaUrAGxrjvikMfANnempJES+N6bVv9YPjHwc+KQ23B6oB+HVueit28MTcFpHCgRk5niQAf/RHZLT0oaKYHvNXShYicJtLeK7QzoNWrU/O6dX36nuBBmMBj1jmPFhlwo1QzSq04lrZM0cGEPXdqxjejH+hDlBPBRyZQC3uEJGB168boDRKwCay4Ldi4Iyp0JR21TUlpJgeKV4rn1+SSncCUMfBgzf0yAC0UgWJzgFRR4saOUgaDYPTEwtBrOtSyMZjtsbx1LqJcQEM9Mz97ytwNtVyoVKj2srA5COALt01vgeu3gP3ztu18mXE+HWKAzsLkcYk0I4o1zw1H9tbweimeo+/VyouEGQYsVgLEgFBs3IetAdVyha9u3kLneLqh1aTB4TLAXPALbm1+k3tRLfYxf27qB2PIcUPv8CNtceKi2IWHSUo9RE0MwGQWYdr4K5+b4X5YJiRYak45L4btbqeEJBJ0GnuPfp8Txy7OrGjWPFmXm1dQXx3vplMgkgp6Du4h29A4U3AyyTib3d7BU2gvXUjbUWVFCWC0F5wcXUt6bjLF/hTQZ4cUwy4YSpbec0P8uob8AGyqPNPk8QBcAAAAASUVORK5CYII="><img id="seafloor" src="data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAALQCAYAAAA5N1pXAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwQAyUh19UTgQAAIABJREFUeNrsve16IznOLBgAU3ZVz5x398ee51zZ3v8tnDP79kyXlUTsD4IkyMyUZFd3lyXL/VTLSn1aCkYGAiAgp//5/xLPH+BDn4KE33XnuPrvsnP/ozewdzn/u/YccsMfJD/3Acjn/BqXJ5B/Fsh7oJYDIMsNzzeDV8Jx89/t4I3vvS/eiEB53wfCzwns5Qnmj4BZAtj22FjHb5ozuOUAQAHIsgfcFF43Apw7i+KjVCrv+2D4uUC9PIH8ETBjAm74nTPIb5Ees7yoF9ZBLXtyQ7eP+wgor54x7gfUyxPM7/mC91hWJzAjHJuOiwTGnl/CQcn6u4XnyAAZgB3Z2z4gL97zwdwoRT4JqJcnmG+NePZkgwKMMqAycZoAXu8rxwBh+IX+fBJ1tAOYFcjqt0sANQ9AzXf8zfy4FPkEoF6+Hmg/oinnYCv5a6m/5AJAIBAHuEwBooLxeSgTfNhfQYKEoIEgBAZCAcnlNRzcZNfb0kAtE2t/lJH37iPvZYYnoP9sENOhds3Q2j8uB8yMANzUjpNpKzk4a+3yjjgBhAiSo/0YILmBHgQoNjJ2Oy5FmrTFxCsg1Z2/mANk++fG6RnKLbuE/ItZerl3IB/F93LAyLy4JmT6VnbATA0groBNo56m7gSNLkFkDhyta2gGLV1lBR28FcjtengstEgR0Suo4lVrLy63eD+GYzJRwObVfiGo7xbQ5GWQRoDy3fJD94PABmYNwV2a9LTuAHsC9cDWrsFNC2iZg3UXbLv6OKmvtQZQSwCS7bAvDwG8Rwlsn9u+c8IDf+ZnhN2XBTR/SiPf8jHv2Ws6AbGCOzoZKQSDy440UexnE+PK9PuJ23V0EIl0Nm4MnZzx0dl8uP1WupQDecKD226zB8lu6jwB/aeD+VaQy75+bmBOk5zQAOxlh7G7PClqQjfsz6ZWXf+KgVQPBA0iNkqUJj2SyxTt2lm0MzYPwCx8FzB/BtRPhv5bwHztfuFyzvBxtuUimNXdjARhdTa0OR8ljNKD16uneboDYh7kSWDhwOKVlZmcBqOHXReb7Yhabp9vl8Vn0H8u0N49oD8GZnnH9VlLylZiBDejS4zO0gW8RQawHU/BstPB6tue2tESKoRAaAWmEj4AiVFB9tfAyMyDi2H7fx85fRxHrHurJ7R/v18hO5avB2a5rp25k+LeOBcdtGygnqWGglR3NnRyO+rCCdm/BjSC1RGJTFwJljVgxCg/4BKkBZMXABkTOXKLoXkfLL08NpjlRlCHL4t6AHBtMoNInuAozK2iEE0QKEScuWUBrCdUSH+sB3rtcngv5m5H7iAiAKwO1Oo5E8JUWFqCwyJ5J7DldRbmnrZ+So6/+Ud2oCkXNPGejTfdxq2OFmhxFBwkogW0qgtUFKoLki5+fIGIustRwEuTdpmzgATM6MdKQoV0oFJBYZcLrCnu1CVGY+HKyHTClx3pK/vSZg+olIOA8b5+lntg5w0Pi1xk6DEzKNP3LL2+Z5N00fYYiEBEIKJQTVDVAuBUAZywpBM0KZblBaqKUzpBUoJKAbRAYVZAXAAN5JXIGbBM5GzIK5AzkdcVZkTOa3kMBMYoD4qUkPa+iqshrWCpJlZm4OrkKPdPg9zah7woM+QGxn8Cej+YOEazY1o2MGYDo192eHrMJcNzMkoK6bd3QCtSUqRFsSwLliVhWU44nRaklPDy+oJlOeHl5YRlWcrxZcHLywtSSkhLwulUjr++JiynhNfXBctJkZJgSep4I9az4T//WfGv//MH/vtfb/g///sP/P6vN/z37z/w448z3t7eYDkh5wzRXKROIogVKgSRYFxRa0CMCtIAIWjd8TArwaOZgaSfJUqisSgPdsdvWOxPDf1zYkL2bxHZgrpd+u+FvcoxjfeR8ngRba/RFoBoK3QTP1UXQBcQpyUhpYTTqYBXk+LlZcHr6wuWU8JpOeH0csLLy8sIfH+ciEBTkSHrSuQsOIuBRogWoIkK/uv//oZ//I+E/+d/fcMff5zx++9/4I9/r/jP7z/w9nbGup6xrivMVhgzzFbQMowZOSssZxgNZoKcS9lpYXxnYJbPIOfytxvNzyCEmbkcAujHixvIlpm/lkx5AvqmWE8GpTFcijQgQ2QL4AnkdUFIZHHHv6pLDlGoKkSA0+mEtCxIWgG9QFOCasLplKDaX5MUWAZWt+DIjGQCFUCTIilwFvdCEgcgkBm0AqICsgL0798WvLwofvuH4O0t4XxOWNcV67ri7ccPrGfg7DJGzoYVBrhu33yWnIDnZyXxeFdVYaQzt7rej8CGV/h1T3uo8ZBfX4336YPCjbQYWFlaaFg9T0aRQQDqLkP9Ghg4JpYQq8A8YyypfHEiCWaEeiAnCqyZSCSwFO0r4gDwHSZCQzIBLYOLlMeJYVkU2TVwWVB5+Luy1eCQMCuaOGe6RCDW1ZmU5b1V6UAvkaYRRsIMw/3qe6sygsMiYkc3B7u7l43U12hPEvS3L05+IiVyny6HxKow3QU+K6zZv6GBmeoiqNmKsPOJVgBevkiBZUISYNlfkwXI2fXqsiSsMJAGQQFbYoLRkLJBVZDziqQaZNP4HrOzcmXqbOXSyovi/GY4r2dYzsiZeHvLWNeMvBrWTNBKwGm51FDTyjFz0JJeouoSggyq2KpuZgd7ZWM5Cr/751kW9RPQN7MzZ5NuyEBxOr796JvNWp+TQ6w/fF1NL+ZC3apwxiz/RIq+lGCbCdjAfEaGJsCYoZkwdVdCy3PNMUADmZUFQQCWDZlF/+bVNXE2vJ1z0ciWsZ7pWpywXFjdnKXJ+Hth7xIgSgcyOeRW6oYBNlCzfR7vKCD/5YHjXSRWjtOn424SEelBXStb6L/3J5VuTHl9MjExuSfxLLO9h5wLkAVADnXLUgGDwHBmSElANU+4WCtXVtVRi8IBDUM2K64EicwMy+bBmmE9F6DmNSOb237ZpUk2WGXq3B2MumTJ/oEao36ftPyO1u7gHo298hl9LufjDiWHdGtPJl4YdHSXFYPb6oHiiCeWxeDulqmzmAEGg4iWCgshMgyaErAaQIPAsCIj6QKi1DNrEqgWCaAJUCFESxF+CTzZ3mf9Y8wMFIC5MHVh5QySWHNxNXIuPjWNOK8F7Dmb+9eGnLO7E+y2XHMsZhAzgLazcmTk8X7jYzjXg3yS3Vl3vwUrMnJlDKk6O4CefkViUZrryXgqbr40AWoBhkhl6SA1tBcIJRJIztI0SF6RlpI1lLxCNUHEoJqc5euCkrabu4DPPNgDcl79MsOsBIVmhZmL7DCsa3bgmrNyzUJ2CdJBK34WkVFWyCg5RtbmBPj9U+gzKPxTvLzLRUbN9YhMXj1VmVieQT9LSUlDqkMBqNJZThzU6kASD4gySHG9DaR0gtnqGcaSDCnMXB4rVXtURgR8McA95FLLYVb0cskeOqBz0dHZCkOTFfTlNgGabKmLJNJm85eb1OGEzy2ot9Lk8ka2J6D/VLaer7N5sgNbt/scpXN7sGjZXQHpm1NVC5gL4wrIFaoF/DkTSzohCzwQVNfetV5iDcVNo8Ng7O4Gaci2tutmVWJUre0LgOV9AMVNKY+zC6w7/41795tBzYHdD1n7CehbkNptip7JY2DIfiyeYiuYRyBvuxZJ2D1Sn0vQ2TNbLsWiUiuMcynE1wRyRdIaCGZQAVXBygwRwhRQsZZOr5GhzKBhDd6sZP9A0NmZsJINtKKTq+tRwF+YuQSVdIa3Y4BG9yKAlUO+e7tTcHM743N8nrT48vmR7HUER8XiwZFokrRKh3ZsY/xNGz05leMYDOp2HHt2UVORBbns+VN4Cp0GpYAiMAoSFWbrwMytmMi3UDU5hA7knggxlzElKKysW1m7pqjNcpMoJcOYC2s7O9fL/WCQ+5/1xMAjcHHAys8tWD8pLdgSIh2qIxvv6b0SO/YVIDsb9tkCRw6MVFPSSq9eM3MHRFAsYykypBW+pQJmYbHtmrsxl6yWwKwwLEBb/XAF7bm8lgPcrASLFdTZ1rAIMFyPGn38LMbdLXvgJvfBugH8J/u53zYGCF0kGAO9GtiZ13LIsPNofLQ/jvQ9fP3UX5lXRP30X1gbzBBJJZHSAr9SFEQDNAkMGWIlda41hWzsiZ9oHTYAWQjaaoLl3OVGY2y069WfRnNI1oHlByfiknwYiAJTqekeLT87J/2ZKN69PkqNmDxhawewER3SrSzQsGmh0jWMg3oFpBQkNXFiRR6UXSsK5gyVVHZvS5EvqNV9XsFXAV1ZmrVQn4WJWxswMkgKa/8KcItXDaLcDvOiJmup7ghGbupBOUiuvd+37P0sH/0L+HhUvjKBvPn9NVhE722xaYbS2JhDxyAGb9ZhWOQFBVR1Oy+jVqmqCEwEwgyvQgVEvS5KvUWBuhzqBUpjdtE3yDaGzU1Lw4O96FV3gEfdXcBstMGmGwM5hKQOD7XwkcZ+Avqv0M8ywhrudozVGdK2JzWtis7ihbWkAxhjsqMGh2X7EwuYpRTGq9buROXSvKGiABArylyEyFKPa+unIZtdMtz60ejyw1rBUmHhGih2eZHd3ciD1NiXHBIO2SFW+dyC9TfrjS5yMabCOwWRMfhDyKDIbiTf2g/W313khtL4ZuUVUGsDjkrf40dPvAhKwb67y55MIfa2NjUAsXvHPQ29lRK2y87RS7ae/btow92aHOET0H+5mddsPE5yY/LwpGvUCtq9PYrRl43VefWxUPWyUW0WWtk4wCZHqn/dnsMqK2toVF5bdcXTQvBUarknMMiOHizmlpmst5lX5iHachcGDEX77lbr9Anov5SlK9OGfdwyttaqAB6CH9n7iuKm2cCZAgi0uyZhB0EvQc39iSW5Bkbb5tU8bBkTKDHYLO9AHazWJEe77gmTzsD99xHICMkh/mRQ95Qcvw7U9dqgrWOoN3q+vaA/LoLeLLw9c+iaRXLIHPbX9xSh9q77NaFTn6TtZKkW4qTne+9nQ+sT7cqpSIu1NT5H7abk2rltiwp/dWffa0HdY07zu/Najqmb8VBAvddCVobySJn9WJn7RGNcMO1B5s1mwo4PT28LrfjUnlLvSR8/Ijr5NHEXVLXvcmDruj/Qmq4mbbR16Ityt17jccH7QIAm9hqozMCWGHztgjx6InN7LA1iXSeHgEOOkaEeZN/XjZ54jucBd0g42Gu9AF8DuCtQrdU4V5CPZw20kRfc8Lc8Af3JQ0Nsm59s7adBS0e3Y2cWCcGhOU2XvWNrMIYK/aKXJ2nTdDf6jJS28GTQ0dbsQuvviSxArjKiBYVjt/0puo1K+qG08ReRHHtsfQTsiYWFEwAQWF32mZelLqPUe7DXT0+FUT1zwdD1CIidjLqtxtblpaV3mFFbgdF7P5PAXF4hlOCOXIOtPAF9n6C+DOyhxcH2Lt1zZpQaEtrWog3pEde3IiFwFE/CRCYV865MdbDP9K5q131nZAzJlpCeb8yNXYnFQzQ/NfQDgFoO7hU7d7AP8tlFdw3kvGrai5gEfWNtLwNVjFMDezecAvC917AQDHYWrUKpbT6gNotw7K8xDzayAzaWLwNsfaw/Z29u9j5LEXt3nWl7Z4IW5/LP8f7cdP2X0PhFxunH9P7Robl6cd10eJ2QtMe8033M+Al+fsbME9CfGNTXvtSZkXvnfg71wjITd7Dael1I3YiABt4aLGoDfwO3xSeT/hgf6knK0M2ovTdilEFDH+uvBd4vAugjgbztVnrIxoylpjGIm2YPNlB7nw2XFg2MjbURWtfG7v/dAGRgfA4zw2fg1oUjOO6BLV8W2A82Glmu6OojyMukPbWP227OhPTu+dH3Hlr/9tZkjCn1liofulRvdDVhO1KigphTABnByyBH9hrf3mc3/idDvxP4srsAdEK9To7Cnm4NbkWUEE0Xc2Thxsozk6prbsHRJNrd98z5b5EvKzsejKGvSQ254HhE9opBXaXqaPpJ31neXAntrCteYUdMLCwH07W5cTjAORCsEwbyqPGrAzJIIsPlYT+Py9IPyNDyji9uT4fOg+oV2zFve5aZNmCj1VzUik6Zgrp6TNvxrQui02LQ7ev7ZC3ZjHK+RUs/Jnsvjwnko+ty5b6ylRGNl8cG3xIYVoZ0OjfygLHdaXBCNuMD4yJst0sZJESEhVNZmBjnhm9ZfRtPPLYXvTwmmHFDpH/JJcAUrM0suO0eJAfTtiS+FqcRBIx13WPI2BcQJu+bk6yQMNhzKoTalRmPHSAuj7dGb2FfOfBC9uQEd/0SDo9iuD4z+M7zcZsA4iBd8u6i2gMmdxYnY83I5lTw2KDWxwLwkb6dj8nkdExe8MFHxCOGn2aNxDEPiLo4Xo+6POhrxoKmoK8F8RjaXMNxOOj0GXDv+NPluD9wD4V0lxMQnIBLYGikODsn3UUe2bvL3pAxnJfBJsvn728Yc9ytvuF9Tk0lm45mmE1Imc4Umw47t7se8gT03+/OXQryeKST9YokwVRKOt9Ht88VA7iwDatOBwhT5zdCp+w0kUEsSNziFdsNiEzuy7zg5r+X7wPxk6H/LuBekho3DKPfkRrXLS0Jm6h0WihsmbsaLJIatlsBw5AiADAZcno9ua79WOiwOu6wcQ+6lZEqfHhcGOooO9pf3vEhPwbA71RyyA0AF9w6+/v4mHb4cS7Sr3Aw/xh1yCxuwYLmU3MnqOstyzQEjr6nUI4YNp4BZJphHtn9mhe/t43rPmXH5wU0bw0C5UpAKDeCexvo9e9YWs8M2SQ4eqknN5sMytB7oey4DltWpBz8fVEnQ0I9iYTMor+GHM3ovum0h+fw+l/qaMiVY3ojqHfKSIfvf87IxeZNe1tepG2ylclh3l9MoSWClDktwxyNwYbrWh0+XWu07rhrO34IvHfI0st9AvkSu94gPSjveI2xsc14Vh6GtwwMKOy10rvvT7D/PiigJJ+wVes3gN5tafbQa31HXIxEHwUrN5wCLwCb9+V6fE5A872gPnIytrtHrksWXGB33dQml/erm4XDQXbM2lo2Tg1DmCie7uaUEUTdACDrJI0ieGNa/IMgvmO2Xu4PzLjAzHv680hy6BUgz++pBmkaJILsP54xqDz6O8c+1Z3xHYyczhKt2WP1nhH8aw19hKMvjWmOx17GEAcB587xOwC1fm4wyxVpcfCPe2BWDLtFZuaFtCBuK0/majfdWSQMW7gUm0Y4rU5aOyDja3C+T7QLdXIvIsvrjtwI96Vc+Txvue0dMeWToS+B+UgrX2DtDZj1ciBJ2ZcEh4/hlpk5FgfF4qJdULbAca6s6215UX1ncY0cF6dEhyWPu9YpGw/7OrM+jvzQzw3mW9haR5bbteu2Oz/G++tBUKnb98mRLVk7Ola93HbBTrZaCEbbPsIG7L4oSBnlhtdjkLrrwvSNuf1vmLdvHVuZf4Wt+tUZ+qYP5pI9x/Bl9ts5dcvve/xCx9LpeTpP6fZs0HaoyGRoSCgSIrbp8QhGAaz6xf4UjHPppuaTcT8jcnA8MM5u3FnEPGTVrUctU7fWzfyXG6OZJ6CvMvM17zjuv5PwBfWUdfSLOQG8A5c7IJCpjVdfDOW0zh0XJQaFGqcPtZrpto0rsqjMQwl6xV0p7s9eF6IhiIzyIrTpFRywMacgcd4QwIOvZVOlPXx/Ik9AOzNdi7lv3T4ku+wcWZXvyhzyOFmCIDk4aXaEnhrtedL0vnzGYXjNoZ1u29EdHY2tH94aVw+gnj6LXZ0rkxtiF0Tx1uLj0ff4iUCtvwrM27PhXk3yheCMshPB69bpGL6cA+3YnosH9cN7iyFdqDWe9De1Ow71d3+vpGzdFHLjaJCyv7AOqwgvZUmvxSrXSORp2+2CmZhO5bjFg76U6TvKFOoFH9jlg8yncF7X8MJxP0tsEhML+mcWrK1EOSVHGB0Obgr0awDIuQCK02aBoTnOESH8hMtxhaCett1NQeEOEI9AzAsMX3WkxHrnmqioGhUjM7aFlBB3/4kHg5drNWTyiHUCeKjRaPrbpilfoQGO5AHIlHnAc0yyHEgI7lXYzQvvUpHTc5LsO1byLTUYB9lBXrekCgF72CgYGqBjqL9IIGzoFz2yojZy5+6ZgOXjbViZHQrt7Cp7dsSsmYMulxzS4hGOMe09bQ7YY+EW3F5i6/vaHLB8KvY9DPpwIDNufY4KZh+9JiiD5mVuQQCfcVJfM7U+cmxbo9QHCgU93PpoYDrty0ERUu0r7Q6Hj5AYwNWeIw9uSY8izSvzwmQCWmhHZiEIvJbyvsa810H9WYLDTyY5ZPJfr7H2nibeZ2URgQqgi2BJCk3ajneQlZe22njRwd3AbAWkZv16meqawmbuWMTETSDL8JocZpDveMWC8p5TgibgdBIsi0KXxYc1ryDXMAO8PKcZYFTQ6vi37nVvOt8NFuT8nu0uWPmXAPp9gcNt/TKuRd/iQ+JFBSkplpNiWRLSIkgqEJ8GKzFOczehzNPWxsqgA5laAGMCmsAoUCvH6lyV0io39tQIqQpui+1jNlFoEAU0CU6nhJfXhG/fE779plgWQ0pl0CYtY82CnAU5l4H2OQOWV+QsWFdDhsEoEAuNJrnfaOE6wdzHz/I5GPknH88DVlYgqSItimVRnE6K00uCpsLWojHzJ85oNXudCuPVszoVRgGztIH12QNDa8aEW4/qU62Y+mi2Nnqbo96P171YX109mGRgUQgyVIDXV8XyAqSUkPMKUvDjB0FTrKtgXQXns2FdXaOLAtnH0Nm+ZOdN8uI9+vopOW7T0O+oiCkaGUiLNCC/fFuwJEFa1G/XzdmDGvrLSWHg0vdZIbqUx1CwLEuZt5kWwBSQhPVM5BVYV/XHFTmyLCeclgWnlwWvrwmv3xNev53w8qr49m3B67eE04vg9JJwOgGagJQIVfjgzjJ75Y8/3pCz4ff/7z9Y14w//njD2483/PFHGe1mOeG8Kt5+EOua8fZGnN8E5zfg7WywTGRDkyGIE8B44IZcSKh8aYZ+n7txgz7mPjuoCl6/JagK0pKQVPDttwUpKVT9lC6jVVWPV90hKlhSgqhAVaGpBG/fvn3z51EsKRUm1QSaQPWEdS0sbBlQPflzJ4gI0rJgSYq0JCyLIiWBJPHfFepkalbcC8sGCgoLG2GWcT6vOJ9XrGfifF6R1+x/c3FLRAjRBGCBKqG6ICV4UJmQsyFlQbbsEwR6dWAc5LkvR6QNHm0JUj5djpvZWSacVxCqKkS1yAUpmlhEsCwFOJoESSsYu5sRv4DaL8PMXPP2kciiAhiQYRATUAHLAk2K9WywldBErEKklJCSIaWE5SR4fU2ACFJayntIitOyQFSxpMX1enkcqrb3eeB98msJ5M65gNWywczwdl5h2bCuxHoulznDf/f7GmFmWNcaHBZuTanMcREhsgDKVBaMMswQ70NFZdr53cqfpBQqyc54jksC5ssAWoZfpEX+3Vor/1TV//kx1X476jF3YaVE7BJ2gORsoZez924OCTtpbkLR0NLeD5EtY9Gig1fNZUEZsCwKWu7BITOWpQSeIgYRRQKwrhlpEaxYIZYgAndIqpjNrs+rdmcBcZ3pnbMHfIZsxPltRc4FtOtajuXVnQzLIFikhf/NtXu1aOlejersqfTYAOILgKP8isU2jaGn6rtZj/OLMPSRP9lB3K018StaL7UHcFozfNO/Jodr+SXDrGu330RT6BuXvVLMnY5qokplTSsAB5EJKBJyJnLOKHcvt0vOyKn41et6xmlZgDdiWYC0EKoJ6bz6fO9cLmUdLMk2kM08SWJsbWwsr4V5c0a2Au71XC7bPyt/Z84F1DmXADCbwapejv3xfPEa2UbSqUoAcgVzXeD++bVL9Mun5OgIlxnoMg7tafvs6qBJ+tDgkDEWiHusFmRD+NArQ0HAbO2LEh8iT9KHzQuyEEkTzK+ba/I1G8S8fErUAS1IqTBuzhm0FaolGIQY8gosJyApsErVtxIqN51BNWYP+xhkWhmBbGaFec2wrsVvXp2BLRPrWixCswwzNpCTfnvOsFw7PJXPqg+79c9OutDrQBUfPBrHx+140vxMYuMXAlqOIsdpzLAgFLAbQ44iaF0VWK5zeejNYGQS4HSWLM9TTAyfw+3sU07HbBJGUMBcArsCTBWDCZrUSKr+uAzR4lurKkwysgEihqTJg7cEEfV/FUU2nKcJFinjoC6sTdAKOIu0MFhG0crZkNeyOIz098Xmo1tmud60MttZim68j9Nne/BnQXrwhmbpn8XA++VB4fBRhR4WFdQ1KT31DmoMIplNoojoMDo49qdQ6TKkaEhrwU79RxSZYKu14iWzFSktQDZQvLELi26mFqbLsHIfWGFjMagYRBNUDSqp2MKwHgMM/Tk6cBoTsoDKsoHMyEZnXtfU2ZDXchys7JybHi7Zwg5ooP7uS4dBXkyuRQ9U9+j3Uq/pLwpoXmouHzSlqNdNkDDX0W2rUqxjp+9sQh41cdgrRQnHWvBXdXyvqzBjkQ/1hCA1sMyglvnfCdoBrX37ldkZmhaoKEwIVQOTIDNDlc7ObGcM8V0tFcRVcgiAbLnILMtFdtCaBFnXM8xlRQsmnZ3NpVWRJbkB1DwKHIE7Ekk9w5EyBYmyC/yPZ4K/hG3HNgg+fkpETylLHY9G8yRHAUYDggDM1jX58Hyxi9Fcxtmtq9YKFwClFPnUNdLrfFZAF093F1eDBqS0wGyFaoJqCdQ0EyqKFRlJF3dkpDzOHZbOlGx/Mz21t64rQCsyhOYuR4bZ6jKipL1Zg0Ajsq2+CMpzmzGAUiarEKEoC8GXlote89OHPvhQKoBiXGjGwQGpX0TxjtWjevPovB8rgAtNVsKXJlIvfdyaN2npNp1H/FJvE3cmvCGXltcUqayYIVIkSk0tl6o5g6bivAjEmRrFZbHcJFC1EKP90OUG/XNY23Ss7KAtKe+SwGmTtkxKAGh5St+7lAlCOTpA5B7DHkmQWHz1nqq8LwHoCxNe2W/joKnrqdBc86oDv4KMvx2nAAAgAElEQVQrbpztIK9asAZjlcnB+EX6Xm/RUl9WA0uXKJnFRVAP6koCJANiSFpz7UVXq6agr9WTONm1dZc4FdR9wdV67Fr7kRtTm63Fm7bVdXJ2ieOetbsc5llFsDA1/fYZyEeA7do6nMm4dybldP3z+ByfRHL0U2FlxMjeImOp5d7w3/qhVsCQuWnUvS+tbVINroMVDdETCNLnsIhvCVRV2LoipROEwMoMrc0VpZ5dEkRY7iO5XPdmi1pXk+zVIku3yFz3krklTszrSugLjNXNMGsOCA3+uJ5QKmeGMeCLki5mCQd7jr2S47NKjE8EaA6yYwvqbt2NI7OLR2rhfsO0qvh45NDPuS6SWjLaR0L0TCJamSWCF0sAkjNUBdnOUE3IfIPoUhaEKnJeobq0mhL48xa7roJcYLXrf8V1tG/a31ADwHOTHpXNzdb2OVUgdzC7U1KZuwaC7e/fEu92oXPoeT67HJsNznwC+iasV6+4bomqzNw0tWx1X5UJUbb0Bi5bvddqEyRWlfV6hVj7XoCytgUiohA6yGsyxlaoJM8kKjLNky2E1q1foh4M+vvLseDfgmdsTT4R0l0P1+9Wky/V1Wi3hwQNR9Rt2ZmRnrvcCfvvR3XBXVJ6AvogOJynscYgr4Av1H9xLG0UiO/95BSicJAXdFnRMojhC632HWeKanarubVX9yYW8GnTy1oyjQSoxcmgSAE3CoMbzGVHlzM27AxBs9jgvrQxuy/tUsL1dWFouG5GA3lLmgTnJDIFh+IjDh7eCOZpwCg+Nzt/KobuWVgOzQabdq5AG6RnZ28LA3zaeJ5QhdT0oYSB9O310HZCk2EUsshmB56iB6HifjN5hsjiBVTe1ibnoL2L5DDL7nwAgHq9yLYRGZtDY23jrpnbd/4uLJ8L9BzUo6tRt13FYiNrlzJDNLL1nmbmtir6advd4kMHmSAi24ARMhQg9dHZ3RUZ9qSGkcU1yBMOiebmZGzmDvb0pd9dPXNegrzeL7q3Iii2IlppqIhCvExVkIF6zBde7FvUgTYGbAwBXq1rKbUeaAmXmvpmdDNamps+Om5i2noGwNb3n7Ufb/junoC+Yud1O0tHyIeeEnT0tr4UIkPmSwKQh899F/AOTpntqr6LWtrObnOpsTQbUaX0zNDQGqGl6h08xdJjS6g04SN92CZD4U/z35vlJl7rUSvzet0zwhavmD4nbpASB9p4j4QbkTxdjg961FPWZVDNLZgZr4/D43dS4Oy1FByrobx5zKwdrVO231dieSUIQ4ZicfeFrv1XqL4U2ErZiSKS/D3UINZBUkEn3bYrejr73yBlcywxBo0IzM0w0D4ydAgG+xnAjpl2kBhbi4PPRjM/wdThwxSRjUSJO/AHBq/ZQ3DcA9PLpINVF0/zAbzR725bw0sTcjIPGhgsm1oLa9OZOw3ZTbQUfnxeZ2H0qwwNb4gK5NwCZbYWCzbIkFgLXnecc9eC47bX6C5YeUGOfE6n4xO3ApsruWTD1u0LaMQcZl2zdz1iQL0MG0MZ+h92HSKjou9fqIzji40GhfbdHU1Hx6Hy5vl0+nzBoOc3/TvowW0M4th6VvcMYe7GBPtMWzjAew8O9lhh0MzEWDt6obcgL4D8ydB/Ripmj62dKeei9CFfwWnaap86NWZuIhCAsf13fU5DmUbloJbUC37KNtdeM9KiPx9SLz7HhSWpU1Pesa+zMQdZIEELe9Vc9aeD54yos0NNCEclccDGe3WivAHMTx/6gxCWfZ9vuGm/L/IgOaYd+mGQWvO5hzsMD4q7pLsEaRtuW0mewbxuusSHp7pfBIJlaJkljYGlVRJuh2vmoJG76zDUXESWrgt8ADU30uJYPx/p5Gfq+6/g5ingGwHcM30YgjjuPZJTIkdk25EiJmmEmx3Ow6ZRM7flxmGbZaEYKKkxfNse1gr9S0ocQ/Vh3zLG4HiwNsSBtQbpHEpCR/eCWx9yp95oJ2i8CuanD/0XBIxsp/Y4IiIq5kGCd99jj8p3PPG59W0c/1Bba1WdHnV5cZrpW7xq8JrF+nhlKVu2ZK/IqgJRfGfNVHvB6otjHQYEXS7CrwFvlBS3uBb3A+Y7ArRc/7wHUMdQZys3+lz6ozLWnTkkUyazvJz0sSUtFmNj83ZYEFL3cRAQw7iMvToL37ENTEX50mqhe8kpmi7vk7BCXfgQZPMn6jKeQeGf703jgkYWDmC5xMYUOVwyQwAlMsyIkmlCa7eyg4sRQkoLLkw/VqxFGcTSXowWbLi2sybKjA7kZtVtoCeTs3ErPO9PR9+R5Djqa8xYazc4Glso70x3jZAKW/ol+q7kMAievkmXDaCdOUtyRpr2RbAFBylQpdLot4TMHlo39daj2vtFb3eaxL9nWyPK2fYM+v42SfHsPvoLIV9bgUbtPAZyh1KG2NGUoeG69Abi0RVhS2WXdrtz1d9msL1YGdMtMhUl9R3alJDObv+3YdMqQw/nLkX2/haGFWvX7bo7BTPw2WZ9/wlOCP+UL2Gvh/M0hWozLqOr8tgTrm9A5fa+oTquZgfZhgPF4UN1x7b22o3oFe+MFeO8GHcngclNn+k9/Sz3B1p5l8YeS0Uxba3aG5zDwwBx0NCIvZZ7QkPqrMK2saA6xDZMru0pdpmE01RINMwqXMtx64VZQ21zq2u6vCfwOivf789DSY7Ruzia8oRN5kwEF1hrO5ukpcWDayFDMBqYeMy0N2DHYUQcUt8WFC6Hv2eoywiOxrYD0tEivbVBDJ+A/jyMzQNDjjtSQi74tui1IFF5C7tORmXkmNBgn47VtPQMKAk12/G2sS1YY+g4lJQCIgPTfO6x1nm3GmXaXLwH8nkx3N/PA2no7en0up7mVabnBK+ubXk8STb0kOt1QfSGjOz9NDgGaCEkDOWg6EVHxIU+c7JpL3AknR75564zhdevc7DEtqdceRfYCZ2kjEwzy8dES9Hr1nmDsZKP/nzNVd7AetetACZnA0GTzw/jTsj8OXvSPRn6XWzbey5/XCvGFIgOoBthw2l3tYTJQ+h7/TDuNB/fYWjZVYv56+yX1nsuhpBz4Hvtc+KxdfnU0J/V7eBuQBgqLXC5k+bekEofG4Hc3Axsep0OomH3uYY2C8HtGO8fWwroAXDjaGU7kFm8MumKD8XUd8zQt2jjg71yNU2Mo7pgYOvXyubkzRmwnOBRm7GHkk/4Tu6+ITAI7WZi9677/WYfxxxqOEbSDbPMESsJ97OrH/tcnwz9axmbl++zz2O1FZhNWcX4eB0Cx/hsssnQFVDXvYq1xhlMHV4MIqTVeVt4Z9YZmkH6BPkikDDSWSYHBAdnrhtcDXkC+u/HMY8OyhVm2vGo4yDO8I3K4XjmCHJiHNcw+hYdYtosv60fU0GpGLdHzRKmR5/jMJ8+51sGhgY+NISe9wXq5a7Ae/H4tU+eBwC+RkWyOWGPz+UuRkt9s7U7kAHoUxcn6u6SG84WLdPYa7OLfp93tdTCqLiA5ECbXwP1jr13R6B+MB8aF7Xz9gu7VGm2DwZuPBMZZpRslTmbQ9Gm1BLTvkVMurwWIg37rYrt12o+dHq0hrnishMLvBeRcpfS+n4Yevfs+B5Pej4mF4B88NgmA3QIuGQo0B8BfhSWdr09124HGTzJnr6AShlpGQWWxziAE5BFds5M7w0U74ep74uh5SNROd/5+0eYjb1SjrLxk3dfh2ijkMZtAV73zOmrGTKTIzOzSRwZ5Mn+QpULevr+f+4vKLzK1Ne08qXHTuDjQanodIzUSaLELVrENHDYC5tCQ8mwOaDtuqlWXNudHh0MhAkAYU637EgNepAo3LEiHy9IvE8N/aEPlBc09Z5elAsvemGTQGBJhus9CVJBFn3jelzCZdTnMxB1W/M8PO/8fnSj8w8X8ZOhP3tQeIujwS14KAdfvgam1C3YBrbWqC8moF97f+5Xb14re5szC++xjt7IgZHrYzJiw8nLFHv/WvpBXY5r38bOqZbXGKsy3Y6+bbcl/0h1eu40SZlLZ4G4U8WdC8YFlgLANQA9fp2z46HTmUduONPcp65eHguocsWGu2ZJ6WWZwZ2AkRFU3NGv7owwbQPNuDWKOnXOkdYYcih0kjU8N0L73+TsHeo7Qs+9vuhy0+D7i/e+q/AetB76SCPufHmUG8AvjRU5aGIZWbECjT3QK/axhnNDeDzZXIoGamf58nANjkcoWPLXaLcN6XAN7+vIl76k/29k6U+K+Qfc9R2NNNk4vK1uuU0Eql+bbIOsAOQIENbd3XtB3iBBXCJMQSGduUujcp12pvdFFkcmd9liKBXUCgz7FIE2FT32J2H0oyP7j40tgX0HngdmnzwZ+q9xOuYAi4dhH4dmsmNnRpmAEX/Slu0GDa3bRYBpEcSgLlbDMY23h0zi2LPD359okC7o9x80c1gU1K2Dsvl0dIouZDfSuJfmBsu9M/L1D1sG+2z/jvPxEIANm2tniaETg6MHjkFGEAJYnbDlg42oUzHSjpdO36/oPnKspuvthL3raZ0M2oZ69iKlbdBpgaVn3cybBN1TQ/8qCr+YPJALulEPQI4dfSoTmFPQ3Ay/a9PJ22SJjgHi8BWlzetwE2TKQfCK/njeqpVv0dZPQP/5kJWPgnsOBo/ALROYQzKEQTuzBmqVtUtam5wWBncydQy3celgjvIjFDjF1yWD1GiBYtTvOLAIJ3vwQdLeDxcU7lt3ck14Y9uzY/7Ct3Ze2eAqjZFlw7aylR8bv3tu2Rt5JtZWa9hsW3fB1LS4BulQ2/XawSL19xsKmvYX/3NP4a+BL2+xmC6B/HKQtHsf7jge7WUuWWUxmDy6HeMIOuGW0etYZp9ki9ZzutSFCAWUWBVooxsie9amPRn6PvTzUcC3B5TZKcBUGxFst9ANtAyi77MGIckBVrUvuxRojgTCrphq4/W2vf1t1SCvAF2RIIlQreOY6yLKvYce0OaDt86lgwU5beoV7Ow7vMdw8M4BfZmdb9mVst9s8VAa1NtFSjcNUagoRBWqCtVULiWVSbOyNL+bFNAAo4K5TK+o//oYZPFKiz7uuZ4wFGXAfVoEywlYFiAlQ1rQpQgVBgVtRbb6etJew6yCXYdJWdslP3bZezL0p9XW+s6ofQK8NycXEWhKWNKCZVmQloRlSUhJoSlBZYFq8lkpCjOB5QLYvCrW1ZAzkFcgZyKvBWytwjOUeogSCsXyApxOgtdvipdX4PVFsLwAohkiZehnmf2dwJyw5oT1TGTL5TUykLPBjAXYDAuNU0sxXmj+/gT038XOwH5d8y31zDvMvsn8OZA1IanidEp4/bbg5XXB6+sJp5cTTqcC5JQUKS3F5XAwWxasq+DtR8b5zXD+Aby9Gd7OdVtV2SkoNG9E4/DKBBMhVJwWwffvin/+1wkvr4KXV+9GqoTlFcZULk1xPhPn8wvO5zPySqwZWM+GbEBerSwyP2t0UBeJQ5kgzIOKD3kC+i9RyHuQ5kXpsd0AIDULhy4rym/agKwpYVkWfP9+wvffTvj+2yteXxe8vJ5wWhYsL4Wxl6XcT5NA1a+fElIqksQy8PbD8O//XvGv/33Gv/51xn/+e8UfPwy2ln2EKSnSi+Db94Tf/iH45z9O+PZbwsuL4Pff35BO3lFJEvK6wkxAS1hXQc6CZSVOZ+L8RrydDScTrGdiXYHzmssic8mT8wpAkTP7HsbYJ0+mNBWfDP03IVomzpXheA3aRLTt9histXo7qi5OSGlBUkFaEk6nE769Lnj5dsLrtxNeXk5YTgteXk5ISwFsUsXLy+LgdxmiirSUS4EgCQAkkIAmxetvJ/z494p//2fF+oPIRhhLuLm8EEkL2N7OpTnO9+8LJAE5n2GmKE8obcZ4WrSwb6010YSc/QyzKJZ8wvlcmPl8JjQpaLUkQH3Esrcsi6OVN3Mhn4D+S9HdToHuOKh4AFcvtbCtND1cG7MIRFI7rlokQwnypIA6KZZF8fq64HRKSIsUkKbuPefVIItiXYnEopNTIpZFYEZoMg8aFaLAy7eE5aR4fQXWfyz454+1SIU3w3q2RoU1EHw5CTSVINCMfR4iy/PnXMqwiswo92ELPquGN5iVsRZ5rbvKpU2nBccmkOQzKPz74SxbeKuMHK3+5YsIRJ2btQBfRJHcA0vqTJsWqLPrsiz90oFdX9OyYfURxaoJ60qQGWaClFCYj4RlQ0oCkYyUXFIkxbIIXl/Lk63rCevZsK7E248Ms4z1XPR0Wpw/tU/BsuxsbvTfDZYNORfAFllRXrsAPteOvuVYWwy5tRojp3bVwN0Be7lvMMvu9ThmrenBOinWCNHecUgErncVogJVgS7AkqodB6iGvdksjWTM2La+ig+tT1qmvBYWLKA9nwuYc87QJMgm0JyRUnndZSny5iUBp5cEGrF+E6yrYl2z226lAynNkM0AK6PicrYA3OKa0IDVgW2ZDnCfUJ4NOWcHM4aRywxIJnm3ucKHq4eu6Qp6RRq9/WzrISdFH1b7NrEwlohC3K+VTJjU1rhlBLHQQMtYqDAhNGfoUoDNVBIddTgQQdAM5qDNmUXCZIXoiiUtyJlIQtgCpMUXmZR+emlJEI9T81rY1HIBKhzI67q6JVdAX9m5yQpn5cLGztLmUqT60mRbgBsmvjYa/Anov86+k8Pqc47zAhs7w4+zsZXkAtykhAmwrgZVZ3A1pKSgFEmhCqgACeU+AGFWjifVsgi0PL9agki/X1LBqmf3sgWSiXSWlspO6psSpCyEwvb09+redabbgQZby9+QcwGxsTwmV+b2BVtBXkBs7XkxMfR2BuIT0J8C6DKU+/Yvq45SUzdYCZSgiICiTna14tEm732RSvSvSWBmUBFoUhjNnQxzS87AxZAIUAUmCsgKFfXFIcgizr7E6g6KAEXqqOAsZZd2qcuogC7Mm1fD+bwiW8Z6LtJhXQ1rXstr1/s5c69rhuXc5Qk7O2MjNdDOLnKnAeHDAvoSk7etWeyDfqQlE6zVXYgYzFCAamvJAlrR0yZAohTLzJMRJoa0LMCaQRWoWgGuCEzQgF3tQ1UWe3Bl26+q3p5AhL4ZoLApWYO+jHzOyGaurwvTWs5FbljPBlZ2jwwNojFzZH7GWYe87+4cyyODWKatc6VVM0M32p5AKDoyjnqQYstZ0NIq4fkMoMF09cBRYecVKQlUM9Qr4UoavFbJEaravHFNcACrg9961yRhmwlOmoOya+LzuVzPeXWJEa93sDOPEqOzcum6VPXzo/wsjwTeOUQsgWE/nUYAD+1wWUSxWZEOOWeopnIal8LW1eIq9ps3bCEhZqBay82QGSooIBZB0u6HS9mJ5S+vkPUM1QQyt7OEiISuYLlNy7IW0BlyXl0zF9ci2wqSWM8rjJWB2bJ/TWrkCGrrm3A5jmbmdujhE9B/r6/hrWpdPgwSg5GNCxu2oT5Eie6EhYm1gFYkATT3qg0qCtNi4pZMIgCuQAJEE4wrEheIOus5oEUVls9QXWBS5gsmXfw9nMvr1Omw1QJ04PeedQ5QZ+kK6ALywsTFjy7aurBxZejc2NksO7DZJEc/bU2Djp4MfQ8s3hm6MXVEPAhahni20ECI5d6PxQjR0sxFPOgDyhB6UYBmpTqugrnJjQSztWQjRWB5Lel3jM12Rer+w55iNsutJt9s7Xagg7kkcQpg17y6y9F1NVkkS2Ftr5Mmmi6vlDAGhsBzx8qnDgY9ASLxy+pyQ5AKa7WdH0VPF02rEMmgsADdpEhwrXM0CdUFUlULU+mwryysnomUaqrdgSwKFXOZJMPA5WqW1yL90krDHJDFBye6XCgBYHYmZmNosgd+3a5zueHPESVGSE3h3nevPFwrsAKUsaKOrpOl+dK1FgIwmP/utQwa9HVtxSWlNYB50Cb0dLqi6d9ihxUXRIwwBVSKPgayL45SQGR1q5YHgCK9RrDrWAvWmpXJXSEBYjTQtXPJ/nnKu0oMZq/nsMDQbHMSe5wxnsWekuPTB4k9OOzb9GLgUzsNecdOK+5EY2WIg5b+z1m87prSrlxUC2uLJiQosljvCiKeTaxnhlb1t/f+rY9XRmDXOkqZRM5v7Zh5Wrxc9jR5BzPb9e7H827rNb4QoC80bGRPpLSzu9SGiAnS6hesNSuHCjJd/7Jk+wq7F+1sIBJPJWUtJWGR9ASh+fOyNYUptwOoyRvxSVVxZAQ5Ds6MIA7ug9m5ux/0wI9FU1cp0uw5Snc2MLkbD0hmD8jQvSApFi9V2dGckMbW4rIgbtFyT9rYNsFCtGnr3q3IkFvAR6gIMtYyBcv3JKqevKCJrVajbueqmwlkGN0mbbZhATN86H0926weEFqx9SyDiCC2EkA27b02XT1NBcU4yvkJ6PtwNuJA+aizwXETtLB1Ax09PwLInZlFHaS5MLvUzF4q4HQNXwqiFGZvfeMs+ugJFe1gkm2jRKP1Sd4MiZwgR+ZkSQseq6NhNlTTlQm625qNJ0PfkfRgAHFPc6PLCgmMXpl8I1tinzhBzmeILJDaYR/Ft86yQoXlNlkhsnhDmDAXxXU6pT+fiLS9hAgTtSz6xC5JSjE+ByCzATkHB6RKi6Hz4/D822NPQN+Jng6/h4iRXrvRoC9j48Q6Irmwc2o6VmovDJYdKJBcenSIwMQgsnp/jiJHSraxdECqSRuE3TOgDVKj4ysGhjnoX3c42vEM49oAX46x9+pgrHHmULfxZOi7A/XIQK3vhcRO0my6uDbjLLq6NI4pAdtaPi4pjChutxlWByrrjsRyu8sY1uo6SRA/5YvEZjNr29/IqQMoW6CYXUdXiZG7xmYuOrkFfLkFgLE+e4Nebocyz5/VE9B3w9bV9AjSQsJXG5IeYC5BXnVEWDfa0kEWe8kZsrO5NI7PvkO7HlcHfO9oxLpIUIPUqJ/NzyRVejho3SdvJbHgOHl2cni4seeO2pvzydD3aunFjUYyfM8ygI6S+xTZ2oeZ1mqWpSZQWjswtCJ9qQGfOLiFLSPZbLvY3NwDOWngtbZ4zOUGaoDHHrBW0DNkGuftVLObwY/MKXwC+g509eYusZtQafTSQFolQAW2dMZl6P5ZrucAlTouYhyCSe73BTBn9tGPtsC4VXrEhIsN8mJIbU9bUfbbffEpOR4F1NzrFtTmbddKOwetJA8U6e6IlY6f7G1siRWC0u+ZQoiDr8mXOluw9pcTCe/IQv8584VRQVq7ic7lnwyFRt3q60VI3EQWjxgVfkFA7weLM8jrIPjGnxIG87AXEnUw0x/lrO6gL02+Zn+7+soS2Jtty5g1ljXE0k626zaWl4JA1dUt2owsbNNi7SObj+ecP+uhH0iCDDVwLjscuC4nyuyUklzpwK67y63700MO0Nq4tiEoDPueZkeie8k1OIzeMgd2HgYCtPS2fRCcz+6jDxMsXu53xTCevjsDRXpXUPdNBBIcjOaUxEGYw9D7nbNIs92qhnbJ0YqrIlvXx9j4t22Cwcf9eQJ6t690mOc3IKGmrqP86Hqb7T6eOPHG49WxKHdIAb5zDcneYmNwOhi0sAVWjoFgPl4c4BVnY15Y98fST0Bf/AKj9OgZxeIrV5b2rf/zSDVWV8R6BhLisqPLjaZmB/susr+5NddfG1Klho1gHZiZ4w6YB9LJT0D/aVIkDvKpDWE680kbA8G2ySA6JGW0Gxort72DYfNu3yJmU21JlRxo87pjrcZWe++x8zXh8Szwf1DpgQOWnu81lqkOJmAtGa05v6GBpAW5IlPCIwZ6UV50DxqTxz2AeUh1Hw02PmLoZ2LlCwB8zi5uJ4d3JDuQRUPYWJmbuHV+CTeyh6Mj0otMu/3GvcfKLnvfBu77BLk+gfsejY0DkI+EOO6clmnK+Db9ztGIm4AcajgGFrYehM5zUnbfr3yJb+vJ0Fc19JVU+S7QzUcXc5gm4L2QvNqua/CabWfbNBsdkxnY3F1F3NmtzRvmdz/az5OhD5n42JvmVZmwZeC4KDhsgA12XOwAylkH70kHOWBmuSIx8LCM/QT0zUw9poq5OZVLgJvsLoJjl2EPfJz+O1os+8VF/CIS4yk53u1yyFZutKthvvcW8zvPK737aWvdGx+mfaf4qD2mt8FQTxKfPvYQ5jsX7RPQjxXn3QT2WmREr+XoA47bWGWOEkAOJQsHNm+8H2ujGR5LbBMlIf1dpIoXSPHa38J3uBw8XNdPQN+l1NgD9aVBnqOS25Q88dpE2wAsIfbT8UfHMLkpduH5b9HZOzfxc0vwJ6D/BMAPafEoQxDYdlC+Eh4xJj0k1mezuyZH6oiH78ve+bdcY+pnUPhg4OVFVuYA5iNWFwdwDSBlx7G45CfXp9ML2/+uxQLv0V18X7jxZOh7DR73tm7JjjFWNDdr7QaPEHFhk2poFcbG4EF/V+0udgPjXmPg+w8Onwx99cvkBba+nEUktBbHhYL+GXCys1h0YHv6YqnTKsZmOLLrptwmhK/9nU+X406dDbnhwfu1HfsWnaDXV4zbuRhci417wKNgMJwBfMdL18hTByTBhWDwUiB4I5s/XY7PCuL3yoydY9x7QR0CwngiZNW/0Zmg3x77ZbQu/qVNwQb5lJ231DuYQuShWfjrSQ7+1WC+IDc4BoE9iJMA9CgtNBzT6XH1sbr9umpgeHjWEOx7bfz46pcnoD8xkAWXq9COxtPWndU7GpYziI5AXJ+mTg6ImnkH2J7I6UCdzwY8QJ1ckE684cO8LzA/luT4SxmZVxbBQaKFIbM46GrdPmZw1Bj0cPSi6wzDrR24YWPyJ0B4v1JEvyaYr+/q3jI5d5wCGfqHMwJ/KLiPTKzuNdctWRpYWYNP7U3TKZuFFIeDdlbnTyhk3r3UeAyG5scAfLRb8FqJ6NG3Gys3xiq8VI5Rt9xB35bV3Atu8zKtfVgMEteBxAdNLXmqIrGrXsYdY/fBAM1bbhZcj/flAtSla9VBz06yI2pbyg57zppap/tWVrdym9SCo/j6DJZdvgK9OAFsr0Ejb/crjKsAACAASURBVPpY76Qe6TEkB6/8G6vYftKeu7rhFAcaWnau4wJMZAoY4/Pp9mvjnjTC7qfxEXfjHpX0XTI0fypJcut95QAsOGBn3uCcHAAzetJT3XMPAuctYbLjhMgFD/ox+3DcPaB/Hsy3FO9csL6IA0BNHnTYKMs29qJq5iqFRvD3vh7W5IV4q686ESB0ZO+vI3s1zvOJ4BZP81nLcSdgvoU5LzHxEcC5D2zYzutGF2Pv/cYtXEWzSwN/acnbHit7X6E+LEi/jsvxU7Lili1XFwZ5bpIf2+fmUM8xLoKhsZHMr9l3iQjqjBbxtnrig+7z8PhhN7jIYZA81nk8W4H9Unbe7sHrp2q+K8jDDWDekxvTcR44HbuNHmOiJJZ9bpWOOnhVBSmxjF9Whao7H1J6S1udR8gMsvajltYsvW8S4CZE5oMy990A+lgIvIeBbzl26/PJsVxhXAB6sCCkb4KVAmIRgSqwnASnU7lcToQuRFIAKOPbyIxsK/JKmAlyJswyzBRmdKBjanw+krI8KE/fj+S48g2MMbxcttNufqnA2IOhEEdKBA08s3QbOSHbP0Dq6GSBJiAlweur4ttvCd+/J7x+U5xeBJrKpNoy0zvDbMHb2w+sGVjPxLoS66rIuVxKNogJzEpr9j4X5th1lAuMLfIE9N8Ibtl8MfvZvmMwy/CLNFUgoZaiDpiXNvm1ZgB96I8lGF0JkH17VW0WytiIPAEEVA0pFeB++37CP//Hgn/+1wnf/1GOpQSoApkrLGesWZBXwbqeoWnBjx/mM8YBFeAHM5ZUph627VzWG9EIp9kqss/YfDL052XusQ1XV99xboq0QKuykcTpbB3QPvlVVaGavCGj+iwVhWWFGUATkAsECUDCsixYlgUvrwteXl7w+i3h9fWE11fF67cXnF4STi+CpApdKu5Lt9G8ZogpyvQUhQqQLUMUOJ0SgFxkShJAFeuqSAbklciZkJXI2RDXU2u8QO6ucblzVD+Wy7EBt0ynTukzA6tuVWm3t+mybTS3tMv6LyXFsiSoFpcjJZ90RYGZFkAjQWWBSELSAmhVxelFkZIWAEJhNKxrmTJryaAG1HETZmX+YNHHwHo25LyCRqgqTLLL8NIfL6WywHJm27K1tNFx5bVK3+nYg3pbdsqfqtJ7AvovQbMUqyB4IB3EwMjKEbCcmuUO3pr0iVWWS8PFpKXGIiUZGBtISClB9YRlKcBeUoJqQjolJBWkBb44NJwdfP9gA7IVhjWDGZAzYLmw7nnNsGzIRl8A5TLn+hjAHLDmE2brzO9jJ+nZ8PyXAfc4WJFwmwSWDQwcvd3xkRvnuVpf4kPrzQhNAjODamosX1kyLQlLWqBa5EZKC1JKEE04LSe33xQqVb5om59SHYp1PRdwrmV67HpeYXQAZ8OaDbACYssFyLQqL8IoZAd6Gdz5eOB9CEDLZqiTDJIiSoUB+OIsjVFO9K14IWQS2YB8GsTqrFhAbWaFibWcGKqsSAtwOilOpwUiClVCVXxcRQn6yMKmhV0JmhUmJkED1lxmslgG1tVgZqCztGXCHMQFuJ2pzawxMhugOfSu5mz0PwH960B9dL3LiHjsgmM9LY7qCAzWsfdubt+5lXbjqoWpQSBLkSEmQM65SZC8lqE/SQEsJ5h5T+g69g3aJIFZZenCwGteHegZtJJUsUxkYwE8gGxlNWSzAmYrUoV2JC+4g99ns8Zf9vOx1mq9SGjD8sFLlr1sd6i+ZN1RzTL1ysyblysgVnQv2uhkg9kZaTlhIZHFoOvqwWS1BRVxfHEFdGNVE2TLhYmtg9assHNerZ0taHStbU07h1Hhw9zvRwXzfTL0DqLJLWvHbXnNoRJu78/Q4pa8qC/LJDW2qVWWCZXCqILkPnNh5NPJ5xFahmXxILHIDZWum9GcBwStW56zBoNVQljORWpUUDdWLpc5574gwsI4VhbPWo7Pj3cHhIS0cqv92O21wiAtOGjybSBVdpQUHBbGy2ZQ0ZLNs1ySHQBWZFgSJJclNIHICvH7SmPqMVArHjRdF2dn+uyOB93tMAe2NYlhZt1f3nU0+PBgvlNA17EOIytvrjcQ+/1lTBpwAu9mfPC0aW8Gh1nJ9NEIKpDzimV5KcGZT4glDVS41iZUl2bR1bNFnZg1zu7uPnQBc+6M7Cxsjam7TMk5I46Fa4uENv09j1tS+gAMHdrahpmBhzv+mxnXa/Q4FDqMwG1N8YekhD+HlSSEgVARt83K5lgyY0k6uiE5Q5Qo5Cwl49gmvpbC/ma7OTOXy6KLq8So/nMNAquLMV4ySCg8PJAfUHL0gTzR5WAtn5Q9/dituy4jRnAzVM6Zywu22/rYCALttA+u0LRgRUZi8alJlyYuK0SKYyES2xNYe17Smo5uTO2gzubHq9xw0O+B+asA+c4BHZmnA/aaRGnaOvB0fzq5fFqWwOl+P6NBWbKFBoOagiJuqZkXTxiUJfHClqG04InPmj16xxXMNQBcu/xgB3O9b12AVUdfCnCfgL5D+dHBEoZgcuyUv2Xn/Y38DGPWaiUejT5b0ABNxas2K3ac1VGZGaS4w2Gun32LlcjOIh0BaraOoK63G1vwSLOQTLH4bHhuwbr7IHEz1WT3NoY2tXtBYNfQYZ53ZV5JbVomrYAZDii4i6Hq2hkCE/OBPsW6g/gw+1AfUsDZ6k1hbUB9BbWDNtugq8Gq7W0TwJLEFyPoB2PonWKiOakyy45rDDZ401UmhLndxgJa0aKtjRma1C08h6sAKgso5v1j0vA+hkZjngcfdHQLEtdQiVcA3G/3wNJsMpyfDH2/LD0Z0vvAlY2urPfrDsZsB4bbIS4zgvRwdq5105YzVE9lS6oRIgQ9NU4hBNmfR7t7gj5NluxedNkvaI1tK6h7AGitoz/7jgKvPzF8tZ+HczlmdhWRAxqXXc95lh5xe4C0Wg8ZtrpwmsldQGqwGkbSt7OIeP85LdKEeRiuWS5qcGf9H9hkRikvdY+aufU4b8mUL8bIXyYonCXDZvCaHBVtjL/Tdba4lyFIjaEJg7a9hb3oqASG4sNVyj/3Rfy5LDiG47ZvxvnfgWm7x2zByRCXKOiP+4JW3QMDeh/UQxBYfYohdozVSZeedbT8BNouC1OXwqRS2xH+I92qS30hkR3KMeFTU9+D/KgLJQ9ypPsY3RWpnvhXs+seGND7sgJ7IsJBdTGHPiwSaTq6S4UK714oIqJhRHL9Z954pjC0hE5JMnQJrR62z7tyHd0eD/HrXUOj+c5fc2D9FwD0JWDP7WXHFKIMQ3vm3eL0ks+If2tOxpyJDMl11CopAb2xaO2+hB7Mdb51ds4hLY4BvPSed1vt/PW85y8E6MsyZK8DB9GlgMQuoLGD/nCbTHLE/epq2TE+o/d/bmcBwzCAEBhYPWYOBwuxZgR3/p6vHRJ+GUBfm2Qlx0OD211kszbo+wyHmurAvdwsKHpgGZ9LKv2OTE36LplYpS+eeWRIwYe/selo7rRH4xPQX0lTXwwEZaenXdyqXTFYjQ1Rx1ph5IvgijM2oc0CJOiNaqLXzFCnwRaI7g/NxA3HnoB+QOlBvK/nXZQdBXilG6grB+HUmMjrPULxP2MH/lpDYiEgbGDNXQSxB54cLMk4wBPhTHA0ceCxgf0cXn+BoeWwY/7M2AgNyD2TOASZ3UFhRS+lZQlHxpYAbLRkS5cY0yjkUNDPAOgxIAXeP8j+Ceg7BO+NnUYjYGt/u9YfurJz3RUjQ5tdIhYgYfScyZ0u+xPhYtyAwIjnFjjKNMVrb4D41wC1fi1A32pp7Q3a5A7ItkzN+BjWQC0cr8OA6mCg9rY4WXDS09oh2DxadHI4E0Y+uKifDH2HLH1JP18YFIR5vsk050fm+0lYEgw8srehoHJvnDFYn9TC6BZO8wxlonO52Cb3UX8Uz58bwFwBIlstzX1NPbsR/eM+Gr9WQ7k4roIjk18w4eTqwpQvwdLPoPDSCIqJjTnsMunTrIp4rtNiO2hLZZ5nA0UCfY8zurdV0VWbWwf8QSIFG8fjkotxSTM/hp5+MvSl0RKHANft4znpY3Tti1qbwX1/eB4WOjA3exP1wzMI9/xmCfUl2L39ads9sEV3VKE3W3etffJO2puT7PChE+5udI0rtYajtFba0dBRhtTnNIw70Wcn5c9wM+6fpZ8MfdU5mEcU6cjIg46WicXDDMHWwlRdJ8e533t+N7auxtAldd6lc22ujDyss/EE9E2gnsGiwT4b53dzsOO67Nhk8qhBIrC3HmN3KYb2t/Fu7T46uh8HMuK2MXePB+xnUPhhmTKy33iCD4EhI7tjGvlGxEwJh9P+ePrnjq9xHAB+jbqNJ6Df5Xpc1tvc8OEsFWRjpRG8CYQ8BONc4LrVv7xZPz/mkPsnoHeZeAvVfVBX0OrofgwE6ynxKjc26Y4wBpmzgTeOSe5sbxi6sm/e6OMB9QnoawTM62CeXQTZBHtRbtRqDZ10cfydU+gSSj/JHetOJuliQburX48NZqIUsY+DWS6u5CegPw9wQ8PozZ2vBUkxacKJaXUc+sm9ugo5CMrGJEt7P7J3hhjtu36WiHsa7ecZ+agr2hPQnwDU79bMxH7JKEJiOqa+Mejm7kp3STKCct7LIuHRvQxUKKH+OXjVwgPczWCPoOaf/aF92p+nbXc1QJr7ZuyxbjwdBJ85go2hrJQuGaium6t0Ca16jwryKQdy6daNCo8B3K/H0Idn3Gu7Vo6KeVo/XO+TJ+PG2MiSQwA4ORybaUWy04fO9bGU4RZbPT/JGpELf/CfKEWeDH0vyN8LCg8CRe7bbvRh9mP27+g1dGL1fcanz/imB4G8aCfKjfHAc0/hF7I+bpGWcw1F3KESXQ8CSJvn2Oxcafiru2wtMP2c+avNrOuiOKPv9kY4ng8Cz2sdSXl5rTwZ+hOQ700HBft1ysFq2GPyoYJOWiA4psDDZQT7HKvV2mlekjzxzobRPpxLU28B7XNP4ReSIZjANcuOKCs06GbsS5Z4u+wBK9aH5H5ddgBKBSTvy5nQAOd94H0McOtXwebPf3FyIXDc2YMYK/HmILEWKlEn12LHl+beWWSvtjouiL/6LPcE9K+Vx+/6tmZdrBi845mJG2APwIyd+2/2FMpUpRdT6RJKTzm+J94a4F5rOPM40kO/HpjlsjQYPho9cD+0sWz1nJt2hoBIKH60dv3NmgJH09T9dh0CTakMzGk3C3XsxwGdNDyvsjTfs+Kfqe97ArNsbLcGCfZcXzvOg61X1cmoLQxag0XtTgi3XZfKLwzdT2VSE1F6KIiMbaIHzdKDrBizhXs+R9+MiwMf5BkUfmIw84o9J4P9hk2zFk7FR9i16WZ9qzvH9QIFTl2T6pg44aihJQag64S+WhQ11nlUi4/D37lXa72jxu8U3Q8nOchbqhe6xbbd5rTXlmCu3ZhKRZtm1ikI3H7EXR9HIMqBRXiUAZStA7M7/ninv/UtevmOqfqLZQrf01Rm9I+3H1kMBv0yDu+st7WOSBJGy1Vg675DsrEMNbwWxmDxUEp9LMUtd647HgrQ5EdoRw7YGcOkq2FTbAjGxkSKYvCkKUOh0gjmDnL68MxNgMf+NY09HSd3BJykUNRd8mXA/IUY+sjKOvJuZf+0PqWP2Xo/K4Y2t1d7e+zrbyIOCNVmE3LTnVQPJIV8WWZ+OEC/f+jTtW3/s4bGJE00vC5bBR4pQa3OskUHdt/3kZ25ISDTEJAyLkbKdlHOKfsbxbA80ObvL8DQt/arkAPbb2ZDHVsX1OfYWG3hufcKkSBhANHMug5OCawc9TInK4JyQWLwLyCDp233iaXHEQNfuP+Q6QvuARN2t2HF8rq2FxChwo47RrBMI+YCaMVbJYiNOn8OTDe/vbe+4wnoTyY35INg37PsZgxMrMr5lD+xdc/aBHUwVcmxM3MlYNEya1xEIKoQAVTr8QVAhojAanDpbfSsdvVnG+gcAs3HrYn+Qgx9iZ0vMbRMVpz/bp4AoYah8dqzfg001l9DCjhTEqST4HRSLItgeQFSEog6CH2aLJABod9mSMkAMaiyNYE8n88gDXldkW2FGZAzYMb2r7ooJKdmNk9AP9YP5QDyZauVQCCpMLFKoUWVEwDB999O+PbthOW04HRKSIvidHrBsiRoEiypyI//+r9e8P0fC06vJ5xOwGlJEAVSSgAIy33UsZnBci5ApOHt7QzLBsuGt/MZ57cV//79B87rivWckdeM85kwyzivRF6JnIGciZzLc+csMCuANhOYPZ5u/iKAloMj/XyuWkL8AlZB0uTHE0QUqgmaElQTkiqW0wmn0wnLKeG0LDi9LDgtCekl4bQkLC8nLEu572lJ0EWhqlARMGecITi/uf8hK8zMgeYD6c1gZGHbXOYR5pyxrhnntzeYWQHr6sxbRxD5XMIqTcrzF9aHHzcrerzWphTmfgL680NXjq2p+oWr6qBLVRJEBSoJqgIRRUoLNCUkB3VKCcuyICXF6bQU4C5a5ENSiEqRIUYYDOcVUCNU/SQv3oZGtOhbEjQfUuG0SZbZ3oVZrTC2Gc7nXGREJtZsWFeDGZHXCnBDzquzvE+ZJQq7k11umCtpPjX03SG6mgO14774oEwVgapLino9AaqCpA5wVSxJAQFeXpxlk2vfBKQFECVU4c/Vh2XmnEvbLwosG0TYgFzeWxw8jwZaEUHOuemBnIFsBhqxrgXgOReZYcYC+OyXRpj5gvDjbQ6R+RkA8AGej/3zcJLj5iRBjfZ1NLRiiWVS9Scs+nZJ5XJdV9fA5VSuohBNSIlQXSBiWMXzhZrKy2jo7VH7RLOAsb6XnOl6umjpbNk1dWVsNG1szsbmgKYVdmdbLNZb884O41NDP0oQWOwrM0JV2qm5MGVpGEAIMg0iChFiPWcsS6l5fmMuQF6c7Wgl8a3FSrMMiJwhmsokQ1WQGZqKpyza2+taPg9mg5m1hQMS2awzsTN19gAxO4CLzkZnepbbG/vHVr3smvmBFcfjAbpZvbL98sSzeQzNyGlWqog1NW2rHkSRhiUJVhRdbba6BZbL9bx6AEmktPjzC1TLuGQtg78h+WjnC1xHdzYt4HMZQcIykI2wnIs9l4lsGSBhBHI2X1w1uOyOCaZjfV2/tz3YE9C/GNFTkm0H7OV2r8EIDG2sA+dLH7kVBjV3DKT0wDADkgKmAhVzcGdPdhRdrUqszP4eahBaykVrqqOCt449Nov62pAtg5b9sgDZGNk5t0CwsngPCumD7TGA95EDwgeVHHMOuXchkjK7ODCUn/ONoPZTcgnOBCoF5FkM5ApNC0iDGsEqMxRIWgK6yswiK8Qdk2KbVWBlvx4yjIKud41tkiwtSIfMFjyaZQdtB3IN+iobl8ugoQeGfgL6oXDOxtBswBq/+HK7wXwvrJT7u5MhyuYY5FyCvSzmAC4vpEJ3QdLAyrWAae6HVLVu1eVVS5sVFi5aevXA0YpMshoYOnBzbgFlZWZutDNbdeAT0PcZA+64Hl7fIONQnxKUabt/uS4QWvOuM1eIlUSLmXlgae5x19dTUAh1CSHCIjdC9hFSQCj/P3tv0iRJjjMLKkDzyKzqfveRuc5h/v9PmxF5X3emG6HvwA2g0dw9qiqX8PCUyooM3xelUqEAAUhn0uasgCVz6GUEMSy+5mYYHVtXcMMlaeCcDoBuV3ox9McV0mgF85xYmU5DN69aKrOpA3musqEARVBqKsDiYWcWf7ncndU9UahsoLTnqo6JrzhaeI2sLkcDoTFX2WE1SCyaGmDPIg4Wt+7UtJVsC4nx7Pr5k0iO8wP6BeCyiPqt+sQAaFBNIHKpdrPCsM3Wy2LQlnJWgCbIyH3hqKYaYM5uiziQWWfRoX0bsKvE8D85tHOXEjbcjp4SN3O70ueYiPVpNLTXzqi6uLkMIv6YU62O0yIJDAYaoJpK8ChWJIUWr0KIGlBa6bove9fmnfmBOLICcDPDMRyPohmq8+FA7cHagkcbvw+fmU4/W3+/nwXMnwjQ1eng8KLDLGxany3Yh6oZIVIuJ61m+za0mmKrlpyogmZQSWg1zaNRDIeuboOHxPvPDKdYyDE8yAO0p66rVh5gtsDk/d0aDx78Z5lZ+IkyhQ3UQ2a0yjS683jjEImbKigjWSGq8TGtdNrPKFadVE3dajfKWrB+3EpWnUFdARECO7uipeZRryy57l9bv12UGi+G/jygxuxNN8AVdjYUKVFaaBT5kWSrTN4YOENlG/KhWnEEUaxohdSA1FhFCHk4aN6r7+jZ2nobMrPrCABZpFHPeHZHQwaQP98g2c9c4N+sMjeniizs3Ib/tIBNUj+JUmZSVXZvCwJSLmt+Mxsja+9dV0615ALuwyQsAJadxVa96Abq5noEJp+0t3M1wozxT4bq7VPjWSZfOpy5s8qsQw+P+T4ZMOkmdx9n33vJESKslh1cEFpKTekaKpI5ArsnWTxbezBHfd1Ou9BNBRhztF6zvj+P7KgBnC9Y6l5xZerCtKnOMJnbdoVGt25Ct3UGFgiMJYvYA1KrvnN3IXzAxmG70beFlj7n/pDtW5DwZwTyi6F7PDaCw9DutuvskUH09lu3wloUSVY50ZySVN2OcltF0c/lLjbA2pnawmvqvjQsFhl1BpfgygyCnkfFvQD9ifSGG5XsJEd3IipIj6nj1I9UlShvzAJsNh0aM9cspXmZgQHk5moMmWCuYnAU53OSIR34XE23mtspvAD9qcDdwj1vo/VhlmSRHX0daHVH3Obes33JrZOifQVpdDaYXBQPRmv/59C+w8HA0MptZDLdjuKnBEzQfjH0p/vj+8y1oE6KDy3sQC/SpP0cTkKJ9UbRvnQNXKfOhtkA8+CfWK88nDYPaHNMHecXHgqb/PvBS3J8ep6WMKjeQaPr6ZqyrtlAIZ3tp+N+Im59tGH0WgmZXY/PqoAuMGzyxRkfmNrxYz0tC2EOy2eTHZ/ch141Oeco82zA9V5GteZ6O7oK9NDqq0sWH2jaYO1amlrEtQVWBhura51XGIPQ1iSyd2vygeAndjfan9es79DveYxN48R2IfPm/8nBnC1I89b1uFt1JQxDO9s4Ahbst962S44tFw/PjXWw+dLQrz9jWy9JlS4omuToLp0VCdFOszQ7TuZeiOOkLiUOpZdG5G4hNQi3QJPBn5aglI+d++dm55+TsT85Q5/VSp+BobGv+9jaOAm42ZicJ06Jm2Zkg80dRNl3Apna7/rkCxdzX1YngT8vS78Y+gBYdv3sz28Vq25oY9ZTK2xD5NvclD6i2+qoCqyBd5gO6IEuiH5HlBd04OZiUXI1NeuTsPVLQ092l2AxeGey9wLIKBFEbUosJZweGTbdVDTk09kTu/qps8dpXYvWv3zZdp+coY8DhEY7sDF6QsTfqgaAYtWwk9DDHLVaj36ixCw9FgEpWh3fVL/M6bX660MlCc8Y+WXbfWJgn8mBeFxK2oxumdU4D5ZwH3/sgDUKVuWG9TYB8jCeYrWRvILCF6Dfo0o6lny99KiTLgxd60EOU2rpyk3hWDWy9FldEaOQdr8zsDjxORrKvAD9bsb2gsHxactqTybJKNfgBCynzMkFw3o7zhbTYela0/jaDS5g+3mDwRegb8zyG2nwEwssnG31mrb1oq6P0E+uWJkeK3KD/ccEK8aIcKGZ5aCh47JaZQ4/B7hfLsdNlTEx4+EWXh87Rq1+88CkDlukdyLnIgEjiDGh3ND3K6aXT/+9vSTHXVjLnUvWG328zhbcIXce1csdvOO2nxvUL4Z+SI6slOo8dBNxuDynwo+aIQzs3P86Id66j/ajVnKoIYn1IfPPz12g9LkPyZ4COd5kzYeyvs6DLdSIzlbgfBCQ69fmi5/87VYOyc0x0AtdLS9APyGIZ2266i095+/mQI2uD5NMo5Y96PQGk8qR9YFD+epxSu28Y8hi0fBRRfUC9HNJjHtrgVNqfJjGDO3FRpf+nk28+0yxfLU0ulk1pIl1pjGTaJ/S2Xhp6CVr3Qu+/IwsAHdabPGgtyOjx3rpdfDHg2RoJ8xlArrccTo+T6D4YujDFy9314DXyNJ5YQrQOD/e+Hks2vcaezqxLXFZBM7tM8R5R3K8qu0+mczgkQkPpZ8j8TF/fMN5cI7HEkTEulbjhqZ3lXt+iuKhkOSTV9l9ToYW3CiA9/p1kbxY4sRAJBzPAzbJ0E6Po7dAmNPf4SWFq1qpacb5YVfeepO4W6z0hErk5UMfNOgCKDwBBXXyn0c3I3pVwNK4MXjTYAwEea7AjwS/SMsvakDWoH5uPf1JAc07101dlYKPrO4hePthyKkmY6WrF960Pzi7AuEhNd5qrf3j6Ym0emnoJ8fxWdssueN6nGljuDOHGgFJz6ji6jx4W04crpaYYbypH4jbafcXoJ88QJylxoJFuVoMzU5TLDN39LeZMpL9Mr8AFMcT3SdyY7mjrG7z/Jh+AfrGCe9ldo7Rolu6Dhhe8Thz2G4f//pX4Fvnxj51dN1KJxnCWVroDVBjWmAvl+Np5EaE4KorqOtdFx6jXS4DpJTbW31ofSu98fl4SHEunTtI2/PeijZWmQct7m839RWTecnWwUUvDf08mKb7Sjn5Dav6Yp58VDEm9PLAuRpz8McpyGxSw3UsPWT9yON9Vno/MLXeDB2eeVCFfiYwP6ajZX35ISiTCGLMv/vLZmlgTj84iUC9EYjK9HiycED0BOTr98cnlB2fAtDvbmZPOQm6zmy0FXjm065yDPhW+nu1IJbB4ZnevxfwPrd19woKH6LyCYTLI1KTs8G5TmNImNFOzAeHTuJwyBH6LkqcgD1nPXkL7M9v2b0AvZIXctpDYLF1x5rknvfrve0mrU6BUN0Zw4VkaaPg6msp16YHpdACwHd63T3bKBZ9gXjaynnCuDcfZzxGNPokABsYBUwjOJXegtdbgG0BrMtL9eTrkzt23fOXkT49oP86A53oZq+vlyPVUGcRen19JgfmAqizYJ6u3QAAIABJREFUIPCBwHMpmT5fBd6rHvqUtUd/u3O2drNV/L9E+4i3oYvH7XujGLpOSYxdSKP/DDe0s41tPjFDwmuzkwX6AvSTAhc3bK3eFzcyolSYSvvbBtYrkipEFaqpjnaTOpe7oNbYOvgTZuIOgdP1mdY6QSvKjwhLn6mU6VjY5/7z1IA+lxvy4HbttLGUeSqqgpQSUlJs24akCZcvCVvakLYNKRVQC8rf1sEAUFgu87tpgBlhufzMmbBsyNnK5Wb1bwZNy2jkOkmg9KluA+1zL1aVAyN7xSGAfA62/kQMfaMajTGkEGktvYqE0CTYtg2XywWXS8LlbcPlkvD1jwu2LeFyuUCTYtsKM6sqUtpAAJctQVPCtilSStAkSEmhtTNY2hJoBk2KnK9QSSAzzAx73ssolgrw6/WK79+v2K87rteM/brj27cdljO+fbsi5x3fv5ffr3tGzhn7voNGmJWdwCw/pbvxqTW0eEUhAlXtP1UTVAuAUyog3C4bLpcNmrT8rIAFCmNrAlSlSo/yOASxpSY9isQADGYKy6VeQxTAf69lnot973MJc84wK4BsrGxWAGxmyJnY94x9L3p7zwZVQc6AanlzF1GIGFRSWRx7aU0mWd1AT8KML0B/GODK6vciH1R1AFIVmlJxgZNCRbBdSlCXLgrVMvObJth3Q0oCs4ykWw3oCEjGtglUN+QKMMsC1fJcmjNEBSKsz+2lUTlm1SbUmlmQJvu+AygsW8BcAGq5yBMSVa6Ul2JG7NcdBMrjuKXsq/b4hDT95ICOiBYdYyeGrGjFQWVSVVOXZqygZG81Q9e1SIRAIiQbsGkH5pYKS5sUqVKY26CCCuhyeWvxP4Z7Wu+MlPNeQGp7uc4KYHPeYdk6S1vV3OW68ppbpzEjwAp2y1a1PO+ftHkB+iNFiUNz9OQHWUe0xVESNMLa4Hkr7KnSRhUXVjfbsW0XcM9FWkCx0yAiEFUkElSDqMKQAREkTbCc++sQjBHHZYinwQhYzvV5y3D7wroFzDQfQBbm3ve9yBNDlzQNxG3GYm/QLvK0IvoT97arswP7bG4AojASCikMLWX7BwQqRJY6v9AKKwuAvFt3P0jDthWGFiOgBlMgJZQxFlLA23YIjytiXFc0tNXnbrLCuuRgdUbYHZHKzEZkY1kQjZ1p6zj4FRQ+n9Mhnrn7PHnWw9wEskFEy5Zf6ypUAAqBPUN1K65wG1LPDLAFiamQYWbX2CIKsToOrjJyAfXwnRt4RzDoWNkyWPV1sQCrfraip9t9RlemYudZfZxo2fEpQb19DvCOFHDRzhKnv07/NisygVVuQAyqCRQg5wzVBJMGihIACq36zyxBXpKiWUWQ6kxD1VGXIW5geB++6cBsVh0OWGXe4VGz+9QVxDba7za9zT4ujj1W4NSH7xm19PbMYC6DoyKw2zCpdnnTlSI6gj6RgrWmo7UASiXBpAyiVxGQBdwEoSK1xSKrm8IO4syyIAZLDqYUkSILKkOTg3nNrLoe7OBtTGx1TngJIJv95n1mc3PKrQe1fObjKs8JaDm17do2P8Debt/YS9ugWBgLMJMUxtOko+LNABOWWSrMSBCYACIKswpoWi1F3SBSgNarQmSkq1vQV/4rvhs5uvEbDXnPIAYjR396sHDvpU7WSr143boV2QvQH9KDXtl4y8GbnbGd/BAFs4FJS+BWg8A+cKIGkBlVfghgMKiUtLUAEE39NYgM2843Mh8jWDheC1ktOBYN7aSFNa0dAkEe/rb3SOd08Entu+1Z2Tl6dRIYmtVH7pKk69gKXkZWL/AUwKzeXpGZkVQALVrZ2mRZZqikwsrCoq1Fge5uoNRVMC48BmA3mUBkK6lrYjgZ5tnZ/R6DQnpDpwPYa+kXQ38gozkMXl0y9Orfw33wmrZd3+SESpMDxcWgFk2NCmaTHapbSaaIliSM2znOprux2nQNzEYro1kqK3cdbdbnsBgxLutJFd/Hw7fpfW4wfxLJsRpKH4PEUSI6MZgIpGlRyVABoFqDvJL+VklAY2gRZAFUtIBRWo0HnctSOiuJa5rB6hX3xMc8/a0D1rkfNbMYAj16WTEWpLcFn73D/1MHhWvhsRi02diccGMkqodbZUjzc9VYMn9WMocNyCJWfGnXZF+luB6lIL8UL1HG4x5egJMdBgPM6kB7dIlh3WMWGHPwq33GhM6LbsX+PMwEf2noD8nOK7BHSTLmo7Qa5pYs0do3o50VYfHRiqNRmbrUIafiZEiC1UQKVCAtOSOlV7QIQdGpXZ70AJA+6eFdi8bg5se+VQ+6ecu+ECn82/ouRbwkx4f0n28DewHyguKeXeu2GVpQ1/VB0OuWM0RYC4+06Otm/dWBmyJWT7lIlRyGVb8M0suEZudVqVHZGDUtT9ro6m/ZyRT2BQBGkH+Gk4VPrKHlqKE5UC6rCbGVtpsv7PV0s6xb9q8nY7oQrtLA1VfDhpsyzsFyeo0OfO38ybAlqoNhnYnn4G5IiuqMuJHLR1HBp0f29txgXpp3CCe45xNYi8yh1Aq1Yfsdh8ST0jOEBfQEpRQ6dTkzzbzsrfB6ARHDDkPvTfeMX11U7kiWt+uCPHHxAA5zPV+JlY8TCBLxWMpKljTXga5FZz9wStc8UYf7EIBQGFmlpL5bkZHj3FLbLCV4FEqp7kPNKFYWlvCaZkB7De0AW9PjcAHjeIWxhI8H+M4tgl+A/u3BfT7e3TVGmgFfrwxtZ1sLg8bWMp6BrIX8DZQ0iKTDyDZBS6PX9F71ONrzxyCOfYdo5aQ+He5ZO/Sgdl1K6QYgjgXC6Ki8Cvw/Il/f6GIvMqvKCsqjBJn7X7AGeu2EddHRGsBCWgkSKyhFUmfvLuXpyjjrY/dSUniGxehx1yWIT2uP5A/hCpJOfOZnDwyf0+WAb7W86q4/Xzd1Q5q6hbdqtVLxqWHnZp/z7bxdYasMhe+kQffcHrRSj4CZ6/3MUK8sfvhsbdrYEO/KYeeYb/adP4HNsT0LiM8vjt2NRg+LOB2KB+eDGCl0V3Ah/oTLAEloAMOScIEIFNpZtZ9jbG4LZfKPS/2zuPR3WQR5uB9NnsyuSDhfZm5hfK4/2zOCWU4SKstRgHK8fhpEMdyNqpWLt9w8ax2tF4X9svaAJq2YH27Q/Sxj4h9zwd0I9CQEiiVY3Q/OyHjc6eiVlzKrpjQvQP9OYD6p9hG5HzTy+LAxAER0HqRKjJaAEdZpsaOrESQdJ2VxJGtEMM0vlIOxGOSDq492NvP4yRnUfqbLrbF1L5fjA8D8fjtZuRUwhhGwLU7023lVzK1xIv3kFg+kJkm8oeaaM4bbr9PSPejziZP+2izikXLC1hIWxHNPWHn2Q7InFh19tdvB05jHuI3p3dJncDvXAqws7Wy/WkONmlbxtSGkQeoZQzuo+Ph6hp1sGFV4o9aDLtgbDG7urZ8d737e3nZP0B9aDnJi3Yw8BojH+oY7c/0w1xc3oLlxEkQ8CdI7i45jUd6t6H5xPf/HcHu4mgxXiecP+PanklsrGcc+0S+G/k1BvNbRh1LR/v3yBNTyQGDZWFOGXJBRA1JkSGPviQjFACbAeSntDGNsmVtPeU/zBunkxQD1MYVDxveF01Pez5st1I8L5kfBPmVK+EBAueQyOy4GDs+4pKH95FhfmyxTLcVwr/1AogLkBD/EvjymBhnhF8PRc/bI/1wDg55fQ5+BnSvtPBMWe+1FdKm5fKwSPGplUw1w9d61dADXOhFEtyNmIr0unsdjGFz7/zg4lLJ4f3YiOZ6LpbcPCcg7l4tzDm5bfLKm5bBl++79cTQF/bTunr9WByM6ILvgT7hgfZ/qs3mpdVCOpE4bnCm4PYtQpkd6Ffh/WHNj5fGe//ueE1BLRP0ziIRkTMd00Ogc17WKPp4tLlaGdkGuywiGqrzZpmsdVKdxzDxdrc/penwgQMtfuM09hn5sWRwFikSdKnJDv6uzADESN6evTcrxrc7SUeJwYnQ2P3Ea0onTQPCloT9s0MjDpTfGUjw01y+ypbgRyMPZlgEyn0SfJsoWBROECDo2TWPxFFk1uisXPT0f+Agnv2asfFDGZjdyhjkmJ/qSd5h8nuWtC7ngNbVM+tdr7clN9CYhq7bm9Fy1QCkeBhhlrJyCxvNTKfLOy1+23S8C7y0mil4CMY0yPvx+tnX712HT118st1m1sk3C6nO+eVgQ8+sq1+niNalzQmKp6XGB3Zsq+5IcH9j5uP3FctKZZxA+gmUxJ5CtZcGZpq5aV9RVn9r0DO0Vpeh+dGdDa7LGurSZA9g1867e1XNZeE+oobkA8m2IxgL4eOaQjvnODndFUI/Z3UNM1IH1okF60I+TkyE1ho1s0bWgG3DfC5VwkBp/eRCnfHxQP3liZQFA3loEPA0Ej2w8D6VXp4PFyQbtxf9y6hu3l6D1dHiaxJDF+/kkTpcie19AXFqTDwCV/6DaewH6HyDkd91B3vntjcze4cxiOwZVp8di+suqfQdzx91jQM3tDnJWa6Gx8B9tSuw4j3OUF3YSCD/wIfJjAXv7+CC+ecYbaz/21v1Wenm2A8W5GeoCMh0+BxN8jUZPriyCt1XpaHnceOaRMr+P+Np48/2vPjz+MzzwAvSDSoF4B/j+riS51QxdHdPVhozhsIAi1lGkiYkb8O+l4D2ArfTA85fTvdYwfcAlcAR3JIacfcgnt7mv4l623SNMwEdveOsyPni/Rx8/TVVsGoF7AG0BeJuDOOy7VO28VOWIOnvPPTudvTfrb2qUHaevQe6AVPBojcxfl3mfGdB/W4O8F8TRiT4FQuuRiygxCkNqBZiC1Nq2qzobVNcYBhGgRC0R1fp7Cg5JiwfJ5NoXNPD7dDcOiwjL4PUf9Kb5AvQ7rKO/ysi3AH5rYM696H/BYh2EOkI7uuu6nvaSYwNMHYvOANNh8XEGXpqYuwWIWKTVvcXHBz90eT/If0NQP5Fttwp+bn1JxEPFTMstfAaPBhniAcfD9T79rYuFVtPZEuVFvKw+puRJu0vsR0IB5D2+8l9Iif9mmlo/HmjvsfGKffkAQ50x88ySM0Ax2XQNcMmBW6brGsM73cuVFlb38jkV7g+JEh4b0wLkySJcLnb5C3bmS3L8Ddnx3r3vnvQ4iUQ5ywx3/o8x29fB3eQHHbC5CMbmI2BcSZtZNqAuEPE9eNcfGhcfJuUf+ALkQ0iPDxgUvge8jwSM85d/Q196MB+8Z3cb+t51Use0qQviBqMS02W9MSPc7VN83iCD3IKCnjMw5YSF36Off39Q/74a+qZ0e0S4nWnkk/t2Aj12uItArnXOBIjkDsVqZHP6rqQ1oBNxdtusbrykqKfL2yxwcTJEbNLR8X5DO6/eefxQxbWZPFp7H7PM9APbdu/xoX0zxFhQevQ+6Eo96xcorjxzTF47auxmy1GnBMeR/SMzI7J3b46+kh6K2CfhTAMvKvwmuTPD9ijG5Hd36T6Yy3GXDM6KiVa9NuikwS0xMm3NPNOTU2DompDTyxPqiYtSbytzoX57nPp6XefT3vSxS5Bc2bjen62uA5PD4U6JTwdzeagB4QLUa27/HR2P35+hz9XBknWJdeOY9b3lQY04W3eKdXZu9TVzEei568iTXiHHEzPh6IAPXAODr1wP4JggupVkkXfsffPhhBegHwI1sU6N8LQwab6lnIObjxQsMbBwB86c5GiX0QH98OJ1qmP2qJhAHu6rJ+Bf9QqZA9xb6e17rob87ubGx9PQIn9hFbzr+kVmbfZ9G/PJ0NntOvJsOzkyLyedzoPboFinq+uBg4lheShc4g0dIAv7kHh/evwE5HwB+m+Cmu8IELmyNRbX3zks63tiuAaRDFba+UuS/s3P2cOJyg99vqreFRfM+t2FC4vuNAaQOxT7Mc8hfjiXQ95V6/FXvrCTSjVOP4enN5i9B4CGOMvFMWcvPR2Tq9DHsk1aPWwYflyb6+jvbyzz53BHNshZl9KP++dD2nYiK2AL7oWPa/DLDS3pNbafoDX7vh7UY9WJ1nMsOl6z1Abq5WHkIEYO8l9ONLMs3juPs2NuauVTBwYPXP6y7f7RP+/Taitv9pF+HGdjHOoclRmQWv6nWsYja0rYNJUB9yJQSYBISGhYZWljmRBbfs/IZnU4fQKx10wj+/xw0B+gNbfYuJYWcjJThWcL5GMelH3iarsHgprTLp3+pLcACmif4V3+akoQbBAoRLY6uP6CbUu4vG348scFX79u+PL1Cy4XxeWSymOkNnAIMCMsE/tuyHvG9WrYr4brNWP/vuN63bHvhn0Hcs7Yd8BM6l/WOeCt079NM1YQT3E/5BF//DksT3bq+53Ovvimh37+NytogS0ptsuGy+WCy9uGbduwpQ1p25BS+fv2dkFKG77+8YbtcsGff74hJcWXrxdsl4QvbxekS0KqTH25JIhIkSIqSCrIZlBpM8Vzde8ycjYYM/KeYZbxn//9DUbDf/73N1z3K/7zP9/x/ft3/Oc/33H9fsV///sd+7UuhuuOnOtjmCHnXKbTmpsRjnZwIA78XEclvz/An3Lw5jnQJ63bJlJBKsDK35QS0qa4XApYt0vC5XLB29sFl7cLUkp4+/KGLW348vUNKZXrL5cLtssF27ahjH5DHZ9s4LU2GlCFZYyRykFGlWDSLFfAGfZ9B2j4fr0WUO47zDK+fbvW2xWpctkUggTVDdcr8JYF12s57WI5Y88Zliuzm/UCKsulA5TlInGyEczWJRBvAvwF6F8HcRmHWkWkjC7uerdcllLRu5oUKSm2LWHbtAR3Wn1gI0yI6/cduAj0e4YlRc5X7LtguwKXrT6TKlTbeGQgbRsEhm/Yq5auo5VpfahQG6qZcwZAmGXkfUe2DMuGPWfs+xV5z8g543q9wgy4XjOu171ebtj33MFLApZZq/7Gom7zFwFCU6kKFDUwlUVACswMZhhDkn5zcG9PxcnTL4N9C1OOv+iMLKo1oFOkNK5vvZnNyoSrjCIJyIxEgRkAJJhdcbkINKXCnhfBfgWSJogS2g6SiOL6/TtUU58DXl5wLovN2Mcwm5XG5ixIwp4LU+dcZIcZsV8zzAz7tbDvfi2BZANzkRkN0Bya3dwULXNlWxa97AF07fqcpB/yfEA28VcSYC9AL/xomS4boB3/dnafCFTc5fXr6MM0K5hMBAKDaIJICd5UATNg2y5QaYDJ2DYFNYMsrLylckSq6eL2EyBUtO4Qddwbc2XLAjrSkC0X98OaDt5BYwHtfoWRyHsJIIs+rsFltsrGRUZYjRSLtKBrQeaHEtXbuNHLI3C2vsCrLXO4ySMh+AvQfxnc0++LwyHixxHbAJpZHTxfg7RWzilVq5oBqgmXS8K+Z4hYseSYQVNYKgBUBXYlkm7QapgUL7q071JYAbQm1yYXI1Azq1t9YWSCyNnAGtiZEdky9uu1MnLR05YJM1R2znVxFNYfOZxmDQ5g9tmKlD5xq4+YE+kzzoWTczJN43pJjp9lWItEVvJT08gqO6QPnRcWa82sTIwVESCPrhr7NUMTAOzYtgJ6s72DPqXaKGaTev/C1oJcJA2KhgdzqQtxwCmMbx3c1sFdQJrz3n+noWvlnFl1L6szUv1tq0FfBbG5zGTT6+OTqaB2c8K75q6vaVz20tA/3LDrWGVk67ad+wHwPcLx+rWykGptd5gNggxVgSnB3UAF0laixMKaO7gVCw7M0CTIeUfShJy/QyVVXV5qmFMqvx8WlUhnY1YNbV1DXwEWVqaxgJoGy4WVuy1ng90LE1tnfHbwugm1np3d53YA+zR4scgkdq39OwF8ew4Snkaz+U73zqkTP5ieI3HiW4YTjZnL/XIu3V66BIYAex71FEmqv1v8XDWDKGBCqCawZwnLc5nZeKY2Ni6Ax7oHDQIGK1q4Mrexuhe5MXYBdO7gzz0AHD5zXRzu8wqDO8MZSIb6Ec/WmMjid+wK9jna6U7DetZtzVnl9GAl1dS1ddHLWu00qVNjc3cOUkJJnGwJsAyqIufC9ioKU79rjILR5ke3KS3WhgU1Lc0KZMvT33J5zsXOK6xuVXaMx2hs7Zm2T5zlXNkX2dt/Nu13cTVSvsDvBegfK5kX/x6ea7k8bpWjrzKdBLD+BRZwF7/YslUrSyuwi51nQhgFqgmWUbxdq7pcRkAlGC+MDtDFPhszU8wy2GQI2ZnaatLFrNh5jdW9RdcDzMD+w80JI+Gwut7/PrMzf9sM4tN3ThpARrflSiB2pv/Yay3G41QdWruBZmaIJBh3qG7I3EElmDbwekVKgGqx41CDSqkFRFI1dVxYFp6/vabOxGhBYGXsytLZruOAS2NjQ2X5IkVm6bBmax6eewXcc7380tA/RVOLRGtpZOPKdaQPHNudDd1rc1+0gUDOEC1FRjnv0HQBrPm0Ca0Wrth7VpIotZipvB6F+PZdczPyXl/B6k1bT4EXcO8ddLmDtyA6571bct1yawujjVb2QaDIxNR04F9djihRfkMwPyGgeSo/jrLjGLE3dNOsV9mJpP5FGgitqW8Rg0iRE0Wf70jcqpGiFYSE6lafp8kT7RLH79gNgD39jQHm8je74K5adG3nYB6OhlsAI8jjNPh+HCxYarbwz7WciMz+AvRPlCBRigyQe/Ye6e7OXhVYVlm5DfQxKQROKJD3wtgAshYgl2zfFSm9Ff61vVp3LeOTA5zpoivvNJjtbtyx96cbuKV437XAyGoum7TubHTWd7Ub3aJbHEfzMiu4GUvw/p5e9NO7HOSQHv6yurn2LGHPbWAK46XKENcptDl/BoOaFouOUsFfWD3nvRf0lxYZ9VBAlUJzp4u4teee4YOXHfV3a4DHsObgg8B+BGwwfg9CD7vYrXOWWPbQeyVWfgNQe983zJV1+XAynnjytR1jJmBxH0TLHBXrdt4OlQ1C63UarAkVaZ3LxWq1n2J94roW6XMMsbeeumYFOCuwaz90X9vc7TgMxqdPlLhdKIB7su7i3nEqSV6A/sXywzseDu5OArTfNFw76HtYamoKyo4kl2K1icJgpXLPatUe8kh91y5IIgqhnXqO7Dq3gTh3W5CI4I4a27HxAcyx6dkRqn4i7bmqIH//iuhPAuhqx0FjawDP2JQYoMGPnq8sXe0QqeOJBVv1quv8QlqVF3VJ6FYXEXtgSGQHIumPFZIYvSrOBrt2lrVextkCxZal7GWh7S1xgm7Q6WPJHsM+/pZJkxegZ+136DDkPOrG1F2HSBQEDRy9WIQAWrmoVkmSuvVnIhDLtYB/eLi9DlqaHDLHmT49bcMyNHO3RdfJXWYss3vtPds6EBQvPY6SYgXwF6B/ewfE2x4D2DK1o21p6XId2vFuB9S2CNjXgk9giIzSzMbmYxcYLXxZFwMDqDkuD7aey+CF2pW5LPSGhFj8xpMbvrqP/rYe9dSfQlYzAl0TFzoNXI9kNI07PGpDGflGl4WswBNC3RhjD+RgD3bkMLSf9HoazkumZ10KiOyCQnNsW3+XmZV5YGQ50fUvyfFBwC1YNVoZjAwnN/oUV6ICXDtwu78rrCyc6k0zBAnGHJ5LxB8+5RJcPkgcdpk5hnaOBsbp9QjqUGY4MTAPUowPM/PLtvttdPSsqeXWwKBeqCcOyBIIvcmOYfONYE5EXaLGRpg5TO/hEzK2TGJga6edvV/tTq8HCeIPwk4B4MDjA9Ot/uoY5Regfz6Yo5yOnZXk0P2+Ad/pVEHV2jIxGQMb0504CK3Dw1x517koMKnWwFOc7PBa2cLxrSFRJCyK2elYcy0nZn51TvpAGvrWVVNz8jBTexy8LTIk3rdg1x2+cyPZimU3LahDRs6ZaD4g7Nk+hLa9bcxEPwfYH9vG6wfjZNozeREk0C2J8Uqs/GYsvWgXJhFoDCytJ3JFBkN3vavjyBfqIKDmkHDaAUKi5sRt4MSfHI7HfDsP5sbo4T1ynjcjWLfu/Tjy4gXos953XlPKsbPo0M61GQuKddfcOlbHotlx4sZHCOI8bhEppajeEg7BH12gKE4/I/xenA1GRu2FSCMgPNR1L4F8g7U/mI7ePi+Y/WUyWXhx1t+hDBUo6Wtx8wMbMCSNUNONcPMnZ+Lze8diNU5t0SGVEwRXUoE86ONbdRl8km95+zxgvteZ1M/GPoJ6BI/HUWxFV2uv1BPUsWy0ftr7mMUj1q18LRoM4vU1j/cNQd+KlXGwAuOD28LK+5js/Aklxwrgq0mqxxknhEbN7G7jGbzJDXFDfgYHr6ZLGQSzPzwdrXbnHdui4yqdvTxRIjelBO8G0R+Lv7fPAd5HfxfXAPyE1V1Tf/GBJGVM75MWNKKfYAGwKAOaR8ENfTuOO/kArwV9E9BCwBc18ngc3rfr7gL7BejfBMy35qm4KbGOoYfToWtW72wtOBYyAaPeWibDcFW+iqm1QvszuRkBwFwIBC5tuHjBrTEdq9myLw39G/45HzvBALR5gCXiWOQJ1OQoAR3bO0aZqUjM1C3HPrhqP29si03OBk/ZdQX2Mzg+7mS8Eisf0umQu7P4FGOMsQe/OhBLZGCWRUA3qk1a8ZI7ynWUE64w6eQQKifJwsNgIx4mmp/bcvzwmvkTAfqxwZvrag4nIfyQy5o8GbOu9bgPCJbSpMsJ2uTjNc3u2hu0QLInX2aArZyJ9XCj+UjVWnrwab71T+pyYGJFmWTHmQaXUxHTWHvIkHGr3uDGq2kCsSdHbSwemonPmtZc4Dc1pzkBN5eTuPm03+gn8qHvMfUKoOdjLI+D6icQA5MsWdW3yYO+g9SjW3IiI3hg5Qj4VeBnN4LDF6CfeAF4ECyGbgbrzhUvuR4fI8usC0DNnO93DU6AtdFWwfcNFkwAbXag3dDI8nRgfgF6AmY8Pyg32FoWvK4D1J059aTop+lvW+heHILNcrvYmy6efPWSpLE5T1iZJ0bLc8iQ7fNimHFut9OccsgkzoCsiROkyNhUd55QH5A+iuMc5IXIEekLQA5SZ+WCPMK+nDqcPxhLvwDyewxTAAAgAElEQVT9C1FLuQ3q5WxvPTlfd0yXj5jNtfZicguCOE/ucIwMmA7s1haLQ3z05Mz0mHIr+/cXWtx+/Nn1TwZo/pU7nCVcxGUKFwmXqUifjnHHLq4TC0d2LmDVg/vRWpJxmSCRCNa+bt+bHJluL88BgecC9CkpnQ2qx5KhMfkIwhss60+3BD0u02Nyed9Yx2HT7sHFWvM98GQ8NsVp4XJ06yaAX7bdxw747l++0M3z4dKQ5q6M3FkYkxMy11WIcyZOnBKRhaYeO0NPtrgC/nN8rspUb6S9f8eBKS9APwLuFaAe+SZr64LOxiM8rN3uxmVE6fZPCX4I6ANBPbCmhHKOer0IjvXPOiw8v5Ba6wJZjYywR7ezD/1Hn+4dyXuE9cxi/tc5oOOiOL5pXR1TsSiHge8joFufT/RLIwayttg9NLJ+eI3pJOB9cNE+Ab4/oW13ltqWCSQn4OMEjqWToiHIi2hRzPXNcSQdnAYWB87sZImTNaENg+GQ1DkNNJ5TR+vTY/ZedH8TnA7kVGB2Pag4HNXyNdbE+XWYT74IwM19JcldNwefq0X2ngX8ZF7d0wP6JogF988Y+g5J03GsXi4xBYJswHc/cWyw7odaRvlQ/2f+caqUaZImsL5Mr3NaHAH8ckeCyAvQH4ul5Y5QlHWASL89e0dDI9jpGTVKFVLqX0L63OyqtSkgU9Hg4rRvfVxObRbICaytGeO8OObd5abUei5Qf/IzhR6Q0yg3qgNKqtm6GJhJLxfVCUBpyYRcWIIjIPTaemQR2R+j1WkwOiVk7RGSByNLb/6LNuwIMs/QPXE5Prh99wkkx+1ChVjOUIDFDt7BhoRGmdABL92dls7g4ti6sLCXA/1o1Ry0cVoIXEgjzpJjhUu/+EY7s3Z2Bmtv5ylY+lMwdGMknmyzDawDxGfBHoamhZamjOI0rsQjXWXKse+VkRBGXDAGo7KiR6o7zeJfBwGZbT2dnI8piymxYcKZCJMXoH9PYl5/aVxq3fjle/fg6EiIuL812cLuQUuXJ+ra7PKski7McXHZa58cCU0jebT2OvBtus+t4JfT8z/Hnycdjfygpp5tNS7+zdQvEwEkKZImN7O7ShQCpBbJKnBZv6ppa2suElPdRQxMSz21H0XHLlPaLtMHh3YQ36rqE6xrO14F/k8YGK4SKnELl9rGQESguiGlDSkpNG19IJDlAmZawX+djwlr81g4HJQy7NM19eLsskydlqiAsJ9eGd2qfQdUZ+f5VDl9GlyOzP+Ef7bPB+S1yzB+pm6bNQZWVaSUkFLC5ZKwXS5QVQgE2QQ0Qc6AZcIMsDo9FhTQ/AyT0d5AxUp7MdgE7uPZQmm9qA8lpV47m3M9ZgY+KZgKCynO+BZ5AfoDyA1MbHyefZM6JFNVkFLC21vC29sbLl82XC6VnQ3IO5CzIGci5/q7CZgJyYC1ueAkaGVWi4IgtcCzyhGEoFVDfw7KFOgxBniYpnaNQLJdZws9zhdDPxdbz6wtUSsLoCq4XBLevmz4+nXDl68XvH0psgMQWBbsO7BfG5iJXQWyExkEqsSlsRTCJelFcZSaFWwBZe2U1JqjU/wgIAXUgnTpgPcJmW7TcWLlFRPfYuwXoD8YsH1HpMjczb3QKi++fL3gjz8u+Pp1w9c/L7hsG0RLssV24Pt3gypwvbYmi3UCFQViHBlsq6dZSFCrubexT1srTdWB0pvD+uO0AVvjbwU4rHrmuc9ILDvUWar7keDvFRR+ALlxq2GjB/ew51QV25bw5UvCv/51wZ//uuDr1wvevrxBk1arTrEre4aPFMDKYPuNCSKA7RnUmkhJ1S4UQoVQFWgCNAEpCVJqO0ObdZhBI7JlWDaYKXI2mBGWM7KxXM7id5v5KVm+kTqC9yyySqa8bLsPKjfEFRVN17ECShXbJeHr1w1//vsNf/wr4esfGy4XRdoEKgrV1Af2AESSBE0FqKKCXQnNBLeL6+wPJAU0KdIGXDaFbsTlIrhcNqSN0ARsm0AUEMlIWwsaCYEBQuScYWbYrzuMhut1h5nhv//zDTkbvn27Yr/uuF4Tcs7Y9x00KwvDrMifdvqFcmN40AvQH0Qzxy9OKthUBdsl4Y8/3/Dl6wV//usN//73V2yXhMtlg+oG0YQtVQ9aBV//KAM2cwa+/9fw/Zvgv/+54notetoqW6oKVImtgjltUhaHsv5EBS+hij5C2SxX/rQ+uN6MYeyyStHlb18TLAsub4LrVbBfBXtWXL+jLgDAKNh3VpuxzBAnx1xDhsY1L0B/DIhrCbw0JWxJsW0bvnx9w9vbBZcvG97eLvj69Q2Xy6VadkUyGAnZDRlagFiQB5HKql8VaUtIW9HT+TpOf6sCkgSpSQwtEoMwpFTT3vV1GYs7se+5d0kq88KBnA2kIecMkvV3Yt+t/NsMRgvSq73+tAmQpT6fuCGd6NWAAdQvQP8+nBwqIqpLJVrlQiq+8uVSEiRpK8Hf5aJINVgzI/JudfRDcSk01WqPPIZeFtkt0CTYtq1KBcXlrdh4tDrnHsS2KVTK64CUDCBtJFLKoilMbCTM2E99WdXGRTIU0JoZLBc7br9mZCvgtmzIZsUTz+yMblZtQ45gQ0TKYwNhfPIL0L8ZortiFuksVTJ9haHK7+PyMQKigjnX4G6r/rG6Gd4waLfTDCklwAATgybFW52CZbnIhj51tq6sAuQxAMhIsG/9TVJUcDaAWy6gN6syoYI6G8wyrM7ptH59ZfMO4pLgseqFA1Ifi+785PjMPjJLP73kaM0TR51OncjqZ0pVBsy5ZNvybkhJYZmAVhshKVRYmDA15yJVwBs0JSgNolpdDI1bOBsTF7ZswLNs5ZCKlZqMJifMcn1d9SdYXBQH+uJ6VIBbc0DYZYdZc0Gs7zTEPFouVt/xxdC/J1WLjCIemcYQ+qkoNANVC1uBAHItPPLDe0qzRJOqffPwiVUJ1YTEjCyov7cqvNLOa0iGwpBlF8h9R2C/PoNs9zEH/FKkb8xVkli9b7XrrO4snaE50vBm4TnQvWwPbFn3uXsB+ndh5ePvMrXkIIvGLUkPwGS0Ed+veyng18LGRWoQKbUaZwOpMKtMbhmWpVpz9UyJat2+rRcx7dkAA7JVKQEpXjUZhs97qWFVfpDN6RgORdPSnIBMNpsuB+3cdTqKvIne/av76O+faKn4NZYyoAEYQKWw1xg+WZAtFCAJ9t2gmpGqq1FqMXJxLLSU1IkqLOcOXoggS9HOork+99DN1ljYBku2uu3GxA3cTUYA7D/HdVUiNRBXf9kcsFvFH/3uwBFoMrQh4Y3E1AvQvx7I3U4dDcdbG62mW3vNg7DWeRoECgWRWTRzYbEMqJR0tdR5hEYgbUBm9YvbLiDYWdhZtTB4m7nSwGR96y+AbH98MNh/VhaG0/pDW3v9nB1oMQGbEbxc6OgnGVz/xAztaovZdHSN9MWgSap2FlgmJDW2LkGhCEHuUG3F/QlC6zN8NG2g1Wo2KUFgDz611Ee3Vl1s491aYNaZFj1Z0q27BsLqoHSpgKGh2/MWp8MFl53hR/3H0M7W5Qc44oohdVpk8bLtfnumnofGi1RLrTkVKF92s+RQkx2CrQIgg3atBf2lsMi4Q0UBKeA3ZGjV2cgCETs6CJ15vefLyMhVD/gAsV/mFgKrjddu0+7TZA1odXEMcJfKPevsHINCHmqxX4D+jdm6S5DqsUqTHmZQTTAjVGsyo7UL0OojC2r1Wy6HTyxDdQNrckRq2acJa8VcLbbvp0MKc5vtaHXWpHUfuyVS4IBbdHMDZ/2dI/3dPem6ENpjsAG5aXJnSXoQk8/bCuy5O/jDD/IZ2z/QwCz9y22/m7Ui+lyygFp0Rjm+R1BRg8pS7KzQmg4slp83BaVaLMOLrqlDB8oxHqjsFMGB6KAczoTX2T1YhMskNoaeg0g3Iauk2DnFHC+X48Ox9MiC1TPYZqXtbf1WDZWxcwkERVPV3bVZrhA571BKKcCHgrpq5tJz3oXZ3dEpgQv8+qD62cVwM74bC3fAO7auOBzsPC47+s5Ou78anj+DlhanpccRpnIsKpcAsGpXrb4xjDDJpf7CUAPGBHKHiNYgsXQuEpfQKX/GKWtzh6uGD0ynaRtAzbG5lx8ZPj1P80kf1iwjwmOVVmM5PN5Y3JwsoVc99AdLtMTWs2Wya6syNihSSZJoKlt3TXerSD3zl8vPWpxfjuUV/VzAbFU/cwC5nfaW0fTc6BkZU4LDBkDdbbzM6KOOJyZvdSC+rLQHoC2gbIuIWAP7BeiPxNA1EKyMJHX2tjQACSHQ6nYohAqr7FmEQyppbiFMUFharNy/Bn7aWV/qAdoSVKLp4HkCLOGkRdO5I1hsKfCmhbuXToYRGTEZU1nf2XEDyM977OqTAXruPKpjyxe/HRtEtu4QqBaGNbEeAGaOY1KCBFEtSRYBMqyzsSxGy48G5QY2Q9ud6G5ec9fAcG5IlyADnL3oH1PgFwr2cwcyF4v8paE/rNsRm7CUkx5pOA9NElT5gZoWl6qNrWYRO1TrYqAV0VKavah7Kj8WOY5CJgaYh5MRB8+3wG3IXxcgOu0MRnnSA8BuCTqG9/HDKyh8ApcDvpGijIGWLskhsah0/CWLxBCB0AApEkQqi1OGlBnp7oEbMkfrsEsJO2hq6bsHFsmPmHwZLom7vskY54Icgr/e9sBegH4CuyOOZuvTWxm++BFHspd9lH+rq3rKtagJNcHC2jOa1Q3JHaVm5hZXe/BxWBV9fqytnYzWe6kHf9L1sg8UMcJKt5S5OAP+YugPr6NHEgOVnd2B/8acUo9/+PZaVXtLlS6cJxSjFi2JVDhafbwKWEOvnfA1E5wmb/UWu62NWO382F5faW/QdI05vX2ijYPUWGnn5xvt9skazfgMng8YtYNParfQgT1XmUd2s29070d1Saw2gPEyxyVYbBqWiSEfYs1He84WGO4RjK0mo4+p8NJjsHhfMIcefq9mjU8DZmluQZgZ70ewSayQoxvjUN0Jbb6voHcShUkFNcf9fWPy2W3huJ3v/FJK/mUsFL9D9PkrrLKELuAc9kiQKFxIricXHp/EtjvpnsTqQVOcAyLdnBBxrCmlC6ihJmraDRrzVonQykgDGF2gF+JNkVqLjTIjxf/kHJR6u85xekjGIDK9/wzIT0Fbn6+3XZMAnbOlW2KlhHTuSorYELEzsE5juwerw2vhMGPQJThEeuAX/9R+dfAtvTid+ZuCSqz6TMfS1WcPBj8ZQ2PaatfjKIZ13AJE6d1Ae4OP3vCz9WMes1VIHqUF/JRXBJCNBqNTFjFGiu53w+qgySFJEgB8Y2D9C9AfGcwSSpTF9bRj+L2yZ3BCYqfS4Y7URyVq83JxSWnpiRWegIkHJ2LVTMB551w4JH1UxexoyMTyZ8Hhq1njB2XmSWJEN9rdJ3YijU6as+7c45Q/GUByv1u43QpWM4BjIOeva5rcT6NtLXUDmbvFa0eJ8gnYWj8LmM8vY5iVTecocOFOxEOm82yUEfFx8ft4HHdsipyOSc1wc/clj7eZZc4yAOYN+fWSHB8Y4BLsubGmy3be2nWN2Tp1ME9v6VWCQbQxbZynTzHsBofpsAFYdnh9x4KhuQ7EORvi5qyQR718+KccHu844u0F6Cdi8Wneipsm1f1o1w5hzAEEjmMf5mNNXPDuemAPlhw9k6q6+o+RFh/pcJwq5pfL8USgHXCT47TVeS53tea6xdZ5d4D+4GqEeeE8Cby82I1BnrihbVzNMAzszqGT+wpaB3/8RDSlnwXMQEh8w/dIjkwYK/KCdg194dpRJ49fw1TU7O6/aFl08D+44uRwDUKaG4hzYuQdu9Fz6uinP/UdPWc5auouMXRibR8YRofkDI6x6aFLSc9Zu+Y9d9URgc6lTli4yjy5/PSzWNl1zyVInpihsQbwmdtBZ9vR/aSWxo10g+65GGhfGXz8PfZePiCWo9k5yyGuBetyIWdiQLsSVVGrywNgfzH0BwCzLEKxmCUcfrQP5urI4WlBtMQGMVtsUe/GQ1hpur79O0+v0ZZyYyhqW7A1D95KTKacse9zsvOTAvpsRp8fRDmnvnViPHEJETmcdjnKGN+n12Gdyb0Om17bHED63h0xABwS2UukljpfLRa+A8CvTOEHCQRvgTqCd719t8CxnuKmDJdjqcnloKMJwzqZsZrqii5fWmXdGDTvpwA0OVQXCY+vgWefjRhi7ez8/K/edr+1q3FeOhqttjZzUA6slcr1DsxFAujgUbq0cse0TPjgInThifvAOkuRA7RincMjZFdD6dXtCHPRkgCron95nrqO7blAfEsztpplcY5FCtezAlX6APn2XWuQITyAxwNFT6w5nS6qg8Cn17yEE1fPm/0phaPV2KWRLYLGM1/8HVzxAvTP1tBy8kWtnA8izP/GsV4ap1IDi5nDq92AR1amdG1NNwvmKAHm+45DBe3UDadiprXroTj6iIJ1dV+UOh8F4NvHAekt50lufDnHv0RyYG3gT84LSUOKVKBLZ/f2DDrZfpwCvbnCTdbgZupbfpx3EutEKHSfQmXongJ3jy2eleVE2swSYwX0G5/7WcNHeQH67+1zlNsMfbhM1/qaadqmp7+UqVxU7zgrOuyOeoK7J09kYvYAUJw4MlZ1e7Pv6rQj8cVGri91kDO6sA1XAH9v9nARSPLXg3v7EEDmo24GToA8g62yrSvMH0yt5wuAK3dk1sCexYurEA+liAvYvDSKgPKnvsZteNTGrWtTcFQYjoxF0Ho25t8E9Y24hb8G1NtzgnlmJjmClFLBrE5CtIAwwffjiCAfwSNa2wMH/FAJIlMRfmDdOWCLUoEtIdPmJIrXwOaCT7rZG5wWIl0bBpl2gTMa5UIe3QP4ie33C0C9/VZg5iOSQx5YFDOwZZIo2q/r4KQEC2/cTo+OiHscngZTTVenG2yoRxAJ4Ruoj0XFAeZ56GL3peGkhkzyZ+WsGI5Jl1tO0T32ll8O6l8PaN5j3feAW3Ce8NhOgkQcazMCm2voZrR+nsW35ppDDlBxabWFnYR0wG4SqAEV4zJf4HSQTO0pfHBo7r6cXA9dgPlW0uVWIubXgnr7PUD8iB4+kxa8EQjqxKiOnScZMX53P6kRRFyx/yob6AA8Ve6VHiA2BYyz3GiY4zEAFp98yY55Bb5Wen0AwbVT6DLmTG7YHWfkFrB/Hai3nw/kR8D8CLDPKuhuOR9NP+sNJ6SBW3HoqnSW6g5BWg0MBZgTJqOvXnUs6NyQQy+96iy3jkxVixfNXkbQURKkux4N2JxkiE3HzSxKPJlBOLM3JhZfAfmMxX8+qLefC+RHwPlXNDNOWHKWE4tSSnp9PLMwg7aWcKJF18/XvGxfW93adE2qIt7PgqZmKHYaFiA98FtHJR8stkUidYgo0KcBBCkTToXPH+HM1OeVhbelyM8H9W8QFMqDrKw461exZtn36OXmRbejVc16E8QKvNmzPtuSRyqcVfuK3/571ZxFu69//6kCkvG9dA3uez37QA8O2NLbBBcnx8b7lOFrj+AQcVLpkn3P/Ot7DsjPA/X2a9n5PdKDd4CME0931tKunqMnTI6LgqHYfzxmkA6exCtjNwiNcRcC340/AiRu476RjASGTOj1011Lj/ksXVp031mcM5JAyfG5agOd+fNrva378wvjOcwlgHnC1L+mbe/2ezGzvAP4WEiHE01ND9IGZEzB35wC17A42JeBq1qmvx1jSuSQJWzszEnPrgMrUuuIZZ1AjbjTiEXZwrl6rmUV82TzmftoFZhaordDDP3VSUwZrdn7HUz9g1h6+zXs/B4dLfdvRycJwkenNyTH4jXSMyLcAgDQaqJl8RiHgiDvciB06A/lpv4xxEuIdhC2CR910sFNIRBzBO8CSzrwt3FzXVtPAeISWK5rqt9BwndggdtvuyE/788vPlMoN37Knd8bLjyYj/92x0dwTIHr6YIhnRb2fXW5SrLoDftKF8+/8LHpd4oIjGPbMYk7xBQzPFZvcpRi4zHm9L8cpNc60L53flFuEN7vztAPs7Pc+YkjQxChMeI5c+NGogWuzlgGQS494Nn6s/gl++uFC9YbVtvtck23aPrgTr8YrI+/oJMjDGWsI90ufXdJAHZnSdokSzC30nNB4+TcdLfEuzHHRjsx2vh5f7YfD+Zbq/U+mBmCPZxsf7jD8FwwlitSmswyLh/rXk3D1CdvPsnStSujxDjzcVcYCYu6BW3Tdt+Lktp7s8nfvhOXyGRNh6uiXOIDX/rxkNmP1dI/OSh8JBC8D+bbyZW5bmNm2QGsjhmOk9882H9OEsj02L3BsxyBKXGMxMEl8M3U+1i4cRi32NgutS2Y2LKMag4n0QNe5HYipKfYI6pkBrZfTNNJHDmcYVyz9Oqc48mJyt8Q0Hw0MSI4r8VYrXd5zOM8PdUtC0fEhz46gd7ifcNTzfXMcuSsEBu5bZ6AFuRCFUibQFWQUlELqq4/Xs8Stk5NhmwEDTDm+rMsDqNvji5xmKdvjL3aUaoB3rzylg+SKdkyJhTE3n1yaFV2PzD8UU0Utl/JzuNNuV4Wh3f63sIlWTgZExf0x5+q4NoXW7/o8sWKS4wUr7gMafXsXMGU2b941QIUUUOSMmo5bcDlIkgXweVN8XYRXL4o3t426AaIss93aYAxy+itv1orXzPsuyHnjP17Rqbh+n0HSex7hhmRc4YZkHOZzjXmGK784Zj0aTuDyJhO0CeAyWpWUdTLvEs8P67n3vZzQDyvTq9b5ajHblbg3SvoXyg3maJ4Rl3YgCsQaFKIKLZUqvNSShBJyAbYTqS0FQvPiK9/lutEiO2S8P/8v/8L20WxXaQMtlfg//q//8C//n3pASFpsExkqz9zudyyIdsYCNR655XL2Ad3fvvvDoD4/j3DzLBfM75/3/H92xX7brh+z/j2LeP6/Ypv33Z8/5Zx/b5j31FB7h0SFy2IDGauMen4XBhbDNNqtyfvxIwT8L/y7Pg/C2ieA3sO4rgs5pcHWfn2dSILueFTe9KuEWgSJBVsl4TLZcPblzdsW0JKii9fNgAJ2yXhz39doCr44483fP1zw7Yp/v2/3rC9KbZNi8ip/+tTY0nsOeP///++gZnFezApYLZcWNPGBpJzqZ7LzCWcoxVZkXMdxSwD5DBYNmgSvL0ptm0DVMCcse87cr5g3zOu3zP+53++4b//2fHtvwXc37/tyEZYHjpfqgQq7Fx2GJHxe9wlyllGmo5Z4nSOC1f6+gZsfMb9d5ccvJskOQniHmbjIVoaxRTW1WmD015DIQIkVbx9ueDt7YI//rXhy5cL/vjzDSoJmhRAAk1wuWwgFduWsF0UaRsNxv/nf19x+b5BdIdqqgPtUWol6t5sxj6zuzBbYbgiBdg7mhoBWkmGtAXRWHTPuW/5fiaLWWPx3c35RgFqlQwpCf748wJV4PImuH4X7NcEM8P1Wqy879/2vthTanoZtdIvyovRRFVB5CLQrCn2qQUZz3QyPwhDn7LyDaDy/e7H4VFEoKL9SxRRqAhEtf6eIHU+yuUtYds2/PnnG758fcPXPy/YtlQAybYQkgskicumZRGkYpVlI2Q3ULVPek2p6khlGF1hRteGd7TBzdmqHi5AtIoWD9Q2K8Ua+Ku/3JjSaOW+dQH4n+xjLww01sBTwK3Yd/sueNMEkPjy9YKUpAal5Vmv14xsxH7N3VVpUqMtNhOBGSFJQCs7yuhAzJ5VlZ/Yo3r7sYz8d7S33LmFVJ3XwIv6pWn5uylUFZoUKSW8vV3q5Qlvlw1pS7hcLkV6aA38dET25THL42kFs6ogbUVja9J62xIEWmVXZnTmtRrAsWthqYBD1dIWRslls8LSvc+H9S2dtKCtScLyXpqeGvtQ+/46OmMXZ6S4KgJNQKoJGTMpc0NRdhJtBX0qRUoZcbkAeWeXKI2hjYCi1JuYAVBCWQA+bD50Z+dn6ep/DNDvH1S6cCMeYPQ2B0VVAoC3i2DbFNslFf2n2gO+7bJBQFwuBeSsX+C+l4Kdy6XpOEKV2BSQRCQRbG+pBIaQzmIiLNqS1TZrBU9NYjT9vFcQZvTnBFkBCOQKvvI4VWaYG29cHzpbrrq0AlrKQrEeLA7QWwV3GXJrQ4bQenyhSrBrZLgspPTdYWhj6Y8nWuRFa4ZmNWhUeGdmLBJngJRv7icMAP1FiZV7tt5aZgi0M/G2Kd6+JGxbYc2UdJo/WN1lTbBMqCr2TCSy1PQkwnKGpg3kXoIq6HCl68uwXNLN0iSNFSmVlHUabGEhyxiArraeZXZNa80ZIAqLtsXgZnR7hi2s78AYRiA3SYHK3PX+TYN3qZHrZm+xTlCAtCnynmuGvS6eKpNyPRDgvW0ap5mIfWsJAZ1qORpW3JSRBAq3n72oj5kpfBTMOAGzQIX48nXD1z8SLm+pWkpFp5oZVNKYxY2xvYtLW9OsWG9g7SqaYULQBKrWm5fTAFGDKmFWrDm5Wrf3rmo9+LRqt5XAjuU5pb2uwsjW9DOr1hUBe/DntHN/rda7IjWWLQych3vQ9HPNNJpZBHwFutQkzRg0VD4L1fL55b0mTWCAMfjMHdQ+DmjM2x67HzlDn3euih4QixxTAbE55odl6PcLcqls/PWPDf/6d3IeqgDWynWqnUUGMDcdK+5wiKogZ4PlkqUDBGkT5FyZREpSYksoskUN+94YWavOrKPeWjqhB0LSt/02h9vMABNYXUzjtlbBUcpCzYZM6BO+iR50dlA3tuxB4HA/2uO1BdQ+HauuRVn0fmptuV9jadiYimh+sZmFcTIhQzq5L+ION6hKSJnz0YPjvzeg37kOa+C0XQR//msrrkTS6hggtKuNg+JllCD7OiTXk6UAzYrjoQLuBq0snVLTwDuSJpgVrSlSrKwsrIU+VX64+YVSEy10TGY1CKSbaUiX6QP8eM7BqsPDdmPbakAIWgVqvV5a0DkCxr5QOMBeGDIXGdN41AF2uBb6NwcAACAASURBVBJSP2Q3M7G5LczTYkR/P3AJl1GuVz4M1aapJ139F+Hx0wAdtpU7Yd0otRRnrBeANDZOSfs2ljP7kxS2pfOcK/n6w8zZqi8sfesHy2WswFUWlJuwBisVsCKwanGVyN/6QmpAVh3sJK1ckwyWVtPR7NOxpGvb+YMy7h2I0lixsW5ldhE4W6zzaJQqNbgs97eewrIa1BXfmNC6u5RgT2rw6Bi+B3fWJ+s2APfANXQmrWAWz9iosoo98DyOwePvKTl4UpAkN5DtqwhSkmqv1eCu6mJVCaeU/KJoHxRd5Vz7fFq0rrrV833DRrJsEK2MCumOhdkOVa1sW59fUl1o1u1BANiRx9RZWliUBXOF6ULBaV00Fka7MbJymPkdmdnXYQTAMzsAlffXPtxcgdxsuX7AVoZbYjnXJFC9b3+N0Tv3L5tT5V1n97rgQqmM+6XZk+O8xD9v5m3/DIBPcCvBNR6sWn8mLQkRUekFPb1+QBBkhah2Vm+ZqR749VRrDehECgs1TU1fNlMWSUa13lpfft26xmy1DKy7BqSe72NbJJyK/h3jdU1tk34cvTakMRpdogRDkgg4be8WAroW+I0tqQRjzHWRNplghFWHoyfvaDCJNY25ui4lY9lYOAas47u3I5jRkkJw38dYo6R3NKTvRJC/Yvf+g4D+a0AeAG2yooBXw+8i/vcBluZ/9m2yyg3p27r108oi3rJr1hd74qRtm9KTIcNyUuZSdITqufaAMvq1OV/ROxWtBsfTAhNLLbRv+rKV3LftHhVENBuzVVwQ2EHDAWRfxjn0d3bnWtgTOk1DC8cI5SJhhl8eFpQL4M7+HYA81Wx0sE6HIppS9PqU/EWS49HnlclU7GDRCAxV/28dH3q9T//ZFoHTX5383GXG4jO3bT8wezvc2n1prfXD1llMVbssL6eYBNgJ7YU5ey1lknqYu31p7bVrD5wGqGUEqra7cgALYGaXIkMH0w2o77eRAaKSjeO0C0jXzn3SbU1/0qw7HSsdPGRT3E1G1g9Ltp4dKQ/y+FgSS0/5zzPzw4B+H5hjtVsBpK+xcA1X6r9LkNKAIW6QK/t9xwfkOwpxgBbqWESmmdk+iCqWnap0n79p63LfXCRFzhBRZMeqpTmLdE0rqt0xaUU6oZDHgaSX6zhhSbCDe0zN8n02EPxnP2bZy6fiPngbzgWjPaAbLQt6YtPVV693BB6fj7fqHTyYJ6fkR6cHHwH0e1aQuP+NonA4+eAkiEhYxSKFLVV0bVEay4FQPzinfyv1cVoiqp46KSybxhftjjsJWnZPAJPuFjSWLfXJbeEZRFIHTytwKvXLTZ9bZGX3GgvYm2/sOyUxJD7Cts1425aybjZdv20HZGyf6zVufDx2Bm97PycgezCHoRoB/Aw7cbDqwAMzx4X5iwD9PjCP825Nqw5ADC17iIidFobIHBB3O0kgmIoCAtPF1V95WKumbgDvRwDFnV1tX1ocuONZuLw0c+f8cj1VLdNJG3P3bq8xw1d999ssg0SGANb66W63ALrUmqru4tGRUYMBRID1FmEWdrxZIvjP1w605QaE+oXA8U4O75Fn3ZZ+EqAflxgySQwcwByMeWGwaaR7oWM7tu4wuKqtOJthFO248WxNrsyZu9B4XEZ/CYbX4BeGuM4EI60+zts11hZQ0qjIEXHDe5xt2Q6xStSNhK/NcOcAe+bPLQC4ZFI1uIPk6LKAjlEdaOgXkzhb0Ad0NhZm2/Xm7+/QXne1KBazEd08jmOu4gfbdo+5GFEDrwpLRsJhvt/QuMUn1qllAEIwMo79OE0djg+dtKtd8IDU7Vs1Oc+YYzGY9MeHcwti7w2n4yVG+xIO0nJIIVhdqaXwyLdMQKg+mw6W1v7PfcHRLf5j065+JErC1u9+ThrY/07wWLHs25sx1n+uXu8sK+ZefmG34U+WHDehLAiHRz0rr6Z9jcub44CerZrPth1soLknBRkq73j4UFm0szHsEn60sdXyR3omog4rjAQkHaJ5usXoA5/hXiwYgcOI7Rk3QaicG+ylVaaoS374ccdjJ6M7me6fl96/Dtv/OOR6GHI0a/iAeh52HR7GP/NmwOgXPH+C7NgeY2cP5sFYMh08CQOb5sd1iQ4JnnBNResANxdToc67XiIw/0jFYnn/PiTI99iokr6/J2c/CbTIpbZriJ8x6Bh1GlkxNGt7fg3BHEQWDCdDmzIycC/jFIn6tc2KaZrfB3Jg0NGY7L/hlKxOgju7zcuLeVdZ/E7MWZOffWKFt0bFSATyRM4ix9LPlqWLrsZIlpTPNc7GLtVmeqj64DxzpGlGMQjScC64bsEjnj0X2n6w/0hd+3ZeBGudjUyTGiRsvkLfE3oYcyPezKP2iqvmOXbkLcZe0ASiqyII7sWQSctYORRJHd+DP4svQ2YEwB/nwaxP6xPrBjQ/RjefSo5j4bUsADwFfSIHAuWyDcEovBFxhUnO1eAUEXeHhITvKVTSuzXQNITuQuPDV7eRaj+J7DlD+lbP0Sua0cMF8ujz1qfJ1ihf4lbdTxxx/kJd/znj0sgNOlVmb5dhjwkftGNEOhkjLgILdSDzAmoF+pTg9rSFPKOCnunnxMpCahzl2Y+F9PZokgQ1gxbSmg7Mo1hnRLMiiw8DfvfzwVX7VFqyInbqGVubYG5R43XiWEdxxENoCtOzfAjVp7O/3W5nq0Hvh0YrMUw99lrmonVhvPWhD8w0v4KY6kFcsjt0TZqKmeATTK4ofzzv6nS9xW+tuzWL+d8HZPMmK/+8I1hnUqIeGo1LLPoWw6qaF0KUAsGTdgARFyS27a8XuXQ2b6MUMGUCZUhYIgYhzes+bJ2FnY3O6pPharQA9hgQNbchue+aJ6WRMjr4i0zakrMeaBUP8aT0ooqHk88R3QRns4Uwg+4z40GnS3AHeZLbjosjBpuYjqUssoWLGpAfA+iTdpMS2mBFibGSIyc50ekDnx5vKmqZm7CyT2v1CQrf1Z6TXmYA86idjqPNpFVksFbVcQpgbTV7UA7920j/+pxfSxcswmtvHn3eINuKlAqWJVf9TXgebtFN0u1Xp2EDhp58cfckzzSvO0t4Guz5nnoxKFxT8w/2oR85rCjLGwpu95OUxSp1i8XXFzRN6+lF4sy/VvbZblckSj2d3O08xgGV/RNNw0q0KB06Ux+KecVlw+baB52+1rn6DouZOdO4tFF36QZPSfSKT8FQKwR9ECo+e0esJgbwMN9xnnal0SGZQCvLoPPsff6UeDACWuScnbspFmpD59s7twByZ9iuHJIw3a/s/ZN1Squ6oIjuORnzbXMQIJSJw92oidaUsT6P1S9VDlr9mP7tzcXJGBwv96czhjpObeWyDfW8w9gh4KILJI9NGSMbh9fe34OEVDymve/wG2drjCektZIjP7tzUqi9GF0oSZyCml4Ln36xcjTY6WWE+2o512rEyrnhijjLLIz08RLAzdtuksnpcAs1HP6RdFHj4UFtAxChOIkBHgjMKTGdvzy7JreZzjsR3inqk7R8YHej3NN9zjwJ3g+S0dt75InkWUvOn+FGb8f0g4QsSRyvt7LiJBQgtYW7Avbpm+JRrhCu2Tdb1dtJcCEMjoZw0nItmKw+cy9uP+THnPwJjcllHs4QS0lxbD7ZMpBeW3uv2ksXWdZKrABu6zIE+sUsMUV/c2TE/Lq9T28IkbasN5oYZJ4Fk2dt+n9ggf9ZB8hbrIwAZgnWmnA1ZSOe0D7qusGGoXv+5Igc3BKingNR5wFIbTYoYXscVWyLgUF9C54jsVinwclxsdlGPCRGZPKAueDaW182QyFW2MbFO0bTCIzDqZKZOCTGJ4vmzzyVS2siXteEyBQ3/JJajkfGFc+nQ3z6eeFAc86Ny2LEhhzcAOlPosGzlYkzQ+ekoL+1ZydnCSJIk404de5vMLA5XT0vsqm24TQI9H9s/Rl7Xe7fh3DMEp/qp0M6KmBQFoMzB9OX3Wyu6Z5nsqze09/F5g+u5Vins3Eo0L+5AHwR/mFU8PlbWg2VGMJQEb8ymWDipqOQy+BTmt1XZ/lJ87pF4VMiPdnjon6v/4/VbrJoA8xTy/Lo+rJnNOMCjvKtLxJzQskX+2OVCZx3htX3wcWApIlIQjCaJ8V0ljDhwTf/WX+22ytIDuf0jrUbchoArr7MtY4+GYTpWGP+4CWwXL1e5uE3UVsOlp6tNz/bpXWmwWI7X03f8oraIqAOMxINsfgHEyDlAHiGPibWdxB/yGEYaTbZbhYlU5eF6j4rHmw7Tp/b8Dh1FF0tfZxFjHBTAfwwDX30ieXElD7vti7nzc15A+RLX+9WB//2wcqU3kZvGOO/DDlE2ghfXkt5x4ynBXuw+dcnntqChXjgYn/o9Vir4bJrh2o6f7h1PVebC0DKPIGKMVk+j81q/enGMM/jbPV54d0OWtdB6C9kaETbTBYAlbPxwPP9uMYvY+Bwa5s+92SPW2vcMOfgqn3xilZ2uZ68xWjRwSbbyi0m5lrWebbIp8CKq0IIWaQEYwJp6RaIjbS5jENgDN8R44zyE7HA5eu6Vzk3vYe7R65+uA8ta+adbODlVXxAX/OosXg6R09OhI+MIFBc+anXg1PNgs8o+oqp+GoMxwGdFhMYlBtBnsuucWWZLQboHD689tz5JMPKEwDY0Wqgt//iua8ZkP2VyVl7rlVWcUaDPeaC/MQ/2xqyOAxkXDHPzIRyI2jkTdY9SShwEWbx5GM/oEa7PScL/Syge83HcclzHbI4Z4Qhm5iHJOkFQcSyMeF0oHXoXL0B3ik7KDZbPE4z82S7t5OM55Qokpjmj2cuJyzw1mE3ntae/ARAc+FmeLkgp7qaU2f94wm/9r7lxpKdI/v77gimsG+8GnV8kp0v7XWxBK+6tvheMHUECvusbP+F+fZcuUsRX4552NoD5lqRfo4yR9YMPZwcWciWY7A+KvaOk24lBME4gnuZJ5kDV8N9ycqfytsniZUT22bkpQ9alovbUmSxmmfmmY/PCx4bsLnyrI9H8eNS88GTlxYzK8uizsGWX0zc8W2qG+ZpMuU4E/zGd+4C1+PwULgG2LIQRuJKfa07Hjww+4rRV9+b/+71hiSa/euf08pguwWQWfP1dc2FNOkZQImgWARvsSHLPLeQuGclAhItqOWHLQcb6pgwaO9xBrfrwQyd9A7P9WaztzifQueNKgneYLdaLOWanfd8JB17nwbVTmCJLSTPapLsLHO42JV5A7C3XseP73i+3Qro6A+Nikyu62jkcjt95Fmco7yTq60Qd5hc7wSiM6PM+nR2EqZTLR3sUktPzVlocjNa58Jnrn3s/+KcPt96cWUIDoat7R+nEfQn7slBzmjcfegtRFskYbC433xgQU7cEjnbfn4MQy91MnEI9uKA9HnK80lBSKhFOMuyrRMxsf5j7QCPRcYDO8ZAb36YXAM6YJwWqV2SOGrxjl8I48LhGePYucW1BMEjX/DkNExVrStbrd+kFW6Fo2on07k5gV1WSZtFUdm5bvoFPjRn62AR0MmqqOgMxLjhId/RyK1APzDHGpickjvlCJIuCnI4BVvqAOmCx8N70+gMSGRuSgRN/P5WGtwe8Nw5aeOTwNV/NvMxKFk4RVhN8fG983zZqa4tQsGdhfqIBPkJgPaOg+fAUEsRDu+dORjxQOqZx3kszLlhK+FOXcChDNWOuvvwpd/XdvQlqKF3SDjRGPmWZ2n+2wdIb3r6B28Ji11i0r4rfT1JiXhbW/ihPFlEbrM9lVSPgPmfA/nahw6WR6xhkCVzn0XDZ+A9YW/O95sDxjMvdcUQK5/UXI3ySSJldm1cbfh8HH8s92NwxJua+cwXliMr88iIxFlQdzaQOp7mKVO8bpVz8oY25kR0zlHyJ21xjzR+jCTZzgKqs3oOLp1gOQfp3dhnrqATXz93/oaJm19cWIYcCYzmu4rbNdi0NGcLizf98zXbuvhA7MYXxsWucsMm44oo/ACaaQdbSkc/IWAGurjgcqo9ryd9uMh8Mq4UHAxseeRz+5EaOkilI1PjsD5X2+EtJsYUDMohSXIKZAK308ErvcjpWGdMrIR7SE3G8OzAr93YiabFJi5tLi6NLvNxLFvYm2dxBk+yg1w7TAdQ3/CJQxttn0KPvgrfJSF8h6dH/vx9kG93Y+nTD2XNymcBYjuSNZ9KiUvgpBrvsNyPC4YHQGHqDyfH4Iu4I5VWrgYHMMMiW8QCjaEX5yPHw8m5z35qg3ng39oh3fMJ1+8lfB7s7c8OwXccM/YOcC7OTIrc8d//Oqj1ntHfSx/9F8HbBNWbFBOheSInpbc+mCk3gLzml7kL6bo8k0dNKIvLwl87/8wXUcUxMVOny67eH471bLFj9Pzp3Aoi5y3cpvfQOz0uPofpfRN39O+jYP6rl+FHMPTJWT+uAMfF0aoVI8r9N8GV48EDUNbgv1UxttK6K1/7JOtI37JMXTubKLTWbq7cCdd4PxLg2WBsLuSe9UWxjIDCY60CZ0bgC06ck1WS5l7iaFUiK74J+N9m6Qf6Q9/a8uQdq3CVlj4D+foN+Sa3PLDJmfUlZzrk5H1huTB9gml43vfe/6o+HDdKSBGLk7gIoD3AZnPqtMx1tbnYQkryhlV3C2R/BXzTdyE/hKF5M6DjXQFww/w/Ay9xCoJDHQQfcD/CA+vxcU8qyGL9si61IJfM63an/lL05DvkUTsL1zuTTI1cegApw0eW99SGzPPA0HuiiNzS1XDPc+I0yT2Zco9x5Y4z8rc0NG5Hww/f3n/xumbkkyNNkR/E6b9H2UCOb235GC1ZfpQPtwEyMSq9hLlx8oYy7U4y+eKIxV6L3npD6/qA8ywWwGmM0FvvSpMpWAsVObMSbx1x5gO71j24yT/B0I/Jj5hD80Uucv/+PH+DPN36H9VXc1GDY7XwHHrQdJRVbYneUGC3Dj4c7c3WiUlcAZN3dc+mfa0OTHVg86QoiDIFvDeSKK0sVVantF2rtFu7wbskwzuYm+9nbL0XaNzyFmJ0zwUzyyKJcAuccuOpeUdzn5wy59gl1oUzirlXSPSdfRA5psMG56dLFtfVNOxM5XmGnJGwqMapvyipZFmrvQAFpyNkYtHhWH6vtrDhzsFP3mrGyRuD6P+B5Al/GEOfMeu6fUCs/YgFNjxhi8eWI+945zw58qXr5xQBFgfI6OUA511DTn2CDtw+Ts6dxBGfuh4nbBqrrpJCsVzTpellXnhnhxFWdDed1D51UmKjdb/05kzAD80CPsjWD9h2t/qSyemlo6+zLHOMa334YFQc5IIcAi7eDBDjIhPXod3XErNOxsLU6+//tPetP5Zl112/tfe599arH9M9r56Hp8c2Bjue2GMzjvOA2HGckCBiJcCHIAFCgIQQiJA/AokvED4g8YEPCIEiQIIEYVDiCYbBToyDnTh+jePYHs94umd6ZvpR1d1Vde/Za/Fhv9beZ59btx/TXT1TVypV1X2ee87aa//Wbz1+Uk19GrIdVHbeVKJBw/FiUZyx1SUiFbeMik7TxV9uSWJgfIcFodEfyG3nEQLEwYzsAXEpt9/1AivBm261Vx6QjRo0t1LlianhMQ+CFStQRdIaybWsek0ZM8URwVnOOcu2mdR/KKIK9QfDZnSEbhqfX7dGtTKVKIU1FXsxKNYnDd+lUrNa1UikcezUMPZlae1h00NpIcvKE+7GON3RE2GWcNOmuaBXW5wrdK4U9Q9Szms5sM2n+p98Kt4YhB9TDJwUGAgHYxYKP4H/JjVzOi5cokqnxGBYOotxrzcEKyATfsjAkIS/yf9OEICTqoAwZ53CUMSfxnkkEfsSQjRVf4gwnGsXfpOWpJPKsHnAHd1273zzkOMABqFpcMv2AloCLWg1z0wyonBwUKWaMjChJL1sjIG1BrYzMGRAJu8q7CSMfDZgzkbtRTvDNdUOVsqxw4Vaquhjl1CrJDDWwFigswSyhK4T2M6i6wBrDYwRmA7emJNkdG7EFxawMIQFzjE4/BYGHAvYSTh2L+/MjsEMMGdF51L4dCzbR43Ar/biQ9GmupfpTt1u0EOPZdZoPPottmKNdQmj6XGtQ11JlSwvHl++IKMxW2NgJwaTiUXXGRhr/WMgf9Gdn4vILhhDMGxjvEF4Xe54CDaPngPBGIKxBtYSuolF1xGmU0I3MZjODLrOYDL1BmsNQDbsFDaO4vLsAxHgnFNipP5nsXBwzo9dcL1Xu3I9B6MmOOcNzwWDZhb0C/9Y3zPYUXoucwCFdWMzje16siRSkwatyfWUPKyuV/impL4PYh3kgGCu1eWyBIPXbV5Co5N/Dj7G8vMowAzbGUwnFt2EMJ12sJ331mkj51BIFYzD9eK9GnsPLQiKYGRgLMGQRddZWAt0EwtrCbYjdJ0Jf5vkha31x2QtJbFME1gPdgIO4pzsOBmwFyRF8LT+fnYxw0dgdmHxdGpkmD8Djn16fLHw3nu+79AvBPN9Qt8L+l7CwpQl43GXVaI1OsNpvNlh3P3I3fDQre9Jq3HEsoI3rUt5hRqGOVZjsJw2iooCXWcwmRhMZxaTqUE3IUy6LrEcCB5aggFbC/AkDrAJBi3+fzI+mLTWG7Y14XfwzvF+ouh9MzXoek7/98wBkoTRNSweRrBLx+89sjdoj4WDYbODrjBk5/znGZtOi7EGax3B9Q6zmcXerkM3Yexd70Ek6Puw60iZUlluzAfVpC9/nNCupr8DBk0HGHKd2LgRio+aCSdgle1Olm97+v+AP60lv/2vGcxmBt3EBKyKpP9tTPS6JuFL13vYxFwuIBNwuP+JwZqHG/5/CixK1g0kGLBwGPXMSaaYhZP2uYhAmMNuEIM576kjn+1/uOg6EQkeOyR9hMMoL+YUH4gwJlMTFpANi5LR9w7O5bINGXUiWi9G2s+RsawiMGwWiLz2inCD3kwPvVLg2GIxar3osZ7DFam7UZoxeCgi2A6YTg1maxaTifEww3q8SwR0E5Mzc+S9LYIokTd6A72bR3loIm+8AEDGv4c1phg54vUOvdE6x5n3DkaaDJk5ic7HYC/CLQ6BXTRcYSmYChaBMQHGsIDJfzcJWJySsJgBhGGNwWQS39N31hvjj8cHxJIDR2pQkIMm21UxcLttTaoxQ/TmeeiD6ipaz6UlnrzVkSjtNPnotrU6eW8MeVgxsZhMKcGNrvMe2Bu0ATtJXLQ+Ymtt6raJgwuJjA4zVUIGCRYQmYRfvYHmUbhEkX3IlJeLmDl4YRYOHjnjYVad6vGxSNd5uBGHK3qxUQaUtLSkoJdC8tJYwnRqfW2JAD04LFA/o9oYk45zMe/Rnmlyo9h3/HreLuHkbuXjwCqZPGXYtIzKo0KbepxFqU+EWckzRw9qLWE2s5hODdbWO3QTmxnGEIiB/IWk4NVABMfei3HArsaaxL/Grb9UY6yEO8OgSGZOokWc1K7ClDnO8AIU8XGe0xyrDD0c4aK7WkDFAvBQQxBoCzUHUgLcCFLPYbEZCGDYj+qUGOACfc8pKAYJJlOCtRYbWxaud5jvO+/FE0d/q9zE+CDHgeXQbTHougvFrJ5sl+WZR8FBHvygQGO09xhEwGzNYn3DQ4yusyBLaYuPql7GGCVjF4yOPQ52eq41u7hKUr1cxL9JLzHgDKKYYXRh285zn1lcIY5ZU3KF11UZUAEVAaBI1ZnN9YR/tcg4slESNNvDIjA+1PW7VZihZ2ygLEMVZRimDhF0Ex9QCxiuZywWfaI3JUKn0NVU6jmOiykdZEk3Az+6G0vmyPJs3tIEh2mYHg4IJHEA3CiFNMkC06nFbM3DCtv54Myx89OdqRxTELnlOGXJhOQF9xyovDjOYQg1IvZNMsvFiJBSSF4LiUtaDCiMUxI9J4UKa4QrkobAVBNfk/evCrMoi5MSAA4JGjaUWB+f4CFMyPPwzAJnTMbzXDYDxxpq2xlY2/m4oOfAeceEDSUWpog7lJHLDfDOtXjEzRn0aB3ysuGIYzRbNRhF6kmg4wHdOGYOk4zCN7UTCskKYDKxnrmIWbVgNI7ZQwrx6WsTdO2jyzYUnoMuJDU4rBfjh4MRZ+ZAFyiFyaVEpGRUWKndckrqFGnowjMj8c9pvoZiQQSsdoMSi0bPzGoIjjFGtXJJ+NOfexONmSUFtDDeSIl9ij2m/b2Rclj8rvC2MQA1NkCW8L+IwLmwI0UUVU2RIpGV4QnRrXpouUHQ0qycG4caA3HHGy1WCTPkyADWIiUujPXUWSAokragUPayIs7jZTFwYJAxfns1lBTTvFEhFSkVQ82VyyFjFAwo66ZrwXc9wiFLwwVPDc4yGilNrgw9LI74HAnHOxSVN5m6SwsvY+zoaVOXSljQEWN74w6tCCbGF5Q46sSixLoRtZNQWNjR+EwIOeJvkXy8+tIf2Jh1E3RHd/uSNUOZMCmKXFbFzCNTPoORJkMOtRA2JC+IxNNrkECVxQsZmAxmWNtlrBeuqBDDMBUelSgYeqCn4n2ZLgtKuSwFrRWxdpp1V3e3SFkhJxUMKQuHZFC/FLu/2XHSm0mwCN5T1h48hq5aGYFSfQgFiJI5blDs7g/NaVRNY6rHUYjSh1THnFghXZGX/FkeuDMGQugmubsVeeiDcPOqUa6oL1u/E1fFMmWpZPS+1kYOWXnjivONUT8BsJTZBuf8ZxifBYGIXwTeeNUFqiaIFLOfUyVbn4y8KMNhbizvWm4jbOVpt1JbstTCo1QuBN0orBMrHgSHBZW/UzKqxNJI8tgxZkh99FU5a7ELiBR0tCjoASkXTlx4VBFkRJWqXaBuBimxWyCib2tiRUa9NVDPNaWRCi+q1g7Bc6bGAtYE72Iqlbm0DXNeLOECJj5Y1XN4TtfBGJOSHSYEQ/4oGOVALFWZrIyLo2CQUGY7isFjpdxcYjK0XAaVcGZQ46xf06wNq3TTpUxY1F7GQ4Ec23AfDZ+LxVYsIEFOAEWL44ShhgFu4c2lhBdVVWINP+gW8+Hd7TJlOaiI8vHVPwAAIABJREFUKa5hUuZBVVBZtQGZUAeRvLEK9OKWiRAwgWMbam59EpdrJkDkgxi1GLzn9t4tzd2IbEbVxlXoGWpFA0bA54onFpURLLQGG2cmeVzOQyqrKacijVYcCrBHpKL/KemeN9XlkRy5jyOo1GsXzYWLKLiTjVrTjpKOV0obrY45wQ0eBwC3o7bjNqe+pTlugJQoZKytoMHeUsomxLS0tSGzZSgrqhKKKF+UFkoSJ464V2XoKCRGfCJFA4qMk1HLJQ+kiJWRCQp6TpRWi6REyjJmNXpVl3oN9cKB0i9PWDOcM4oZyPrcxwWvOtAFVSCW5n5Q9b3y9xCF+XUqXNQiEjVCYbAQilghy0uXO+vt717pbi/UaC0+SZxo+jE5AaI9VfQW8XFfpSYpAo9YGupi5u1RkuekpPQkIUKXqqMkisZTqeNNeahkxPW1zmBePFIEZFK0WoVOkqKzpuSy9WLJ69lV75ExMAdjpiJeVFRYrBZMC1MV/0Ss3xgBnrpLihnYUmQCtVEXwSC0cpmCJPXOMoA9h6IFa+hhpPVYPQkoeOQIGUwybhQBoknGrD15nFGd1WFFzSDmZMzDYE4L3/siHA4wxWY6LARQyfNxrjMuT3o0/Gi0LgWHxTFRtdMUuy6l9DcimwIo3M+ZAlOjaGVEhUoq2EIwiZaMtR/+lKqgt1Ca1UZdZhpFOAHaWIOddyUptQ1RSyZWTEd1XW6tFuRN8dCyPMFSzNCWVCPhOzkydNCIw9pMm+nonqoIP2Jex6zuJ4VZsziEHtguwjBkq6n8pEZhmZIma3plqVoBpfKu2XWqo0gLRosJ6yBOQCEYpcEo29THGDFxQROWjIRfLKak+PQskpQHr3RqBgbHlYY1Fbg+Lc5Qr1166orJUW9TQxCiQ+Ch26Ln7UGOqXqNvNeNwV2GEJ5+i8FfOegze6mY+k3dGqJxs+J7i4sEJbkQCnOSA5XRKaDUAkzCo/Up0hR918I8nLy/FFBHFDxQc/WEUDKz5YnR9RultF4IgotFZ4Zb+8AXSfVtZLizSIWPkc99MnIZ4uj2ghlzGnfRQ99IujJCjVQAT7nJ0xpS9U+iRk2oCcnswLqskqWskQheOwcYoVsDpLJYZbJCCtIfReYyF+VIVXcgjeGlXNBkpepBHSAjpdE1jac/M3lW4oK/5kIaxAQ2ZDgeQo+aHBZ06X/LEQkD9d1cXVTGF0WblsL/Bc5uQ5EWnf3mK8muOGZsldR0ZBViN7VJ2DnTbiltTCoSL1Z8WeSSOzSGFFPuG/RJFEO5QD8FiYUgezVdqZ5qxDWoiDxfuW3mJtByVh2JUYuIVJu4qYazq1ZSRSNr3UWg1j+PmN+kRUopQwjFnGs5DJ2NVMuJaGhkouZR63CApUjB55EPGASQ5a55Z2/d7fDIg8RJqE7TtRXFJq3afEhK04kZPikoo3r7ivRbqCZLNskqgFMFQcLlDACW3HkiKsQJxlFLG5cdNqLGL5K6wBlSSLF9S8LoZYEnJf45wxtKjElKseuaDMAzMaJoxlDDkd/ZlPBBZ0dqIc5CXFfUuaxS7xF2CRc7kdQp+oYu480Nw7njtN3y3kFCWShUTMXiMi06SOEKitqDVKxVUGgN0r7oliEFPSUFSzSQoKNGACgqYMQQTkBScFcaRV0Oaxp1GYw8JkZXGyIHwHFojeqRiNQgIzcMoMHEaNZoOKq4nHlXD5shRdtluTY1004xLkUgKJWsneQuJA1B7pTH7m7cO4+XXZOi6XSNhc68xQvSuhgyWO0yqA8uIMkgqFN65EJZdCp6taasmpQiNoM69Kr/TUoUnLpvEtalYRJmNDkmw2dJlfWmEitLNTQyZjpJeU3/ioC3UyzAxbfRUtI54OOC7SiYiRSYKuysrks5lWnMI8vdMeihbvcyqq6u0SiDv9TIiUaXoJSBCgo8VnuDOuDgYkw5dFYwSRVXBkakuGSDYY12SemlXiZlCiLjZ6tIdgoGc7RLbXQqomiR0tw050Xa26OckyokRZpeVLH/QFu9GtdVGCBpg63k76RWUYCi8aRRh9IK/uTue+gbI/I0TaecHtUetW7VKoecDAO+VpSsp142ZCcUxpGU4lYSgUTtoFK/f5EmHkqTtfvpyh5D0ZP4Q5IjG5kq5xxM/q9gUVEmQGU39gEa6O1tZ4nEmwxHrMkgy6eYj8aJGHDbdzBA7G4lAMQgh0VpAEtJolN5cTH+haWaYK+x9XDTFlUeiXFto4q2Ej3etmB+qYje85D8sYHAVGDGZS39IlScpzILWRuzGpROdWHSmLIsMJyoUY3FpVwmmq4JiuxH+PrcmHlU1W+DB5OaWtfpTrMdXc0fjx/AQdg5jAVoTKaOMCJ7xpFPULyc1Fm4xqHo1G2xDUfSSob1IvGFBB38xVoOak5PzQVMy1SmSkyuMXWBQ6pJb7URAmaJAOwyFdvKBKnmxVFABxmwM6UFClrlrJJKAJrXRyp1h7vA3d30bDuqV34xezFDDGoZ7Ihnpqr6S5b2yUplxGWmDRXHSrq7o2ACVBxQLKR6+hpXixRVzcdQMq5mUIo6B7QC25bilnpvquMOvWIrb1zgOG7ADQ3bWt3ZpcDQ0LlI4XiiVy8zrG8+TXfTGJrGDDwxGjLwyrrIvb0FjdQByBhHoKfsVDW/CmPmVhd9PPXgSHiwL+W3a10EKYyIcmB30G5WbFZc0YNQvYvl68ozSY3UWxmD1D46wo3UUhYqCWsPmlxB4c1l4M1bhg0M28XkLhjwgQZNA8m4ES89KMquRDVJhQ4FxKTCi7RmbpfYU5oJjuEItZzuiEPAi2aCuv1fZwulHDVQDvzWNJmU+kcN1QBpoPyoJDUAY9KS55BBgDnOFLB6Q5W9DImcXCsiA/m3IqEt6r1kxEPLmNeuqEe5e8Z8oIduT66pCpCQm1SJSpkyUVuUcDsgLCvfpAlPRCVspJWSTquHhviTlGREJd0maIjKD95bV70pcCB0QDBYljtJfb+UmuQJvpBOErWLhwZOZNC2xe3SVpGCJpWG7FuEIDIy1Kd4jQgOj28+wKALGefG/I1cgKSLZ0qs2rqYTU9c8XKyNA1BIyiflOqU0niREcGeSg0kBn3l3A00LqTidwsZ47aSbv0pBUIWGWYsZcw7t3YBZVzF01ldtypgq3BwGUpyRf7JaGZxGBSPBYGHYIL/8lrVbMBEw9eUHqGFA0f82cpbVY0whxLFEtrzc6Q6PjtkkGiQJcFwyj5qTRlSdc+6Eq7RrFq09ZOC5OVoWhqMfxib9qm9ZWWQRZlprVfIiXWRGrspvQ0ZU+BCxZDI3csM3kJiJRfLlB5a49cWT4oldbk0bsyacyv4tyrhgFqgUtVhiDTEK4G2HEaDwB4wHRGfDxFxGkEQ58GNyHdILdMhjPY8e0Jby6QyFOIR4xHFzuRMYfn96usjDQPlQTJGBjurNGZr3B3wcUOiQTSAH8qQq+Cg3LplwJWMcnLNudk0IrxYG2aNgU0VpLU8NhUfraccDVmaERjQLCTTEEOPPjtIGbbyooUxDgPCyCZhABFR1mZXnnUIYFoyE1XwiDp4xFJ26JAbdO1ntCErubTK67ZoujZvO6wO0SrZsRdvOLyhIPJGvARV3G6pq60DvkzvmUGb1TAlLVVhfQtHt/TFa+ZFioEzeSW32OjhQpA6saOPR9opb4GS8qIGc6IYEKEKx7S89V2GGjcNOcpWpsprF7hrmTY0ioxhW5O0ccKkEqaPXLIKrIhGIEViPCp6Ea2h65UqVHMgZSvP19JWlBE9mnpLp8oohxBIqHq8qdGt35Mr7y6jSarh6FtU50CagObu8xq34KGhiozyZM962DgNkhJEDc55hLsdEyAaFiKhaXAitWZ3Re0VtRLUDFrHsHoT1gwk6ipoIHrCqmlk9RqGO9D3GxbTk2I0BnQeocoCNonDRsK85WWHWUbB3Wczbo9BN2O2YYZwmGmjGzwBMo51B0GeGQtfR96zHuHb0lo8QKdJ0IYNlfeVosDVlEtYTEOZVQMLQdklKBgSZq37UDIeJM1F1DZmLMH0Y5Th4TLsmxxjUG/xsiwls/zxssKowczVsg+1MVbVaZXGuL6PFDddJ0qQcK40rjo1+ODa6KOGOBcLROPrVif3MJAcO3PUMLD4eUuK6uvFRy0WeZUJRjSYx3HYvPMteeiCS625YV3ySI3tnKRJ61FF9y07uUOmo3V/acwCHfCNsSUVjpbhfVJ0WWsowwFiHKR2u0xBqhU4j+Bg4pHXygibQg38vaLzugeM+RYNunXiqBHTSDNBgZHkClUMRJ3lkSaoGAnK1AKR0ecEzyzLFoVUQqD1ZTRV8oNGvLkKq0WqAVNU1L4QlZQZNRaJNKr3fJawFZDXJQeNEQdNEkAOWKByqIz7Nsy2uxnt7WUiuaLgAXI6W4ZRTxsr00hwZxufZ4Ixtz2/NAvpa9xs1G9OgaAEYUqSJfJGIoPAM9chq4VDcuCZ1SKWxRBX4hV2O9V6VsAvHtkZuWHU8lbw0Lgpox3nimt2mRr0kirzkzHKbCzhQg2vXAsaRRGiKohLOL2iDkmamJxCRlFqLewDEzWNQEL08BtWX5sG1N+AoY6VfqRTRbq2hKtmgizYKc0Z32PX7i1v0CsGhQdy3suZhmFSZczAa8hT/uipQCXephzwtSCWkOrJtg3+obXQ1BCapiprQ68xHbMa+RsNvfCmw+9d9jaSYmC4LljIdSkxJU8tY9UQS3BTWjn3tkGv4pnbBUFlHpBGab0WRqbGTDoJRqHNsOSS9YhYg1Zq2H+WVd6RFOcsbT66VWMdpikVgTI1mIxWcFfHKNQy6vqccDlvhFrNtVVZbJHEatGqWIKp3zYGPaYEOxZhYyRZshxVyigU0XqCpAxuLKDTFxoKd1fjEArDpQb3DRSSdjoDJ4QyK2cUzSZoisgPYgaj0uYtAzfDwJBa2URqLESpdoXDZciHwEOvwpy07h9r7y7roIfn11RbN0pDkyEFKNJS8Wodw5JFIKQmoRr42dImY2Jqc+GxFHXQRUOMcRnpGhoZdXg1d49qV4nvT+PBo2iohEMFNQ6ZQcsNmrks4XSX0G8ygquFhpCDsMTTkXpN9VjqCDFJU7GkyAyK5I+UtF2CH7Aoh0ECIJc8ZBE0N6GR/t5af12zGdLA9VJp35SDIlMl3oAVedtj6BvH2qPNo0vXRasgSXtxU1ZCCFVGXnlDQcPTmWHwVXxuw8NX71NU7AUPLgX7EpM2XDX25gGZqblhtERWedjiNFZGK6wWFo1QfSgpTQKWJ4ze1gY9Pj5BxniP5jlsT1USEEi0sdRcNDXgydix0DCl3TQAzlBjkAGk4etFd5bEhWkKryhp+CODxCrPqWpFlvLPrVNtquOtg1vrYRMOn3c+hAYtK3HWUkxkrjtLZBRHizJcGfDR9UdJk1kZdKkPruUYt2xQjFBIeNiM1HGTSq3X1JhBqZVuyxlQUqbtaVQIUMrdoqAQ425Ajan/Qz33oqeS7p6XPsSQQw7wjtkYpZAxQxbehKq3aGUExQwj+kE56BhjIksgyBKsn+aYEACbtBahusjjOAKCVrNSHlhXGRawgJvHImlaFI3gbAV1UoNtDc1kWJ+KW9Bsf/sZNK0ArdXUTejqNhSFSOU1JHXx6lrplsRDjcUrgxqUsLa7ViiMZfVyxCbIdWQM7hUKwqhh9ruJFGMHygWayMdae7jJyOhTVnUBDU41VXReXaeiAsOWpy48vhwZ9EqeW1pBjzS29WpITDrhtrmAyspmU5WPDoPAAWMgpqSzKA+wtJZgjICMhQ0KuTqQZCEwe3UpoWjULXnjuv6bKwpOedNi2k7G61J831qVqmZ1WnTfSHdPq9eSjgx6hIaT0So9FN6q9jZo8NQY0HgShempGjyzTARWBY6iEg/xLYyxsFbQ2Q52QrBB1s6Y7FGFBcICx14fhrmD6xlCAscMYtV+JQgBmXaE2QApcdhUNRhzNlYa6QIsiAryi55c+3wdGGzK+GV8exo0jZwFqThXM8JHU8UVE4YTkrSyVIuWUxdRtZvRyNCbpH8SRj3YjtB1BpOJQTclTDoD0xlvzGp8ADPAjkEsYEdwvQAWQcaO8nxrEdSD0/PxmSRnQVUfCjUTUtIMYqVYkGonTLOtXbUzHcRwVI3Q9LYz6JG6DhnrUFmlio0GW7YUGJqamLfQZJFWZ96w8zzqLXadxWRCmK0ZTGcW06mBMSYJjMbxws4xjABsLIxj9GHmchxJbYyXRBbOicWUvSQuO14I1UKTYq5fbm4mtDvLc6JIFE4mLOHSx7zyUVB4EPKghoduNas2EiSDt2sV/FA5/CaKEFEO+EhoMNGiHrrgjdlgOrWYrRusrRvMZhaTiUlBIYTguIcwwRgDdgiGjIC1TYL5USGXSPKUI8XI6J7OYcaaGkM31deUOiEi41x7MRjoBuDG29ugW6nqVs2EjCRMxmg/UoyIqYKnzGgoUZgGPYXBAigGvIRd31qDydRgbcNgY7PzBj21MF3QarQ2iIiuebzsGMxA3wsWc4fFQrCYMxYGkEXgmXsPlh3lQe1RJEiza7HbnqLWTKydTjuMdwRFw0EiT7hRHqM9NoZlHrLsPB0Z9AhWRiMSMyNwojWKQPtkXZykumIIKDpkgnFTNWNaRApeOJX/kwGR1xKfTA3W1gw2NjpsblpM1yxmMxP0zsP7wgZFXANmL5jJDMznBv2cMZ84zPcBa4CFYfQGcD37Mg6hItHh9Wy80XlxUwobmIBZwvtLEs1kpqTbKKgIoFZiiQA0JqgOp6MeFScdwDlTQyprzIuPPU8X7Q+bYvUo4GQc5O8nBK4YZZ11nv8Y2GDyyQ7bdZhMDKYzwsbWBGtrBmvrXYYaYIAsjAme3fhjsCHBZq0/jq4jdBMLaxcwHaGbe8/d996LExkQWRCJpwEthdcQnGNP9Un2yiwMdgCzQ98zhP3znBNPETrGcOLoQayTqlGXQ+WcD4tB02jSZLy7u3UzI6d/+Fl64CQZggkeOf1tKMg6e68nCiMTJMg9e0PqrIGdEKYTwiQEgLYjTCah2i4IaQo7uBDQUR3rCqPrKFB6AJkOxgJz42C7CfpFD9cDMARDEuCNDz6NDXSgteg6k7rwOdCBi4WD6zv0PWPROyzmhH4O9D2jJ4JjTx1mRd+6Vp1H8fEhtOe7bdC0JABcoYhfhuT+UNShrjcIRmUI1kRe2LMQMQHiHzMw1iQDJwKsIZD13nEyMYmaIyPoLMFY/7chgogDwYBFAgUHELESE+LAVZsgUu8DwG5iQBAYsrCWPK7uLIQtmHuQscGog5c24T0MPD4nAhnBtPPHv8YWi4UDO8Z8bjDf77FnAdoPJ7z3R8IcG2wp0HNSRZBSTNAcKGwdpb5X5Z/HyhfbBk+ifKBughUkWTNjjN+urYEN3LC1/rexBsZ4GNB1xt9vAWNNMFrABO/oh71HHOuloIV90ywH3pidNzJ2nCayRmjj49NcUUckAAcP3IW+PvKfzU4g0mX1LohXHSMkCo+MAYsDGOiZYUIQaS1grQ07S+CzidNoYOkpDHGSUpJkdIgQNzmjw4Cnu8PjnekAA8ZSzz2cLEqNd/E41BiC7YBuYtB1Bp01wWC9oRtrYC0wmVjYziRPaLvorSUsjCzzLBCPV8UbsYj/O4+kUyL0YcAkgQCTAytDfreIshS28wGgIQGbwGFzRrHFQB/jsXIqShUXumWQ6qU90wJ0U8AxwTkCSxSr90FjHljfULGiesyvSsgMZoe8rQx6zJhXNWIMGl/zdgkoV41CQiPiZAtMOoPZlGAnIYtnDay1IdgCJlOrDNkbdeRthT1scM6lJIgwJ2MGCOycMmqtW6gVqkQFoX6jN4ZhrUnekmLdR+ChJc7dFj0fmiCchzayUoP1O4RfRBS8szHAZEJwPeAcwTmATYBqLJmqG0z+l4aBo8GCvJ0L/AUrcMoVHBkMS2wMVqye4zN5mk0w6CYG05n30iZAjG4SIUeEIuUgFe451FxkgU12DOdcKDKSlAmMYkMsXByPHvjumQtKAaQh8niWsnd14vz7cCkcH7OO6f0LgTFXnB+TZkNKWuC2I3RMYJdFnRgcnLCezjT2c2ux/1vEoFfp2KaV/pYGuzGWYIlGYwOb0HWE6cxiMvVMwXRqE3b27Ib3QI6DBLBELjfUWYRAz/O9nAybiOCY8yB4EbBkZQANObzhsjLs4JFdKGAiwBrvrZ24kDThtFiYg4SxSFMsnshjeWvCYkiQB4kpsRboJmH76nOQOCyWk8ZM6LaR3032ozvc3nlYaA9pzJaTZYNk4vBI8VQcBN0EyhPHv2PzKQcON5eAimQaLHpndlxgZMcuYdFoYMw96jkhPjbN9cUUsTVF1oNCIsb/duR8ilwEBJfLTTnCC1ZzRCS3IlAwYgKckzArTzLvHrx21/lFKmHRO0tYLADhvLPE+hIqpqjKCpdW7rhhH8JM4cGwQ+oxBEXNcoz6TeG9Y+Kk64IhhQApGnt0cezEe2fja5NZGML+cjoX4Yb3wM6FYiLxzwWQsnLRq3PsFEl1IlFEPkteEOUiej+wiNLx+iDRJU+sDYlFIMyVnk2eaBTrRygod4m44r7IYnSd/7+HAOyDYte7UJ/ti6M4GbakbqDst8dZ/0HX11vSoOVmDHtJYmWsL1VCPQTFDhHvYyZTEzpHojfldMEIBCc5+eLhhQQYATjuwb2Dc3O4fgHmBVy/H4xkBuE+cMpdwMrBy5GBMV2APyZPWjUGhroEiz748n/F8f3X8Ny7/67C1QaMDC9iljNPH1UBYkG1cfDuHGbVcXityy7BItGARAbUO7+op77hwPV+PolzfQhyg6CqT0uqoicZQJ67ERp2h8P70oqvafX2IWXwBmO99C9jQCSYTk3y0MICssbbXCgpy0GdhMQIJ3ghLHDOgZ2/6I4Z4np/sXkB5y6CzBrs5D4PCXgO2Fng0RhMnTdwAoQX3qCdAZPD/Xsv44ntb2DqruHVjXfCuVgzyuHYy4ItCQaIUJeRpUCCvnd4etw5OIjLk0FK4vgabw6LPS4Igel89jD2Pzq/wsFMvm471oaoDvXWUDHcYe98dzz0LSzbSMu19LtbZzAWHBkjMCSYBGO2lgIG9RfH2oAjhYMRe2Nn6VOixDlOgZzrHR7c/hbee/5ZgPexB8ILW38KvTh8b+s9cIursLOHYGjqR+tK73H54iqIpgDZVJAP6SHCeHXyIJ7a+yw23DU8d+avwLg+f50AK1Kio4AXWQBBJ0K01o03Wkli9nEyU6bePFMi4NAAE4w5QgwjpTqMZOBX6rgfZQpvck1QNmrS92Rqj1QlKBnCZOoLlGznjbmbIBizwLHPHnojdiEI5MQk+PslQQ8WwbnNd8E96MDi8J4Ln8UTO8+jlx6P7jyPferw8voTePnE07CTkyAzgaCLxXkAHKTfA5kZRBxEFnj/lS9j2u/gWncMzAswm2TEkSHJJaK1Nzb5Pqrl3wJ3HuZ8GBMbBLT6rKjGA5e4chkI12dmox6PkxUvVLfMXaA6aPLAX71zy0pWSWPXGUOTA8GocBX0vCkI0lMsSE5loCYEWv45nqqzmEw7WEOYzjpYaxK7QSbi0ezqnPM8rmOXqtg4BIWeOmM4nkP6BZjnePfF/wsnPc5c/WPMyULg8P2Nd+Py5ruxP30IZNdD0Nf7zZr3IbzA05c+hzPzV+GhbIf//tjf8h48YG0KXB5pr0tG8djeqI2xKuFR4ug43suQyUkdBFwdoIgE+OGznJxgV4Is7MIuJQVVOeCq73AP4T3noQdNVoU0c5mA8N7JZEwd6pAN+VQ3wCAbZjgbhEQFgWKNMef2J++dXcgChosoEX64TM3BALD41okPQ3gBAXBs/wLW+h08ee3beFEEr544BkfeS4vbx8b8PE7u/QDvvvpNdACu2nVcWD+Lb538cRBHJsJXznUk4BhgJu/s1E7kH3HOpXORmw/K7u+eXeh+QWHEETtIkRQKFGUIajkEnMwa1rRVz+5mCV53Vyxz9MFWAKhAmi77DFAiFfoET+2xovfOsZLN2NC4av1zTMClsCZH7MjJilj7nGBHSGP7eg0XEiySiuc9Q+K7Qv5464PgzX08tHcOj13/Y5xYXMLL7MBuAWAf3F/Dwzt/hAfnF/Dq9AFcn9yP7x/7YRgzgbhdTMjCygJ9dwwEC0eBiAvp66TDGFgcSd0qpFiOik4jCW8iuQBJsyPMicP2MYMLhpx5daialXgNIhde1Hfc5XrS7s4a843lkFoS8YMJRLFTg8JvRCOPVJ1JiYpYnJPBbMiwBW7YX0woDyUqI+i3Yg41E/Hv3I8YtnwDkBO8OnsMr0wfxsdf/++wbg89zQBx2Nz9Hh7aP4fXJg/im1sfhJ09CIgDux4T8izLwszS7D2KONdQgg5aoDZpqiRj1cmkIIkhUvQRSq3/Lap6Ln7v5K0zJZhT9jFJo9Lxh+RmDheoKL11y2+TMmQif519TXCeeWEsUho7P4ZUKec9FacLJoFvdiGJ4vFi7wuOIlXHOeXsDT8UIYUpRx+48iWIGIhYCNlg4B22pw/gmcvPeX6a9/CO/ZcgMPjG5vsBMwP3u2A3B2DR0xp6moT36SG8D+Y5BC5s/yHRI1zVjKj0O7vwwx4yxaA2JIQ8Deny/zHzyS58T1dUEGqlLaidLLZy0VHHyqpm3pjGQ8ozK+P2pZGmvN8gl3lan0gxET9Snj3BnJte/S4bPRRSllAHSRLvixc2zOt4aucreH3yAF6cPQbA+ucSYQKDTZ6ntqhT/RXfj2jWQqf3FCTz8L6LHKz1c5CZ+vdn55e38XTfmtvFXrdRLXVu0Jyc+es6aGt4Zi1Yrwcwpu+v3kfk8A07vzMeWlYB02NeWhpNWJK6rD1UlgA1fFKAkidGSqDk+32xT7xIyetyjtydy94tezVJsCN6uZ89/5+UoTu8Pn0QH97+fXCcs0cdQNYrsdAkSL11WJh1j4XMNDVCAAAVw0lEQVRBILsF0AQMA5Z99PPzEF4AskjVxSIOwr6Gg9nhzO73A9UXYY8rCpbK+2Oth6Tf+XG1Q6W/Y3CYnwsVJGdIprKBh8ueDwPkGBpye265lB46VqaZWHqZi2eSpy642OxtaqyYtl2JNQvhd/DYomiqaASvzB7F49e/G4wL+PT9nwIB+LM7X/KDFsM85y3eQ2+mnt8V4DjvwZAFYLxH5h7Ce+D+GuLIMgmNvex2Pc0njPv3z+Mde9/Dtj2OXbOZqMMMN/TxZcPzQSunchVtqHlAJQ0MVFNyIpWmuACHseP7zTdouRnKo/bOeQRWhhm5+F1TeFm1LGTCCMP3ClVn0SOzY58siRwz58o6X69ADUMBvnz8I/jo5f+Dj73xmbR9f607ibN7L6SspgB4oL+EDn6kl8BhbjYwSd+qB/FVbCwu4icu/04azugd5cIbvDAemp/HD137KmbuejBcp46FFeWmPG0h9KMf52K0gXBkMFTrVWHUZd2I6LruO115dNcNmm7W+itPHQK5XLOhMYioQSvxAnNeCFR6GuHYIiWpo8TXamSYoY23KKGM3ioETN9efw9O9ZeSsTx/8uP4Ls3w1y78Rzxz7cueBwfw6uShUP3nIcgsGiEvMOsv492738Ir00dAZhOgLtRW70KE8YFrf4j3XP8mfjA9gxen78C22Uosi/f6dYCblWgzDNE102qsWQr8Ku8ObbSSs4lSOZDSwt/uQWGrvbIegFi2YZCi6/Qo8yx0E7haRfPFghu9bSYoIZTwsYTm2nhxfTDGKdLPHi4HVMf7y5hTV8CYL536OZx87T/hzPwV/PTl/43rZhOvTU5DyM/5eA2E4wA2ZQ8P75/Hn7n+TXz6vp/zdKNZD0oUc/zQ9a/ipLuOM/3r+OyJT2AmPa7TLAR2nLKisaiKpK5yK+s8CBWUQD1zWrFL6vsMCVQ0RqK9nQyaDoIe1QRPPSuuOYpLF91IGPpii4SCoUo0PnkZo1iNOtCBqmdQ3hhSeDcge7Mz83N4fv29RfpXADx7/1/G1v6L+ItXv4Q13gcv3gCmj0JAWJcFiCx+/spzuGi38AdbHwbMBDCbgeddALyLd+6/CMDi/OQMLBzOTx8uWIqsDB2UXykr2+aMomJiCiZEhgKfMuwdLC8dBcdwKO34DnvoFYxaBr6aqnujLrVVF4yTMXMo0jFhGr731HqWBCks6S+8r1MgCJMqytfXmFWUz0XE/8Te97EfPKY2/FhUuTN7DLPt38PXuxP40OJVvBRee2FyP+7rr+CY28YbkwdwbvqIz1rSvmdGpMePXfsyBBafP/7juNSdBqhTU/fzfkVhXJckhSxRfQRSFdiPaaLocZPx/U2m8BSqqNEFEY5YjhsOEmlYOK63zkhH5S3VZV5ZclNpYioYqXqOY0+guMwYiEsJjJxE0Djcb8f3z18DAPz+1oeLyP+J/Zdwdv8lfOrSb8GRxfPr7wMAfHL7cwABL28+hVcBgDq8J3jhhAtC8dZD/UXM4HCxuy9rcxeNtsAxtw0jC0zcXFXNUVHFWWBlvQsNcq/V/DoVIbYMluiguSlvB4Omg9D0CBfdaP7U1JJPv7rUBOqzfrn3TmfPRHdlcz0zAwOWQFQuIhu2f/3D83OY0wS6xy4e4Y9c/QOs8RxfnjyA7dmjuGbWsc67AIAPXf8G3ilzPDc5jcvi8LGrX8Rjiwvhlb4Gek16XDObiVLT3SBn91/EX37jv+BYvwMX0u1rvKtGG0iF+6ua8fSdyh6TEi9no89DcehQGvDdDQpHoMfq4UVlbHG2RbjozOyTKqG+QnS2LLb2hyo2SGYCYoe26FFkCnvqQDHef8Jt49898MuqXtjffmLni7Di8O21s/j61ocBAKf7K3h2430eEixeg4HgwtYzmF15Dg8uLsAAeGl6BhAfOM7EYVPmKEYBk+Bntp/Dqf4SfuvEJ3ClOwkIMKdp9uLJpVJj9G2tcTcyMD4FnBhAjjhLZHjlDk+AeOchx9KZMS0vLaW3FlVQoyvA4lOYi4Au7aBAnjPHuYcw1nLoIpyylJKLxSQQfOLS7+Cq2QzPzcNYzu69CCsOl7vj+N2tD+Ps/kv462/8Bjb5Ojb4OgDBnDpsm00IGXx77UnMzRoe7i/iL115zi9QMjAALncni/3ryf2X8Mj8HP7j6V/Ele6EYij0MarzVpw2Hcy1BICgQPLYIHk0g/TDVstxdzA03QSeFtFhisfPwTirYWyKmZDMYhSVdDnZUGJMKfhcbdwJnApwqn8DP5g+WnzmL1z6H/jJnd/D5e4EfvPkz0AA/LmrX8KCOvzm1ofx4sZ707FN0AMQfHf2GP7DfT+Hnjrc53bwzPWvo5xVnU/UT+78Ls5Nz1Sd31ImORpaPfV9GTqNK1sJDhive4hZjrsXFNKNnZu6cT47kFxJVpQ6SkiWpLoGSUkIJBjBA2ycyyQHyB2A4B37L2AqixAM+tsTey9hjfdxbvowfvPkJ/HMta/gk9ufwyuT+/HpradxffIgJjzHGu/hQncax901/MLl/4U12QfA+N7sURgAjyxeS3zDJu/6zyTCz+w8h3PTM/jM8T+f2RQZDnvJx51ndEhRkFRrOWq9GkXPNXVnVLYQQ7bjsGDru8ty0I1mTnUqN2MJUX1z0nBJsTgnDmUpkiki6f+CzSg+LsOYd+/+ySBIfbB/HQDwmeN/DgBwwm3j6/YEPnvsGSy6+8BE6NDDCuPFzffjc1tP45Tbxo9d/Qo2ZB+/v/kUzk0fxDrvJ674ulkvMnIPLS4MwFneOWiwm5Xa5hg0sUbqchAcahZEsokv6+Q+TNTdoaDt6EDDXlahJyUkqYqIChqqqibzSRIuXp8plFzIrwqAscHXcNVsQJdRvn/3m9gzMwDAT29/DucAXF57FzoR9GRxqt/GptuFI4MH+0vYsZv4n8eewZnFa/jzO18CAFyxxzCV3gdeZLDBu+m7rfNuYkma3JCUXrr01EWM2ziNJbyhhpVKTdcNpPEOT0nHoaqHzqMnys6UNqSW4iRrtkOL+xTpCNFyv5LGIujqs0T3AcqY87Gs8T4W1KVr/sy1P4AVh988+bMQMK7YY3hp7UkwEbb4Gs7uv4zH5+exznt4bXIK9/U7+J3jH8EL00fwu1sfwA/tfgcfufZVIPDaALCgCdZ5nv5f533smvXGMHc1S64QmQ/fuhKeb4r/QO1y8fyRhi0a38XxaEO3fFi89CErH1UeezDlUgbG3Arm9AmWoh+OVWo74OfkoWMBO1edGlKYjQiSJ9bH9Hp3X7rnhNvGdbOGDd7DA4uLeHx+Hg/0F7HF1/HE/jmccNv42M7v475+Gy9MHsFF6XF2/xw2ZF58zU2+nnaUN7qT2LZbCvdye78q3XPGz9KACAO4QgduiHJj0f2RQa+GoRuZQ1VHEeuD4/25eqxK95bVleUVE+25UPDPx9zVwiOddNuYyRwC4FOXfxsbvItJKND/0Wt/iFPuCnbNGnbNGuZmCiaLk24HT+8+j03exRdOfAzPrb0TU+mD5xcsYPG6PVlY0n39lQOgGFXHijJdNYAgdKBnlab5yqG2kMNt0DTkO4uOCUKZDRMpC4yQGzv1/8OOC7Xt1lgaeZQuAFhxxeI63V/EG91pAILrZgPrPMeEe9y/uOjlIISxzvtY5331WsID/WVMZYGJ9NiZPIjr4rBLUwDABA73u8vFon158jBO9leKQv1ykfNSum5ovVI8V1awbFnNbR8Z9I1z1RjwrqzmOJdxkqK5NJPRMmbUZZVArTp7uTvRSP/ke3bNFFNZ4FJ3AiQCRwYMAoPgyMIIwwpjIj2MME73V3Cyv4jT9hjWJePmFycPp79Puyu4Yo/hsj1eHIssaV2r75NROCGFgP1gnLk0LowcXkr6noEcIm1GWrfRly1Goi67DP5r1UGWwWH8g1eeo3nFHMNxdw2vd6dwzF1L91/sTmDfTHHVrGPPTLFrpiAI9s0MD/YX8ai7iutmPT1/KovAcgCb7homssBD/esYAOLB16iZDimzq2rHqsVJB6huLFEziHfoyKBvFj+LtLe7vAU30mLaSKv7BllBTd+BC6gR32ON9zGV3nekhEefmL8MiOCLmx/AOu/hk9ufx6uT07gSPOq/3TmH3+Zd/Of+Iv7VtYvoxOH5tbN4Y3IiBJJX8ejiAr4bs48iyVtfsxtY5128OD0zigXaM5pl5F8pIYjgQC8v+gTK4UbR91hQiKpBsxxgWLYR1Zi58tdFrUebUZHqPoHge7MnsGem6Vm/fupTMOLwqcu/DUDwveljeHL+Mh5ZXMD/3Xw/Ltnj+BvHHsGLfY/d+Rx/b/MUrtgtfHv2Drxn7/vJmPfMDF/cfL/H6RB8d/pY+o4Wgq+svxetlHdZ5zJkOWSkmVXvWa2Kxhacai0eETky6FtjOcaou1hVJ80TXaSBBxSHLmKSZgAVDeWLmx8q4ESUXj7O/r7PHP9xfG/6KJ7cfxmbvIvPHPtRbPIufnntQfyD2SaumXV8e/YObPAu5jRJ0OLTG08BAH7p8rMwqrPkk9ufx25svRoY3XChtvCClFsZWoqwtYKKoBpivsr0iSODvj1GXY4Iq4xaZMj4FSYuyH16XLEaov7n4vMfm59L/7/anUYnPc7u/wAAkpG+f/c7eGr329jhOV7tL+NPZIF9dw3rso/L9jg+sPstnHLb+IY9hp3ZI/jIta/hwf4SLpstXOhOpfe6VASDyyyptWhlnKEoIJoOnsuSWE2LHvbbPTkfurwotTxDqwPDDzLPF4XUBacBFyuDoZGlt3518gC23E6yh12+ijWZ4y/s/B9cv7YGAeGUuwIDgesJe2TxsHgBod4ew1O7f4I/vfd9vDI5jU4u4ppZx9n9c3jn/GXs0wS/furn0gI9ztdHvnM8ftOGIEtcAS1Vih3ZvRqLQBpsypGHviXGgxrllA3vXJjl8uLIWpdvyPcSLpvj2OJd/NLlTwMAfvvUL+K56RP4Gk1xRRa4AMELNMV5EHbMJk5ThxfJYmo2sc17OB+M9RyAlyan8PUTH8VHr38VU1ngSsgIAsDZ/Zexzrt4JfDcDbDc/u6azTgQniyn+5afp0PI9t7Rgee3maTOkzapui9MIU3lfNQoqNGZMkJ7rFWs96iCw9DD98sXfwPfmT2BL25+AIDgWIAcO7NH0vM/cu2reN/ed/DssR/BM9e/BgLwx2tP4E9d/SPM7CYAz5x0cPjm7Cw+t/V0Orqz++fx0et/hC9s/DBemD1a+R9TEfb6/0qWgzDqwUm5bFHT+THA5EBKuUuLWTny0LdkzG2Pkj0qYSzAawEJQZOIrScQVYVPQgZP734DH7n2lWDIjwZjzren9r6N74JxxW7iovSYyQLnJw/g2VM/C0CCVz6G17qT+Pyxp4uj+Omd38Mb9kQwZmVQ6W8ZodtKbw1pqb8qb62af0t2pBFkihxaY34LeGj9tykeLwTi60J1/T7S8s5UwY9G0BUu6i9d/h/Y4ut4fvYufHHzqfS8s/s/wAd2n8cFOLwwfQTnNt6HH7n2NZzY/z4eFYd/c/8vLIVUP7P9BTwxP4dnj/0oXpg9Ui3k2jvTyN/D3WxpYKjvIwXZ1N9yADw5MuhbMGqi1tZKBewYXui6pnfUiQ+GFGIEm3784n/FY3DYNpshISKYSA+C4AuTx7Ez/dvpop/pv4Gzu/8eG+Y0vrD5FP5k7VTz233q0mdxfvKAWiSrGPCYUee7y6pnGTHutmD9YTfme5zlqBo6SUpjVepY0o71sVxRQFamXS90p3Af72Cb97CHHlYceurw3cknId37iuee796HDfs43utewcd3Po8/e/wMdi/spMePO89qvDI5nRItbYajhYmlYaylcRdjwprfTkbY6dXO0ZGHvpWDHwSD8RIaVcXe8GaDHjtU9+n2/THIMcY4+Oc8sv+h0YsfD+2nrv4z9Gc2sbZ9CdOdbTiyeL07getmHZ85/tHwMqrsdjgxe7kHH5y0wTFRgcsxAi0Ov3d+ixh0DT9qTN24yDKsC26WPrTKLWXsAnP6fWZgzMDF/lkAwOnJJ4r73/jb78L8Qz6QlF/9tRUCYVTyeDTyu/G6ZhfQsjqQe8uY71keuoV763oMGUnzHnwdZDA8ZlyybHjBp3xsiQD08so0K9MlW7tU8Gr5LtEum6vvazElPPK+OPTGfI8bNI3g4hr/YQm1VY8gk0Yw2Qqixi/o/Yv3NPHpk+4Xcbr7xNJvxP/8768QN5SKBG3DXmbcXKb5m8a9QvXekUHfHZMv6xMavriafoSobkJyA15Kliwuf9uebYeRt8Cp/l0JbhQG/fh9NxgUjxnwmDfmAzz4KsZ81IJ1ByGIrOBTZMTRyxL8fDORflYk3+BjhX1f7L4zMGYA4NNbN8H04ABjvtmFgRFjP/LQdwxHL0fFVfbsIANpJCJo1IO173+jfxaX+t8BAMx4AwDw2j/80zj/L/1Amuv/7Z9i/7P/BD/2wcdvAXUJli/jEY8sS5IqSxfLkUG/Waa8gpdeVvcro8aPQbq4ek3Wwlh6oU93n4AQ8AP5DRixmPEGzPUe/RsXMP/QI5BNh35rC7/8Dp9cMW9cvelzIWPp+wMFmg563r1Fgt3j5aPZSw8d6kGSFq1xsjL678HbcYsmA053P4Xz+/8WP7C/ge7v/WMYmYM2Z5h++Rw++mv/Hh978gR+/cWL3qBfunRT5qOYc7T5Z7ltDuPQx033Mg9djqIqM39EB6WGKwMcndzZqv8djiGL04SmvJWYDk2xvRGgh/37v4Ltp9ew+f8uJv453ia/8q8xN1dv2O7aYSjd6Cm8pw35LeKhld6VSKElooXc25659fsmL6fSSZzbHZz/Fz8BAJh++Tz2PvU+8OktmDd+Ae5v/jzw88chxmDv0R+CeelS8TY3bMzhs0l0DeCYYa6e4r+nma1720Mv99IplUErZNOWzlYe89LjgRP9s380ONL+g49DvvkF4OmnQXszdH/4Un71aJbwxoJjuvnT95a4vQVoO1mKc8uxs63ofiwo0lx0FTCuQGfJr/6L4Xb4hy8BP/Jx0N6seu6v3fqypqw99Ha+vQU8dAM90tAbJ+VDGvPUGB95dUO1wOVjU97C4tf+Dvjx++CefMDj6O+9BvPSJdhf+Vfozd6dJoLe0l76LWLQynBJsx5tiEGl5auLX4+QRaFTsnxXOMjQj25HkOMm3JHUE0UbhlZODioHM0oTiqySOTsy4COW401jPfJ4AhqktksvTIXeyKr7ttzA40e3I4O+LUYdnbBU8KOeqXzjskVHxnxk0HfZqP19VOPmkZTE8N3kyJCPDPpwYOo61UDVRNHMXa9io2+Neocjg34LGfbA11bleqTIXFm5/PLodmTQdxmGjD5LbgVTH92ODPqueGvg5rIJR0Z8ZND3hHEf3d4qN3N0Co5uRwZ9dDu6HRn00e3odmTQR7ej25FBH93evrf/D12ATLZtkAnRAAAAAElFTkSuQmCC"></div><div style="display:table;width:100%;max-width:100%;height:100%;margin:0;border:0;padding:0"><div style="display:table-cell;vertical-align:middle;width:100%;margin:0;border:0;padding:0;text-align:center"><div style="position:relative;display:inline-block" width="480" height="720"><canvas width="480" height="720"></canvas><header style="position:absolute;top:0;left:0;font-size:14px;width:100%;color:black"><span>Vilma, the happy Vaquita - Sunken Moon v0.2.249</span><span> - </span><a target="_blank" href="index.html">Save Vaqitas</a><div style="text-align:right" id="fps"></div></header></div></div></div><script type="text/javascript">gameObjects=null;</script><script type="text/javascript">__hasProp = {}.hasOwnProperty; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };</script><script type="text/javascript">var jaws=function(t){function e(e){t.mouse_x=e.pageX||e.clientX,t.mouse_y=e.pageY||e.clientY;var i=t.canvas?t.canvas:t.dom;t.mouse_x-=i.offsetLeft,t.mouse_y-=i.offsetTop}var i,s;return t.SpriteList=function(){throw"To use SpriteList() you need to include src/extras/sprite_list.js"},t.Audio=function(){throw"To use jaws.Audio() you need to include src/extras/audio.js"},t.title=function(e){return t.isString(e)?e?i.innerHTML=e:i.innerHTML:void t.log.error("jaws.title: Passed in value is not a String.")},t.unpack=function(){var e=["Sprite","SpriteList","Animation","SpriteSheet","Parallax","pressed","QuadTree"];e.forEach(function(e){window[e]?t.log.warn("jaws.unpack: "+e+" already exists in global namespace."):window[e]=t[e]})},t.log=function(e,i){t.isString(e)||(e=JSON.stringify(e)),t.log.on&&(s&&t.log.use_log_element&&(i?s.innerHTML+=e+"<br />":s.innerHTML=e),console.log&&t.log.use_console&&console.log("JawsJS: ",e))},t.log.on=!0,t.log.use_console=!1,t.log.use_log_element=!0,t.log.warn=function(e){console.warn&&t.log.use_console&&t.log.on?console.warn(e):t.log("[WARNING]: "+JSON.stringify(e),!0)},t.log.error=function(e){console.error&&t.log.use_console&&t.log.on?console.error(e):t.log("[ERROR]: "+JSON.stringify(e),!0)},t.log.info=function(e){console.info&&t.log.use_console&&t.log.on?console.info(e):t.log("[INFO]: "+JSON.stringify(e),!0)},t.log.debug=function(e){console.debug&&t.log.use_console&&t.log.on?console.debug(e):t.log("[DEBUG]: "+JSON.stringify(e),!0)},t.log.clear=function(){s&&(s.innerHTML=""),console.clear&&console.clear()},t.init=function(o){i=document.getElementsByTagName("title")[0],t.url_parameters=t.getUrlParameters(),t.canvas=document.getElementsByTagName("canvas")[0],t.canvas||(t.dom=document.getElementById("canvas")),t.canvas?t.context=t.canvas.getContext("2d"):t.dom?t.dom.style.position="relative":(t.canvas=document.createElement("canvas"),t.canvas.width=o.width,t.canvas.height=o.height,t.context=t.canvas.getContext("2d"),document.body.appendChild(t.canvas)),s=document.getElementById("jaws-log"),t.url_parameters.debug&&(s||(s=document.createElement("div"),s.id="jaws-log",s.style.cssText="overflow: auto; color: #aaaaaa; width: 300px; height: 150px; margin: 40px auto 0px auto; padding: 5px; border: #444444 1px solid; clear: both; font: 10px verdana; text-align: left;",document.body.appendChild(s))),t.url_parameters.bust_cache&&(t.log.info("Busting cache when loading assets"),t.assets.bust_cache=!0),t.context&&t.useCrispScaling(),t.width=t.canvas?t.canvas.width:t.dom.offsetWidth,t.height=t.canvas?t.canvas.height:t.dom.offsetHeight,t.mouse_x=0,t.mouse_y=0,window.addEventListener("mousemove",e)},t.useCrispScaling=function(){t.context.imageSmoothingEnabled=!1,t.context.webkitImageSmoothingEnabled=!1,t.context.mozImageSmoothingEnabled=!1},t.useSmoothScaling=function(){t.context.imageSmoothingEnabled=!0,t.context.webkitImageSmoothingEnabled=!0,t.context.mozImageSmoothingEnabled=!0},t.start=function(e,i,s){function o(e,s){t.log.info(s+"%: "+e,!0),i.loading_screen&&t.assets.displayProgress(s)}function n(e,i){t.log.info(i+"%: Error loading asset "+e,!0)}function a(){t.log.info("all assets loaded",!0),t.switchGameState(e||window,{fps:r},s)}i||(i={});var r=i.fps||60;return void 0===i.loading_screen&&(i.loading_screen=!0),i.width||(i.width=500),i.height||(i.height=300),t.init(i),t.isFunction(e)||t.isObject(e)?t.isObject(s)||void 0===s?(i.loading_screen&&t.assets.displayProgress(0),t.log.info("setupInput()",!0),t.setupInput(),t.log.info("assets.loadAll()",!0),void(t.assets.length()>0?t.assets.loadAll({onprogress:o,onerror:n,onload:a}):a())):void t.log.error("jaws.start: The setup options for the game state is not an object."):void t.log.error("jaws.start: Passed in GameState is niether function or object")},t.switchGameState=function(e,i,s){if(void 0===i&&(i={}),t.isFunction(e)&&(e=new e),!t.isObject(e))return void t.log.error("jaws.switchGameState: Passed in GameState should be a Function or an Object.");var o=i&&i.fps||t.game_loop&&t.game_loop.fps||60,n=i.setup;t.game_loop&&t.game_loop.stop(),t.clearKeyCallbacks(),t.previous_game_state=t.game_state,t.game_state=e,t.game_loop=new t.GameLoop(e,{fps:o,setup:n},s),t.game_loop.start()},t.imageToCanvas=function(e){if(t.isCanvas(e))return e;if(!t.isImage(e))return void t.log.error("jaws.imageToCanvas: Passed in object is not an Image.");var i=document.createElement("canvas");i.src=e.src,i.width=e.width,i.height=e.height;var s=i.getContext("2d");return s.drawImage(e,0,0,e.width,e.height),i},t.forceArray=function(t){return Array.isArray(t)?t:[t]},t.clear=function(){t.context.clearRect(0,0,t.width,t.height)},t.fill=function(e){t.context.fillStyle=e,t.context.fillRect(0,0,t.width,t.height)},t.draw=function(){var e=arguments;1==e.length&&t.isArray(e[0])&&(e=e[0]);for(var i=0;i<e.length;i++)t.isArray(e[i])?t.draw(e[i]):e[i].draw&&e[i].draw()},t.update=function(){var e=arguments;1==e.length&&t.isArray(e[0])&&(e=e[0]);for(var i=0;i<e.length;i++)t.isArray(e[i])?t.update(e[i]):e[i].update&&e[i].update()},t.isImage=function(t){return"[object HTMLImageElement]"===Object.prototype.toString.call(t)},t.isCanvas=function(t){return"[object HTMLCanvasElement]"===Object.prototype.toString.call(t)},t.isDrawable=function(e){return t.isImage(e)||t.isCanvas(e)},t.isString=function(t){return"string"==typeof t||"object"==typeof t&&t.constructor===String},t.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.isArray=function(t){return t?!(-1===t.constructor.toString().indexOf("Array")):!1},t.isObject=function(t){return null!==t&&"object"==typeof t},t.isFunction=function(t){return"[object Function]"===Object.prototype.toString.call(t)},t.isRegExp=function(t){return t instanceof RegExp},t.isOutsideCanvas=function(e){return e.x&&e.y?e.x<0||e.y<0||e.x>t.width||e.y>t.height:void 0},t.forceInsideCanvas=function(e){e.x&&e.y&&(e.x<0&&(e.x=0),e.x>t.width&&(e.x=t.width),e.y<0&&(e.y=0),e.y>t.height&&(e.y=t.height))},t.getUrlParameters=function(){for(var t,e=[],i=window.location.href.slice(window.location.href.indexOf("?")+1).split("&"),s=0;s<i.length;s++)t=i[s].split("="),e.push(t[0]),e[t[0]]=t[1];return e},t.parseOptions=function(e,i,s){e.options=i;for(var o in i)void 0===s[o]&&t.log.warn("jaws.parseOptions: Unsupported property "+o+"for "+e.constructor);for(var o in s)t.isFunction(s[o])&&(s[o]=s[o]()),e[o]=void 0!==i[o]?i[o]:t.clone(s[o])},t.clone=function(e){return t.isArray(e)?e.slice(0):t.isObject(e)?JSON.parse(JSON.stringify(e)):e},t.imageToCanvasContext=function(e){var i=document.createElement("canvas");i.width=e.width,i.height=e.height;var s=i.getContext("2d");return t.context&&(s.imageSmoothingEnabled=t.context.mozImageSmoothingEnabled,s.webkitImageSmoothingEnabled=t.context.mozImageSmoothingEnabled,s.mozImageSmoothingEnabled=t.context.mozImageSmoothingEnabled),s.drawImage(e,0,0,i.width,i.height),s},t.retroScaleImage=function(e,i){var s=t.isImage(e)?t.imageToCanvas(e):e,o=s.getContext("2d"),n=o.getImageData(0,0,s.width,s.height).data,a=document.createElement("canvas");a.width=e.width*i,a.height=e.height*i;for(var r=a.getContext("2d"),h=r.createImageData(a.width,a.height),c=h.width,l=h.height,u=0;l>u;u+=1)for(var d=Math.floor(u/i),f=u*h.width,p=d*e.width,g=0;c>g;g+=1){var m=Math.floor(g/i),w=4*(f+g),x=4*(p+m);h.data[w]=n[x],h.data[w+1]=n[x+1],h.data[w+2]=n[x+2],h.data[w+3]=n[x+3]}return r.putImageData(h,0,0),a},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws);var jaws=function(t){function e(){for(var t in h)delete h[t]}function i(t){var e=e=t.keyCode;h[e]=!1,d[e]?(d[e](e),t.preventDefault()):g[e]&&t.preventDefault()}function s(t){var e=e=t.keyCode;h[e]=!0,u[e]?(u[e](e),t.preventDefault()):g[e]&&t.preventDefault()}function o(t){var e=f[t.button];"Microsoft Internet Explorer"==navigator.appName&&(e=p[t.button]),h[e]=!0,u[e]&&(u[e](e),t.preventDefault())}function n(t){var e=f[t.button];"Microsoft Internet Explorer"==navigator.appName&&(e=p[t.button]),h[e]=!1,d[e]&&(d[e](e),t.preventDefault())}function a(e){h.left_mouse_button=!0,t.mouse_x=e.touches[0].pageX-t.canvas.offsetLeft,t.mouse_y=e.touches[0].pageY-t.canvas.offsetTop}function r(){h.left_mouse_button=!1,t.mouse_x=void 0,t.mouse_y=void 0}var h={},c={0:"48",1:"49",2:"50",3:"51",4:"52",5:"53",6:"54",7:"55",8:"56",9:"57",backspace:"8",tab:"9",enter:"13",shift:"16",ctrl:"17",alt:"18",pause:"19",caps_lock:"20",esc:"27",space:"32",page_up:"33",page_down:"34",end:"35",home:"36",left:"37",up:"38",right:"39",down:"40",insert:"45","delete":"46",a:"65",b:"66",c:"67",d:"68",e:"69",f:"70",g:"71",h:"72",i:"73",j:"74",k:"75",l:"76",m:"77",n:"78",o:"79",p:"80",q:"81",r:"82",s:"83",t:"84",u:"85",v:"86",w:"87",x:"88",y:"89",z:"90",windows_left:"91",windows_right:"92",select:"93",numpad0:"96",numpad1:"97",numpad2:"98",numpad3:"99",numpad4:"100",numpad5:"101",numpad6:"102",numpad7:"103",numpad8:"104",numpad9:"105",asterisk:"106",plus:"107",minus:"109",decimal_point:"110",divide:"111",f1:"112",f2:"113",f3:"114",f4:"115",f5:"116",f6:"117",f7:"118",f8:"119",f9:"120",numlock:"144",scrollock:"145",semicolon:"186",equals:"187",comma:"188",dash:"189",period:"190",slash:"191",grave_accent:"192",open_bracket:"219",backslash:"220",close_bracket:"221",single_quote:"222"},l={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"caps_lock",27:"esc",32:"space",33:"page_up",34:"page_down",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"delete",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",91:"windows_left",92:"windows_right",93:"select",96:"numpad0",97:"numpad1",98:"numpad2",99:"numpad3",100:"numpad4",101:"numpad5",102:"numpad6",103:"numpad7",104:"numpad8",105:"numpad9",106:"asterisk",107:"plus",109:"minus",110:"decimal_point",111:"divide",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",144:"numlock",145:"scrollock",186:"semicolon",187:"equals",188:"comma",189:"dash",190:"period",191:"slash",192:"grave_accent",219:"open_bracket",220:"backslash",221:"close_bracket",222:"single_quote"},u=[],d=[],f=[],p=[],g=[];return t.setupInput=function(){var h=[];h[0]="left_mouse_button",h[1]="center_mouse_button",h[2]="right_mouse_button";var c=[];c[1]="left_mouse_button",c[2]="right_mouse_button",c[4]="center_mouse_button",f=h,p=c,window.addEventListener("keydown",s),window.addEventListener("keyup",i);var l=t.canvas||t.dom;l.addEventListener("mousedown",o,!1),l.addEventListener("mouseup",n,!1),l.addEventListener("touchstart",a,!1),l.addEventListener("touchend",r,!1),window.addEventListener("blur",e,!1),document.oncontextmenu=function(){return!1}},t.preventDefaultKeys=function(){for(var t=arguments,e=0;e<t.length;e++)g[t[e]]=!0},t.pressed=h,t.keyCodes=c,t.keycodeNames=l,t.on_keydown=function(e,i){if(t.isArray(e))for(var s=0;e[s];s++)u[e[s]]=i;else u[e]=i},t.on_keyup=function(e,i){if(t.isArray(e))for(var s=0;e[s];s++)d[e[s]]=i;else d[e]=i},t.clearKeyCallbacks=function(){d=[],u=[]},t}(jaws||{}),jaws=function(t){function e(e){if(t.isDrawable(e)){for(var i=t.isImage(e)?t.imageToCanvas(e):e,s=i.getContext("2d"),o=s.getImageData(0,0,i.width,i.height),n=o.data,a=0;a<n.length;a+=4)255===n[a]&&0===n[a+1]&&255===n[a+2]&&(n[a+3]=0);return s.putImageData(o,0,0),i}}return t.Assets=function(){function i(e){if(t.isString(e)){var i=r.getPostfix(e);return r.file_type[i]?r.file_type[i]:i}t.log.error("jaws.assets.getType: Argument not a String with "+e)}function s(s){var o=this.asset,n=o.src,h=i(o.src);try{if("json"===h){if(4!==this.readyState)return;r.data[o.src]=JSON.parse(this.responseText)}else if("image"===h){var c=r.image_to_canvas?t.imageToCanvas(o.image):o.image;r.fuchia_to_transparent&&"bmp"===r.getPostfix(o.src)&&(c=e(c)),r.data[o.src]=c}else r.data[o.src]="audio"===h&&r.can_play[r.getPostfix(o.src)]?o.audio:"video"===h&&r.can_play[r.getPostfix(o.src)]?o.video:this.response}catch(l){t.log.error("Cannot process "+n+" (Message: "+l.message+", Name: "+l.name+")"),r.data[o.src]=null}r.loaded[n]||r.load_count++,r.loaded[n]=!0,r.loading[n]=!1,a(o,!0,s)}function o(t){r.loaded[t.src]=!0,r.loading[t.src]=!1,r.load_count++,a(t,!0)}function n(t){var e=this.asset;r.error_count++,a(e,!1,t)}function a(t,e,i){var s=parseInt((r.load_count+r.error_count)/r.src_list.length*100);e?(r.onprogress&&r.onprogress(t.src,s),t.onprogress&&void 0!==i&&t.onprogress(i)):(r.onerror&&r.onerror(t.src,s),t.onerror&&void 0!==i&&t.onerror(i)),100===s&&(r.onload&&r.onload(),r.onprogress=null,r.onerror=null,r.onload=null)}if(!(this instanceof arguments.callee))return new arguments.callee;var r=this;r.loaded=[],r.loading=[],r.src_list=[],r.data=[],r.bust_cache=!1,r.image_to_canvas=!0,r.fuchia_to_transparent=!0,r.root="",r.file_type={},r.file_type.json="json",r.file_type.wav="audio",r.file_type.mp3="audio",r.file_type.ogg="audio",r.file_type.m4a="audio",r.file_type.weba="audio",r.file_type.aac="audio",r.file_type.mka="audio",r.file_type.flac="audio",r.file_type.png="image",r.file_type.jpg="image",r.file_type.jpeg="image",r.file_type.gif="image",r.file_type.bmp="image",r.file_type.tiff="image",r.file_type.mp4="video",r.file_type.webm="video",r.file_type.ogv="video",r.file_type.mkv="video",r.can_play={};try{var h=new Audio;r.can_play.wav=!!h.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),r.can_play.ogg=!!h.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),r.can_play.mp3=!!h.canPlayType("audio/mpeg;").replace(/^no$/,""),r.can_play.m4a=!!(h.canPlayType("audio/x-m4a;")||h.canPlayType("audio/aac;")).replace(/^no$/,""),r.can_play.weba=!!h.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),r.can_play.aac=!!h.canPlayType("audio/aac;").replace(/^no$/,""),r.can_play.mka=!!h.canPlayType("audio/x-matroska;").replace(/^no$/,""),r.can_play.flac=!!h.canPlayType("audio/x-flac;").replace(/^no$/,"")}catch(c){}try{var l=document.createElement("video");r.can_play.mp4=!!l.canPlayType("video/mp4;").replace(/^no$/,""),r.can_play.webm=!!l.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/,""),r.can_play.ogv=!!l.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/,""),r.can_play.mkv=!!l.canPlayType("video/x-matroska;").replace(/^no$/,"")}catch(c){}r.length=function(){return r.src_list.length},r.setRoot=function(t){return r.root=t,r},r.get=function(e){if(t.isArray(e))return e.map(function(t){return r.data[t]});if(!t.isString(e))return void t.log.error("jaws.get: Neither String nor Array. Incorrect URL resource "+e);if("*"===e[e.length-1])for(var i=e.replace("*",""),s=0;s<r.src_list.length;s++)if(0==r.src_list[s].indexOf(i)&&r.data[r.src_list[s]])return r.data[r.src_list[s]];return r.data[e]?r.data[e]:void t.log.warn("No such asset: "+e,!0)},r.isLoading=function(e){return t.isString(e)?r.loading[e]:void t.log.error("jaws.isLoading: Argument not a String with "+e)},r.isLoaded=function(e){return t.isString(e)?r.loaded[e]:void t.log.error("jaws.isLoaded: Argument not a String with "+e)},r.getPostfix=function(e){return t.isString(e)?e.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1]:void t.log.error("jaws.assets.getPostfix: Argument not a String with "+e)},r.add=function(e){var i=arguments;1==i.length&&t.isArray(i[0])&&(i=i[0]);for(var s=0;s<i.length;s++)t.isArray(i[s])?r.add(i[s]):t.isString(i[s])?r.src_list.push(i[s]):t.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource "+e);return r},r.loadAll=function(e){return r.load_count=0,r.error_count=0,e.onprogress&&t.isFunction(e.onprogress)&&(r.onprogress=e.onprogress),e.onerror&&t.isFunction(e.onerror)&&(r.onerror=e.onerror),e.onload&&t.isFunction(e.onload)&&(r.onload=e.onload),r.src_list.forEach(function(t){r.load(t)}),r},r.load=function(e,a){if(a||(a={}),!t.isString(e))return void t.log.error("jaws.assets.load: Argument not a String with "+e);var h={},c="";h.src=e,h.onload=a.onload,h.onerror=a.onerror,r.loading[e]=!0;var l=RegExp("^((f|ht)tp(s)?:)?//");c=l.test(e)?h.src:r.root+h.src,r.bust_cache&&(c+="?"+parseInt(1e7*Math.random()));var u=i(h.src);if("image"===u)try{h.image=new Image,h.image.asset=h,h.image.addEventListener("load",s),h.image.addEventListener("error",n),h.image.src=c}catch(d){t.log.error("Cannot load Image resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}else if(r.can_play[r.getPostfix(h.src)]){if("audio"===u)try{h.audio=new Audio,h.audio.asset=h,h.audio.addEventListener("error",n),h.audio.addEventListener("canplay",s),r.data[h.src]=h.audio,h.audio.src=c,h.audio.load()}catch(d){t.log.error("Cannot load Audio resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}else if("video"===u)try{h.video=document.createElement("video"),h.video.asset=h,r.data[h.src]=h.video,h.video.setAttribute("style","display:none;"),h.video.addEventListener("error",n),h.video.addEventListener("canplay",s),document.body.appendChild(h.video),h.video.src=c,h.video.load()}catch(d){t.log.error("Cannot load Video resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}}else{if("audio"===u&&!r.can_play[r.getPostfix(h.src)])return o(h),r;try{var f=new XMLHttpRequest;f.asset=h,f.onreadystatechange=s,f.onerror=n,f.open("GET",c,!0),"json"!==u&&(f.responseType="blob"),f.send(null)}catch(d){t.log.error("Cannot load "+c+" (Message: "+d.message+", Name: "+d.name+")")}}return r},r.displayProgress=function(e){t.isNumber(e)&&t.context&&(t.context.save(),t.context.fillStyle="black",t.context.fillRect(0,0,t.width,t.height),t.context.fillStyle="white",t.context.strokeStyle="white",t.context.textAlign="center",t.context.strokeRect(49,t.height/2-30-1,t.width-100+2,62),t.context.fillRect(50,t.height/2-30,(t.width-100)/100*e,60),t.context.font="11px verdana",t.context.fillText("Loading... "+e+"%",t.width/2,t.height/2-35),t.context.font="11px verdana",t.context.fillStyle="#ccc",t.context.textBaseline="bottom",t.context.fillText("powered by www.jawsjs.com",t.width/2,t.height-1),t.context.restore())}},t.assets=new t.Assets,t}(jaws||{});if("undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){function e(t){this.size=t,this.values=new Array(this.size),this.value,this.add=function(t){if(this.values.length>this.size){this.values.splice(0,1),this.value=0;for(var e=0;this.values[e];e++)this.value+=this.values[e];this.value=this.value/this.size}return this.values.push(t),this},this.get=function(){return parseInt(this.value)}}return window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,16.666)}}(),t.GameLoop=function(i,s,o){if(!(this instanceof arguments.callee))return new arguments.callee(i,s);this.tick_duration=0,this.fps=0,this.ticks=0;var n,a=!1,r=!1,h=this,c=new e(20);this.runtime=function(){return this.last_tick-this.first_tick},this.start=function(){t.log.info("Game loop start",!0),this.first_tick=(new Date).getTime(),this.current_tick=(new Date).getTime(),this.last_tick=(new Date).getTime(),s.setup!==!1&&i.setup&&i.setup(o),step_delay=1e3/s.fps,60==s.fps?requestAnimFrame(this.loop):n=setInterval(this.loop,step_delay)},this.loop=function(){h.current_tick=(new Date).getTime(),h.tick_duration=h.current_tick-h.last_tick,h.fps=c.add(1e3/h.tick_duration).get(),r||a||(i.update&&i.update(),i.draw&&i.draw(),h.ticks++),60!=s.fps||r||requestAnimFrame(h.loop),h.last_tick=h.current_tick},this.pause=function(){a=!0},this.unpause=function(){a=!1},this.stop=function(){n&&clearInterval(n),r=!0}},t}(jaws||{}),jaws=function(t){var e=0;return t.Rect=function(t,i,s,o){return this instanceof arguments.callee?(this.x=t,this.y=i,this.width=s,this.height=o,this.right=t+s-e,void(this.bottom=i+o-e)):new arguments.callee(t,i,s,o)},t.Rect.prototype.getPosition=function(){return[this.x,this.y]},t.Rect.prototype.move=function(t,e){return this.x+=t,this.y+=e,this.right+=t,this.bottom+=e,this},t.Rect.prototype.moveTo=function(t,i){return this.x=t,this.y=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.resize=function(t,i){return this.width+=t,this.height+=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.clone=function(){return new t.Rect(this.x,this.y,this.width,this.height)},t.Rect.prototype.shrink=function(t,i){return this.x+=t,this.y+=i,this.width-=t+t,this.height-=i+i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.resizeTo=function(t,i){return this.width=t,this.height=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.draw=function(){return t.context.strokeStyle="red",t.context.strokeRect(this.x-.5,this.y-.5,this.width,this.height),this},t.Rect.prototype.collidePoint=function(t,e){return t>=this.x&&t<=this.right&&e>=this.y&&e<=this.bottom},t.Rect.prototype.collideRect=function(t){return(this.x>=t.x&&this.x<=t.right||t.x>=this.x&&t.x<=this.right)&&(this.y>=t.y&&this.y<=t.bottom||t.y>=this.y&&t.y<=this.bottom)},t.Rect.prototype.toString=function(){return"[Rect "+this.x+", "+this.y+", "+this.width+", "+this.height+"]"},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Rect);var jaws=function(jaws){return jaws.Sprite=function(t){return this instanceof arguments.callee?(this.set(t),void(this.context=t.context?t.context:jaws.context)):new arguments.callee(t)},jaws.Sprite.prototype.default_options={x:0,y:0,alpha:1,angle:0,flipped:!1,anchor_x:0,anchor_y:0,image:null,image_path:null,anchor:null,scale_image:null,damping:1,scale_x:1,scale_y:1,scale:1,color:"#ddd",width:16,height:16,_constructor:null,context:null,data:null},jaws.Sprite.prototype.set=function(t){if(jaws.isString(this.image)&&(this.image_path=this.image),jaws.parseOptions(this,t,this.default_options),this.scale&&(this.scale_x=this.scale_y=this.scale),this.image&&this.setImage(this.image),this.scale_image&&this.scaleImage(this.scale_image),this.anchor&&this.setAnchor(this.anchor),!this.image&&this.color&&this.width&&this.height){var e=document.createElement("canvas"),i=e.getContext("2d");e.width=this.width,e.height=this.height,i.fillStyle=this.color,i.fillRect(0,0,this.width,this.height),this.image=e}return this.cacheOffsets(),this},jaws.Sprite.prototype.clone=function(object){var constructor=this._constructor?eval(this._constructor):this.constructor,new_sprite=new constructor(this.attributes());return new_sprite._constructor=this._constructor||this.constructor.name,new_sprite},jaws.Sprite.prototype.setImage=function(t){var e=this;return jaws.isDrawable(t)?(this.image=t,this.cacheOffsets()):(jaws.assets.isLoaded(t)?(this.image=jaws.assets.get(t),this.cacheOffsets()):(jaws.log.warn("Image '"+t+"' not preloaded with jaws.assets.add(). Image and a working sprite.rect() will be delayed."),jaws.assets.load(t,{onload:function(){e.image=jaws.assets.get(t),e.cacheOffsets()}})),this)},jaws.Sprite.prototype.stepToWhile=function(t,e,i){var s=1,o=t<this.x?-s:s,n=e<this.y?-s:s;t=parseInt(t),e=parseInt(e);for(var a=!1,r=!1;;)if(a===!1&&(this.x!=t&&(this.x+=o),i(this)||(this.x-=o,a=!0)),r===!1&&(this.y!=e&&(this.y+=n),i(this)||(this.y-=n,r=!0)),(a||this.x==t)&&(r||this.y==e))return{x:a,y:r}},jaws.Sprite.prototype.stepWhile=function(t,e,i){return this.stepToWhile(this.x+t,this.y+e,i)},jaws.Sprite.prototype.flip=function(){return this.flipped=this.flipped?!1:!0,this},jaws.Sprite.prototype.flipTo=function(t){return this.flipped=t,this},jaws.Sprite.prototype.rotate=function(t){return this.angle+=t,this},jaws.Sprite.prototype.rotateTo=function(t){return this.angle=t,this},jaws.Sprite.prototype.moveTo=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.x=t,this.y=e,this},jaws.Sprite.prototype.move=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),t&&(this.x+=t),e&&(this.y+=e),this},jaws.Sprite.prototype.scaleAll=function(t){return this.scale_x*=t,this.scale_y*=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleTo=function(t){return this.scale_x=this.scale_y=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleWidth=function(t){return this.scale_x*=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleHeight=function(t){return this.scale_y*=t,this.cacheOffsets()},jaws.Sprite.prototype.setX=function(t){return this.x=t,this},jaws.Sprite.prototype.setY=function(t){return this.y=t,this},jaws.Sprite.prototype.setTop=function(t){return this.y=t+this.top_offset,this},jaws.Sprite.prototype.setBottom=function(t){return this.y=t-this.bottom_offset,this},jaws.Sprite.prototype.setLeft=function(t){return this.x=t+this.left_offset,this},jaws.Sprite.prototype.setRight=function(t){return this.x=t-this.right_offset,this},jaws.Sprite.prototype.setWidth=function(t){return this.scale_x=t/this.image.width,this.cacheOffsets()},jaws.Sprite.prototype.setHeight=function(t){return this.scale_y=t/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.resize=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.scale_x=(this.width+t)/this.image.width,this.scale_y=(this.height+e)/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.resizeTo=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.scale_x=t/this.image.width,this.scale_y=e/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.setAnchor=function(t){var e={top_left:[0,0],left_top:[0,0],center_left:[0,.5],left_center:[0,.5],bottom_left:[0,1],left_bottom:[0,1],top_center:[.5,0],center_top:[.5,0],center_center:[.5,.5],center:[.5,.5],bottom_center:[.5,1],center_bottom:[.5,1],top_right:[1,0],right_top:[1,0],center_right:[1,.5],right_center:[1,.5],bottom_right:[1,1],right_bottom:[1,1]};return(a=e[t])&&(this.anchor_x=a[0],this.anchor_y=a[1],this.image&&this.cacheOffsets()),this},jaws.Sprite.prototype.cacheOffsets=function(){return this.image?(this.width=this.image.width*this.scale_x,this.height=this.image.height*this.scale_y,this.left_offset=this.width*this.anchor_x,this.top_offset=this.height*this.anchor_y,this.right_offset=this.width*(1-this.anchor_x),this.bottom_offset=this.height*(1-this.anchor_y),this.cached_rect&&this.cached_rect.resizeTo(this.width,this.height),this):void 0},jaws.Sprite.prototype.rect=function(){return!this.cached_rect&&this.width&&(this.cached_rect=new jaws.Rect(this.x,this.y,this.width,this.height)),this.cached_rect&&this.cached_rect.moveTo(this.x-this.left_offset,this.y-this.top_offset),this.cached_rect},jaws.Sprite.prototype.draw=function(){return this.image?(this.context.save(),this.context.translate(this.x,this.y),0!=this.angle&&jaws.context.rotate(this.angle*Math.PI/180),this.flipped&&this.context.scale(-1,1),this.context.globalAlpha=this.alpha,this.context.translate(-this.left_offset,-this.top_offset),this.context.drawImage(this.image,0,0,this.width,this.height),this.context.restore(),this):this},jaws.Sprite.prototype.scaleImage=function(t){return this.image?(this.setImage(jaws.retroScaleImage(this.image,t)),this):void 0},jaws.Sprite.prototype.asCanvasContext=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");return jaws.context&&(e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled),e.drawImage(this.image,0,0,this.width,this.height),e},jaws.Sprite.prototype.asCanvas=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");return jaws.context&&(e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled),e.drawImage(this.image,0,0,this.width,this.height),t},jaws.Sprite.prototype.toString=function(){return"[Sprite "+this.x.toFixed(2)+", "+this.y.toFixed(2)+", "+this.width+", "+this.height+"]"},jaws.Sprite.prototype.attributes=function(){var t={};return t._constructor=this._constructor||"jaws.Sprite",t.x=parseFloat(this.x.toFixed(2)),t.y=parseFloat(this.y.toFixed(2)),t.image=this.image_path,t.alpha=this.alpha,t.flipped=this.flipped,t.angle=parseFloat(this.angle.toFixed(2)),t.scale_x=this.scale_x,t.scale_y=this.scale_y,t.anchor_x=this.anchor_x,t.anchor_y=this.anchor_y,null!==this.data&&(t.data=jaws.clone(this.data)),t},jaws.Sprite.parse=function(objects){function parseArray(array){array.forEach(function(data){var constructor=data._constructor?eval(data._constructor):data.constructor;if(jaws.isFunction(constructor)){jaws.log.info("Creating "+data._constructor+"("+data.toString()+")",!0);var object=new constructor(data);object._constructor=data._constructor||data.constructor.name,sprites.push(object)}})}var sprites=[];return jaws.isArray(objects)?objects.every(function(t){return t._constructor})?parseArray(objects):sprites=objects:jaws.isString(objects)&&(parseArray(JSON.parse(objects)),jaws.log.info(objects)),sprites},jaws.Sprite.prototype.toJSON=function(){return JSON.stringify(this.attributes())},jaws}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Sprite);var jaws=function(t){function e(t,e,i,s,o){var n=document.createElement("canvas");n.width=s,n.height=o;var a=n.getContext("2d");return a.drawImage(t,e,i,s,o,0,0,n.width,n.height),n}return t.SpriteSheet=function(i){if(!(this instanceof arguments.callee))return new arguments.callee(i);if(t.parseOptions(this,i,this.default_options),t.isString(this.image)&&!i.frame_size){var s=new RegExp("_(\\d+)x(\\d+)","g"),o=s.exec(this.image);this.frame_size=[],this.frame_size[0]=parseInt(o[1]),this.frame_size[1]=parseInt(o[2])}if(this.image=t.isDrawable(this.image)?this.image:t.assets.data[this.image],this.scale_image){var n=t.isDrawable(this.image)?this.image:t.assets.get(this.image);this.frame_size[0]*=this.scale_image,this.frame_size[1]*=this.scale_image,this.image=t.retroScaleImage(n,this.scale_image)}if(this.frames=[],"down"==this.orientation)for(var a=this.offset;a<this.image.width;a+=this.frame_size[0])for(var r=0;r<this.image.height;r+=this.frame_size[1])this.frames.push(e(this.image,a,r,this.frame_size[0],this.frame_size[1]));else for(var r=this.offset;r<this.image.height;r+=this.frame_size[1])for(var a=0;a<this.image.width;a+=this.frame_size[0])this.frames.push(e(this.image,a,r,this.frame_size[0],this.frame_size[1]))},t.SpriteSheet.prototype.default_options={image:null,orientation:"down",frame_size:[32,32],offset:0,scale_image:null},t.SpriteSheet.prototype.toString=function(){return"[SpriteSheet "+this.frames.length+" frames]"},t}(jaws||{}),jaws=function(t){return t.Animation=function(e){if(!(this instanceof arguments.callee))return new arguments.callee(e);if(t.parseOptions(this,e,this.default_options),e.sprite_sheet){var i=new t.SpriteSheet({image:e.sprite_sheet,scale_image:this.scale_image,frame_size:this.frame_size,orientation:this.orientation,offset:this.offset});this.frames=i.frames,this.frame_size=i.frame_size}if(e.scale_image){var s=t.isDrawable(e.sprite_sheet)?e.sprite_sheet:t.assets.get(e.sprite_sheet);this.frame_size[0]*=e.scale_image,this.frame_size[1]*=e.scale_image,e.sprite_sheet=t.retroScaleImage(s,e.scale_image)}if(this.current_tick=(new Date).getTime(),this.last_tick=(new Date).getTime(),this.sum_tick=0,e.subsets){this.subsets={};for(subset in e.subsets)start_stop=e.subsets[subset],this.subsets[subset]=this.slice(start_stop[0],start_stop[1])}},t.Animation.prototype.default_options={frames:[],subsets:[],frame_duration:100,index:0,loop:1,bounce:0,frame_direction:1,frame_size:null,orientation:"down",on_end:null,offset:0,scale_image:null,sprite_sheet:null},t.Animation.prototype.subset=function(t){return this.subsets[t]},t.Animation.prototype.update=function(){return this.current_tick=(new Date).getTime(),this.sum_tick+=this.current_tick-this.last_tick,this.last_tick=this.current_tick,this.sum_tick>this.frame_duration&&(this.index+=this.frame_direction,this.sum_tick=0),(this.index>=this.frames.length||this.index<0)&&(this.bounce?(this.frame_direction=-this.frame_direction,this.index+=2*this.frame_direction):this.loop?this.index=this.frame_direction<0?this.frames.length-1:0:(this.index-=this.frame_direction,this.on_end&&(this.on_end(),this.on_end=null))),this -},t.Animation.prototype.slice=function(e,i){var s={};return s.frame_duration=this.frame_duration,s.loop=this.loop,s.bounce=this.bounce,s.on_end=this.on_end,s.frame_direction=this.frame_direction,s.frames=this.frames.slice().slice(e,i),new t.Animation(s)},t.Animation.prototype.next=function(){return this.update(),this.frames[this.index]},t.Animation.prototype.atLastFrame=function(){return this.index==this.frames.length-1},t.Animation.prototype.atFirstFrame=function(){return 0==this.index},t.Animation.prototype.currentFrame=function(){return this.frames[this.index]},t.Animation.prototype.toString=function(){return"[Animation, "+this.frames.length+" frames]"},t}(jaws||{});if("undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){function e(t,e){for(var i=function(e){return void 0!==t.isSpriteList?t.at(e):t[e]},s=[],o=new Array(e),n=0;e>n;n++)o[n]=n;for(var n=e-1,a=t.length;n>=0;a=t.length){for(s.push(o.map(i));n>=0&&o[n]===a-1;)n--,a--;if(n>=0){o[n]+=1;for(var r=n+1;e>r;r++)o[r]=o[r-1]+1;n=e-1}}return s}return t.collideOneWithOne=function(e,i){return e.radius&&i.radius&&e!==i&&t.collideCircles(e,i)?!0:e.rect&&i.rect&&e!==i&&t.collideRects(e.rect(),i.rect())?!0:!1},t.collideOneWithMany=function(e,i,s){var o=[];if(s){for(var n=0;n<i.length;n++)t.collideOneWithOne(e,i[n])&&(s(e,i[n]),o.push(i[n]));return o}return i.filter(function(i){return t.collideOneWithOne(e,i)})},t.collideManyWithMany=function(i,s,o){var n=[];return i===s?e(i,2).forEach(function(e){t.collideOneWithOne(e[0],e[1])&&(o?o(e[0],e[1]):n.push([e[0],e[1]]))}):i.forEach(function(e){s.forEach(function(i){t.collideOneWithOne(e,i)&&(o?o(e,i):n.push([e,i]))})}),n},t.collideCircles=function(e,i){return t.distanceBetween(e,i)<e.radius+i.radius},t.collideRects=function(t,e){return(t.x>=e.x&&t.x<=e.right||e.x>=t.x&&e.x<=t.right)&&(t.y>=e.y&&t.y<=e.bottom||e.y>=t.y&&e.y<=t.bottom)},t.distanceBetween=function(t,e){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))},t.collide=function(e,i,s){if((e.rect||e.radius)&&i.forEach)return t.collideOneWithMany(e,i,s).length>0;if(e.forEach&&i.forEach)return t.collideManyWithMany(e,i,s).length>0;if(e.forEach&&(i.rect||i.radius))return t.collideOneWithMany(i,e,s).length>0;if(e.rect&&i.rect||e.radius&&i.radius){var o=t.collideOneWithOne(e,i);if(!s||!o)return o;s(e,i)}},t}(jaws||{}),jaws=function(t){return t.PixelMap=function(e){return this instanceof arguments.callee?(this.options=e,this.scale=e.scale||1,this.x=e.x||0,this.y=e.y||0,e.image?(this.setContext(e.image),e.scale_image&&this.setContext(t.retroScaleImage(this.context.canvas,e.scale_image)),this.width=this.context.canvas.width*this.scale,this.height=this.context.canvas.height*this.scale):t.log.warn("PixelMap needs an image to work with"),this.named_colors=[],void this.update()):new arguments.callee(e)},t.PixelMap.prototype.setContext=function(e){var e=t.isDrawable(e)?e:t.assets.get(e);this.context=t.imageToCanvasContext(e)},t.PixelMap.prototype.update=function(t,e,i,s){if((void 0===t||0>t)&&(t=0),(void 0===e||0>e)&&(e=0),(void 0===i||i>this.width)&&(i=this.width),(void 0===s||s>this.height)&&(s=this.height),0==arguments.length)this.data=this.context.getImageData(t,e,i,s).data;else for(var o=this.context.getImageData(t,e,i,s).data,n=0,a=4*this.width,r=e*this.width*4+4*t,h=4*i,c=0;s>c;c++){for(var l=0;h>l;l++)this.data[r+l]=o[n++];r+=a}},t.PixelMap.prototype.draw=function(){t.context.drawImage(this.context.canvas,this.x,this.y,this.width,this.height)},t.PixelMap.prototype.namedColorAtRect=function(t,e){for(var i=t.x,s=t.y;i<t.right-1;i++)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;s<t.bottom-1;s++)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;i>t.x;i--)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;s>t.y;s--)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);return!1},t.PixelMap.prototype.at=function(t,e){t=parseInt(t),e=parseInt(e),0>e&&(e=0);var i=e*this.width*4+4*t,s=this.data[i],o=this.data[i+1],n=this.data[i+2],a=this.data[i+3];return[s,o,n,a]},t.PixelMap.prototype.namedColorAt=function(t,e){for(var i=this.at(t,e),s=0;s<this.named_colors.length;s++){var o=this.named_colors[s].name,n=this.named_colors[s].color;if(n[0]==i[0]&&n[1]==i[1]&&n[2]==i[2]&&n[3]==i[3])return o}},t.PixelMap.prototype.nameColor=function(t,e){this.named_colors.push({name:e,color:t})},t}(jaws||{}),jaws=function(t){return t.Parallax=function(e){return this instanceof arguments.callee?void t.parseOptions(this,e,this.default_options):new arguments.callee(e)},t.Parallax.prototype.default_options={width:function(){return t.width},height:function(){return t.height},scale:1,repeat_x:null,repeat_y:null,camera_x:0,camera_y:0,layers:[]},t.Parallax.prototype.draw=function(){for(var t,e,i=0;i<this.layers.length;i++)for(t=this.layers[i],e=this.repeat_x?-(this.camera_x/t.damping%t.width):-(this.camera_x/t.damping),t.y=this.repeat_y?-(this.camera_y/t.damping%t.height):-(this.camera_y/t.damping),t.x=e;t.y<this.height;){for(;t.x<this.width&&(t.x+t.width>=0&&t.y+t.height>=0&&t.draw(),t.x=t.x+t.width,this.repeat_x););if(t.y=t.y+t.height,t.x=e,!this.repeat_y)break}},t.Parallax.prototype.addLayer=function(e){var i=new t.ParallaxLayer(e);i.scaleAll(this.scale),this.layers.push(i)},t.Parallax.prototype.toString=function(){return"[Parallax "+this.x+", "+this.y+". "+this.layers.length+" layers]"},t.ParallaxLayer=function(e){return this instanceof arguments.callee?(this.damping=e.damping||0,void t.Sprite.call(this,e)):new arguments.callee(e)},t.ParallaxLayer.prototype=t.Sprite.prototype,t}(jaws||{}),jaws=function(jaws){return jaws.Text=function(t){return this instanceof arguments.callee?(this.set(t),t.context&&(this.context=t.context),void(t.context||jaws.context&&(this.context=jaws.context))):new arguments.callee(t)},jaws.Text.prototype.default_options={x:0,y:0,alpha:1,angle:0,anchor_x:0,anchor_y:0,anchor:"top_left",damping:1,style:"normal",fontFace:"serif",fontSize:12,color:"black",textAlign:"start",textBaseline:"alphabetic",text:"",wordWrap:!1,width:function(){return jaws.width},height:function(){return jaws.height},shadowColor:null,shadowBlur:null,shadowOffsetX:null,shadowOffsetY:null,_constructor:null},jaws.Text.prototype.set=function(t){return jaws.parseOptions(this,t,this.default_options),this.anchor&&this.setAnchor(this.anchor),this.cacheOffsets(),this},jaws.Text.prototype.clone=function(){var constructor=this._constructor?eval(this._constructor):this.constructor,new_sprite=new constructor(this.attributes());return new_sprite._constructor=this._constructor||this.constructor.name,new_sprite},jaws.Text.prototype.rotate=function(t){return this.angle+=t,this},jaws.Text.prototype.rotateTo=function(t){return this.angle=t,this},jaws.Text.prototype.moveTo=function(t,e){return this.x=t,this.y=e,this},jaws.Text.prototype.move=function(t,e){return t&&(this.x+=t),e&&(this.y+=e),this},jaws.Text.prototype.setX=function(t){return this.x=t,this},jaws.Text.prototype.setY=function(t){return this.y=t,this},jaws.Text.prototype.setTop=function(t){return this.y=t+this.top_offset,this},jaws.Text.prototype.setBottom=function(t){return this.y=t-this.bottom_offset,this},jaws.Text.prototype.setLeft=function(t){return this.x=t+this.left_offset,this},jaws.Text.prototype.setRight=function(t){return this.x=t-this.right_offset,this},jaws.Text.prototype.setWidth=function(t){return this.width=t,this.cacheOffsets(),this},jaws.Text.prototype.setHeight=function(t){return this.height=t,this.cacheOffsets(),this},jaws.Text.prototype.resize=function(t,e){return this.width+=t,this.height+=e,this.cacheOffsets(),this},jaws.Text.prototype.resizeTo=function(t,e){return this.width=t,this.height=e,this.cacheOffsets(),this},jaws.Text.prototype.setAnchor=function(t){var e={top_left:[0,0],left_top:[0,0],center_left:[0,.5],left_center:[0,.5],bottom_left:[0,1],left_bottom:[0,1],top_center:[.5,0],center_top:[.5,0],center_center:[.5,.5],center:[.5,.5],bottom_center:[.5,1],center_bottom:[.5,1],top_right:[1,0],right_top:[1,0],center_right:[1,.5],right_center:[1,.5],bottom_right:[1,1],right_bottom:[1,1]};return e.hasOwnProperty(t)&&(this.anchor_x=e[t][0],this.anchor_y=e[t][1],this.cacheOffsets()),this},jaws.Text.prototype.cacheOffsets=function(){return this.left_offset=this.width*this.anchor_x,this.top_offset=this.height*this.anchor_y,this.right_offset=this.width*(1-this.anchor_x),this.bottom_offset=this.height*(1-this.anchor_y),this.cached_rect&&this.cached_rect.resizeTo(this.width,this.height),this},jaws.Text.prototype.rect=function(){return!this.cached_rect&&this.width&&(this.cached_rect=new jaws.Rect(this.x,this.y,this.width,this.height)),this.cached_rect&&this.cached_rect.moveTo(this.x-this.left_offset,this.y-this.top_offset),this.cached_rect},jaws.Text.prototype.draw=function(){this.context.save(),0!==this.angle&&this.context.rotate(this.angle*Math.PI/180),this.context.globalAlpha=this.alpha,this.context.translate(-this.left_offset,-this.top_offset),this.context.fillStyle=this.color,this.context.font=this.style+" "+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var t=this.y,e=this.x;if(this.wordWrap)for(var i=this.text.split(" "),s="",o=0;o<i.length;o++){var n=s+i[o]+" ",a=this.context.measureText(n);this.y<t+this.height&&(a.width>this.width?(this.context.fillText(s,this.x,this.y),s=i[o]+" ",this.y+=this.fontSize):s=n,this.context.fillText(s,this.x,this.y))}else if(this.context.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var i=this.text.split(" "),s=" ",o=0;o<i.length;o++){var n=s+i[o]+" ";this.context.measureText(n).width<Math.abs(this.width-this.x)&&(this.context.fillText(n,this.x,this.y),s=i[o]+" ",s=n)}return this.y=t,this.x=e,this.context.restore(),this},jaws.Text.prototype.asCanvasContext=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled,this.context.fillStyle=this.color,this.context.font=this.style+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var i=this.y,s=this.x;if(this.wordWrap)for(var o=this.text.split(" "),n="",a=0;a<o.length;a++){var r=n+o[a]+" ",h=this.context.measureText(r);this.y<i+this.height&&(h.width>this.width?(this.context.fillText(n,this.x,this.y),n=o[a]+" ",this.y+=this.fontSize):n=r,this.context.fillText(n,this.x,this.y))}else if(this.context.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var o=this.text.split(" "),n=" ",a=0;a<o.length;a++){var r=n+o[a]+" ";this.context.measureText(r).width<Math.abs(this.width-this.x)&&(this.context.fillText(r,this.x,this.y),n=o[a]+" ",n=r)}return this.y=i,this.x=s,e},jaws.Text.prototype.asCanvas=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled,this.context.fillStyle=this.color,this.context.font=this.style+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var i=this.y,s=this.x;if(this.wordWrap)for(var o=this.text.split(" "),n="",a=0;a<o.length;a++){var r=n+o[a]+" ",h=e.measureText(r);this.y<i+this.height&&(h.width>this.width?(e.fillText(n,this.x,this.y),n=o[a]+" ",this.y+=this.fontSize):n=r,e.fillText(n,this.x,this.y))}else if(e.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var o=this.text.split(" "),n=" ",a=0;a<o.length;a++){var r=n+o[a]+" ";e.measureText(r).width<Math.abs(this.width-this.x)&&(e.fillText(r,this.x,this.y),n=o[a]+" ",n=r)}return this.y=i,this.x=s,t},jaws.Text.prototype.toString=function(){return"[Text "+this.x.toFixed(2)+", "+this.y.toFixed(2)+", "+this.width+", "+this.height+"]"},jaws.Text.prototype.attributes=function(){var t=this.options;return t._constructor=this._constructor||"jaws.Text",t.x=parseFloat(this.x.toFixed(2)),t.y=parseFloat(this.y.toFixed(2)),t.text=this.text,t.alpha=this.alpha,t.angle=parseFloat(this.angle.toFixed(2)),t.anchor_x=this.anchor_x,t.anchor_y=this.anchor_y,t.style=this.style,t.fontSize=this.fontSize,t.fontFace=this.fontFace,t.color=this.color,t.textAlign=this.textAlign,t.textBaseline=this.textBaseline,t.wordWrap=this.wordWrap,t.width=this.width,t.height=this.height,t},jaws.Text.prototype.toJSON=function(){return JSON.stringify(this.attributes())},jaws}(jaws||{});if("undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Text),"undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){return t.QuadTree=function(e){this.depth=arguments[1]||0,this.bounds=e||new t.Rect(0,0,t.width,t.height),this.nodes=[],this.objects=[]},t.QuadTree.prototype.clear=function(){this.objects=[];for(var t=0;t<this.nodes.length;t++)"undefined"!=typeof this.nodes[t]&&(this.nodes[t].clear(),delete this.nodes[t])},t.QuadTree.prototype.split=function(){var e=Math.round(this.bounds.width/2),i=Math.round(this.bounds.height/2),s=this.bounds.x,o=this.bounds.y;this.nodes[0]=new t.QuadTree(new t.Rect(s+e,o,e,i),this.depth+1),this.nodes[1]=new t.QuadTree(new t.Rect(s,o,e,i),this.depth+1),this.nodes[2]=new t.QuadTree(new t.Rect(s,o+i,e,i),this.depth+1),this.nodes[3]=new t.QuadTree(new t.Rect(s+e,o+i,e,i),this.depth+1)},t.QuadTree.prototype.getIndex=function(t){var e=-1,i=this.bounds.x+this.bounds.width/2,s=this.bounds.y+this.bounds.height/2,o=t.y<s&&t.y+t.height<s,n=t.y>s;return t.x<i&&t.x+t.width<i?o?e=1:n&&(e=2):t.x>i&&(o?e=0:n&&(e=3)),e},t.QuadTree.prototype.insert=function(t){if(t.hasOwnProperty("x")||t.hasOwnProperty("y")||t.hasOwnProperty("width")||t.hasOwnProperty("height")){if("undefined"!=typeof this.nodes[0]){var e=this.getIndex(t);if(-1!==e)return void this.nodes[e].insert(t)}this.objects.push(t),"undefined"==typeof this.nodes[0]&&this.split();for(var i=0;i<this.objects.length;){var e=this.getIndex(this.objects[i]);-1!==e?this.nodes[e].insert(this.objects.splice(i,1)[0]):i++}}},t.QuadTree.prototype.retrieve=function(t){if(t.hasOwnProperty("x")||t.hasOwnProperty("y")||t.hasOwnProperty("width")||t.hasOwnProperty("height")){var e=this.getIndex(t),i=this.objects;if("undefined"!=typeof this.nodes[0])if(-1!==e)i=i.concat(this.nodes[e].retrieve(t));else for(var s=0;s<this.nodes.length;s++)i=i.concat(this.nodes[s].retrieve(t));return i}},t.QuadTree.prototype.collide=function(e,i,s){var o=!1,n=new t.QuadTree,a=[];return e.forEach||(a.push(e),e=a),i.forEach||(a=[],a.push(i),i=a),i.forEach(function(t){n.insert(t)}),e.forEach(function(e){t.collide(e,n.retrieve(e),s)&&(o=!0)}),n.clear(),o},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.QuadTree),window.addEventListener("load",function(){jaws.onload&&jaws.onload()},!1);</script><script type="text/javascript">(function () { - return (function() { - var screen_x1, screen_y1, sqrt; - screen_x1 = 120; - screen_y1 = 180; - sqrt = Math.sqrt; - - /* - * an ad-hoc redux of hammer.js - hammerLet = do(window, navigator)@> - mobile_regex: mobile_regex = /mobile|tablet|ip(ad|hone|od)|android/i - support_touch: support_touch = ('ontouchstart' in window) - prefixed: prefixed = - global: window - get: (sym)@> - { global } = @g - for v in @vendors - return r if (r = global[v + sym])? - undefined - vendors: [ 'webkit', 'moz', 'MS', 'ms', 'o' ] - PointerEvent: PointerEvent ? prefixed.run(window, 'PointerEvent')? - suppourt_touch_only: support_touch && mobile_regex.test(navigator.userAgent) - */ - jaws.onload = function() { - var Demo, game, gameloop; - Demo = (function() { - var AiVaquita, BoundParallaxPlane, Bubble, ColorPlane, EvilBubble, GenericPlane, GrumpyBubble, HappyBubble, ParallaxPlane, ScaledImg, SeaFloor, SeamlessPlane, Sprite, Stilla, Vaquita, Vilma, WaterPlane, downKey, encounter, leftKey, rightKey, seafloorPlane, spaceKey, upKey, waterscape, _ref1; - - Demo.prototype.keyCodes = (_ref1 = jaws.keyCodes, leftKey = _ref1.left, rightKey = _ref1.right, upKey = _ref1.up, downKey = _ref1.down, spaceKey = _ref1.space, _ref1); - - Demo.prototype.Sprite = Sprite = (function(_super) { - __extends(_Class, _super); - - function _Class() { - _Class.__super__.constructor.call(this, { - image: this.image, - x: 0, - y: 0, - scale: 2 - }); - } - - _Class.prototype.draw = function() { - this.flipped = this.lr >= 0; - this.x = (screen_x1 + this.px + this.lr) * 2; - this.y = (screen_y1 + this.py - this.tb) * 2; - return _Class.__super__.draw.call(this); - }; - - _Class.prototype.cr = 4; - - _Class.prototype.sqrt = Math.sqrt; - - _Class.prototype.collide = (function(x) { - x.coffee = "(o)@>\n { px, py, cr } = o\n opx = o.px; opy = o.py; ocr = o.cr\n dx = px - opx\n dy = py - opy\n dc = cr + ocr\n if (qd = dx * dx + dy * dy) <= dc * dc\n @bumpedInto?(o, qd, dx, dy)\n o.bumpedInto?(@, qd, -dx, -dy)\n # if true\n # @lr = - @lr\n # o.lr = - o.lr\n # @px = opx\n # @py = opy\n # return\n # @py = py - 1\n # return\n # { sqrt } = @\n # if false\n # py = opy\n # px = opx - dc\n # else\n # d = sqrt d\n # if d < 0.1\n # dy = -1\n # d = dx * dx + dy * dy\n # d = sqrt d\n # d = 3 * dc / sqrt(d)\n # py = opy + dy * d\n # px = opx + dx * d\n # @px = px | 0\n # @py = py | 0\n \n "; - return x; - })(function(o) { - var cr, dc, dx, dy, ocr, opx, opy, px, py, qd; - px = o.px, py = o.py, cr = o.cr; - opx = o.px; - opy = o.py; - ocr = o.cr; - dx = px - opx; - dy = py - opy; - dc = cr + ocr; - if ((qd = dx * dx + dy * dy) <= dc * dc) { - if (typeof this.bumpedInto === "function") { - this.bumpedInto(o, qd, dx, dy); - } - return typeof o.bumpedInto === "function" ? o.bumpedInto(this, qd, -dx, -dy) : void 0; - } - }); - - return _Class; - - })(jaws.Sprite); - - Demo.prototype.Bubble = Bubble = Sprite; - - Demo.prototype.HappyBubble = HappyBubble = (function(_super) { - __extends(_Class, _super); - - _Class.prototype.image = happybubble0; - - function _Class() { - this.lr = 4; - this.tb = 4; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.draw = function() { - this.py--; - return _Class.__super__.draw.call(this); - }; - - _Class.prototype.bumpedInto = (function(x) { - x.coffee = "(o, qd, dx, dy)@>\n return if @dead\n # if dx * dx * 2 > qd\n @dead = true\n "; - return x; - })(function(o, qd, dx, dy) { - if (this.dead) { - return; - } - return this.dead = true; - }); - - return _Class; - - })(Bubble); - - Demo.prototype.GrumpyBubble = GrumpyBubble = (function(_super) { - __extends(_Class, _super); - - _Class.prototype.image = grumpybubble0; - - function _Class() { - this.lr = 7; - this.tb = 7; - this.cr = 8; - this.life = 60; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.draw = function(collisions, game) { - if (game != null ? game.slowedBubbles : void 0) { - this.py -= 2; - } else { - this.py -= 3; - } - return _Class.__super__.draw.call(this); - }; - - _Class.prototype.bumpedInto = (function(x) { - x.coffee = "(o, qd, dx, dy)@>\n return if @dead\n # if dx * dx * 2 > qd\n # @dead = true\n ovy = o.vy\n o.py -= 3 + (ovy > 0 then @life -= ovy; ovy * 2 else 0)\n @dead = true unless @life > 0\n "; - return x; - })(function(o, qd, dx, dy) { - var ovy; - if (this.dead) { - return; - } - ovy = o.vy; - o.py -= 3 + (ovy > 0 ? (this.life -= ovy, ovy * 2) : 0); - if (!(this.life > 0)) { - return this.dead = true; - } - }); - - return _Class; - - })(Bubble); - - Demo.prototype.EvilBubble = EvilBubble = (function(_super) { - __extends(_Class, _super); - - _Class.prototype.image = evilbubble0; - - function _Class() { - this.lr = 15; - this.tb = 15; - this.cr = 8; - this.vy_ = -7; - this.life = 2200; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.draw = function(collisions, game) { - var l, life; - l = 0; - if (game.slowedBubbles) { - this.py -= 3; - } else { - this.py += this.vy_; - } - if ((life = this.life) < 2200) { - l = 2200 - this.life; - if (l > 1100) { - l = 2200 - l; - } - l /= 55; - this.vy_ = -8 - l; - } - return _Class.__super__.draw.call(this); - }; - - _Class.prototype.bumpedInto = (function(x) { - x.coffee = "(o, qd, dx, dy)@>\n return if @dead\n # if dx * dx * 2 > qd\n # @dead = true\n ovx = o.vx\n ovy = o.vy\n @life -= ovx * ovx + ovy * ovy\n @life -= 10\n o.px = @px\n o.py = @py + @vy_\n @dead = true unless @life > 0\n "; - return x; - })(function(o, qd, dx, dy) { - var ovx, ovy; - if (this.dead) { - return; - } - ovx = o.vx; - ovy = o.vy; - this.life -= ovx * ovx + ovy * ovy; - this.life -= 10; - o.px = this.px; - o.py = this.py + this.vy_; - if (!(this.life > 0)) { - return this.dead = true; - } - }); - - return _Class; - - })(Bubble); - - Demo.prototype.slowBubbles = (function(x) { - x.coffee = "@>\n return if @slowedBubbles\n @slowedBubbles = true\n "; - return x; - })(function() { - if (this.slowedBubbles) { - return; - } - return this.slowedBubbles = true; - }); - - Demo.prototype.quitSlowBubbles = (function(x) { - x.coffee = "@>\n return unless @slowedBubbles\n @slowedBubbles = false\n "; - return x; - })(function() { - if (!this.slowedBubbles) { - return; - } - return this.slowedBubbles = false; - }); - - Demo.prototype.Stilla = Stilla = (function(_super) { - __extends(_Class, _super); - - _Class.prototype.image = stilla0; - - _Class.prototype.Bubble = _Class.Bubble; - - function _Class() { - this.lr = 16; - this.tb = 20; - this.patience = 490; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.sqrt = Math.sqrt; - - _Class.prototype.pow = Math.pow; - - _Class.prototype.sin = Math.sin; - - _Class.prototype.draw = function(collisions, game) { - var closest, closestDist, consider, d, dx, dy, ir, lr, pow, px, py, r, sin, slowBubbles, spin, v, vilma, _j, _len1, _ref2; - px = this.px, py = this.py, lr = this.lr; - if ((spin = this.spin)) { - pow = this.pow, sin = this.sin; - d = pow(px * px + py * py, 0.42); - r = 5 / (d + 1); - if (r < 1) { - ir = 1; - } else { - d = sin(d / 8000); - d = d * d * 8000; - ir = sqrt(1 - r * r) * pow(d, 0.01); - } - this.px = px * ir + py * (r * spin); - this.py = py * ir - px * (r * spin); - if ((d = px * px + py * py) > 40000) { - this.spin = null; - if (this.patience < 0) { - this.dead = 1; - } - } else if (!(d >= 0)) { - this.px = 0; - this.py = 1; - throw "ir " + ir; - } - } else { - closest = null; - closestDist = null; - consider = function(v) { - var dx, dy; - if (v == null) { - return; - } - dx = px - v.px; - dy = py - v.py; - d = dx * dx + dy * dy; - if ((closest == null) || d < closestDist) { - if (!(d >= 0)) { - return; - } - closest = v; - closestDist = d; - return game.quitSlowBubbles(); - } - }; - vilma = game.vilma; - consider(vilma); - if (game.vaquitas != null) { - _ref2 = game.vaquitas; - for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { - v = _ref2[_j]; - consider(v); - } - } - slowBubbles = false; - if (closest != null) { - if (closestDist < 7000) { - slowBubbles = true; - if (closestDist < 4000) { - this.patience--; - if (this.patience < 0 || (closestDist < 1000 && closest === vilma)) { - dx = px - closest.px; - this.spin = (lr > 0 ? +1 : -1); - this.patience -= 100; - } else { - dx = px - closest.px; - dy = py - closest.py; - this.px += (dx > +2 ? +1 : dx < -2 ? -1 : 0); - this.py += (dy > +2 ? +1 : dy < -2 ? -1 : 0); - } - } - } - } - if (slowBubbles) { - game.slowBubbles(); - } else { - game.quitSlowBubbles(); - } - if (px * lr > 0) { - this.lr = -lr; - } - } - return _Class.__super__.draw.call(this); - }; - - _Class.prototype.goodnight = (function(x) { - x.coffee = "(game)@> game.quitSlowBubbles()"; - return x; - })(function(game) { - return game.quitSlowBubbles(); - }); - - _Class.prototype.bumpedInto = (function(x) { - x.coffee = "(o)@>\n o.dead = true\n "; - return x; - })(function(o) { - return o.dead = true; - }); - - return _Class; - - })(Bubble); - - Demo.prototype.Vaquita = Vaquita = (function(_super) { - __extends(_Class, _super); - - _Class.prototype.twist = [pixyvaquita_twist_l, pixyvaquita_twist_r]; - - function _Class() { - this.lr = 16; - this.tb = 16; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.draw = function() { - if (this.vx < 0) { - this.lr = -18; - } else if (this.vx > 0) { - this.lr = 18; - } - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(Sprite); - - Demo.prototype.AiVaquita = AiVaquita = (function(_super) { - __extends(_Class, _super); - - function _Class() { - this.image = pixyvaquita; - this.time = 0; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.beat_lr = 0; - - _Class.prototype.draw = function() { - var rx, ry, s, vx, vy, y; - vx = this.vx + Math.floor(Math.random() * 3) - 1; - vy = this.vy + Math.floor(Math.random() * 3) - 1; - x = this.px; - y = this.py; - rx = 0.5 * x / screen_x1; - ry = 0.5 * y / screen_y1; - if ((s = vx * vx + vy * vy * 2) > 6) { - vx = Math.round(vx * 0.8 - rx); - vy = Math.round(vy * 0.8 - ry); - } - this.px += this.vx = vx; - this.py += this.vy = vy; - if ((this.time++ % 3) === 0) { - if (this.image !== pixyvaquita) { - this.image = pixyvaquita; - } else if (vx * vx + vy * vy > 2) { - this.image = this.twist[this.beat_lr++ & 1]; - } - } - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(Vaquita); - - Demo.prototype.Vilma = Vilma = (function(_super) { - __extends(_Class, _super); - - function _Class(game) { - var _ref2, _ref3; - this.game = game; - this.image = pixyvaquita; - this.time = 0; - _Class.__super__.constructor.call(this); - this.fpx = (_ref2 = this.px) != null ? _ref2 : 0; - this.fpy = (_ref3 = this.py) != null ? _ref3 : 0; - this.touch = this.game.touchInput; - } - - _Class.prototype.beat_lr = 0; - - _Class.prototype.move = function() { - var aq, ax, ay, itx, ity, touch, tx, ty, vx, vy; - touch = this.touch; - tx = touch.tx, ty = touch.ty; - itx = (tx >= 2 ? 2 : tx <= -2 ? -2 : 0); - ity = (ty >= 2 ? 2 : ty <= -2 ? -2 : 0); - touch.tx = tx * 0.9 - itx; - touch.ty = ty * 0.9 - ity; - ax = (jaws.pressed[leftKey] ? -1 : 0) + (jaws.pressed[rightKey] ? 1 : 0) - itx / 2; - ay = (jaws.pressed[upKey] ? -1 : 0) + (jaws.pressed[downKey] ? 1 : 0) - ity / 2; - if ((aq = ax * ax + ay * ay) > 1) { - aq = sqrt(aq); - ax /= aq; - ay /= aq; - } - ax *= 0.618; - ay *= 0.618; - vx = this.vx; - vy = this.vy; - if (ax * vx < 0) { - vx = 0; - } else { - vx += ax; - vx *= 0.9; - } - if (ay * vy < 0) { - vy = 0; - } else { - vy += ay; - vy *= 0.9; - } - this.vx = vx; - this.vy = vy; - this.px = (this.fpx += this.vx); - return this.py = (this.fpy += this.vy); - }; - - _Class.prototype.draw = function() { - var vx, vy; - vx = this.vx, vy = this.vy; - if ((this.time++ % 3) === 0) { - if (this.image !== pixyvaquita) { - this.image = pixyvaquita; - } else if (vx * vx + (vy * vy / 4) > 1) { - this.image = this.twist[this.beat_lr++ & 1]; - } - } - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(Vaquita); - - Demo.prototype.addVaquita = function() { - var angle, v; - angle = Math.random() * 6.28; - v = new AiVaquita; - v.vx = 0; - v.vy = 0; - v.px = Math.floor(Math.sin(angle) * 300); - v.py = Math.floor(Math.cos(angle) * 300); - return this.vaquitas.push(v); - }; - - Demo.prototype.addStilla = (function(x) { - x.coffee = "(x, y)@>\n return if @stilla?\n v = new @Stilla\n v.px = x\n v.py = y\n @stilla = v\n "; - return x; - })(function(x, y) { - var v; - if (this.stilla != null) { - return; - } - v = new this.Stilla; - v.px = x; - v.py = y; - return this.stilla = v; - }); - - Demo.prototype.addInto = (function(x) { - x.coffee = "(n, v, x, y)@>\n v.vx = 0\n v.vy = 0\n v.px = x\n v.py = y\n b = @[n]\n if (i = b.indexOf(null)) >= 0\n b[i] = v\n else\n b.push v\n # v.draw()\n "; - return x; - })(function(n, v, x, y) { - var b, i; - v.vx = 0; - v.vy = 0; - v.px = x; - v.py = y; - b = this[n]; - if ((i = b.indexOf(null)) >= 0) { - return b[i] = v; - } else { - return b.push(v); - } - }); - - function Demo(vaquitas, cameos, stilla) { - this.vaquitas = vaquitas != null ? vaquitas : []; - this.cameos = cameos != null ? cameos : []; - this.stilla = stilla != null ? stilla : null; - } - - Demo.prototype.encounters = { - __proto__: { - encounter: encounter = { - add: (function(x) { - x.coffee = "(game, x, y)@> game.addInto('cameos', new @creature(), x, y)"; - return x; - })(function(game, x, y) { - return game.addInto('cameos', new this.creature(), x, y); - }), - vy: 0 - }, - random: Math.random, - log: Math.log, - exp: Math.exp, - pow: Math.pow, - poissonSample: (function(x) { - x.coffee = "(m)@>\n { exp, random } = @\n pgen = (m)->\n x = 0\n p = exp(-m)\n s = p\n u = random()\n while u > s\n x++\n p = p * m / x\n s += p\n x\n s = 0\n while m > 50\n s += pgen 50\n m -= 50\n s + pgen m\n "; - return x; - })(function(m) { - var exp, pgen, random, s; - exp = this.exp, random = this.random; - pgen = function(m) { - var p, s, u, x; - x = 0; - p = exp(-m); - s = p; - u = random(); - while (u > s) { - x++; - p = p * m / x; - s += p; - } - return x; - }; - s = 0; - while (m > 50) { - s += pgen(50); - m -= 50; - } - return s + pgen(m); - }), - generate: (function(x) { - x.coffee = "(game,left,top,width,height,vx,vvy)@>\n { probability, random } = @\n depth = game.getDepth()\n genRect = (m,left,top,width,height)=>\n c = m.p(depth) * width * height\n # c = 0\n c = @poissonSample(c)\n if c is 1\n m.add?( game, left + ((random() * width)|0), top + ((random() * height)|0) )\n else\n # c = 0 # if c > 1000\n # c-- if random() > 0.15\n while c-- > 0\n m.add?( game, left + ((random() * width)|0), top + ((random() * height)|0) )\n 1\n if vx * vx >= width * width\n for k,v of @catalogue\n genRect(v, left, top, width, height)\n else for k,v of @catalogue\n vy = vvy - v.vy\n if vy * vy >= height * height\n genRect(v, left, top, width, height)\n else if vx > 0\n if vy > 0\n genRect(v, left, top + height - vy, width, vy)\n genRect(v, left + width - vx, top, vx, height - vy)\n else if vy < 0\n genRect(v, left, top, width, -vy)\n genRect(v, left + width - vx, top - vy, vx, height + vy)\n else\n genRect(v, left + width, top, vx, height)\n else if vx < 0\n if vy > 0\n genRect(v, left, top + height - vy, width, vy)\n genRect(v, left, top, -vx, height - vy)\n else if vy < 0\n genRect(v, left, top, width, -vy)\n genRect(v, left, top - vy, -vx, height + vy)\n else\n genRect(v, left, top, -vx, height)\n else if vy > 0\n genRect(v, left, top + height - vy, width, vy)\n else if vy < 0\n genRect(v, left, top, width, -vy)\n "; - return x; - })(function(game, left, top, width, height, vx, vvy) { - var depth, genRect, k, probability, random, v, vy, _ref, _ref1, _results, _results1; - probability = this.probability, random = this.random; - depth = game.getDepth(); - genRect = (function(_this) { - return function(m, left, top, width, height) { - var c, _results; - c = m.p(depth) * width * height; - c = _this.poissonSample(c); - if (c === 1) { - return typeof m.add === "function" ? m.add(game, left + ((random() * width) | 0), top + ((random() * height) | 0)) : void 0; - } else { - _results = []; - while (c-- > 0) { - if (typeof m.add === "function") { - m.add(game, left + ((random() * width) | 0), top + ((random() * height) | 0)); - } - _results.push(1); - } - return _results; - } - }; - })(this); - if (vx * vx >= width * width) { - _ref = this.catalogue; - _results = []; - for (k in _ref) { - v = _ref[k]; - _results.push(genRect(v, left, top, width, height)); - } - return _results; - } else { - _ref1 = this.catalogue; - _results1 = []; - for (k in _ref1) { - v = _ref1[k]; - vy = vvy - v.vy; - if (vy * vy >= height * height) { - _results1.push(genRect(v, left, top, width, height)); - } else if (vx > 0) { - if (vy > 0) { - genRect(v, left, top + height - vy, width, vy); - _results1.push(genRect(v, left + width - vx, top, vx, height - vy)); - } else if (vy < 0) { - genRect(v, left, top, width, -vy); - _results1.push(genRect(v, left + width - vx, top - vy, vx, height + vy)); - } else { - _results1.push(genRect(v, left + width, top, vx, height)); - } - } else if (vx < 0) { - if (vy > 0) { - genRect(v, left, top + height - vy, width, vy); - _results1.push(genRect(v, left, top, -vx, height - vy)); - } else if (vy < 0) { - genRect(v, left, top, width, -vy); - _results1.push(genRect(v, left, top - vy, -vx, height + vy)); - } else { - _results1.push(genRect(v, left, top, -vx, height)); - } - } else if (vy > 0) { - _results1.push(genRect(v, left, top + height - vy, width, vy)); - } else if (vy < 0) { - _results1.push(genRect(v, left, top, width, -vy)); - } else { - _results1.push(void 0); - } - } - return _results1; - } - }) - }, - catalogue: { - happybubble: { - __proto__: encounter, - p: (function(x) { - x.coffee = "(depth)@> 0.0001 * (1.5 - depth)"; - return x; - })(function(depth) { - return 0.0001 * (1.5 - depth); - }), - creature: HappyBubble, - vy: -1 - }, - grumpybubble: { - __proto__: encounter, - p: (function(x) { - x.coffee = "(depth)@> depth < 0.08 then 0 else (depth - 0.08) * 0.00015"; - return x; - })(function(depth) { - if (depth < 0.08) { - return 0; - } else { - return (depth - 0.08) * 0.00015; - } - }), - creature: GrumpyBubble, - vy: -3 - }, - evilbubble: { - __proto__: encounter, - p: (function(x) { - x.coffee = "(depth)@> depth < 0.35 then 0 else (depth - 0.35) * 0.00005"; - return x; - })(function(depth) { - if (depth < 0.35) { - return 0; - } else { - return (depth - 0.35) * 0.00005; - } - }), - creature: EvilBubble, - vy: -8 - }, - stilla: { - __proto__: encounter, - p: (function(x) { - x.coffee = "(depth)@> depth < 0.01 then 1 else (1-depth)/100000"; - return x; - })(function(depth) { - if (depth < 0.01) { - return 1; - } else { - return (1 - depth) / 100000; - } - }), - add: (function(x) { - x.coffee = "(game, x, y)@> game.addStilla(x, y)"; - return x; - })(function(game, x, y) { - return game.addStilla(x, y); - }) - } - } - }; - - Demo.prototype.touchInput = { - tx: 0, - ty: 0, - ongoing: {}, - __proto__: { - "eval": eval, - start: (function(x) { - x.coffee = "(ev,el)@>\n { ongoing } = @\n for t in ev.changedTouches\n { identifier, pageX, pageY } = t\n ongoing[identifier] =\n px: pageX\n py: pageY\n "; - return x; - })(function(ev, el) { - var identifier, ongoing, pageX, pageY, t, _i, _len, _ref, _results; - ongoing = this.ongoing; - _ref = ev.changedTouches; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - t = _ref[_i]; - identifier = t.identifier, pageX = t.pageX, pageY = t.pageY; - _results.push(ongoing[identifier] = { - px: pageX, - py: pageY - }); - } - return _results; - }), - move: (function(x) { - x.coffee = "(ev,el)@>\n { ongoing } = @\n { tx, ty } = @\n for t in ev.changedTouches\n { identifier, pageX, pageY } = t\n o = ongoing[identifier]\n dx = (pageX - o.px) * 4\n dy = (pageY - o.py) * 4\n dx *= 3 if dx * tx < 0\n dy *= 3 if dy * ty < 0\n tx += dx\n ty += dy\n # tx * dx > 0 then tx += dx else tx = dx * 2\n # ty * dy > 0 then ty += dy else ty = dy * 2\n o.px = pageX\n o.py = pageY\n @tx = tx\n @ty = ty\n "; - return x; - })(function(ev, el) { - var dx, dy, identifier, o, ongoing, pageX, pageY, t, tx, ty, _i, _len, _ref; - ongoing = this.ongoing; - tx = this.tx, ty = this.ty; - _ref = ev.changedTouches; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - t = _ref[_i]; - identifier = t.identifier, pageX = t.pageX, pageY = t.pageY; - o = ongoing[identifier]; - dx = (pageX - o.px) * 4; - dy = (pageY - o.py) * 4; - if (dx * tx < 0) { - dx *= 3; - } - if (dy * ty < 0) { - dy *= 3; - } - tx += dx; - ty += dy; - o.px = pageX; - o.py = pageY; - } - this.tx = tx; - return this.ty = ty; - }), - end: (function(x) { - x.coffee = "(ev,el)@>\n { ongoing } = @\n for t in ev.changedTouches\n { identifier } = t\n delete ongoing[identifier]\n "; - return x; - })(function(ev, el) { - var identifier, ongoing, t, _i, _len, _ref, _results; - ongoing = this.ongoing; - _ref = ev.changedTouches; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - t = _ref[_i]; - identifier = t.identifier; - _results.push(delete ongoing[identifier]); - } - return _results; - }), - handle: function(name) { - var touchInput; - touchInput = this; - return function(event) { - var err; - event.preventDefault(); - event.stopPropagation(); - try { - return touchInput[name](event, this); - } catch (_error) { - err = _error; - return alert(err.toString()); - } - }; - } - } - }; - - Demo.prototype.ColorPlane = ColorPlane = (function() { - return { - document: document, - init: (function(x) { - x.coffee = "@>\n { color } = @\n if color and typeof color is 'string'\n e = @document.createElement \"canvas\"\n e.width = @w\n e.height = @h\n ctx = e.getContext '2d'\n @color = ctx.fillStyle = color\n "; - return x; - })(function() { - var color, ctx, e; - color = this.color; - if (color && typeof color === 'string') { - e = this.document.createElement("canvas"); - e.width = this.w; - e.height = this.h; - ctx = e.getContext('2d'); - return this.color = ctx.fillStyle = color; - } - }), - frame: (function(x) { - x.coffee = "(t)@>\n # t.save()\n t.fillStyle = @color\n t.fillRect 0,0,1024,1024\n # t.restore()\n "; - return x; - })(function(t) { - t.fillStyle = this.color; - return t.fillRect(0, 0, 1024, 1024); - }) - }; - })(); - - Demo.prototype.GenericPlane = GenericPlane = { - document: document, - init: (function(x) { - x.coffee = "@>\n { document } = @\n e = document.createElement \"canvas\"\n e.width = @w\n e.height = @h\n @ctx = e.getContext '2d'\n\n "; - return x; - })(function() { - var document, e; - document = this.document; - e = document.createElement("canvas"); - e.width = this.w; - e.height = this.h; - return this.ctx = e.getContext('2d'); - }) - }; - - Demo.prototype.ScaledImg = ScaledImg = { - document: document, - zoom: 2, - init: (function(x) { - x.coffee = "@>\n retroScaling = (c)->\n c.imageSmoothingEnabled = false;\n c.webkitImageSmoothingEnabled = false;\n c.mozImageSmoothingEnabled = false;\n \n { zoom } = @\n { width, height } = @img\n @w = w = width * zoom\n @h = h = height * zoom\n c0 = e = @document.createElement \"canvas\"\n retroScaling(c0)\n e.width = w\n e.height = height\n ctx0 = e.getContext '2d'\n retroScaling(ctx0)\n ctx0.drawImage @img, 0, 0, width, height, 0, 0, w, height\n @canvas = e = @document.createElement \"canvas\"\n e.width = w\n e.height = h\n ctx = e.getContext '2d'\n retroScaling(ctx)\n @ctx = ctx.drawImage c0, 0, 0, w, height, 0, 0, w, h\n \n "; - return x; - })(function() { - var c0, ctx, ctx0, e, h, height, retroScaling, w, width, zoom, _ref; - retroScaling = function(c) { - c.imageSmoothingEnabled = false; - c.webkitImageSmoothingEnabled = false; - return c.mozImageSmoothingEnabled = false; - }; - zoom = this.zoom; - _ref = this.img, width = _ref.width, height = _ref.height; - this.w = w = width * zoom; - this.h = h = height * zoom; - c0 = e = this.document.createElement("canvas"); - retroScaling(c0); - e.width = w; - e.height = height; - ctx0 = e.getContext('2d'); - retroScaling(ctx0); - ctx0.drawImage(this.img, 0, 0, width, height, 0, 0, w, height); - this.canvas = e = this.document.createElement("canvas"); - e.width = w; - e.height = h; - ctx = e.getContext('2d'); - retroScaling(ctx); - return this.ctx = ctx.drawImage(c0, 0, 0, w, height, 0, 0, w, h); - }) - }; - - Demo.prototype.ParallaxPlane = ParallaxPlane = { - __proto__: GenericPlane, - ParallaxPlaneSuper: GenericPlane, - lower: null, - x: 0, - y: 0, - fx: 0, - fy: 0, - logzoom: 2, - frame: (function(x) { - x.coffee = "(t,dx,dy)@>\n { fx, fy, x, y, abslogzoom, w, h, ctx } = @\n nfx = fx + dx\n nfy = fy + dy\n nx = nfx >> abslogzoom\n ny = nfy >> abslogzoom\n if nx isnt x\n if nx >= w\n nx -= w\n nfx -= w << abslogzoom\n else if nx < 0\n nx += w\n nfx += w << abslogzoom\n @x = nx\n if ny isnt y\n if ny >= h\n ny -= h\n nfy -= h << abslogzoom\n else if ny < 0\n ny += h\n nfy += h << abslogzoom\n @y = ny\n @fx = nfx\n @fy = nfy\n @lower?.frame t, dx, dy\n { canvas } = ctx\n t.drawImage canvas, nx, ny\n t.drawImage canvas, nx - w, ny\n t.drawImage canvas, nx, ny - h\n t.drawImage canvas, nx - w, ny - h\n "; - return x; - })(function(t, dx, dy) { - var abslogzoom, canvas, ctx, fx, fy, h, nfx, nfy, nx, ny, w, x, y, _ref; - fx = this.fx, fy = this.fy, x = this.x, y = this.y, abslogzoom = this.abslogzoom, w = this.w, h = this.h, ctx = this.ctx; - nfx = fx + dx; - nfy = fy + dy; - nx = nfx >> abslogzoom; - ny = nfy >> abslogzoom; - if (nx !== x) { - if (nx >= w) { - nx -= w; - nfx -= w << abslogzoom; - } else if (nx < 0) { - nx += w; - nfx += w << abslogzoom; - } - this.x = nx; - } - if (ny !== y) { - if (ny >= h) { - ny -= h; - nfy -= h << abslogzoom; - } else if (ny < 0) { - ny += h; - nfy += h << abslogzoom; - } - this.y = ny; - } - this.fx = nfx; - this.fy = nfy; - if ((_ref = this.lower) != null) { - _ref.frame(t, dx, dy); - } - canvas = ctx.canvas; - t.drawImage(canvas, nx, ny); - t.drawImage(canvas, nx - w, ny); - t.drawImage(canvas, nx, ny - h); - return t.drawImage(canvas, nx - w, ny - h); - }), - init: (function(x) { - x.coffee = "(options)@>\n @abslogzoom ?= @logzoom\n (l = @lower)? then\n l.logzoom? then l.abslogzoom ?= @logzoom + l.logzoom\n l.init(options)\n @ParallaxPlaneSuper.init.call @, options\n "; - return x; - })(function(options) { - var l; - if (this.abslogzoom == null) { - this.abslogzoom = this.logzoom; - } - if ((l = this.lower) != null) { - if (l.logzoom != null) { - if (l.abslogzoom == null) { - l.abslogzoom = this.logzoom + l.logzoom; - } - } - l.init(options); - } - return this.ParallaxPlaneSuper.init.call(this, options); - }) - }; - - Demo.prototype.BoundParallaxPlane = BoundParallaxPlane = { - __proto__: ParallaxPlane, - BoundParallaxPlaneProto: ParallaxPlane, - pmul: 1, - alert: alert, - init: (function(x) { - x.coffee = "(options)@>\n { screenw, screenh } = options\n @BoundParallaxPlaneProto.init.call @\n { logzoom, abslogzoom, w, h, pmul } = @\n @mx = ((w << abslogzoom) * pmul - screenw * 8) >> abslogzoom\n @my = ((h << abslogzoom) * pmul - screenh * 8) >> abslogzoom\n # { alert } = @; alert screenw\n if false\n @fx = (@x = @mx) << abslogzoom\n @fy = (@y = @my) << abslogzoom\n @fx = @fy = 0\n @mfy = @my << abslogzoom\n "; - return x; - })(function(options) { - var abslogzoom, h, logzoom, pmul, screenh, screenw, w; - screenw = options.screenw, screenh = options.screenh; - this.BoundParallaxPlaneProto.init.call(this); - logzoom = this.logzoom, abslogzoom = this.abslogzoom, w = this.w, h = this.h, pmul = this.pmul; - this.mx = ((w << abslogzoom) * pmul - screenw * 8) >> abslogzoom; - this.my = ((h << abslogzoom) * pmul - screenh * 8) >> abslogzoom; - if (false) { - this.fx = (this.x = this.mx) << abslogzoom; - this.fy = (this.y = this.my) << abslogzoom; - } - this.fx = this.fy = 0; - return this.mfy = this.my << abslogzoom; - }), - frame: (function(x) { - x.coffee = "(t, dx, dy)@>\n { fx, fy, x, y, abslogzoom, w, h, ctx } = @\n nfx = fx - dx\n nfy = fy - dy\n nx = nfx >> abslogzoom\n ny = nfy >> abslogzoom\n if nx isnt x\n { mx } = @\n if nx >= mx\n nx = mx\n nfx = mx << abslogzoom\n else if nx < 0\n nx = 0\n nfx = 0\n @x = nx\n if ny isnt y\n { my } = @\n if ny >= my\n ny = my\n nfy = my << abslogzoom\n else if ny < 0\n ny = 0\n nfy = 0\n @y = ny\n @fx = nfx\n @fy = nfy\n # @lower?.frame t, dx >> abslogzoom, dy >> abslogzoom\n { canvas } = ctx\n # @mny = 100\n t.drawImage canvas, -nx, -ny\n # t.drawImage canvas, 0, 0, w, h, -nx, -ny, w*pmul, h*pmul\n\n "; - return x; - })(function(t, dx, dy) { - var abslogzoom, canvas, ctx, fx, fy, h, mx, my, nfx, nfy, nx, ny, w, x, y; - fx = this.fx, fy = this.fy, x = this.x, y = this.y, abslogzoom = this.abslogzoom, w = this.w, h = this.h, ctx = this.ctx; - nfx = fx - dx; - nfy = fy - dy; - nx = nfx >> abslogzoom; - ny = nfy >> abslogzoom; - if (nx !== x) { - mx = this.mx; - if (nx >= mx) { - nx = mx; - nfx = mx << abslogzoom; - } else if (nx < 0) { - nx = 0; - nfx = 0; - } - this.x = nx; - } - if (ny !== y) { - my = this.my; - if (ny >= my) { - ny = my; - nfy = my << abslogzoom; - } else if (ny < 0) { - ny = 0; - nfy = 0; - } - this.y = ny; - } - this.fx = nfx; - this.fy = nfy; - canvas = ctx.canvas; - return t.drawImage(canvas, -nx, -ny); - }) - }; - - Demo.prototype.SeaFloor = SeaFloor = (function() { - return { - __proto__: BoundParallaxPlane, - SeaFloorProto: BoundParallaxPlane, - seafloorImg: seafloor, - init: (function(x) { - x.coffee = "(options)@>\n { seafloorImg } = @\n # @terror.init(options)\n w = seafloorImg.width\n h = seafloorImg.height\n @w = w\n @h = h\n @SeaFloorProto.init.call @, options\n # { color, w, h } = @\n # e = @document.createElement \"canvas\"\n # e.width = w\n # e.height = h\n # @ctx = ctx = e.getContext '2d'\n { ctx, w, h } = @\n ctx.drawImage seafloorImg, 0, 0\n if false\n ctx.fillStyle = \"magenta\"\n ctx.fillRect 0, 0, w, 1\n ctx.fillRect 0, 0, 1, h\n ctx.fillRect 0, h - 1, w, 1\n ctx.fillRect w - 1, 0, 1, h \n \n "; - return x; - })(function(options) { - var ctx, h, seafloorImg, w; - seafloorImg = this.seafloorImg; - w = seafloorImg.width; - h = seafloorImg.height; - this.w = w; - this.h = h; - this.SeaFloorProto.init.call(this, options); - ctx = this.ctx, w = this.w, h = this.h; - ctx.drawImage(seafloorImg, 0, 0); - if (false) { - ctx.fillStyle = "magenta"; - ctx.fillRect(0, 0, w, 1); - ctx.fillRect(0, 0, 1, h); - ctx.fillRect(0, h - 1, w, 1); - return ctx.fillRect(w - 1, 0, 1, h); - } - }) - }; - })(); - - Demo.prototype.SeamlessPlane = SeamlessPlane = { - withRect: (function(x) { - x.coffee = "(rx,ry,rw,rh,cb)@>\n { w, h } = @\n if (ex = rx + rw) > w\n if (ey = ry + rh) > h\n cb rx, ry, w - rx, h - ry, 0, 0\n cb 0, ry, ex - w, h - ry, w - rx, 0\n cb rx, 0, w - rx, ey - h, 0, h - ry\n cb 0, 0, ex - w, ey - h, w - rx, h - ry\n else\n cb rx, ry, w - rx, rh, 0, 0\n cb 0, ry, ex - w, rh, w - rx, 0\n else\n if (ey = ry + rh) > h\n cb rx, ry, rw, h - ry, 0, 0\n cb rx, 0, rw, ey - h, 0, h - ry\n else\n cb rx, ry, rw, rh, 0, 0\n "; - return x; - })(function(rx, ry, rw, rh, cb) { - var ex, ey, h, w; - w = this.w, h = this.h; - if ((ex = rx + rw) > w) { - if ((ey = ry + rh) > h) { - cb(rx, ry, w - rx, h - ry, 0, 0); - cb(0, ry, ex - w, h - ry, w - rx, 0); - cb(rx, 0, w - rx, ey - h, 0, h - ry); - return cb(0, 0, ex - w, ey - h, w - rx, h - ry); - } else { - cb(rx, ry, w - rx, rh, 0, 0); - return cb(0, ry, ex - w, rh, w - rx, 0); - } - } else { - if ((ey = ry + rh) > h) { - cb(rx, ry, rw, h - ry, 0, 0); - return cb(rx, 0, rw, ey - h, 0, h - ry); - } else { - return cb(rx, ry, rw, rh, 0, 0); - } - } - }), - __proto__: ParallaxPlane - }; - - Demo.prototype.WaterPlane = WaterPlane = (function() { - var waterscapeSuper; - return { - waterscapeSuper: waterscapeSuper = SeamlessPlane, - __proto__: waterscapeSuper, - random: Math.random, - sqrt: Math.sqrt, - colors: ["cyan", "blue"], - randomStuff: (function(x) { - x.coffee = "@>\n { random, sqrt, ctx } = @\n s = sqrt(15000 / (random() * 50 + 1)) | 0\n @withRect (random() * @w | 0), (random() * @h | 0), s, s >> 2, (x,y,w,h)->\n ctx.fillRect x,y,w,h\n @\n "; - return x; - })(function() { - var ctx, random, s, sqrt; - random = this.random, sqrt = this.sqrt, ctx = this.ctx; - s = sqrt(15000 / (random() * 50 + 1)) | 0; - this.withRect(random() * this.w | 0, random() * this.h | 0, s, s >> 2, function(x, y, w, h) { - return ctx.fillRect(x, y, w, h); - }); - return this; - }), - init: (function(x) { - x.coffee = "(options)@>\n { lower, w, h, moltf, colors } = @\n if lower?\n lower.w ?= w\n lower.h ?= h\n lower.moltf ?= moltf >> lower.logzoom if moltf?\n @waterscapeSuper.init.call @, options\n { ctx } = @\n for k,v of colors\n ctx.fillStyle = v\n colors[k] = ctx.fillStyle\n ctx.globalAlpha = 0.16\n if true\n x = 200\n while x-- > 0\n @randomStuff()\n "; - return x; - })(function(options) { - var colors, ctx, h, k, lower, moltf, v, w, x, _results; - lower = this.lower, w = this.w, h = this.h, moltf = this.moltf, colors = this.colors; - if (lower != null) { - if (lower.w == null) { - lower.w = w; - } - if (lower.h == null) { - lower.h = h; - } - if (moltf != null) { - if (lower.moltf == null) { - lower.moltf = moltf >> lower.logzoom; - } - } - } - this.waterscapeSuper.init.call(this, options); - ctx = this.ctx; - for (k in colors) { - v = colors[k]; - ctx.fillStyle = v; - colors[k] = ctx.fillStyle; - } - ctx.globalAlpha = 0.16; - if (true) { - x = 200; - _results = []; - while (x-- > 0) { - _results.push(this.randomStuff()); - } - return _results; - } - }), - waterscapeSuperFrame: waterscapeSuper.frame, - frame: (function(x) { - x.coffee = "(t)@>\n { ctx, moltf, random } = @\n \n ctx.fillStyle = @colors[ random() * 1.2 | 0 ]\n @randomStuff() while moltf-- > 0\n\n t.save()\n t.globalAlpha = @alpha\n @waterscapeSuperFrame.apply @, arguments\n t.restore()\n "; - return x; - })(function(t) { - var ctx, moltf, random; - ctx = this.ctx, moltf = this.moltf, random = this.random; - ctx.fillStyle = this.colors[random() * 1.2 | 0]; - while (moltf-- > 0) { - this.randomStuff(); - } - t.save(); - t.globalAlpha = this.alpha; - this.waterscapeSuperFrame.apply(this, arguments); - return t.restore(); - }), - logzoom: 0 - }; - })(); - - Demo.prototype.seafloor = seafloorPlane = { - __proto__: SeaFloor - }; - - Demo.prototype.getDepth = (function(x) { - x.coffee = "@> @seafloor.fy / @seafloor.mfy"; - return x; - })(function() { - return this.seafloor.fy / this.seafloor.mfy; - }); - - Demo.prototype.waterscape = waterscape = (function() { - return { - __proto__: WaterPlane, - moltf: 12, - colors: ["#051555", "#33ddff"], - alpha: 0.2, - logzoom: 0, - lower: { - __proto__: WaterPlane, - colors: ["#000033", "#001155"], - alpha: 0.3, - logzoom: 2, - lower: seafloorPlane - } - }; - })(); - - Demo.prototype.bluescape = { - __proto__: SeamlessPlane, - bluescapeSuper: SeamlessPlane, - lower: waterscape, - logzoom: 0, - frame: (function(x) { - x.coffee = "(t,sx,sy)@>\n { ctx, random, w, h } = @\n\n x = @x + sx\n x = (x + w) % w\n y = (y + h) % h\n @x = x\n y = @y + sy\n y += h while y < 0\n y -= h while y >= h\n @y = y\n # i = ctx.getImageData(0,0,@w,@h)\n\n ctx.save()\n @lower.frame ctx, sx, sy\n ctx.restore()\n # t.save()\n # t.globalCompositeOperation = 'copy'\n\n t.drawImage ctx.canvas, 0,0,w,h, 0,0,w*4,h*4\n \n # t.drawImage ctx.canvas, 0,0,w>>2,h>>2, 0,0,w*2,h*2\n\n # t.drawImage ctx.canvas, 0,0,w>>2,h>>2, 0,0,w*2,h>>2\n # t.drawImage t.canvas, 0,0,w*2,h>>2, 0,0,w*2,h*2\n\n # t.restore()\n # @withRect x, y, rx*2, ry*2, (x,y,w,h,ox,oy)-> t.drawImage c, x,y,w,h, ox*2,oy*2,w*2,h*2\n # t.drawImage c, 0, 0, \n # t.fillColor = if random() > 0.5 then \"#104080\" else \"#155590\"\n # t.fillRect 0, 0, 100, 100\n # t.clearRect 0, 0, 100, 100\n # t.drawImage t, 0, 0, 100, 100, 50, 50, 100, 100\n "; - return x; - })(function(t, sx, sy) { - var ctx, h, random, w, x, y; - ctx = this.ctx, random = this.random, w = this.w, h = this.h; - x = this.x + sx; - x = (x + w) % w; - y = (y + h) % h; - this.x = x; - y = this.y + sy; - while (y < 0) { - y += h; - } - while (y >= h) { - y -= h; - } - this.y = y; - ctx.save(); - this.lower.frame(ctx, sx, sy); - ctx.restore(); - return t.drawImage(ctx.canvas, 0, 0, w, h, 0, 0, w * 4, h * 4); - }), - init: (function(x) { - x.coffee = "(options)@>\n { w, h, lower } = @\n\n @w = w\n @h = h\n\n lower.w = (w >> 2) * 5\n lower.h = (h >> 2) * 5\n\n @bluescapeSuper.init.call @, options\n\n { ctx } = @\n\n # ctx.fillStyle = \"#0099dd\"\n # ctx.fillRect 0, 0, @w, @h\n \n "; - return x; - })(function(options) { - var ctx, h, lower, w; - w = this.w, h = this.h, lower = this.lower; - this.w = w; - this.h = h; - lower.w = (w >> 2) * 5; - lower.h = (h >> 2) * 5; - this.bluescapeSuper.init.call(this, options); - return ctx = this.ctx, this; - }) - }; - - Demo.prototype.setup = function() { - var bluescape, radx, rady, tend, touchInput, v; - bluescape = this.bluescape, radx = this.radx, rady = this.rady; - bluescape.w = radx; - bluescape.h = rady; - bluescape.init({ - screenw: radx * 2, - screenh: rady * 2 - }); - v = new Vilma(this); - v.px = 0; - v.py = 0; - v.vx = 0; - v.vy = 0; - this.vilma = v; - this.encounters.generate(this, -radx, -rady, radx * 2, rady * 2, radx * 2, 0); - touchInput = this.touchInput; - touchInput.game = this; - x = document.body; - x.addEventListener("touchmove", touchInput.handle('move'), true); - x.addEventListener("touchstart", touchInput.handle('start'), true); - tend = touchInput.handle('end'); - x.addEventListener("touchend", tend, true); - x.addEventListener("touchleave", tend, true); - x.addEventListener("touchcancel", tend, true); - return this.collisions.setup(radx, rady); - }; - - Demo.prototype.radx = screen_x1; - - Demo.prototype.rady = screen_y1; - - Demo.prototype.rad = screen_x1 * screen_x1 + screen_y1 * screen_y1; - - Demo.prototype.collisions = { - Array: Array, - setup: (function(x) { - x.coffee = "(radx, rady)@>\n # Setup the collision detection subsystem\n # Assumes:\n # - radx and rady are multiples of 8\n w = @w = (radx >> 2)\n h = @h = (rady >> 2)\n @b = new @Array(w * h)\n @o = (w >> 1) * h + (h >> 1) + 1\n @l = [ ]\n "; - return x; - })(function(radx, rady) { - var h, w; - w = this.w = radx >> 2; - h = this.h = rady >> 2; - this.b = new this.Array(w * h); - this.o = (w >> 1) * h + (h >> 1) + 1; - return this.l = []; - }), - a: (function(x) { - x.coffee = "(o)@>\n # Add a collision subject\n # Assumes:\n # - all the corners of the object's collision area are in the viewing area\n # - the object's collision radius is <= 8\n { l, b, w } = @\n i = @o + (o.py >> 3) * @w + (o.px >> 3)\n @b[i-1] = @b[i+1] = @b[i] = o\n i -= w\n @b[i-1] = @b[i+1] = @b[i] = o\n i += w << 1\n @b[i-1] = @b[i+1] = @b[i] = o\n @l.push o\n \n # o.crad\n "; - return x; - })(function(o) { - var b, i, l, w; - l = this.l, b = this.b, w = this.w; - i = this.o + (o.py >> 3) * this.w + (o.px >> 3); - this.b[i - 1] = this.b[i + 1] = this.b[i] = o; - i -= w; - this.b[i - 1] = this.b[i + 1] = this.b[i] = o; - i += w << 1; - this.b[i - 1] = this.b[i + 1] = this.b[i] = o; - return this.l.push(o); - }), - q: (function(x) { - x.coffee = "(o)@>\n # Quick collision test\n # Test collisions of object against previously added collision subjects\n # For this to work correctly:\n # - the object should have a collision radius <= 4,\n # - have a center in the viewing area\n @b[@o + (o.py >> 3) * @w + (o.px >> 3)]?.collide(o)\n # t2: (o)@>\n # Like above but for objects with a collision radius <= 8\n "; - return x; - })(function(o) { - var _ref; - return (_ref = this.b[this.o + (o.py >> 3) * this.w + (o.px >> 3)]) != null ? _ref.collide(o) : void 0; - }), - clear: (function(x) { - x.coffee = "@>\n @b = new @Array(@b.length) # Discrete board for detecting collisions\n @l = [ ] # List of collisions targets\n "; - return x; - })(function() { - this.b = new this.Array(this.b.length); - return this.l = []; - }) - }; - - Demo.prototype.draw = (function(x) { - x.coffee = "@>\n { jaws, spaceKey, radx, rady, vilma, vaquitas, cameos, stilla, rad, collisions } = @\n\n @addVaquita() if (!(@gameloop.ticks & 0x7f) and vaquitas.length < 1) or jaws.pressed[spaceKey]\n\n vilma.fpx += vilma.px\n vilma.fpy += vilma.py\n vilma.move()\n\n if true\n { px, py, fpx, fpy } = vilma\n \n vilma.fpx -= px\n vilma.fpy -= py\n vilma.px = 0\n vilma.py = 0\n \n px = px | 0\n py = py | 0\n \n @bluescape.frame jaws.context, -fpx, -fpy\n else\n { px, py } = vilma\n \n vilma.fpx = 0\n vilma.fpy = 0\n vilma.px = 0\n vilma.py = 0\n \n px = px | 0\n py = py | 0\n \n @bluescape.frame jaws.context, -px, -py\n\n collisions.a vilma\n\n for v in vaquitas\n x = v.px -= px\n y = v.py -= py\n v.draw()\n if (x >= -radx) and (x < radx) and (y >= -rady) and (y < rady)\n collisions.a v\n\n vilma.draw()\n\n if stilla?\n x = stilla.px -= px\n y = stilla.py -= py\n if stilla.dead or x * x + y * y > rad * 16\n stilla.goodnight(@)\n @stilla = null\n else\n stilla.draw(collisions, @)\n if (x >= -radx) and (x < radx) and (y >= -rady) and (y < rady)\n collisions.a stilla\n\n for k,v of cameos\n continue unless v?\n x = v.px -= px\n y = v.py -= py\n if v.dead or (x < -radx) or (x >= radx) or (y < -rady) or (y >= rady)\n cameos[k] = null\n else\n v.draw(collisions, @)\n collisions.q v\n\n @encounters.generate(@,-radx, -rady, radx * 2, rady * 2, px, py)\n\n collisions.clear()\n \n if (@gameloop.ticks & 0xff) is 0xff\n fps.innerHTML = \"#{@gameloop.fps} fps\"\n\n "; - return x; - })(function() { - var cameos, collisions, fpx, fpy, jaws, k, px, py, rad, radx, rady, spaceKey, stilla, v, vaquitas, vilma, x, y, _i, _len; - jaws = this.jaws, spaceKey = this.spaceKey, radx = this.radx, rady = this.rady, vilma = this.vilma, vaquitas = this.vaquitas, cameos = this.cameos, stilla = this.stilla, rad = this.rad, collisions = this.collisions; - if ((!(this.gameloop.ticks & 0x7f) && vaquitas.length < 1) || jaws.pressed[spaceKey]) { - this.addVaquita(); - } - vilma.fpx += vilma.px; - vilma.fpy += vilma.py; - vilma.move(); - if (true) { - px = vilma.px, py = vilma.py, fpx = vilma.fpx, fpy = vilma.fpy; - vilma.fpx -= px; - vilma.fpy -= py; - vilma.px = 0; - vilma.py = 0; - px = px | 0; - py = py | 0; - this.bluescape.frame(jaws.context, -fpx, -fpy); - } else { - px = vilma.px, py = vilma.py; - vilma.fpx = 0; - vilma.fpy = 0; - vilma.px = 0; - vilma.py = 0; - px = px | 0; - py = py | 0; - this.bluescape.frame(jaws.context, -px, -py); - } - collisions.a(vilma); - for (_i = 0, _len = vaquitas.length; _i < _len; _i++) { - v = vaquitas[_i]; - x = v.px -= px; - y = v.py -= py; - v.draw(); - if ((x >= -radx) && (x < radx) && (y >= -rady) && (y < rady)) { - collisions.a(v); - } - } - vilma.draw(); - if (stilla != null) { - x = stilla.px -= px; - y = stilla.py -= py; - if (stilla.dead || x * x + y * y > rad * 16) { - stilla.goodnight(this); - this.stilla = null; - } else { - stilla.draw(collisions, this); - if ((x >= -radx) && (x < radx) && (y >= -rady) && (y < rady)) { - collisions.a(stilla); - } - } - } - for (k in cameos) { - v = cameos[k]; - if (v == null) { - continue; - } - x = v.px -= px; - y = v.py -= py; - if (v.dead || (x < -radx) || (x >= radx) || (y < -rady) || (y >= rady)) { - cameos[k] = null; - } else { - v.draw(collisions, this); - collisions.q(v); - } - } - this.encounters.generate(this, -radx, -rady, radx * 2, rady * 2, px, py); - collisions.clear(); - if ((this.gameloop.ticks & 0xff) === 0xff) { - return fps.innerHTML = "" + this.gameloop.fps + " fps"; - } - }); - - Demo.prototype.jaws = jaws; - - Demo.prototype.spaceKey = spaceKey; - - return Demo; - - })(); - if (true) { - jaws.init(); - jaws.setupInput(); - window.game = game = new Demo; - gameloop = new jaws.GameLoop(game, { - fps: 24 - }); - return (game.gameloop = gameloop).start(); - } else { - return jaws.start(Demo, { - fps: 25 - }); - } - }; - return window.addEventListener('load', (function(e) { - if (window.applicationCache) { - return window.applicationCache.addEventListener('updateready', (function(e) { - window.applicationCache.swapCache(); - if (confirm('A new version of this site is available. Load it?')) { - return window.location.reload(); - } - }), false); - } - }), false); - })(); - })(); -</script></body></html>
\ No newline at end of file diff --git a/SunkenMoon.html.coffee b/SunkenMoon.html.coffee deleted file mode 100644 index c9d8efb..0000000 --- a/SunkenMoon.html.coffee +++ /dev/null @@ -1,1117 +0,0 @@ -# Copyright (c) 2013, 2014 Michele Bini - -# A game featuring a Vaquita, the smallest, most endagered marine cetacean - -# This program is available under the terms of the MIT License - -version = "0.2.249" - -{ htmlcup } = require 'htmlcup' - -htmlcup[x] = htmlcup.compileTag x for x in [ "svg", "rect", "g", "ellipse", "polygon", "line", "image", "defs", "linearGradient", "stop", "use" ] - -title = "Vilma, the happy Vaquita - Sunken Moon" - -fs = require 'fs' - -datauri = (t,x)-> "data:#{t};base64,#{new Buffer(fs.readFileSync(x)).toString("base64")}" -datauripng = (x)-> datauri "image/png", x -dataurijpeg = (x)-> datauri "image/jpeg", x -datauriicon = (x)-> datauri "image/x-icon", x - -icon = datauriicon "vaquita.ico" -pixyvaquita = datauripng "vilma.png" - -frames = - _: pixyvaquita - twist_l: datauripng "vilma_twist_l.png" - twist_r: datauripng "vilma_twist_r.png" - happybubble0: datauripng "Happy-oxygen-bubble.png" - grumpybubble0: datauripng "Grumpy-bubble.png" - evilbubble0: datauripng "Evil-bubble.png" - stilla0: datauripng "Stilla-the-starfish.png" - # cuteluterror: datauripng 'cutelu-terror-v3.png' - seafloor: dataurijpeg "seafloor.png" - -gameName = "#{title} v#{version}" - -htmlcup.jsFile = (f)-> @script type:"text/javascript", (fs.readFileSync(f).toString()) - -gameAreaSize = [ 240, 360 ] - -genPage = -> - htmlcup.printHtml "<!DOCTYPE html>\n" - htmlcup.html lang:"en", manifest:"SunkenMoon.appcache", style:"height:100%", -> - @head -> - @meta charset:"utf-8" - @meta name:"viewport", content:"width=480, user-scalable=no" - @meta name:"apple-mobile-web-app-capable", content:"yes" - @meta name:"mobile-web-app-capable", content:"yes" - # Improve support: http://www.html5rocks.com/en/mobile/fullscreen/ - # Homescreen installed webapp on Android Chrome has weird name! (Web App) - @link rel:"shortcut icon", href:icon - @title title - @body style:"margin:0;border:0;padding:0;height:100%;width:100%;background:black", -> - @div style:"visibility:hidden;position:absolute", -> - @img id:"pixyvaquita", src:pixyvaquita - @img id:"pixyvaquita_twist_l", src:frames.twist_l - @img id:"pixyvaquita_twist_r", src:frames.twist_r - @img id:"happybubble0", src:frames.happybubble0 - @img id:"grumpybubble0", src:frames.grumpybubble0 - @img id:"evilbubble0", src:frames.evilbubble0 - @img id:"stilla0", src:frames.stilla0 - # @img id:"cuteluterror", src:frames.cuteluterror - @img id:"seafloor", src:frames.seafloor - @div style:"display:table;width:100%;max-width:100%;height:100%;margin:0;border:0;padding:0", -> - @div style:"display:table-cell;vertical-align:middle;width:100%;margin:0;border:0;padding:0;text-align:center", -> - @div style:"position:relative;display:inline-block", width:"#{gameAreaSize[0]*2}", height:"#{gameAreaSize[1]*2}", -> - @canvas width:"#{gameAreaSize[0]*2}", height:"#{gameAreaSize[1]*2}" - @header style:"position:absolute;top:0;left:0;font-size:14px;width:100%;color:black", -> - @span gameName - @span " - " - @a target:"_blank", href:"index.html", "Save Vaqitas" - @div style:"text-align:right", id:"fps" - gameObjects = null - @script type:"text/javascript", "gameObjects=#{JSON.stringify(gameObjects)};" - @script type:"text/javascript", "__hasProp = {}.hasOwnProperty; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };" - @jsFile "jaws/jaws-min.js" - # @jsFile "jaws-assets-named.js" - @coffeeScript -> do -> - - # reportErrors = (x)-> - # try - # x() - # catch error - # try - # alert error.toString() - # catch error2 - # alert error - - screen_x1 = 120 - screen_y1 = 180 - { sqrt } = Math - - ### - # an ad-hoc redux of hammer.js - hammerLet = do(window, navigator)@> - mobile_regex: mobile_regex = /mobile|tablet|ip(ad|hone|od)|android/i - support_touch: support_touch = ('ontouchstart' in window) - prefixed: prefixed = - global: window - get: (sym)@> - { global } = @g - for v in @vendors - return r if (r = global[v + sym])? - undefined - vendors: [ 'webkit', 'moz', 'MS', 'ms', 'o' ] - PointerEvent: PointerEvent ? prefixed.run(window, 'PointerEvent')? - suppourt_touch_only: support_touch && mobile_regex.test(navigator.userAgent) - ### - jaws.onload = -> - class Demo - keyCodes: { left: leftKey, right: rightKey, up: upKey, down: downKey, space: spaceKey } = jaws.keyCodes - Sprite: Sprite = class extends jaws.Sprite - # caller needs to set lr for flip center - constructor: -> - super - image: @image - x: 0 - y: 0 - scale: 2 - draw: -> - @flipped = @lr >= 0 - @x = (screen_x1 + @px + @lr) * 2 - @y = (screen_y1 + @py - @tb) * 2 - super() - cr: 4 - sqrt: Math.sqrt - collide: (o)@> - { px, py, cr } = o - opx = o.px; opy = o.py; ocr = o.cr - dx = px - opx - dy = py - opy - dc = cr + ocr - if (qd = dx * dx + dy * dy) <= dc * dc - @bumpedInto?(o, qd, dx, dy) - o.bumpedInto?(@, qd, -dx, -dy) - # if true - # @lr = - @lr - # o.lr = - o.lr - # @px = opx - # @py = opy - # return - # @py = py - 1 - # return - # { sqrt } = @ - # if false - # py = opy - # px = opx - dc - # else - # d = sqrt d - # if d < 0.1 - # dy = -1 - # d = dx * dx + dy * dy - # d = sqrt d - # d = 3 * dc / sqrt(d) - # py = opy + dy * d - # px = opx + dx * d - # @px = px | 0 - # @py = py | 0 - - Bubble: Bubble = Sprite - HappyBubble: HappyBubble = class extends Bubble - image: happybubble0 - constructor: -> - @lr = 4 - @tb = 4 - super() - draw: -> - @py-- - super() - bumpedInto: (o, qd, dx, dy)@> - return if @dead - # if dx * dx * 2 > qd - @dead = true - GrumpyBubble: GrumpyBubble = class extends Bubble - image: grumpybubble0 - constructor: -> - @lr = 7 - @tb = 7 - @cr = 8 - @life = 60 - super() - draw: (collisions, game)-> - if game?.slowedBubbles - @py -= 2 - else - @py -= 3 - super() - bumpedInto: (o, qd, dx, dy)@> - return if @dead - # if dx * dx * 2 > qd - # @dead = true - ovy = o.vy - o.py -= 3 + (ovy > 0 then @life -= ovy; ovy * 2 else 0) - @dead = true unless @life > 0 - EvilBubble: EvilBubble = class extends Bubble - image: evilbubble0 - constructor: -> - @lr = 15 - @tb = 15 - @cr = 8 - @vy_ = -7 - @life = 2200 - super() - draw: (collisions, game)-> - l = 0 - if game.slowedBubbles - @py -= 3 - else - @py += @vy_ - if (life = @life) < 2200 - l = 2200 - @life - # l -= 1100 - # l = -l if l < 0 - # l = (l / 20)|0 - l = 2200 - l if l > 1100 - l /= 55 - @vy_ = - 8 - l - super() - bumpedInto: (o, qd, dx, dy)@> - return if @dead - # if dx * dx * 2 > qd - # @dead = true - ovx = o.vx - ovy = o.vy - @life -= ovx * ovx + ovy * ovy - @life -= 10 - o.px = @px - o.py = @py + @vy_ - @dead = true unless @life > 0 - slowBubbles: @> - return if @slowedBubbles - @slowedBubbles = true - quitSlowBubbles: @> - return unless @slowedBubbles - @slowedBubbles = false - Stilla: Stilla = class extends Bubble - image: stilla0 - Bubble: @Bubble - constructor: -> - @lr = 16 - @tb = 20 - @patience = 490 - super() - # Math: Math - sqrt: Math.sqrt - pow: Math.pow - sin: Math.sin - draw: (collisions, game)-> - { px, py, lr } = @ - (spin = @spin) then - { pow, sin } = @ - d = pow(px * px + py * py, 0.42) - r = 5 / (d + 1) - if r < 1 - ir = 1 - else - d = sin(d / 8000) - d = d * d * 8000 - ir = sqrt(1 - r * r) * pow(d, 0.01) - @px = px * ir + py * (r * spin) - @py = py * ir - px * (r * spin) - (d = px * px + py * py) > 40000 then - @spin = null - if @patience < 0 - @dead = 1 - # @patience += 10 - # @spinFrame = - else !(d >= 0) then - @px = 0 - @py = 1 - throw "ir #{ir}" - - else - closest = null - closestDist = null - consider = (v)-> - return unless v? - dx = px - v.px - dy = py - v.py - d = dx * dx + dy * dy - if !closest? or d < closestDist - return unless d >= 0 - closest = v - closestDist = d - game.quitSlowBubbles() - { vilma } = game - consider vilma - if game.vaquitas? - consider v for v in game.vaquitas - slowBubbles = false - if closest? - if closestDist < 7000 - slowBubbles = true - if closestDist < 4000 - @patience-- - if @patience < 0 or (closestDist < 1000 and closest is vilma) - dx = px - closest.px - @spin = (lr > 0 then +1 else -1) # Start spinning - @patience -= 100 - else - dx = px - closest.px - dy = py - closest.py - # fpx = @fpx += dx / 100 - # fpy = @fpy += dy / 100 - # @px = fpx | 0 - # @py = fpy | 0 - @px += (dx > +2 then +1 else dx < -2 then -1 else 0) - @py += (dy > +2 then +1 else dy < -2 then -1 else 0) - # @px += 1 - - # @px += 1 - if slowBubbles - game.slowBubbles() - else - game.quitSlowBubbles() - @lr = -lr if px * lr > 0 - super() - goodnight: (game)@> game.quitSlowBubbles() - bumpedInto: (o)@> - o.dead = true - Vaquita: Vaquita = class extends Sprite - twist: [ pixyvaquita_twist_l, pixyvaquita_twist_r ] - constructor: -> - @lr = 16 - @tb = 16 - super() - draw: -> - if @vx < 0 - @lr = - 18 - else if @vx > 0 - @lr = 18 - super() - AiVaquita: AiVaquita = class extends Vaquita - constructor: -> - @image = pixyvaquita - @time = 0 - super() - beat_lr: 0 - draw: -> - vx = @vx + Math.floor(Math.random()*3) - 1 - vy = @vy + Math.floor(Math.random()*3) - 1 - x = @px - y = @py - rx = 0.5 * x / screen_x1 - ry = 0.5 * y / screen_y1 - if (s = vx * vx + vy * vy * 2) > 6 - vx = Math.round(vx * 0.8 - rx) - vy = Math.round(vy * 0.8 - ry) - @px += @vx = vx - @py += @vy = vy - if (@time++ % 3) is 0 - if @image isnt pixyvaquita - @image = pixyvaquita - else if vx * vx + vy * vy > 2 - @image = @twist[ @beat_lr++ & 1 ] - super() - Vilma: Vilma = class extends Vaquita - constructor: (@game)-> - @image = pixyvaquita - @time = 0 - super() - @fpx = @px ? 0 - @fpy = @py ? 0 - @touch = @game.touchInput - beat_lr: 0 - move: -> - { touch } = @ - { tx, ty } = touch - itx = (tx >= 2 then 2 else tx <= -2 then -2 else 0) - ity = (ty >= 2 then 2 else ty <= -2 then -2 else 0) - touch.tx = tx * 0.9 - itx - touch.ty = ty * 0.9 - ity - ax = (if jaws.pressed[leftKey] then -1 else 0) + (if jaws.pressed[rightKey] then 1 else 0) - itx / 2 - ay = (if jaws.pressed[upKey] then -1 else 0) + (if jaws.pressed[downKey] then 1 else 0) - ity / 2 - if (aq = ax * ax + ay * ay) > 1 - aq = sqrt(aq) - ax /= aq - ay /= aq - ax *= 0.618 - ay *= 0.618 - vx = @vx - vy = @vy - if ax * vx < 0 - vx = 0 - else - vx += ax - vx *= 0.9 - if ay * vy < 0 - vy = 0 - else - vy += ay - vy *= 0.9 - @vx = vx - @vy = vy - @px = (@fpx += @vx) - @py = (@fpy += @vy) - draw: -> - { vx, vy } = @ - if (@time++ % 3) is 0 - if @image isnt pixyvaquita - @image = pixyvaquita - else if vx * vx + (vy * vy / 4) > 1 - @image = @twist[@beat_lr++ & 1] - super() - addVaquita: -> - # n = v.cloneNode() - # n.setAttribute "opacity", "0.5" - # n.href.baseVal = "#_v105" if Math.random(0) > 0.5 - # n.setAttribute "transform", "" - # sea.appendChild n - angle = Math.random() * 6.28 - v = new AiVaquita - v.vx = 0 - v.vy = 0 - v.px = Math.floor(Math.sin(angle) * 300) - v.py = Math.floor(Math.cos(angle) * 300) - # v.draw() - # vaquita.update() - @vaquitas.push v - addStilla: (x, y)@> - return if @stilla? - v = new @Stilla - v.px = x - v.py = y - @stilla = v - addInto: (n, v, x, y)@> - v.vx = 0 - v.vy = 0 - v.px = x - v.py = y - b = @[n] - if (i = b.indexOf(null)) >= 0 - b[i] = v - else - b.push v - # v.draw() - constructor: (@vaquitas = [], @cameos = [], @stilla = null)-> - encounters: - __proto__: - encounter: encounter = - add: (game, x, y)@> game.addInto('cameos', new @creature(), x, y) - vy: 0 - random: Math.random - log: Math.log - exp: Math.exp - pow: Math.pow - poissonSample: (m)@> - { exp, random } = @ - pgen = (m)-> - x = 0 - p = exp(-m) - s = p - u = random() - while u > s - x++ - p = p * m / x - s += p - x - s = 0 - while m > 50 - s += pgen 50 - m -= 50 - s + pgen m - generate: (game,left,top,width,height,vx,vvy)@> - { probability, random } = @ - depth = game.getDepth() - genRect = (m,left,top,width,height)=> - c = m.p(depth) * width * height - # c = 0 - c = @poissonSample(c) - if c is 1 - m.add?( game, left + ((random() * width)|0), top + ((random() * height)|0) ) - else - # c = 0 # if c > 1000 - # c-- if random() > 0.15 - while c-- > 0 - m.add?( game, left + ((random() * width)|0), top + ((random() * height)|0) ) - 1 - if vx * vx >= width * width - for k,v of @catalogue - genRect(v, left, top, width, height) - else for k,v of @catalogue - vy = vvy - v.vy - if vy * vy >= height * height - genRect(v, left, top, width, height) - else if vx > 0 - if vy > 0 - genRect(v, left, top + height - vy, width, vy) - genRect(v, left + width - vx, top, vx, height - vy) - else if vy < 0 - genRect(v, left, top, width, -vy) - genRect(v, left + width - vx, top - vy, vx, height + vy) - else - genRect(v, left + width, top, vx, height) - else if vx < 0 - if vy > 0 - genRect(v, left, top + height - vy, width, vy) - genRect(v, left, top, -vx, height - vy) - else if vy < 0 - genRect(v, left, top, width, -vy) - genRect(v, left, top - vy, -vx, height + vy) - else - genRect(v, left, top, -vx, height) - else if vy > 0 - genRect(v, left, top + height - vy, width, vy) - else if vy < 0 - genRect(v, left, top, width, -vy) - catalogue: - happybubble: - __proto__: encounter - p: (depth)@> 0.0001 * (1.5 - depth) - creature: HappyBubble - vy: -1 - grumpybubble: - __proto__: encounter - p: (depth)@> depth < 0.08 then 0 else (depth - 0.08) * 0.00015 - creature: GrumpyBubble - vy: -3 - evilbubble: - __proto__: encounter - p: (depth)@> depth < 0.35 then 0 else (depth - 0.35) * 0.00005 - creature: EvilBubble - vy: -8 - stilla: - __proto__: encounter - p: (depth)@> depth < 0.01 then 1 else (1-depth)/100000 - add: (game, x, y)@> game.addStilla(x, y) - touchInput: - tx: 0 - ty: 0 - ongoing: { } - __proto__: - eval: eval - start: (ev,el)@> - { ongoing } = @ - for t in ev.changedTouches - { identifier, pageX, pageY } = t - ongoing[identifier] = - px: pageX - py: pageY - move: (ev,el)@> - { ongoing } = @ - { tx, ty } = @ - for t in ev.changedTouches - { identifier, pageX, pageY } = t - o = ongoing[identifier] - dx = (pageX - o.px) * 4 - dy = (pageY - o.py) * 4 - dx *= 3 if dx * tx < 0 - dy *= 3 if dy * ty < 0 - tx += dx - ty += dy - # tx * dx > 0 then tx += dx else tx = dx * 2 - # ty * dy > 0 then ty += dy else ty = dy * 2 - o.px = pageX - o.py = pageY - @tx = tx - @ty = ty - end: (ev,el)@> - { ongoing } = @ - for t in ev.changedTouches - { identifier } = t - delete ongoing[identifier] - handle: (name)-> - touchInput = @ - (event)-> - event.preventDefault() - event.stopPropagation() - touchInput[name](event,this) catch err - alert err.toString() - ColorPlane: ColorPlane = do-> - document: document - init: @> - { color } = @ - if color and typeof color is 'string' - e = @document.createElement "canvas" - e.width = @w - e.height = @h - ctx = e.getContext '2d' - @color = ctx.fillStyle = color - frame: (t)@> - # t.save() - t.fillStyle = @color - t.fillRect 0,0,1024,1024 - # t.restore() - GenericPlane: GenericPlane = - document: document - init: @> - { document } = @ - e = document.createElement "canvas" - e.width = @w - e.height = @h - @ctx = e.getContext '2d' - - ScaledImg: ScaledImg = - document: document - zoom: 2 - init: @> - retroScaling = (c)-> - c.imageSmoothingEnabled = false; - c.webkitImageSmoothingEnabled = false; - c.mozImageSmoothingEnabled = false; - - { zoom } = @ - { width, height } = @img - @w = w = width * zoom - @h = h = height * zoom - c0 = e = @document.createElement "canvas" - retroScaling(c0) - e.width = w - e.height = height - ctx0 = e.getContext '2d' - retroScaling(ctx0) - ctx0.drawImage @img, 0, 0, width, height, 0, 0, w, height - @canvas = e = @document.createElement "canvas" - e.width = w - e.height = h - ctx = e.getContext '2d' - retroScaling(ctx) - @ctx = ctx.drawImage c0, 0, 0, w, height, 0, 0, w, h - - ParallaxPlane: ParallaxPlane = - __proto__: GenericPlane - ParallaxPlaneSuper: GenericPlane - lower: null - x: 0 - y: 0 - fx: 0 - fy: 0 - logzoom: 2 - frame: (t,dx,dy)@> - { fx, fy, x, y, abslogzoom, w, h, ctx } = @ - nfx = fx + dx - nfy = fy + dy - nx = nfx >> abslogzoom - ny = nfy >> abslogzoom - if nx isnt x - if nx >= w - nx -= w - nfx -= w << abslogzoom - else if nx < 0 - nx += w - nfx += w << abslogzoom - @x = nx - if ny isnt y - if ny >= h - ny -= h - nfy -= h << abslogzoom - else if ny < 0 - ny += h - nfy += h << abslogzoom - @y = ny - @fx = nfx - @fy = nfy - @lower?.frame t, dx, dy - { canvas } = ctx - t.drawImage canvas, nx, ny - t.drawImage canvas, nx - w, ny - t.drawImage canvas, nx, ny - h - t.drawImage canvas, nx - w, ny - h - init: (options)@> - @abslogzoom ?= @logzoom - (l = @lower)? then - l.logzoom? then l.abslogzoom ?= @logzoom + l.logzoom - l.init(options) - @ParallaxPlaneSuper.init.call @, options - BoundParallaxPlane: BoundParallaxPlane = - __proto__: ParallaxPlane - BoundParallaxPlaneProto: ParallaxPlane - pmul: 1 - alert: alert - init: (options)@> - { screenw, screenh } = options - @BoundParallaxPlaneProto.init.call @ - { logzoom, abslogzoom, w, h, pmul } = @ - @mx = ((w << abslogzoom) * pmul - screenw * 8) >> abslogzoom - @my = ((h << abslogzoom) * pmul - screenh * 8) >> abslogzoom - # { alert } = @; alert screenw - if false - @fx = (@x = @mx) << abslogzoom - @fy = (@y = @my) << abslogzoom - @fx = @fy = 0 - @mfy = @my << abslogzoom - frame: (t, dx, dy)@> - { fx, fy, x, y, abslogzoom, w, h, ctx } = @ - nfx = fx - dx - nfy = fy - dy - nx = nfx >> abslogzoom - ny = nfy >> abslogzoom - if nx isnt x - { mx } = @ - if nx >= mx - nx = mx - nfx = mx << abslogzoom - else if nx < 0 - nx = 0 - nfx = 0 - @x = nx - if ny isnt y - { my } = @ - if ny >= my - ny = my - nfy = my << abslogzoom - else if ny < 0 - ny = 0 - nfy = 0 - @y = ny - @fx = nfx - @fy = nfy - # @lower?.frame t, dx >> abslogzoom, dy >> abslogzoom - { canvas } = ctx - # @mny = 100 - t.drawImage canvas, -nx, -ny - # t.drawImage canvas, 0, 0, w, h, -nx, -ny, w*pmul, h*pmul - - SeaFloor: SeaFloor = do-> - __proto__: BoundParallaxPlane - SeaFloorProto: BoundParallaxPlane - # terror: CuteluTerror = - # img: cuteluterror - # zoom: 6 - # __proto__: ScaledImg - # color: "#051555" - seafloorImg: seafloor - init: (options)@> - { seafloorImg } = @ - # @terror.init(options) - w = seafloorImg.width - h = seafloorImg.height - @w = w - @h = h - @SeaFloorProto.init.call @, options - # { color, w, h } = @ - # e = @document.createElement "canvas" - # e.width = w - # e.height = h - # @ctx = ctx = e.getContext '2d' - { ctx, w, h } = @ - ctx.drawImage seafloorImg, 0, 0 - if false - ctx.fillStyle = "magenta" - ctx.fillRect 0, 0, w, 1 - ctx.fillRect 0, 0, 1, h - ctx.fillRect 0, h - 1, w, 1 - ctx.fillRect w - 1, 0, 1, h - - SeamlessPlane: SeamlessPlane = - withRect: (rx,ry,rw,rh,cb)@> - { w, h } = @ - if (ex = rx + rw) > w - if (ey = ry + rh) > h - cb rx, ry, w - rx, h - ry, 0, 0 - cb 0, ry, ex - w, h - ry, w - rx, 0 - cb rx, 0, w - rx, ey - h, 0, h - ry - cb 0, 0, ex - w, ey - h, w - rx, h - ry - else - cb rx, ry, w - rx, rh, 0, 0 - cb 0, ry, ex - w, rh, w - rx, 0 - else - if (ey = ry + rh) > h - cb rx, ry, rw, h - ry, 0, 0 - cb rx, 0, rw, ey - h, 0, h - ry - else - cb rx, ry, rw, rh, 0, 0 - __proto__: ParallaxPlane - - WaterPlane: WaterPlane = do-> - waterscapeSuper: waterscapeSuper = SeamlessPlane - __proto__: waterscapeSuper - random: Math.random - sqrt: Math.sqrt - colors: [ "cyan", "blue" ] - randomStuff: @> - { random, sqrt, ctx } = @ - s = sqrt(15000 / (random() * 50 + 1)) | 0 - @withRect (random() * @w | 0), (random() * @h | 0), s, s >> 2, (x,y,w,h)-> - ctx.fillRect x,y,w,h - @ - init: (options)@> - { lower, w, h, moltf, colors } = @ - if lower? - lower.w ?= w - lower.h ?= h - lower.moltf ?= moltf >> lower.logzoom if moltf? - @waterscapeSuper.init.call @, options - { ctx } = @ - for k,v of colors - ctx.fillStyle = v - colors[k] = ctx.fillStyle - ctx.globalAlpha = 0.16 - if true - x = 200 - while x-- > 0 - @randomStuff() - waterscapeSuperFrame: waterscapeSuper.frame - frame: (t)@> - { ctx, moltf, random } = @ - - ctx.fillStyle = @colors[ random() * 1.2 | 0 ] - @randomStuff() while moltf-- > 0 - - t.save() - t.globalAlpha = @alpha - @waterscapeSuperFrame.apply @, arguments - t.restore() - logzoom: 0 - - # PinkWaveletPlane: PinkWaveletPlane = do-> - # waterscapeSuper: waterscapeSuper = SeamlessPlane - # __proto__: waterscapeSuper - # random: Math.random - # sqrt: Math.sqrt - # sprites: [ "cyan", "blue" ] - # wlets: null - # randmix: @> - # { random, sqrt, ctx } = @ - # s = sqrt(15000 / (random() * 100 + 1)) | 0 - # @withRect (random() * @w | 0), (random() * @h | 0), s, s >> 2, (x,y,w,h)-> - # ctx.fillRect x,y,w,h - # @ - # init: @> - # { lower, w, h, moltf, colors } = @ - # if lower? - # lower.w ?= w - # lower.h ?= h - # lower.moltf ?= moltf >> lower.logzoom if moltf? - # @waterscapeSuper.init.call @ - # { ctx } = @ - # for k,v of colors - # ctx.fillStyle = v - # colors[k] = ctx.fillStyle - # ctx.globalAlpha = 0.06 - # if true - # x = 300 - # while x-- > 0 - # @randomStuff() - # waterscapeSuperFrame: waterscapeSuper.frame - # frame: (t)@> - # { ctx, moltf, random } = @ - - # ctx.fillStyle = @colors[ random() * 1.2 | 0 ] - # @randomStuff() while moltf-- > 0 - - # { alpha } = @ - # # t.save() - # t.globalAlpha = alpha if alpha? - # @waterscapeSuperFrame.apply @, arguments - # # t.restore() - # logzoom: 0 - seafloor: seafloorPlane = __proto__: SeaFloor - getDepth: @> @seafloor.fy / @seafloor.mfy - waterscape: waterscape = do-> - __proto__: WaterPlane - # color: "cyan" - # logzoom: 0 - moltf: 12 - colors: [ "#051555", "#33ddff" ] - alpha: 0.2 - logzoom: 0 - lower: - # __proto__: ColorPlane - # logzoom: 2 - __proto__: WaterPlane - # color: "blue" - colors: [ "#000033", "#001155" ] - alpha: 0.3 - # abslogzoom: 2 - logzoom: 2 - lower: seafloorPlane - bluescape: - __proto__: SeamlessPlane - bluescapeSuper: SeamlessPlane - lower: waterscape - logzoom: 0 - frame: (t,sx,sy)@> - { ctx, random, w, h } = @ - - x = @x + sx - x = (x + w) % w - y = (y + h) % h - @x = x - y = @y + sy - y += h while y < 0 - y -= h while y >= h - @y = y - # i = ctx.getImageData(0,0,@w,@h) - - ctx.save() - @lower.frame ctx, sx, sy - ctx.restore() - # t.save() - # t.globalCompositeOperation = 'copy' - - t.drawImage ctx.canvas, 0,0,w,h, 0,0,w*4,h*4 - - # t.drawImage ctx.canvas, 0,0,w>>2,h>>2, 0,0,w*2,h*2 - - # t.drawImage ctx.canvas, 0,0,w>>2,h>>2, 0,0,w*2,h>>2 - # t.drawImage t.canvas, 0,0,w*2,h>>2, 0,0,w*2,h*2 - - # t.restore() - # @withRect x, y, rx*2, ry*2, (x,y,w,h,ox,oy)-> t.drawImage c, x,y,w,h, ox*2,oy*2,w*2,h*2 - # t.drawImage c, 0, 0, - # t.fillColor = if random() > 0.5 then "#104080" else "#155590" - # t.fillRect 0, 0, 100, 100 - # t.clearRect 0, 0, 100, 100 - # t.drawImage t, 0, 0, 100, 100, 50, 50, 100, 100 - init: (options)@> - { w, h, lower } = @ - - @w = w - @h = h - - lower.w = (w >> 2) * 5 - lower.h = (h >> 2) * 5 - - @bluescapeSuper.init.call @, options - - { ctx } = @ - - # ctx.fillStyle = "#0099dd" - # ctx.fillRect 0, 0, @w, @h - - setup: -> - { bluescape, radx, rady } = @ - - bluescape.w = radx - bluescape.h = rady - bluescape.init( { screenw: radx * 2, screenh: rady * 2 } ) - - v = new Vilma(@) # jaws.Sprite x:screen_x1*2, y:screen_y1*2, zoom:2, image:pixyvaquita - v.px = 0 - v.py = 0 - v.vx = 0 - v.vy = 0 - @vilma = v - - @encounters.generate(@,-radx, -rady, radx * 2, rady * 2, radx * 2, 0) - - { touchInput } = @ - touchInput.game = @ - x = document.body - x.addEventListener "touchmove", touchInput.handle('move'), true - x.addEventListener "touchstart", touchInput.handle('start'), true - tend = touchInput.handle 'end' - x.addEventListener "touchend", tend, true - x.addEventListener "touchleave", tend, true - x.addEventListener "touchcancel", tend, true - - @collisions.setup(radx, rady) - radx: screen_x1 - rady: screen_y1 - rad: screen_x1 * screen_x1 + screen_y1 * screen_y1 - collisions: - Array: Array - setup: (radx, rady)@> - # Setup the collision detection subsystem - # Assumes: - # - radx and rady are multiples of 8 - w = @w = (radx >> 2) - h = @h = (rady >> 2) - @b = new @Array(w * h) - @o = (w >> 1) * h + (h >> 1) + 1 - @l = [ ] - a: (o)@> - # Add a collision subject - # Assumes: - # - all the corners of the object's collision area are in the viewing area - # - the object's collision radius is <= 8 - { l, b, w } = @ - i = @o + (o.py >> 3) * @w + (o.px >> 3) - @b[i-1] = @b[i+1] = @b[i] = o - i -= w - @b[i-1] = @b[i+1] = @b[i] = o - i += w << 1 - @b[i-1] = @b[i+1] = @b[i] = o - @l.push o - - # o.crad - q: (o)@> - # Quick collision test - # Test collisions of object against previously added collision subjects - # For this to work correctly: - # - the object should have a collision radius <= 4, - # - have a center in the viewing area - @b[@o + (o.py >> 3) * @w + (o.px >> 3)]?.collide(o) - # t2: (o)@> - # Like above but for objects with a collision radius <= 8 - clear: @> - @b = new @Array(@b.length) # Discrete board for detecting collisions - @l = [ ] # List of collisions targets - draw: @> - { jaws, spaceKey, radx, rady, vilma, vaquitas, cameos, stilla, rad, collisions } = @ - - @addVaquita() if (!(@gameloop.ticks & 0x7f) and vaquitas.length < 1) or jaws.pressed[spaceKey] - - vilma.fpx += vilma.px - vilma.fpy += vilma.py - vilma.move() - - if true - { px, py, fpx, fpy } = vilma - - vilma.fpx -= px - vilma.fpy -= py - vilma.px = 0 - vilma.py = 0 - - px = px | 0 - py = py | 0 - - @bluescape.frame jaws.context, -fpx, -fpy - else - { px, py } = vilma - - vilma.fpx = 0 - vilma.fpy = 0 - vilma.px = 0 - vilma.py = 0 - - px = px | 0 - py = py | 0 - - @bluescape.frame jaws.context, -px, -py - - collisions.a vilma - - for v in vaquitas - x = v.px -= px - y = v.py -= py - v.draw() - if (x >= -radx) and (x < radx) and (y >= -rady) and (y < rady) - collisions.a v - - vilma.draw() - - if stilla? - x = stilla.px -= px - y = stilla.py -= py - if stilla.dead or x * x + y * y > rad * 16 - stilla.goodnight(@) - @stilla = null - else - stilla.draw(collisions, @) - if (x >= -radx) and (x < radx) and (y >= -rady) and (y < rady) - collisions.a stilla - - for k,v of cameos - continue unless v? - x = v.px -= px - y = v.py -= py - if v.dead or (x < -radx) or (x >= radx) or (y < -rady) or (y >= rady) - cameos[k] = null - else - v.draw(collisions, @) - collisions.q v - - @encounters.generate(@,-radx, -rady, radx * 2, rady * 2, px, py) - - collisions.clear() - - if (@gameloop.ticks & 0xff) is 0xff - fps.innerHTML = "#{@gameloop.fps} fps" - - jaws: jaws - spaceKey: spaceKey - if true - jaws.init() - jaws.setupInput(); - window.game = game = new Demo - gameloop = new jaws.GameLoop(game, { fps:24 }) - (game.gameloop = gameloop).start() - else - jaws.start Demo, fps:25 - - # gameFrame = -> reportErrors -> - # if (time & 0xff) is 0x00 and vaquitas.length < 4 - # addVaquita() - # # s += 0.001 - # x -= vx = pressedKeys[leftKey] - pressedKeys[rightKey] - # y -= pressedKeys[upKey] - pressedKeys[downKey] - # if vx > 0 - # zoomX = 1 - # else if vx < 0 - # zoomX = -1 - # v.setAttribute("transform", "translate(#{x}, #{y}) zoom(#{zoomX}, #{zoomY})") - # # transform = v.transform.baseVal.getItem(0) - # # transformMatrix.a = zoomX - # # transformMatrix.e = x - # # transformMatrix.f = y - # if (time % 3) is 0 - # if currentFrame.baseVal is "#twistleft" - # currentFrame .baseVal = "#_" - # else if vx isnt 0 - # currentFrame.baseVal = "#twistleft" - # # transformList.initialize(transform) - # vq.update() for vq in vaquitas - # time++ - - # # setInterval gameFrame, 40 - # window.location.reload(true) - window.addEventListener('load', ((e)-> - if (window.applicationCache) - window.applicationCache.addEventListener('updateready', ((e)-> - # if (window.applicationCache.status == window.applicationCache.UPDATEREADY) - # Browser downloaded a new app cache. - # Swap it in and reload the page to get the new hotness. - window.applicationCache.swapCache() - if (confirm('A new version of this site is available. Load it?')) - window.location.reload() - # else - # Manifest didn't changed. Nothing new to server. - ), false) - ), false) - -genPage() diff --git a/attic/game-debug.html b/attic/game-debug.html deleted file mode 100644 index 52935f8..0000000 --- a/attic/game-debug.html +++ /dev/null @@ -1,269 +0,0 @@ -<!DOCTYPE 5> -<html><head><meta charset="utf-8"><link rel="shortcut icon" href="data:image/x-icon;base64,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zNPUy9LUydPUys3PxMXIv7zHvLTBtq+8sKO0r5SfoW9+gDpOUDNBRDpFST1HTMnJvNHUwszRxMzPxc/Nxc7Kxc/NxNHTwNTVwtnSytnUzNLVy9LUytLTytHSydHSyM/Rx8zPxczNw8rNw8THvr3EurS+s6+8sJmtpIOckniJhl1zcDNQVjdLU0pTVlBYW9DCvdLHws3Iwc3HwMrGv8rFvsbFvcbJtsfKuMfMwcfMwsrPxc3Rx87Rx8nNw8fMwr7Jv7zHvcPHvcLGvLrAtbm+tbm8t6O1qo6lmnuWinmOhlx1ZzdcVjhVU01dW1pmZse8tsq+uL6/t7y/tr6/t8C9tLu9tbnCtbvEtrzEuL7GvMPIv8XKwMbJwb3FwbzHvbfGu7rDuMPFv8LJv8fPwsTKwsPJxMDJvbG/tJu1qqi+sLvKvsPRxDNXTVFsaWh5eMK1r8G0rru2rrW3rrS1rbaurbCxrLG1rrG2rrW6tLjAtsXKwMzQxdDUz8bY0cjczcXXztHU0sbGyb7Fw7fEv7O+urW+u7i+vsLHxczU0tXg3eTs4s3Vy1Rxam+FgYeUkbqvqbWqpbetprOqpaqmnqujnpyemp2gnaKmpKqwsL7Ev97g1fb17v/7+f////j499Ha2rO/v6y2uq+3u6u0tqmxtKSrrp6jppKbmYqXlY6bmJKfnI2bmIKRjomZlaStq6ylnqihmZ+blKCXlpOPiHx8dG50dH2Bg5KcnKOursPJxuDn2+317PHw8NnS07y4u6Kkp5+ipqCprJ2prKGtr6+wtLGwtKenrKClpJObmY+VlYqUk4uVk5GamKyvrt/Y2KGhmZGRkJOOjXFubFxaVVpVU1ddX3t8fpqgoL25usTBwbnBwLG4t6SfpJWQlZKLkY2LkI6PlJSanZymqaaqrayssKSrrqClqJmfnpGdmI+fmZSgm6Glo7m8uNrU0uDY15yUk4uIhnp1dENCQUpDRFdKUFJXV35+gJqZm6CfoZ2dnoCHiYGBhoJ9gn56gHt4fXN5fXN+goCJipWanLCusr26tra8tqywsqyusLW0sby6tsC/u8zGxMnNx8bKx8bMyJWKiIl3eFdPTjc1NEE4OUk/Q0Y+RGBbYnh7gXd8gHmChHd+gnh3fHBxdWNnaWBiZl5iZmRoanZ0eYiDjJyVnqulpaahoJ6WoaGZpK2iq7WorcK0tsS8u8DAvrq7uru8upOAgHZkZjMtLS8rKzYxMj03OjkxOE1CSm5vcm1zeGh0d2lxdWptcGNmalhcYFFWWVBVWFVaXmdobHh0foaFj5OOl5KPloqHkI2IkpWPmKCUnqmepKympq6trKqurKuwrY96e2RYVyMiISooJzQvMTUwNDEtMTcvN0RBR2hsb2hzdWhwdGZrbWBkaFFZWkhSVEVPUkpQUlNYXWJkbXV3gIKCioKBiYKAiYCAiIWEjY2KlJaTmJyamaOhoKKfpaSiqIR1dmRYWCIiIjk1NEpCQ0lCREA6PjkzOjEsMkA/RHZ5fG12eWtwcl9kaFJbXkdRU0RPUEdNT0VOVlhdZm5weX58hYF+hn18hIB9hYOCioiGj4+LlZGOlpWRmpuTnZmSm3ZxcnFrbC8mKEU2Ol9NVGdXX2FYW1BUVjlDRzhESG13emp5enF4e291d25ucGNmaVJYWlJNVkdKUk9WXmJkbHNye317gnt5gXx5goB8hIeDjIuGj5CKk5GKkomEjIF9hXNvcXdvcDQoKzMjKk1AR2JVW3BiaG5rbm5wdGhxdGx2eXh7fYeDhZGDh2ZTWDgsMTYvNVVPV01KVE5RWFVbZGdqcHNze3h3fnt6gX58g4J+hoOAh4aCiYF9hXd1fHZ0fHNvcH1ydTw0NiIaHzsvNlRIT2FYXWxnanBvcW9ydXZ2ent7fXVvcjQzNhULEAoAAA4IBzEtMlBOVktPVlFYX2Jka21tdW5wdnV0fHZ1fHV0e3d2fnV0e3Nzem5vdm5udnFucHlwcVFERhsTGCkgJTs1O0RCR1FPU1pVWGFbX21kaHVqbkNCRiInKRAOEQUEBAYHBhwjI05GUElETlBOVltXYHdud3htd3FpcXFpcXVxeXNzenNyeXBxeW5vdmxtdW1rbHBubl5XWA8PEhgVFiUfIy4sMTk3Oz47P0hBRldPVHVqbm9naiUrLRUUGRMRFRQUHTA0O0lBSz48RERFTVhWX2dhamZeZ11ZYmxkbXFudWttdGxtdGlrcWRpbmZqb2ppaHBubnNubg8bGxAPDBUPEh4XHSQiJy8lKjcrLzo2OlBJTWxiZnBnaVhUWEVBSkRGTk5GTz43QTk0PT4+R0tJUVtUXVNKU1FNVGZdZGpiam1kbW1mbmpkbGlja2dkbG1rbG1sbHNycjs8PgoMDRALDxINEhQSGBsUGxgVHR4cICwpLUA5PUpDSFBGTlNIU0Y+STgxPDAsNTAsNjw4QUtFTUlASj49RkNDS1RSWlhXXl1aYl9bY2BcZF9cZFxcZG9tbWtqanRwcXFnaQ8RFgsJDhALDg8LEwsOGg0PGhYTGh8UHyUVIiUcJiEhKickLSEgKSMhKickLSwpMzUwOjw3PzYzOzc1PTo7QkRHTlBQV1FSWFRUWlVUW1hXXlpWYHZydHBtbnFucH10dltVWgANDwYMDwgMFAkLGAgQGhQTGx0THR8QHB4WIBoaIxscJRwdJh0dJh8gKCgmLy4rNCUlLSUoMDAsNTEvODQ7QUBDS0NGTkZIUEZIT0hKUU1KU3ZzdHBtbnBubnFwb3NxcUdHTA4SFggOEQgPEQgOEgsQFg8RHBESHBMWIBcaIxgaIxsbIxwcJR4eJyMjKyknMCAgKSAhKCgmLysqMicuNC42Ozg4Pz0+RD4+RkNCTkJBT3t3eHRwcnBub3FwbnNxcXRxclJVWA8XGggOEggOEQ0QFg8SHBETHRETHRQWIBQWIBgcIxwdJBwdJB4fJx4fJhscIx0fJh0eJiAgKCUjKy8rNDYxOjk0PTg0OzM4PjM3PoJ3eXx4eHZycnNwcHNwb3Nwb3NxcGBiYhEgHgcVFgsTGRASHRIUHRMVIBcYIhocIxsdJRweJRweJRsdJBseJSAfKCAgKB0eJh0eJiAfJyMhKDEtNjk0PDcxOjMxOS4tNoN5en57fHl0enZxdHRxcXRxcHRycHl1dm1qbi04OBEaHg8SHBETHRQWIBocIxwdJBweJhwfJh0gJx4gKB8gKB8gKB8gKB8gKB8gJx4fJh8gKCkoMDEuNzAuNiIsMiErMYV7fIN9foR9fnx4eXh1dndzdHZzcnl1dHhzdnZzclRUVxseJxMWIBYYIhgcIxwdJR0eJR0gJx8gKCAhKSEiKSEiKSEiKCEiKCEiKSEiKSEiKSEiKicmLycnLyAoLR8oLX10dn55e4N9fXp1dnh0dXh0dXdzcXVycnVxcnRubnBqcmFgaUFETSImLRocIxweJhwfJR4fKB4gKBwdJhweJh0fJyIkKyIkKiEjKiEjKh8hKCAiKCMkKiIkKiQlLCQlLGpkZW5tbHVycXRxcW9tbXBubm1samlpaGtpamxnaWtjbWZjbGFgaVxVXkI8RCQkLB8gKB8gKR8hKBweJh0eJx0fJxweJh8hKCEiKh8hKRwdJRwdJh8gKB4gKCIjKiIjKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"><title>Vilma the Vaquita</title><style type="text/css">body { - /* background:pink; */ - /* background: #69B2FF; */ - /* background: #21AFF8; */ - /* background: #0286E8; */ - /* background: #1096EE; */ - background:black; - text-align: center; - font-size: 22px; - font-family: Helvetica; -} -.banner { - border: 5px solid white; - border: 5px solid white rgba(255,255,255,0.9); - box-shadow: 0 2px 4px blue; - margin: 1em; -} -p { - color:white; - color:rgba(255,255,255,0.9); - margin-top:0.418em; - margin-bottom:0.418em; - margin-left:auto; - margin-right:auto; - width:22em; - max-width:100%; - text-shadow: 0 1px 1px blue; -} -a { - /* - color:rgb(200,255,255); - color:rgba(200,255,255,0.9); - */ - color:white; - color:rgba(255,255,255,0.9); - text-decoration:none; - display: inline-block; - border: 1px solid white; - padding: 0 0.2em; - border-radius: 0.2em; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -ie-border-radius: 0.2em; -} -a:hover { - background-color:rgba(20,70,180,1.0); -} -.petition { - margin:0.418em; - padding:0.618em; -} -.petition a { - font-size:127.2%; - box-shadow: 0 2px 4px blue; - margin:0.3em; -} -.page { - width: 100%; - height: 100%; - margin: 0; - border: 0; -} -.centering { - display: table; - padding: 0; -} -.centered { - display: table-cell; - vertical-align: middle; - text-align: center; -} -.inline-block { - display: inline-block; -} -.dynamic-section { - display: inline-block; - vertical-align:middle; - max-width:100%; -} -.flip-lr { - -moz-transform: scaleX(-1); - -o-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - transform: scaleX(-1); - filter: FlipH; - -ms-filter: "FlipH"; -} -image, .pixelart { - image-rendering:optimizeSpeed; /* Legal fallback */ - image-rendering:-moz-crisp-edges; /* Firefox */ - image-rendering:-o-crisp-edges; /* Opera */ - image-rendering:-webkit-optimize-contrast; /* Safari */ - image-rendering:optimize-contrast; /* CSS3 Proposed */ - image-rendering:crisp-edges; /* CSS4 Proposed */ - image-rendering:pixelated; /* CSS4 Proposed */ - -ms-interpolation-mode:nearest-neighbor; /* IE8+ */ -} -/* -.pixelart { - image-rendering: -moz-crisp-edges; - -ms-interpolation-mode: nearest-neighbor; - image-rendering: pixelated; - image-rendering: crisp-edges; -} -*/ -g.flipped { - transform:scale(-1,1); -} -.dimmed { - opacity: 0.2; -} -.dimmed:hover { - opacity: 1; -}</style></head><body><div class="centering page"><div class="centered"><div><a class="dimmed">Vilma the Vaquita - v0.0.10</a></div><svg id="sea-svgroot" width="960" height="720"><defs><linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:1"></stop><stop offset="25%" style="stop-color:rgb(100,200,250);stop-opacity:1"></stop><stop offset="50%" style="stop-color:rgb(0,80,240);stop-opacity:1"></stop><stop offset="75%" style="stop-color:rgb(0,0,180);stop-opacity:1"></stop><stop offset="100%" style="stop-color:rgb(0,0,0);stop-opacity:1"></stop></linearGradient><g id="_"><g transform="translate(-18,-15)"><image width="50" height="30" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAFWElEQVRYw+2WX0gcVxTGv7l3Zmd2drs6aHaNW7Jd88cYDaQYI1jsQ5GSh1BCAhUsNQ/JWxT6lLKExISCQkBECoW+6IMJEknaIEKQbKXJ5iFY6mKsEhOkk6qbRTe67szs7M7szvQhydKWhibEbQn1B/M2557z3e/ecy6wxRZbbLHFFv8hZ86ccYVCIe/rxkUiEXckEnFvVh3M6wZMTk7un56eHkkkEnsJIdA0DZRSuFwumKbZqSjKRHd399zL4sfHx9l4PL7TMIw5AGR4eLh0YmJi402FkFf9cXZ2tv7SpUv2zZs378fj8b2WZcE0TRDybAnTNKHr+tcOh2N2ZGTk4Iu4vr6+P22Wqqo1uVzuQSwWI4uLi5AkqfJlOdva2pj+/v7tFy5cIJviiKIort7eXlUURei6DgAwDAMOhwPZbBZPnz5FSUkJBEGAruvgOA48z8Plcj14/Pjx516vVxcEQVlZWflsx44d3clkEplMBrZtI5PJHPR4PFlK6dK5c+eSf8z76NGjytHR0eXa2tomlmUftLS0rL+RkBs3bhycn5//CQCSySSOHDkCv98Py7JAKYWu68hkMlhYWMDDhw8Rj8fhcrlg2zYMw/hZkiSNYRhKCPlAVdWCYJZlQSlFPp/v4jhOIYT8ePbs2WgikRB7enp2+ny++06nE36/v/7YsWNTb+RIOByWotHoGsuyiMViOH36NAAgn8+jo6MDtm2jv78fDMNA0zRYloV79+4hlUpBUZRnSRgGhmGgpKQEAGDbNhiGQT6fRz6fh2VZ4DgOlFIQQpDNZpFOpwEAHo8HlmXBtm2Iong1n89fDYVC3/+1TvYV7saHuVwO2WwW5eXlAACHw4HW1lbcuXMHPM/j1KlTGBoawp49exCNRtHc3AyPx4PBwUGkUinkcjmwLItkMlkQJkkSqqurwTAM5ubmkMlkYJomTNOEYRiglKK6uhqGYUCSJDyvoRXARwC+f+3LnkqlPgUAXddx9OhRiKIIlmVhWRbKysrgdrsLDk1PT0MQBFBKwXEcgsEgGOaZ6S922TAMAEB9fT14nocoimhsbIQoiuB5HoIgYNu2bdi1a1e/0+n8ZPfu3R9TSn1ra2s+TdPe6+jo+NtW/4+OpNNpORaLoaurC6ZpYnBwEMePH8fly5fR3t4OALh+/ToAgFIKy7KgqipUVUVjYyMURcHU1FTh6NTW1sLr9SKdToNhmMJXUVGBRCIBp9MJSmnHtWvXvr1y5Qr8fn9uU+ZIZ2enPT4+DgCQZRkcx2FmZgYcxxXa7vLyMu7evVvoZsFgEA0NDSCEYGNjA4QQyLIM27YLR0sQhMIMMgwDhJBveJ7/cnJy0urp6Ulv+kA8fPjwFwsLC30Mw2BoaAiBQAC6rmN0dBTr6+sQRRGlpaU4cOAATNPErVu3CgUTQlBTUwOv1wtKKTRNW2ZZNg7gt1QqtSxJ0jwh5LtcLsd7vd4Vl8uVraqqyhVlssuyLLa3t2vz8/M4f/48kskkDMMAx3EIBAJwOp3w+XyFbmOaJm7fvo2ysjJwHAfTNFFXVwdd139YXV39KhgMZgkhv3IcpzY3N2v/+hPl4sWL72ua1pvNZgcAlFNK4Xa76wghw5ZlCYqirFZWVpY8efLEcfLkybFIJAJVVV9M/4YTJ078UlFRkSnWm48pxqLhcHg/y7L3l5aWsLi4CAAIhUIMiggpxqItLS0zPp8PoiiC4zgYhoGBgQHurRMCADzPv7tv3z4cOnQI8Xh8ezgctt9KIVVVVctjY2PvpFKpMlmW12VZLqqQouP3+5m2tjbS1NREi5mHLbaQQCCA52683Y5s8X/ld5pkXmGj97LmAAAAAElFTkSuQmCC"></image></g></g><g id="_v105"><g transform="translate(-18,-15)"><image width="50" height="30" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAF0ElEQVRYw+2XW2xUxxnH/3M5N+9Z73p9ARuIuSSOGgJRUxq5fQCJqhFV0gZoX3qREkW0riwq1KRVkt5SVa2oRFFFhOzKJVLbpChFQoIXEh6gLhcnkQUFJ+kl0IawXmxTe3ftPXv2zOXM9CGkD30hKWylqv4/jmb+3/zm8n0zwKIWtahFLer/Vfmh8s5b9eC3cTIZAKgOFuofYsxaAFsBqP86yOeHRg+0T5//dFKt+I1MLyCqJJtpbxjvrWhKd48cB/bdzGNgYGBda2vrk2euHe/7e9sGRwTtr98qCPmgHZ98/tgTeuL496K44TACQghBbDyq6rMOZQ5aw4yklBpDV1Zp//YXh768+hkAuO/AO+0Xd6ya+9dC7H7p67kro7sCzvpmbUjGuz9zrty+/mh1sPDjpoN86ci1te6JX7zqcEYceZ0AgDEGjuNIpTStRTW/xfdTxrmVUnLmBoIzYqLgzmTatLavSK+eijKFsbJs+eodC+NtDqx3w1q/tnzbsWL+o2fW58XPKyJlF3esEu/H3fK76R9cuHBuw/Tuhz53W0Ae2XfiNx3Fc9s9MQUlY/R95N7ZVcu7w2q1qpIkSaVS6Q0j52qx6FcqFd91XWMBXxrQwG+FlQsLhroerKHEaKYJKANSACcIZV95M/cxQaJK/a17Ht/wcMfcC+TkcEfSiHhP99LkfPeWl07v+uSuWwLp33fxmbtKo09n3QwTM69j44OfrQdEMqO1HR4ezloA27ZunfF9n1trrTEG71696okkYUKI7Pt3UUoJAHBd90aTI3WaXHccpwGgJxbaGtkg2gIOZ/A8D8Za1ARM6BGiUlifk582hIh+++tfPffv86Q3AwnjyU84NmWqegmZIJAZqhgBMDw8nB0dHfXGx8fdI0eOLOns7PQGBweXCSHMnWvWqE2bNllKaSSVElEUKSGlSpIkrdVqul6v16WKVap1G4CemdkyCAyxACgsXM5JXK+DAgiZpgyWMKQ0BX16gWR/+B9lLVb/xxqrGyCyYh7YuFEX2to8A8AC6O7pMblc7r3DrrXdu3dvUQhBe3t7Xc/zaKFQsFNTU5BSUs65AiHUcRwTxzGEeO8qJELAWqBci60HTQQPbeBl2ZnclvlS4v4o/eUjQ7cn/eqGH0+9ge3btgkpJY4ePZr29/en39i5U+3fvz8EgO8/+6wA4GpjrO+6VCmVlsvlNJPJhGEYIo7jxFqLVOtUKQUCwPN94rku8TyPBpmQ1uIkZY5PKl33Vg+be97MdPaKbMqOVW9X1vrCwLdn/3L25RwAXL58mXmeh5GRkTnf9xkAJEmSlkolTE5OuowxxjlvUEpDzjm6urpILYpsPYps3GgQSghhjJEw20pqUc36rstqUT013Mdo38DXyrV614LbdRBAC4BrH6a43nRH/hzcVbZ4JUdhsWfPnlJfX184Pz+PU6dP83K57FFKre/7yuGcMM4RRVFea80IIXZ2bi4JgoBls1nak88TISUcztm4XgEszf2xgyykE+0r2yad5aVEek8kbuaF6mCh1JTK7q5/8LumOv3c3MnnO4vFYtdf334bWmvOGTOu46RBEKSu57HWbJbm83lWq9XslStXUsdxaLlSQUuYheu6ODXraRWuTNo4P38xs/ZPMyh8qzpYkPmh8gFY3A/gBIBXml7ZH/jJy2O5hXfXVfyuwFUNS4lNXaJR9ZdRxwgbsZDk0IBXK5ngb39QK9ozGSF1OlNTyYXNP3McZq8vaP7N6mDhcDMenh8YJD9UXgagD8DdAKYAzAG4D8BmABUArwGYAPDOx9XE7hVvHHyUEUuFUiqJY33o0KGwmS9o0izjx3YMXHKpXW2sTRuNhrbrHnr14FNf/FSz4tFmGbsPP7W5o2spWoIWzjn3/Esn72/mjjQNZGTr6qJM4u/k2gqxkpJE0hxuJghrpvnY2NjZlb13XF6yZMnhJJr//cTExGSzYnE0WUEQnJ2enmZhGOrFT/2i/gf1T8lwuYI1cNwxAAAAAElFTkSuQmCC"></image></g></g><g id="twistleft"><g transform="translate(-18,-15)"><image width="50" height="30" xlink:href="data:image/png;base64,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"></image></g></g></defs><rect x="0" y="0" width="960" height="720" fill="url(#grad1)"></rect><g transform="scale(2)"><g id="sea" transform="translate(240,180)"><use xlink:href="#_"></use></g></g></svg><div><a class="dimmed" target="_blank" href="index.html">Learn about Vaquitas</a></div></div></div><script type="text/javascript">gameObjects=null;</script><script type="text/javascript">(function () { - var addVaquita, downKey, getTransformMatrix, k, keyDownActions, leftKey, pressedKeys, reportErrors, rightKey, screen_x1, screen_y1, sea, svgroot, upKey, usedKeys, v, vaquitaObj, vaquitas, _j, _len1; - svgroot = document.getElementById("sea-svgroot"); - reportErrors = function(x) { - var error, error2; - try { - return x(); - } catch (_error) { - error = _error; - try { - return alert(error.toString()); - } catch (_error) { - error2 = _error; - return alert(error); - } - } - }; - screen_x1 = 240; - screen_y1 = 180; - vaquitas = []; - vaquitaObj = { - update: function(x, y) { - var m, rx, ry, s, vx, vy; - if (!window.abcd) { - window.abcd = 1; - } - vx = this.vx + Math.floor(Math.random() * 3) - 1; - vy = this.vy + Math.floor(Math.random() * 3) - 1; - x = this.x; - y = this.y; - rx = 0.5 * x / screen_x1; - ry = 0.5 * y / screen_y1; - if ((s = vx * vx + vy * vy * 2) > 6) { - vx = Math.round(vx * 0.9 + rx); - vy = Math.round(vy * 0.9 + ry); - } - this.x = x -= this.vx = vx; - this.y = y -= this.vy = vy; - if (vx > 0) { - this.scaleX = 1; - } else if (vx < 0) { - this.scaleX = -1; - } - this.e.setAttribute("transform", "translate(" + x + ", " + y + ") scale(" + this.scaleX + ", 1)"); - m = this.m; - m.e = x; - m.f = y; - return m.a = this.scaleX; - } - }; - sea = document.getElementById("sea"); - v = sea.firstChild; - getTransformMatrix = function(el) { - var transformListXX, transformMatrixXX, transformXX; - transformListXX = el.transform.baseVal; - transformMatrixXX = svgroot.createSVGMatrix(); - transformXX = svgroot.createSVGTransform(); - transformXX.setMatrix(transformMatrixXX); - transformListXX.initialize(transformXX); - return transformXX.matrix; - }; - addVaquita = function() { - var angle, n, vaquita; - n = v.cloneNode(); - n.setAttribute("opacity", "0.5"); - if (Math.random(0) > 0.5) { - n.href.baseVal = "#_v105"; - } - sea.appendChild(n); - angle = Math.random() * 6.28; - vaquita = { - e: n, - m: getTransformMatrix(n), - x: Math.sin(angle) * 300, - y: Math.cos(angle) * 300, - vx: 0, - vy: 0, - scaleX: 1, - __proto__: vaquitaObj - }; - vaquita.update(); - return vaquitas.push(vaquita); - }; - leftKey = 37; - upKey = 38; - rightKey = 39; - downKey = 40; - usedKeys = [leftKey, upKey, rightKey, downKey]; - keyDownActions = { - 32: addVaquita - }; - pressedKeys = {}; - for (_j = 0, _len1 = usedKeys.length; _j < _len1; _j++) { - k = usedKeys[_j]; - pressedKeys[k] = 0; - } - window.addEventListener('keydown', function(event) { - var code; - code = event.keyCode; - if (typeof keyDownActions[code] === "function") { - keyDownActions[code](event); - } - return pressedKeys[code] = 1; - }); - window.addEventListener('keyup', function(event) { - return pressedKeys[event.keyCode] = 0; - }); - return (function(x, y) { - return reportErrors(function() { - var ax, ay, currentFrame, gameFrame, scaleX, scaleY, time, transformMatrix, vx, vy; - time = 0; - vx = 0; - vy = 0; - ax = 0; - ay = 0; - scaleX = 1; - scaleY = 1; - window.cf = currentFrame = v.href; - transformMatrix = getTransformMatrix(v); - transformMatrix.a = scaleX; - transformMatrix.e = x; - transformMatrix.f = y; - gameFrame = function() { - return reportErrors(function() { - var vq, _k, _len2; - if ((time & 0xff) === 0x00 && vaquitas.length < 4) { - addVaquita(); - } - x -= vx = pressedKeys[leftKey] - pressedKeys[rightKey]; - y -= pressedKeys[upKey] - pressedKeys[downKey]; - if (vx > 0) { - scaleX = 1; - } else if (vx < 0) { - scaleX = -1; - } - v.setAttribute("transform", "translate(" + x + ", " + y + ") scale(" + scaleX + ", " + scaleY + ")"); - if ((time % 3) === 0) { - if (currentFrame.baseVal === "#twistleft") { - currentFrame.baseVal = "#_"; - } else if (vx !== 0) { - currentFrame.baseVal = "#twistleft"; - } - } - for (_k = 0, _len2 = vaquitas.length; _k < _len2; _k++) { - vq = vaquitas[_k]; - vq.update(); - } - return time++; - }); - }; - return setInterval(gameFrame, 40); - }); - })(0, 0); - })(); -</script></body></html>
\ No newline at end of file diff --git a/attic/game-debug.html.coffee b/attic/game-debug.html.coffee deleted file mode 100644 index 3877da7..0000000 --- a/attic/game-debug.html.coffee +++ /dev/null @@ -1,311 +0,0 @@ -# Copyright (c) 2013, 2014 Michele Bini - -# A game featuring a Vaquita, the smallest, most endagered marine cetacean - -# This program is available under the terms of the MIT License - -version = "0.0.11" - -{ htmlcup } = require 'htmlcup' - -htmlcup[x] = htmlcup.compileTag x for x in [ "svg", "rect", "g", "ellipse", "polygon", "line", "image", "defs", "linearGradient", "stop", "use" ] - -title = "Vilma the Vaquita" - -fs = require 'fs' - -datauri = (t, x)-> "data:#{t};base64,#{new Buffer(fs.readFileSync(x)).toString("base64")}" -datauripng = (x)-> datauri "image/png", x -datauriicon = (x)-> datauri "image/x-icon", x - -icon = datauriicon "vaquita.ico" -pixyvaquita = datauripng "pixyvaquita_v2.png" -pixyvaquita_v105 = datauripng "pixyvaquita_v105_v.png" -frames = - _: pixyvaquita - _v105: pixyvaquita_v105 - twistleft: datauripng "pixyvaquita_v2_twistleft.png" - -useSvg = true - -genPage = -> - htmlcup.html5Page -> - @head -> - @meta charset:"utf-8" - @link rel:"shortcut icon", href:icon - @title title - @style type: "text/css", - """ - body { - /* background:pink; */ - /* background: #69B2FF; */ - /* background: #21AFF8; */ - /* background: #0286E8; */ - /* background: #1096EE; */ - background:black; - text-align: center; - font-size: 22px; - font-family: Helvetica; - } - .banner { - border: 5px solid white; - border: 5px solid white rgba(255,255,255,0.9); - box-shadow: 0 2px 4px blue; - margin: 1em; - } - p { - color:white; - color:rgba(255,255,255,0.9); - margin-top:0.418em; - margin-bottom:0.418em; - margin-left:auto; - margin-right:auto; - width:22em; - max-width:100%; - text-shadow: 0 1px 1px blue; - } - a { - /* - color:rgb(200,255,255); - color:rgba(200,255,255,0.9); - */ - color:white; - color:rgba(255,255,255,0.9); - text-decoration:none; - display: inline-block; - border: 1px solid white; - padding: 0 0.2em; - border-radius: 0.2em; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -ie-border-radius: 0.2em; - } - a:hover { - background-color:rgba(20,70,180,1.0); - } - .petition { - margin:0.418em; - padding:0.618em; - } - .petition a { - font-size:127.2%; - box-shadow: 0 2px 4px blue; - margin:0.3em; - } - .page { - width: 100%; - height: 100%; - margin: 0; - border: 0; - } - .centering { - display: table; - padding: 0; - } - .centered { - display: table-cell; - vertical-align: middle; - text-align: center; - } - .inline-block { - display: inline-block; - } - .dynamic-section { - display: inline-block; - vertical-align:middle; - max-width:100%; - } - .flip-lr { - -moz-transform: scaleX(-1); - -o-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - transform: scaleX(-1); - filter: FlipH; - -ms-filter: "FlipH"; - } - image, .pixelart { - image-rendering:optimizeSpeed; /* Legal fallback */ - image-rendering:-moz-crisp-edges; /* Firefox */ - image-rendering:-o-crisp-edges; /* Opera */ - image-rendering:-webkit-optimize-contrast; /* Safari */ - image-rendering:optimize-contrast; /* CSS3 Proposed */ - image-rendering:crisp-edges; /* CSS4 Proposed */ - image-rendering:pixelated; /* CSS4 Proposed */ - -ms-interpolation-mode:nearest-neighbor; /* IE8+ */ - } - /* - .pixelart { - image-rendering: -moz-crisp-edges; - -ms-interpolation-mode: nearest-neighbor; - image-rendering: pixelated; - image-rendering: crisp-edges; - } - */ - g.flipped { - transform:scale(-1,1); - } - .dimmed { - opacity: 0.2; - } - .dimmed:hover { - opacity: 1; - } - """ - @body -> - @div class:"centering page", -> - @div class:"centered", -> - @div -> @a class:"dimmed", "Vilma the Vaquita - v#{version}" - if useSvg - @svg id:"sea-svgroot", width:"960", height:"720", -> - @defs -> - @linearGradient id:"grad1", x1:"0%", y1:"0%", x2:"0%", y2:"100%", -> - @stop offset:"0%", style:"stop-color:rgb(255,255,255);stop-opacity:1" - @stop offset:"25%", style:"stop-color:rgb(100,200,250);stop-opacity:1" - @stop offset:"50%", style:"stop-color:rgb(0,80,240);stop-opacity:1" - @stop offset:"75%", style:"stop-color:rgb(0,0,180);stop-opacity:1" - @stop offset:"100%", style:"stop-color:rgb(0,0,0);stop-opacity:1" - for k,v of frames - @g id:k, -> - @g transform:"translate(-18,-15)", -> - @image width:"50", height:"30", "xlink:href":v - @rect x:"0", y:"0", width:"960", height:"720", fill:"url(#grad1)" - @g transform:"scale(2)", -> - @g id:"sea", transform:"translate(240,180)", -> - @use "xlink:href":"#_" - else - @canvas width:"960", height:"720", -> - @div -> @a class:"dimmed", target:"_blank", href:"index.html", "Learn about Vaquitas" - gameObjects = null - @script type:"text/javascript", "gameObjects=#{JSON.stringify(gameObjects)};" - @coffeeScript -> - svgroot = document.getElementById("sea-svgroot") - - reportErrors = (x)-> - try - x() - catch error - try - alert error.toString() - catch error2 - alert error - - screen_x1 = 240 - screen_y1 = 180 - vaquitas = [ ] - vaquitaObj = - update: (x, y)-> - if !window.abcd - window.abcd = 1 - vx = @vx + Math.floor(Math.random()*3) - 1 - vy = @vy + Math.floor(Math.random()*3) - 1 - x = @x - y = @y - rx = 0.5 * x / screen_x1 - ry = 0.5 * y / screen_y1 - if (s = vx * vx + vy * vy * 2) > 6 - vx = Math.round(vx * 0.9 + rx) - vy = Math.round(vy * 0.9 + ry) - @x = x -= @vx = vx - @y = y -= @vy = vy - if vx > 0 - @scaleX = 1 - else if vx < 0 - @scaleX = -1 - @e.setAttribute "transform", "translate(#{x}, #{y}) scale(#{@scaleX}, 1)" - m = @m; m.e = x; m.f = y; m.a = @scaleX - # @e.transform.baseVal.consolidate() - # @e.transform.baseVal.initialize(@e.transform.baseVal.matrix) - sea = document.getElementById "sea" - v = sea.firstChild - - getTransformMatrix = (el)-> - transformListXX = el.transform.baseVal - transformMatrixXX = svgroot.createSVGMatrix() - transformXX = svgroot.createSVGTransform() - transformXX.setMatrix(transformMatrixXX) - transformListXX.initialize(transformXX) - transformXX.matrix - - addVaquita = -> - n = v.cloneNode() - n.setAttribute "opacity", "0.5" - n.href.baseVal = "#_v105" if Math.random(0) > 0.5 - # n.setAttribute "transform", "" - sea.appendChild n - angle = Math.random() * 6.28 - vaquita = - e: n - m: getTransformMatrix(n) - x: Math.sin(angle) * 300 - y: Math.cos(angle) * 300 - vx: 0 - vy: 0 - scaleX: 1 - __proto__: vaquitaObj - vaquita.update() - vaquitas.push vaquita - - leftKey = 37 - upKey = 38 - rightKey = 39 - downKey = 40 - usedKeys = [ leftKey, upKey, rightKey, downKey ] - - keyDownActions = - 32: addVaquita - - pressedKeys = { } - pressedKeys[k] = 0 for k in usedKeys - - window.addEventListener 'keydown', (event)-> - code = event.keyCode - keyDownActions[ code ]?(event) - pressedKeys[ code ] = 1 - window.addEventListener 'keyup', (event)-> - pressedKeys[ event.keyCode ] = 0 - - do (x = 0, y = 0)-> reportErrors -> - time = 0 - vx = 0 - vy = 0 - ax = 0 - ay = 0 - # s = 1 - # x = 0 - # y = 0 - scaleX = 1 - scaleY = 1 - - window.cf = currentFrame = v.href - transformMatrix = getTransformMatrix(v) - transformMatrix.a = scaleX - transformMatrix.e = x - transformMatrix.f = y - - gameFrame = -> reportErrors -> - if (time & 0xff) is 0x00 and vaquitas.length < 4 - addVaquita() - # s += 0.001 - x -= vx = pressedKeys[leftKey] - pressedKeys[rightKey] - y -= pressedKeys[upKey] - pressedKeys[downKey] - if vx > 0 - scaleX = 1 - else if vx < 0 - scaleX = -1 - v.setAttribute("transform", "translate(#{x}, #{y}) scale(#{scaleX}, #{scaleY})") - # transform = v.transform.baseVal.getItem(0) - # transformMatrix.a = scaleX - # transformMatrix.e = x - # transformMatrix.f = y - if (time % 3) is 0 - if currentFrame.baseVal is "#twistleft" - currentFrame .baseVal = "#_" - else if vx isnt 0 - currentFrame.baseVal = "#twistleft" - # transformList.initialize(transform) - vq.update() for vq in vaquitas - time++ - - setInterval gameFrame, 40 - -genPage() diff --git a/cloaky.png b/cloaky.png Binary files differdeleted file mode 100644 index 1f796ef..0000000 --- a/cloaky.png +++ /dev/null diff --git a/game.html b/game.html deleted file mode 100644 index 6642a17..0000000 --- a/game.html +++ /dev/null @@ -1,310 +0,0 @@ -<!DOCTYPE 5> -<html><head><meta charset="utf-8"><link rel="shortcut icon" href="data:image/x-icon;base64,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zNPUy9LUydPUys3PxMXIv7zHvLTBtq+8sKO0r5SfoW9+gDpOUDNBRDpFST1HTMnJvNHUwszRxMzPxc/Nxc7Kxc/NxNHTwNTVwtnSytnUzNLVy9LUytLTytHSydHSyM/Rx8zPxczNw8rNw8THvr3EurS+s6+8sJmtpIOckniJhl1zcDNQVjdLU0pTVlBYW9DCvdLHws3Iwc3HwMrGv8rFvsbFvcbJtsfKuMfMwcfMwsrPxc3Rx87Rx8nNw8fMwr7Jv7zHvcPHvcLGvLrAtbm+tbm8t6O1qo6lmnuWinmOhlx1ZzdcVjhVU01dW1pmZse8tsq+uL6/t7y/tr6/t8C9tLu9tbnCtbvEtrzEuL7GvMPIv8XKwMbJwb3FwbzHvbfGu7rDuMPFv8LJv8fPwsTKwsPJxMDJvbG/tJu1qqi+sLvKvsPRxDNXTVFsaWh5eMK1r8G0rru2rrW3rrS1rbaurbCxrLG1rrG2rrW6tLjAtsXKwMzQxdDUz8bY0cjczcXXztHU0sbGyb7Fw7fEv7O+urW+u7i+vsLHxczU0tXg3eTs4s3Vy1Rxam+FgYeUkbqvqbWqpbetprOqpaqmnqujnpyemp2gnaKmpKqwsL7Ev97g1fb17v/7+f////j499Ha2rO/v6y2uq+3u6u0tqmxtKSrrp6jppKbmYqXlY6bmJKfnI2bmIKRjomZlaStq6ylnqihmZ+blKCXlpOPiHx8dG50dH2Bg5KcnKOursPJxuDn2+317PHw8NnS07y4u6Kkp5+ipqCprJ2prKGtr6+wtLGwtKenrKClpJObmY+VlYqUk4uVk5GamKyvrt/Y2KGhmZGRkJOOjXFubFxaVVpVU1ddX3t8fpqgoL25usTBwbnBwLG4t6SfpJWQlZKLkY2LkI6PlJSanZymqaaqrayssKSrrqClqJmfnpGdmI+fmZSgm6Glo7m8uNrU0uDY15yUk4uIhnp1dENCQUpDRFdKUFJXV35+gJqZm6CfoZ2dnoCHiYGBhoJ9gn56gHt4fXN5fXN+goCJipWanLCusr26tra8tqywsqyusLW0sby6tsC/u8zGxMnNx8bKx8bMyJWKiIl3eFdPTjc1NEE4OUk/Q0Y+RGBbYnh7gXd8gHmChHd+gnh3fHBxdWNnaWBiZl5iZmRoanZ0eYiDjJyVnqulpaahoJ6WoaGZpK2iq7WorcK0tsS8u8DAvrq7uru8upOAgHZkZjMtLS8rKzYxMj03OjkxOE1CSm5vcm1zeGh0d2lxdWptcGNmalhcYFFWWVBVWFVaXmdobHh0foaFj5OOl5KPloqHkI2IkpWPmKCUnqmepKympq6trKqurKuwrY96e2RYVyMiISooJzQvMTUwNDEtMTcvN0RBR2hsb2hzdWhwdGZrbWBkaFFZWkhSVEVPUkpQUlNYXWJkbXV3gIKCioKBiYKAiYCAiIWEjY2KlJaTmJyamaOhoKKfpaSiqIR1dmRYWCIiIjk1NEpCQ0lCREA6PjkzOjEsMkA/RHZ5fG12eWtwcl9kaFJbXkdRU0RPUEdNT0VOVlhdZm5weX58hYF+hn18hIB9hYOCioiGj4+LlZGOlpWRmpuTnZmSm3ZxcnFrbC8mKEU2Ol9NVGdXX2FYW1BUVjlDRzhESG13emp5enF4e291d25ucGNmaVJYWlJNVkdKUk9WXmJkbHNye317gnt5gXx5goB8hIeDjIuGj5CKk5GKkomEjIF9hXNvcXdvcDQoKzMjKk1AR2JVW3BiaG5rbm5wdGhxdGx2eXh7fYeDhZGDh2ZTWDgsMTYvNVVPV01KVE5RWFVbZGdqcHNze3h3fnt6gX58g4J+hoOAh4aCiYF9hXd1fHZ0fHNvcH1ydTw0NiIaHzsvNlRIT2FYXWxnanBvcW9ydXZ2ent7fXVvcjQzNhULEAoAAA4IBzEtMlBOVktPVlFYX2Jka21tdW5wdnV0fHZ1fHV0e3d2fnV0e3Nzem5vdm5udnFucHlwcVFERhsTGCkgJTs1O0RCR1FPU1pVWGFbX21kaHVqbkNCRiInKRAOEQUEBAYHBhwjI05GUElETlBOVltXYHdud3htd3FpcXFpcXVxeXNzenNyeXBxeW5vdmxtdW1rbHBubl5XWA8PEhgVFiUfIy4sMTk3Oz47P0hBRldPVHVqbm9naiUrLRUUGRMRFRQUHTA0O0lBSz48RERFTVhWX2dhamZeZ11ZYmxkbXFudWttdGxtdGlrcWRpbmZqb2ppaHBubnNubg8bGxAPDBUPEh4XHSQiJy8lKjcrLzo2OlBJTWxiZnBnaVhUWEVBSkRGTk5GTz43QTk0PT4+R0tJUVtUXVNKU1FNVGZdZGpiam1kbW1mbmpkbGlja2dkbG1rbG1sbHNycjs8PgoMDRALDxINEhQSGBsUGxgVHR4cICwpLUA5PUpDSFBGTlNIU0Y+STgxPDAsNTAsNjw4QUtFTUlASj49RkNDS1RSWlhXXl1aYl9bY2BcZF9cZFxcZG9tbWtqanRwcXFnaQ8RFgsJDhALDg8LEwsOGg0PGhYTGh8UHyUVIiUcJiEhKickLSEgKSMhKickLSwpMzUwOjw3PzYzOzc1PTo7QkRHTlBQV1FSWFRUWlVUW1hXXlpWYHZydHBtbnFucH10dltVWgANDwYMDwgMFAkLGAgQGhQTGx0THR8QHB4WIBoaIxscJRwdJh0dJh8gKCgmLy4rNCUlLSUoMDAsNTEvODQ7QUBDS0NGTkZIUEZIT0hKUU1KU3ZzdHBtbnBubnFwb3NxcUdHTA4SFggOEQgPEQgOEgsQFg8RHBESHBMWIBcaIxgaIxsbIxwcJR4eJyMjKyknMCAgKSAhKCgmLysqMicuNC42Ozg4Pz0+RD4+RkNCTkJBT3t3eHRwcnBub3FwbnNxcXRxclJVWA8XGggOEggOEQ0QFg8SHBETHRETHRQWIBQWIBgcIxwdJBwdJB4fJx4fJhscIx0fJh0eJiAgKCUjKy8rNDYxOjk0PTg0OzM4PjM3PoJ3eXx4eHZycnNwcHNwb3Nwb3NxcGBiYhEgHgcVFgsTGRASHRIUHRMVIBcYIhocIxsdJRweJRweJRsdJBseJSAfKCAgKB0eJh0eJiAfJyMhKDEtNjk0PDcxOjMxOS4tNoN5en57fHl0enZxdHRxcXRxcHRycHl1dm1qbi04OBEaHg8SHBETHRQWIBocIxwdJBweJhwfJh0gJx4gKB8gKB8gKB8gKB8gKB8gJx4fJh8gKCkoMDEuNzAuNiIsMiErMYV7fIN9foR9fnx4eXh1dndzdHZzcnl1dHhzdnZzclRUVxseJxMWIBYYIhgcIxwdJR0eJR0gJx8gKCAhKSEiKSEiKSEiKCEiKCEiKSEiKSEiKSEiKicmLycnLyAoLR8oLX10dn55e4N9fXp1dnh0dXh0dXdzcXVycnVxcnRubnBqcmFgaUFETSImLRocIxweJhwfJR4fKB4gKBwdJhweJh0fJyIkKyIkKiEjKiEjKh8hKCAiKCMkKiIkKiQlLCQlLGpkZW5tbHVycXRxcW9tbXBubm1samlpaGtpamxnaWtjbWZjbGFgaVxVXkI8RCQkLB8gKB8gKR8hKBweJh0eJx0fJxweJh8hKCEiKh8hKRwdJRwdJh8gKB4gKCIjKiIjKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"><title>Vilma the Vaquita Demo</title><style type="text/css">body { - /* background:pink; */ - /* background: #69B2FF; */ - /* background: #21AFF8; */ - /* background: #0286E8; */ - /* background: #1096EE; */ - background:black; - text-align: center; - font-size: 22px; - font-family: Helvetica; - color:white; - color:rgba(255,255,255,0.9); - margin:0; -} -.banner { - border: 5px solid white; - border: 5px solid white rgba(255,255,255,0.9); - box-shadow: 0 2px 4px blue; - margin: 1em; -} -footer, p { - margin-top:0.418em; - margin-bottom:0.418em; - margin-left:auto; - margin-right:auto; - text-shadow: 0 1px 1px blue; -} -p { - width:22em; - max-width:100%; -} -a { - /* - color:rgb(200,255,255); - color:rgba(200,255,255,0.9); - */ - color:white; - color:rgba(255,255,255,0.9); - text-decoration:none; - display: inline-block; - border: 1px solid white; - padding: 0 0.2em; - border-radius: 0.2em; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -ie-border-radius: 0.2em; -} -a:hover { - background-color:rgba(20,70,180,1.0); -} -.petition { - margin:0.418em; - padding:0.618em; -} -.petition a { - font-size:127.2%; - box-shadow: 0 2px 4px blue; - margin:0.3em; -} -.page { - width: 100%; - height: 100%; - margin: 0; - border: 0; -} -.centering { - display: table; - padding: 0; -} -.centered { - display: table-cell; - vertical-align: middle; - text-align: center; -} -.inline-block { - display: inline-block; -} -.dynamic-section { - display: inline-block; - vertical-align:middle; - max-width:100%; -} -.flip-lr { - -moz-transform: scaleX(-1); - -o-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - transform: scaleX(-1); - filter: FlipH; - -ms-filter: "FlipH"; -} -image, .pixelart { - image-rendering:optimizeSpeed; /* Legal fallback */ - image-rendering:-moz-crisp-edges; /* Firefox */ - image-rendering:-o-crisp-edges; /* Opera */ - image-rendering:-webkit-optimize-contrast; /* Safari */ - image-rendering:optimize-contrast; /* CSS3 Proposed */ - image-rendering:crisp-edges; /* CSS4 Proposed */ - image-rendering:pixelated; /* CSS4 Proposed */ - -ms-interpolation-mode:nearest-neighbor; /* IE8+ */ -} -/* -.pixelart { - image-rendering: -moz-crisp-edges; - -ms-interpolation-mode: nearest-neighbor; - image-rendering: pixelated; - image-rendering: crisp-edges; -} -*/ -g.flipped { - transform:scale(-1,1); -} -.dim { - opacity: 0.2; -} -.dim:hover { - opacity: 1; -}</style></head><body><div class="centering page"><div class="centered"><div style="visibility:hidden;position:absolute"><img id="pixyvaquita" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAFWElEQVRYw+2WX0gcVxTGv7l3Zmd2drs6aHaNW7Jd88cYDaQYI1jsQ5GSh1BCAhUsNQ/JWxT6lLKExISCQkBECoW+6IMJEknaIEKQbKXJ5iFY6mKsEhOkk6qbRTe67szs7M7szvQhydKWhibEbQn1B/M2557z3e/ecy6wxRZbbLHFFv8hZ86ccYVCIe/rxkUiEXckEnFvVh3M6wZMTk7un56eHkkkEnsJIdA0DZRSuFwumKbZqSjKRHd399zL4sfHx9l4PL7TMIw5AGR4eLh0YmJi402FkFf9cXZ2tv7SpUv2zZs378fj8b2WZcE0TRDybAnTNKHr+tcOh2N2ZGTk4Iu4vr6+P22Wqqo1uVzuQSwWI4uLi5AkqfJlOdva2pj+/v7tFy5cIJviiKIort7eXlUURei6DgAwDAMOhwPZbBZPnz5FSUkJBEGAruvgOA48z8Plcj14/Pjx516vVxcEQVlZWflsx44d3clkEplMBrZtI5PJHPR4PFlK6dK5c+eSf8z76NGjytHR0eXa2tomlmUftLS0rL+RkBs3bhycn5//CQCSySSOHDkCv98Py7JAKYWu68hkMlhYWMDDhw8Rj8fhcrlg2zYMw/hZkiSNYRhKCPlAVdWCYJZlQSlFPp/v4jhOIYT8ePbs2WgikRB7enp2+ny++06nE36/v/7YsWNTb+RIOByWotHoGsuyiMViOH36NAAgn8+jo6MDtm2jv78fDMNA0zRYloV79+4hlUpBUZRnSRgGhmGgpKQEAGDbNhiGQT6fRz6fh2VZ4DgOlFIQQpDNZpFOpwEAHo8HlmXBtm2Iong1n89fDYVC3/+1TvYV7saHuVwO2WwW5eXlAACHw4HW1lbcuXMHPM/j1KlTGBoawp49exCNRtHc3AyPx4PBwUGkUinkcjmwLItkMlkQJkkSqqurwTAM5ubmkMlkYJomTNOEYRiglKK6uhqGYUCSJDyvoRXARwC+f+3LnkqlPgUAXddx9OhRiKIIlmVhWRbKysrgdrsLDk1PT0MQBFBKwXEcgsEgGOaZ6S922TAMAEB9fT14nocoimhsbIQoiuB5HoIgYNu2bdi1a1e/0+n8ZPfu3R9TSn1ra2s+TdPe6+jo+NtW/4+OpNNpORaLoaurC6ZpYnBwEMePH8fly5fR3t4OALh+/ToAgFIKy7KgqipUVUVjYyMURcHU1FTh6NTW1sLr9SKdToNhmMJXUVGBRCIBp9MJSmnHtWvXvr1y5Qr8fn9uU+ZIZ2enPT4+DgCQZRkcx2FmZgYcxxXa7vLyMu7evVvoZsFgEA0NDSCEYGNjA4QQyLIM27YLR0sQhMIMMgwDhJBveJ7/cnJy0urp6Ulv+kA8fPjwFwsLC30Mw2BoaAiBQAC6rmN0dBTr6+sQRRGlpaU4cOAATNPErVu3CgUTQlBTUwOv1wtKKTRNW2ZZNg7gt1QqtSxJ0jwh5LtcLsd7vd4Vl8uVraqqyhVlssuyLLa3t2vz8/M4f/48kskkDMMAx3EIBAJwOp3w+XyFbmOaJm7fvo2ysjJwHAfTNFFXVwdd139YXV39KhgMZgkhv3IcpzY3N2v/+hPl4sWL72ua1pvNZgcAlFNK4Xa76wghw5ZlCYqirFZWVpY8efLEcfLkybFIJAJVVV9M/4YTJ078UlFRkSnWm48pxqLhcHg/y7L3l5aWsLi4CAAIhUIMiggpxqItLS0zPp8PoiiC4zgYhoGBgQHurRMCADzPv7tv3z4cOnQI8Xh8ezgctt9KIVVVVctjY2PvpFKpMlmW12VZLqqQouP3+5m2tjbS1NREi5mHLbaQQCCA52683Y5s8X/ld5pkXmGj97LmAAAAAElFTkSuQmCC"><img id="pixyvaquita_105" src="data:image/png;base64,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"><img id="pixyvaquita_twist_l" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAE7UlEQVRYR+1XSUikRxitdtd2R5S4xCUQ4zLg0KMectFDlIAQRFAMRMhFERUGEcUEFwRPKokHifEQBQ2BVqOI4EpQkaDEBZdRW6LIgCLuW9vunfcq9jAHm14Sh2SwoKj+ra+q3vveq69QId6TpnhPeIgnIv81JZ8UeVLkkTLwZC1DYouKipT4raytrd0xN9nNzc1OSqVSkZ6erjN3jak4ixUZHR19trKyoj44OPjExsZGaLVaYWtrKwBMXF9fF+zt7f1WV1e3ZOxgtVpte3x8HHt5ealobW19NTk5eWIKpDnzZhOZn59X9ff3T+l0OkEC7Hq9Xtzc3AiFQiEcHByEYS48PDw2MzNz6h4Az9AbwHR2doZtbm4ug/zt8PCwqqura9kIUEVlZaUb5s4w3pkiYxaR7e1tZWNj45mLi4sEy3Z1dSXBI7Nif39feHh4CCcnJzlvb28vHB0dqdLKxsbGV15eXjr8PoWKX4eFhaWB/DNnZ2cxNzf3wtvb+xJAF9E90Y8MgJEkm/b2dgXOdN/d3dWdnp7+joTpCwoKVA+RMotIR0fHi7W1tT+4wdHRkUhJSREBAQHi7u5O2orgLy4uBGLE6uqqAHFpNSoGwtOenp5aKIhQ209hP0kSwOQaJKOiurq6Cv0zfO/l5uZqYM0Psf+Ou7u7PxKl5rnY6zI/P/+5MWVMEunt7fVaXl4+sLOzE1tbWyIvL0/udXt7K7CxBFtfXy/txftCchMTE+Lk5ESCZeMcFaRq96BEUFCQjIVicqSK7FT4/Pxc+Pv7y2TgPpHsa5D/HAlah2oXVilSU1PzBazQTeA8KCMjQ1qK49jYmMxufHy8wMUVISEhYnZ2VqqEbApUJ0mI94iJ4N9JKiIiQs4bigUSJRMTHBwsyZ6dncl4EnJ1dWVCVnD2l4hfzMnJubaKSFVV1c/chJtnZWUJ+F3uk5qaKgiALSoqSrS0tEgAJMkMw05iaGhIoEiwmkmgbHFxcXKOJBjHdnh4KBPCb9oUKqvx3YK9WBRc8PsW4w4sfWi1tUpLS6thqW8qKiokoO7ubpGWliazS2JsqERyZAYJhoQMwFCZxMzMjFSTa5KSkt7YjTH8G+1D68Gar6DejyjRPyQkJNyZU60MxEzeEVQJ/cDAgIynnwloYWFBjmwkh3IqxsfH5TcBhYaGitjYWJl1guTItbxPtBarm+H9YTzmv0fct9PT09dNTU0PWseYEmYTSU5Ofrm+vv4dAfAe0MeUv6enR1qCJZlWiYmJkaRoJwJmIwHeB19fX5l5eH0TdlGi8rXBohoo9yv2dcSeOxqN5gIv/d/+s6KZVGRqasqlsLBQi4NEeXm5LL/MIhUhKb4Hfn5+EjQrDtoR7OTJeV5ojiQYHR1Ny82AeBnehVHYUmsFXqNLTBIxrCwrK3uOOl8HEj/Bxz70NypKNOZ/QXeCOruBgYEeJSUlQ7jgfw4ODn5EEvevvQpWsoHvFxMTEx8sn/+UlNlELDloaWlJ09bW9jErHMuvm5ubqri4eMaSPSyNfRQiDQ0NWygAH/BesOziYVP5+PgsGHsDLAX9UPyjEOFB2dnZV7CaPcqxChVrC31vZGTk5t8A/U6J8DC8A66RkZEOeOG1fX19JGF1VTKVgEdT5K2DeYah/6+JkNPbCXvzv4mpLFsy/y4UsQSP1bFPRKxO3SMt/AuU53c9zBm5QgAAAABJRU5ErkJggg=="><img id="pixyvaquita_twist_r" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAYAAABuUU38AAAFFklEQVRYR+1XfSiuZxy+fX+85vtj5jstyrGZM/OfUco/1pKVrOYvoZBNdHLKZ+2PxTrJWl7TGJ2thpFMRCtG1N7DIp/FxOGIfHvx+tx13bynkxjve9A6uevped7nuX+/+7p+1/W7714D8YYMgzeEh7gn8n9T8l6Re0VuqQL31tIWNisrS4FnRUlJyZIuxR4ZGTGtq6s7LCgoONYl7rK5OivS1dUVOD4+/uvq6qq/oaGhUKvVwsjISCgUCnFwcJC+sLDwR1lZ2ehlCwK4Mb4Zu7q62hkbGxtUVlau9fX17b4umWsTGRoaetjW1qba3d0VJMDr5OREHB4eCgMDA2Fqaiq03/z8/ELi4+NVZ+C4xokWaE1NjWJzc9MxMjIyZGVl5TMHB4c5f3//7PNE5ubmLPhucnLSvre3NxbrWBQVFX3zWoosLi4qysvLty0tLSVYjv39fQleo9EIABI2NjbC3NxcfjcxMRFmZmZUaXxmZuYLOzu7XTxvQcV3PD09P/L29k4bGxt7d3l5man6kDcJ9+dQa50vSMLd3f0oJSXl4+Pj4wh7e/uvkE9ZWFj4GEXbuYjMtRSpr6//cGpq6i8mWF9fF9HR0cLNzU1gEWkrgt/b2xOYwwoKEJdWo2Ig/MzW1lYNBY3Cw8N/BrFHo6OjnojRIN4MxWhGDiUAPktISCDIt/DbhZaFegOwnxqFOXJ2do6Pior6E+pt6UWkpaXFDtVbRUIB/4vU1FSZ5+joSKSlpUmwpaWl0l5cnOT6+/sF7CO2tk7X5DcqSJWoGufxN99TZcaAUA1/c521tbUE5uV7a2vrVaT4PTg4OBP2NkIfbkC5vfNkrlSkuLj4U/izicBpmbi4OGkp3ru7uyW40NBQUVtbK2AZMTg4KFUCAFFVVSUJsY8IkH1FsLyYy8LCYpiAOTY2NgK5hta2zOHl5SXm5+dJ9gXsVY9vs4iZxDxVenr6wqtkriSCBnuKRT/f3t4WkF7A7zI+JiZGQCn5HBAQIKqrqwXnkCQrCTuJjo4OgSpyN5MKcpAQiWDXGqYqLi4ubmj45wMDA++RKEmzOOi5UkdHRyXU+QTvInE9RG4NLDsN8j8kJyf/pBORnJycr2Gpx/n5+RJQU1OTiI2NlVUnMY6GhgZ539nZkSRIiGD43NnZKQBSKsAYDqoFoMNOTk6B/I3nRSj39pnFGJ+GvlSipwRsdJiZmfk+SDv4+vraI88civIiIyNjVicikPCkvb1dxqBRJaDh4WF55yA5yt/T0/PSFj4+PiIkJERaCZaRd8ZqbURFtL1CclQGc77H9QjEjysqKi7cmV4Ffv75Smthp/hyenr6CRdnH9C33KWam5vZlLJZaaOgoCBJinbSAtb2RFhYmFQDVZ+HUosAMYvdbx42nUD1f0NuM+Rdamxs1FCB/wJ82bcriahUKktIq56YmBB5eXly+2UFqQhJofnoc1l1nikks7S0JDw8PARObGkvXhgnmBcBtTQ4P/6BitvZ2dlqfUBfFHMlEW1Qbm7uBzgrvgWJH3E5koCVldUDfP8FlznUWcb7CqhxjApbopkfREREHLS2tlLBQ24SONBO/XgL49pErrN2UlLSU5CIBJm/4fMoxmDXmoJaezwA0TfB18mjz5wbJZKYmJgA0N/xlFYqlRH6ANI35kaJEMQZGQco8kRfUPrE3TgRfUDcRMxdEOEa2uv0eL+FcVdEtNBf/i+5aS53QeSmMV+Y757InZRZh0X+BYR+fj33yDXSAAAAAElFTkSuQmCC"></div><div style="position:relative"><svg id="sea-svgroot" width="960" height="720" style="position:absolute;opacity:0.9;z-index:-1000"><defs><linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:1"></stop><stop offset="25%" style="stop-color:rgb(100,200,250);stop-opacity:1"></stop><stop offset="50%" style="stop-color:rgb(0,80,240);stop-opacity:1"></stop><stop offset="75%" style="stop-color:rgb(0,0,180);stop-opacity:1"></stop><stop offset="100%" style="stop-color:rgb(0,0,0);stop-opacity:1"></stop></linearGradient></defs><rect x="0" y="0" width="960" height="720" fill="url(#grad1)"></rect></svg><canvas width="960" height="720"></canvas></div><footer class="dim"><span>Vilma the Vaquita Demo v0.1.63</span><span> - </span><a target="_blank" href="index.html">Learn about Vaquitas</a><span id="fps"></span></footer></div></div><script type="text/javascript">gameObjects=null;</script><script type="text/javascript">__hasProp = {}.hasOwnProperty; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };</script><script type="text/javascript">var jaws=function(t){function e(e){t.mouse_x=e.pageX||e.clientX,t.mouse_y=e.pageY||e.clientY;var i=t.canvas?t.canvas:t.dom;t.mouse_x-=i.offsetLeft,t.mouse_y-=i.offsetTop}var i,s;return t.SpriteList=function(){throw"To use SpriteList() you need to include src/extras/sprite_list.js"},t.Audio=function(){throw"To use jaws.Audio() you need to include src/extras/audio.js"},t.title=function(e){return t.isString(e)?e?i.innerHTML=e:i.innerHTML:void t.log.error("jaws.title: Passed in value is not a String.")},t.unpack=function(){var e=["Sprite","SpriteList","Animation","SpriteSheet","Parallax","pressed","QuadTree"];e.forEach(function(e){window[e]?t.log.warn("jaws.unpack: "+e+" already exists in global namespace."):window[e]=t[e]})},t.log=function(e,i){t.isString(e)||(e=JSON.stringify(e)),t.log.on&&(s&&t.log.use_log_element&&(i?s.innerHTML+=e+"<br />":s.innerHTML=e),console.log&&t.log.use_console&&console.log("JawsJS: ",e))},t.log.on=!0,t.log.use_console=!1,t.log.use_log_element=!0,t.log.warn=function(e){console.warn&&t.log.use_console&&t.log.on?console.warn(e):t.log("[WARNING]: "+JSON.stringify(e),!0)},t.log.error=function(e){console.error&&t.log.use_console&&t.log.on?console.error(e):t.log("[ERROR]: "+JSON.stringify(e),!0)},t.log.info=function(e){console.info&&t.log.use_console&&t.log.on?console.info(e):t.log("[INFO]: "+JSON.stringify(e),!0)},t.log.debug=function(e){console.debug&&t.log.use_console&&t.log.on?console.debug(e):t.log("[DEBUG]: "+JSON.stringify(e),!0)},t.log.clear=function(){s&&(s.innerHTML=""),console.clear&&console.clear()},t.init=function(o){i=document.getElementsByTagName("title")[0],t.url_parameters=t.getUrlParameters(),t.canvas=document.getElementsByTagName("canvas")[0],t.canvas||(t.dom=document.getElementById("canvas")),t.canvas?t.context=t.canvas.getContext("2d"):t.dom?t.dom.style.position="relative":(t.canvas=document.createElement("canvas"),t.canvas.width=o.width,t.canvas.height=o.height,t.context=t.canvas.getContext("2d"),document.body.appendChild(t.canvas)),s=document.getElementById("jaws-log"),t.url_parameters.debug&&(s||(s=document.createElement("div"),s.id="jaws-log",s.style.cssText="overflow: auto; color: #aaaaaa; width: 300px; height: 150px; margin: 40px auto 0px auto; padding: 5px; border: #444444 1px solid; clear: both; font: 10px verdana; text-align: left;",document.body.appendChild(s))),t.url_parameters.bust_cache&&(t.log.info("Busting cache when loading assets"),t.assets.bust_cache=!0),t.context&&t.useCrispScaling(),t.width=t.canvas?t.canvas.width:t.dom.offsetWidth,t.height=t.canvas?t.canvas.height:t.dom.offsetHeight,t.mouse_x=0,t.mouse_y=0,window.addEventListener("mousemove",e)},t.useCrispScaling=function(){t.context.imageSmoothingEnabled=!1,t.context.webkitImageSmoothingEnabled=!1,t.context.mozImageSmoothingEnabled=!1},t.useSmoothScaling=function(){t.context.imageSmoothingEnabled=!0,t.context.webkitImageSmoothingEnabled=!0,t.context.mozImageSmoothingEnabled=!0},t.start=function(e,i,s){function o(e,s){t.log.info(s+"%: "+e,!0),i.loading_screen&&t.assets.displayProgress(s)}function n(e,i){t.log.info(i+"%: Error loading asset "+e,!0)}function a(){t.log.info("all assets loaded",!0),t.switchGameState(e||window,{fps:r},s)}i||(i={});var r=i.fps||60;return void 0===i.loading_screen&&(i.loading_screen=!0),i.width||(i.width=500),i.height||(i.height=300),t.init(i),t.isFunction(e)||t.isObject(e)?t.isObject(s)||void 0===s?(i.loading_screen&&t.assets.displayProgress(0),t.log.info("setupInput()",!0),t.setupInput(),t.log.info("assets.loadAll()",!0),void(t.assets.length()>0?t.assets.loadAll({onprogress:o,onerror:n,onload:a}):a())):void t.log.error("jaws.start: The setup options for the game state is not an object."):void t.log.error("jaws.start: Passed in GameState is niether function or object")},t.switchGameState=function(e,i,s){if(void 0===i&&(i={}),t.isFunction(e)&&(e=new e),!t.isObject(e))return void t.log.error("jaws.switchGameState: Passed in GameState should be a Function or an Object.");var o=i&&i.fps||t.game_loop&&t.game_loop.fps||60,n=i.setup;t.game_loop&&t.game_loop.stop(),t.clearKeyCallbacks(),t.previous_game_state=t.game_state,t.game_state=e,t.game_loop=new t.GameLoop(e,{fps:o,setup:n},s),t.game_loop.start()},t.imageToCanvas=function(e){if(t.isCanvas(e))return e;if(!t.isImage(e))return void t.log.error("jaws.imageToCanvas: Passed in object is not an Image.");var i=document.createElement("canvas");i.src=e.src,i.width=e.width,i.height=e.height;var s=i.getContext("2d");return s.drawImage(e,0,0,e.width,e.height),i},t.forceArray=function(t){return Array.isArray(t)?t:[t]},t.clear=function(){t.context.clearRect(0,0,t.width,t.height)},t.fill=function(e){t.context.fillStyle=e,t.context.fillRect(0,0,t.width,t.height)},t.draw=function(){var e=arguments;1==e.length&&t.isArray(e[0])&&(e=e[0]);for(var i=0;i<e.length;i++)t.isArray(e[i])?t.draw(e[i]):e[i].draw&&e[i].draw()},t.update=function(){var e=arguments;1==e.length&&t.isArray(e[0])&&(e=e[0]);for(var i=0;i<e.length;i++)t.isArray(e[i])?t.update(e[i]):e[i].update&&e[i].update()},t.isImage=function(t){return"[object HTMLImageElement]"===Object.prototype.toString.call(t)},t.isCanvas=function(t){return"[object HTMLCanvasElement]"===Object.prototype.toString.call(t)},t.isDrawable=function(e){return t.isImage(e)||t.isCanvas(e)},t.isString=function(t){return"string"==typeof t||"object"==typeof t&&t.constructor===String},t.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.isArray=function(t){return t?!(-1===t.constructor.toString().indexOf("Array")):!1},t.isObject=function(t){return null!==t&&"object"==typeof t},t.isFunction=function(t){return"[object Function]"===Object.prototype.toString.call(t)},t.isRegExp=function(t){return t instanceof RegExp},t.isOutsideCanvas=function(e){return e.x&&e.y?e.x<0||e.y<0||e.x>t.width||e.y>t.height:void 0},t.forceInsideCanvas=function(e){e.x&&e.y&&(e.x<0&&(e.x=0),e.x>t.width&&(e.x=t.width),e.y<0&&(e.y=0),e.y>t.height&&(e.y=t.height))},t.getUrlParameters=function(){for(var t,e=[],i=window.location.href.slice(window.location.href.indexOf("?")+1).split("&"),s=0;s<i.length;s++)t=i[s].split("="),e.push(t[0]),e[t[0]]=t[1];return e},t.parseOptions=function(e,i,s){e.options=i;for(var o in i)void 0===s[o]&&t.log.warn("jaws.parseOptions: Unsupported property "+o+"for "+e.constructor);for(var o in s)t.isFunction(s[o])&&(s[o]=s[o]()),e[o]=void 0!==i[o]?i[o]:t.clone(s[o])},t.clone=function(e){return t.isArray(e)?e.slice(0):t.isObject(e)?JSON.parse(JSON.stringify(e)):e},t.imageToCanvasContext=function(e){var i=document.createElement("canvas");i.width=e.width,i.height=e.height;var s=i.getContext("2d");return t.context&&(s.imageSmoothingEnabled=t.context.mozImageSmoothingEnabled,s.webkitImageSmoothingEnabled=t.context.mozImageSmoothingEnabled,s.mozImageSmoothingEnabled=t.context.mozImageSmoothingEnabled),s.drawImage(e,0,0,i.width,i.height),s},t.retroScaleImage=function(e,i){var s=t.isImage(e)?t.imageToCanvas(e):e,o=s.getContext("2d"),n=o.getImageData(0,0,s.width,s.height).data,a=document.createElement("canvas");a.width=e.width*i,a.height=e.height*i;for(var r=a.getContext("2d"),h=r.createImageData(a.width,a.height),c=h.width,l=h.height,u=0;l>u;u+=1)for(var d=Math.floor(u/i),f=u*h.width,p=d*e.width,g=0;c>g;g+=1){var m=Math.floor(g/i),w=4*(f+g),x=4*(p+m);h.data[w]=n[x],h.data[w+1]=n[x+1],h.data[w+2]=n[x+2],h.data[w+3]=n[x+3]}return r.putImageData(h,0,0),a},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws);var jaws=function(t){function e(){for(var t in h)delete h[t]}function i(t){var e=e=t.keyCode;h[e]=!1,d[e]?(d[e](e),t.preventDefault()):g[e]&&t.preventDefault()}function s(t){var e=e=t.keyCode;h[e]=!0,u[e]?(u[e](e),t.preventDefault()):g[e]&&t.preventDefault()}function o(t){var e=f[t.button];"Microsoft Internet Explorer"==navigator.appName&&(e=p[t.button]),h[e]=!0,u[e]&&(u[e](e),t.preventDefault())}function n(t){var e=f[t.button];"Microsoft Internet Explorer"==navigator.appName&&(e=p[t.button]),h[e]=!1,d[e]&&(d[e](e),t.preventDefault())}function a(e){h.left_mouse_button=!0,t.mouse_x=e.touches[0].pageX-t.canvas.offsetLeft,t.mouse_y=e.touches[0].pageY-t.canvas.offsetTop}function r(){h.left_mouse_button=!1,t.mouse_x=void 0,t.mouse_y=void 0}var h={},c={0:"48",1:"49",2:"50",3:"51",4:"52",5:"53",6:"54",7:"55",8:"56",9:"57",backspace:"8",tab:"9",enter:"13",shift:"16",ctrl:"17",alt:"18",pause:"19",caps_lock:"20",esc:"27",space:"32",page_up:"33",page_down:"34",end:"35",home:"36",left:"37",up:"38",right:"39",down:"40",insert:"45","delete":"46",a:"65",b:"66",c:"67",d:"68",e:"69",f:"70",g:"71",h:"72",i:"73",j:"74",k:"75",l:"76",m:"77",n:"78",o:"79",p:"80",q:"81",r:"82",s:"83",t:"84",u:"85",v:"86",w:"87",x:"88",y:"89",z:"90",windows_left:"91",windows_right:"92",select:"93",numpad0:"96",numpad1:"97",numpad2:"98",numpad3:"99",numpad4:"100",numpad5:"101",numpad6:"102",numpad7:"103",numpad8:"104",numpad9:"105",asterisk:"106",plus:"107",minus:"109",decimal_point:"110",divide:"111",f1:"112",f2:"113",f3:"114",f4:"115",f5:"116",f6:"117",f7:"118",f8:"119",f9:"120",numlock:"144",scrollock:"145",semicolon:"186",equals:"187",comma:"188",dash:"189",period:"190",slash:"191",grave_accent:"192",open_bracket:"219",backslash:"220",close_bracket:"221",single_quote:"222"},l={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"caps_lock",27:"esc",32:"space",33:"page_up",34:"page_down",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"delete",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",91:"windows_left",92:"windows_right",93:"select",96:"numpad0",97:"numpad1",98:"numpad2",99:"numpad3",100:"numpad4",101:"numpad5",102:"numpad6",103:"numpad7",104:"numpad8",105:"numpad9",106:"asterisk",107:"plus",109:"minus",110:"decimal_point",111:"divide",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",144:"numlock",145:"scrollock",186:"semicolon",187:"equals",188:"comma",189:"dash",190:"period",191:"slash",192:"grave_accent",219:"open_bracket",220:"backslash",221:"close_bracket",222:"single_quote"},u=[],d=[],f=[],p=[],g=[];return t.setupInput=function(){var h=[];h[0]="left_mouse_button",h[1]="center_mouse_button",h[2]="right_mouse_button";var c=[];c[1]="left_mouse_button",c[2]="right_mouse_button",c[4]="center_mouse_button",f=h,p=c,window.addEventListener("keydown",s),window.addEventListener("keyup",i);var l=t.canvas||t.dom;l.addEventListener("mousedown",o,!1),l.addEventListener("mouseup",n,!1),l.addEventListener("touchstart",a,!1),l.addEventListener("touchend",r,!1),window.addEventListener("blur",e,!1),document.oncontextmenu=function(){return!1}},t.preventDefaultKeys=function(){for(var t=arguments,e=0;e<t.length;e++)g[t[e]]=!0},t.pressed=h,t.keyCodes=c,t.keycodeNames=l,t.on_keydown=function(e,i){if(t.isArray(e))for(var s=0;e[s];s++)u[e[s]]=i;else u[e]=i},t.on_keyup=function(e,i){if(t.isArray(e))for(var s=0;e[s];s++)d[e[s]]=i;else d[e]=i},t.clearKeyCallbacks=function(){d=[],u=[]},t}(jaws||{}),jaws=function(t){function e(e){if(t.isDrawable(e)){for(var i=t.isImage(e)?t.imageToCanvas(e):e,s=i.getContext("2d"),o=s.getImageData(0,0,i.width,i.height),n=o.data,a=0;a<n.length;a+=4)255===n[a]&&0===n[a+1]&&255===n[a+2]&&(n[a+3]=0);return s.putImageData(o,0,0),i}}return t.Assets=function(){function i(e){if(t.isString(e)){var i=r.getPostfix(e);return r.file_type[i]?r.file_type[i]:i}t.log.error("jaws.assets.getType: Argument not a String with "+e)}function s(s){var o=this.asset,n=o.src,h=i(o.src);try{if("json"===h){if(4!==this.readyState)return;r.data[o.src]=JSON.parse(this.responseText)}else if("image"===h){var c=r.image_to_canvas?t.imageToCanvas(o.image):o.image;r.fuchia_to_transparent&&"bmp"===r.getPostfix(o.src)&&(c=e(c)),r.data[o.src]=c}else r.data[o.src]="audio"===h&&r.can_play[r.getPostfix(o.src)]?o.audio:"video"===h&&r.can_play[r.getPostfix(o.src)]?o.video:this.response}catch(l){t.log.error("Cannot process "+n+" (Message: "+l.message+", Name: "+l.name+")"),r.data[o.src]=null}r.loaded[n]||r.load_count++,r.loaded[n]=!0,r.loading[n]=!1,a(o,!0,s)}function o(t){r.loaded[t.src]=!0,r.loading[t.src]=!1,r.load_count++,a(t,!0)}function n(t){var e=this.asset;r.error_count++,a(e,!1,t)}function a(t,e,i){var s=parseInt((r.load_count+r.error_count)/r.src_list.length*100);e?(r.onprogress&&r.onprogress(t.src,s),t.onprogress&&void 0!==i&&t.onprogress(i)):(r.onerror&&r.onerror(t.src,s),t.onerror&&void 0!==i&&t.onerror(i)),100===s&&(r.onload&&r.onload(),r.onprogress=null,r.onerror=null,r.onload=null)}if(!(this instanceof arguments.callee))return new arguments.callee;var r=this;r.loaded=[],r.loading=[],r.src_list=[],r.data=[],r.bust_cache=!1,r.image_to_canvas=!0,r.fuchia_to_transparent=!0,r.root="",r.file_type={},r.file_type.json="json",r.file_type.wav="audio",r.file_type.mp3="audio",r.file_type.ogg="audio",r.file_type.m4a="audio",r.file_type.weba="audio",r.file_type.aac="audio",r.file_type.mka="audio",r.file_type.flac="audio",r.file_type.png="image",r.file_type.jpg="image",r.file_type.jpeg="image",r.file_type.gif="image",r.file_type.bmp="image",r.file_type.tiff="image",r.file_type.mp4="video",r.file_type.webm="video",r.file_type.ogv="video",r.file_type.mkv="video",r.can_play={};try{var h=new Audio;r.can_play.wav=!!h.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),r.can_play.ogg=!!h.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),r.can_play.mp3=!!h.canPlayType("audio/mpeg;").replace(/^no$/,""),r.can_play.m4a=!!(h.canPlayType("audio/x-m4a;")||h.canPlayType("audio/aac;")).replace(/^no$/,""),r.can_play.weba=!!h.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),r.can_play.aac=!!h.canPlayType("audio/aac;").replace(/^no$/,""),r.can_play.mka=!!h.canPlayType("audio/x-matroska;").replace(/^no$/,""),r.can_play.flac=!!h.canPlayType("audio/x-flac;").replace(/^no$/,"")}catch(c){}try{var l=document.createElement("video");r.can_play.mp4=!!l.canPlayType("video/mp4;").replace(/^no$/,""),r.can_play.webm=!!l.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/,""),r.can_play.ogv=!!l.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/,""),r.can_play.mkv=!!l.canPlayType("video/x-matroska;").replace(/^no$/,"")}catch(c){}r.length=function(){return r.src_list.length},r.setRoot=function(t){return r.root=t,r},r.get=function(e){if(t.isArray(e))return e.map(function(t){return r.data[t]});if(!t.isString(e))return void t.log.error("jaws.get: Neither String nor Array. Incorrect URL resource "+e);if("*"===e[e.length-1])for(var i=e.replace("*",""),s=0;s<r.src_list.length;s++)if(0==r.src_list[s].indexOf(i)&&r.data[r.src_list[s]])return r.data[r.src_list[s]];return r.data[e]?r.data[e]:void t.log.warn("No such asset: "+e,!0)},r.isLoading=function(e){return t.isString(e)?r.loading[e]:void t.log.error("jaws.isLoading: Argument not a String with "+e)},r.isLoaded=function(e){return t.isString(e)?r.loaded[e]:void t.log.error("jaws.isLoaded: Argument not a String with "+e)},r.getPostfix=function(e){return t.isString(e)?e.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1]:void t.log.error("jaws.assets.getPostfix: Argument not a String with "+e)},r.add=function(e){var i=arguments;1==i.length&&t.isArray(i[0])&&(i=i[0]);for(var s=0;s<i.length;s++)t.isArray(i[s])?r.add(i[s]):t.isString(i[s])?r.src_list.push(i[s]):t.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource "+e);return r},r.loadAll=function(e){return r.load_count=0,r.error_count=0,e.onprogress&&t.isFunction(e.onprogress)&&(r.onprogress=e.onprogress),e.onerror&&t.isFunction(e.onerror)&&(r.onerror=e.onerror),e.onload&&t.isFunction(e.onload)&&(r.onload=e.onload),r.src_list.forEach(function(t){r.load(t)}),r},r.load=function(e,a){if(a||(a={}),!t.isString(e))return void t.log.error("jaws.assets.load: Argument not a String with "+e);var h={},c="";h.src=e,h.onload=a.onload,h.onerror=a.onerror,r.loading[e]=!0;var l=RegExp("^((f|ht)tp(s)?:)?//");c=l.test(e)?h.src:r.root+h.src,r.bust_cache&&(c+="?"+parseInt(1e7*Math.random()));var u=i(h.src);if("image"===u)try{h.image=new Image,h.image.asset=h,h.image.addEventListener("load",s),h.image.addEventListener("error",n),h.image.src=c}catch(d){t.log.error("Cannot load Image resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}else if(r.can_play[r.getPostfix(h.src)]){if("audio"===u)try{h.audio=new Audio,h.audio.asset=h,h.audio.addEventListener("error",n),h.audio.addEventListener("canplay",s),r.data[h.src]=h.audio,h.audio.src=c,h.audio.load()}catch(d){t.log.error("Cannot load Audio resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}else if("video"===u)try{h.video=document.createElement("video"),h.video.asset=h,r.data[h.src]=h.video,h.video.setAttribute("style","display:none;"),h.video.addEventListener("error",n),h.video.addEventListener("canplay",s),document.body.appendChild(h.video),h.video.src=c,h.video.load()}catch(d){t.log.error("Cannot load Video resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}}else{if("audio"===u&&!r.can_play[r.getPostfix(h.src)])return o(h),r;try{var f=new XMLHttpRequest;f.asset=h,f.onreadystatechange=s,f.onerror=n,f.open("GET",c,!0),"json"!==u&&(f.responseType="blob"),f.send(null)}catch(d){t.log.error("Cannot load "+c+" (Message: "+d.message+", Name: "+d.name+")")}}return r},r.displayProgress=function(e){t.isNumber(e)&&t.context&&(t.context.save(),t.context.fillStyle="black",t.context.fillRect(0,0,t.width,t.height),t.context.fillStyle="white",t.context.strokeStyle="white",t.context.textAlign="center",t.context.strokeRect(49,t.height/2-30-1,t.width-100+2,62),t.context.fillRect(50,t.height/2-30,(t.width-100)/100*e,60),t.context.font="11px verdana",t.context.fillText("Loading... "+e+"%",t.width/2,t.height/2-35),t.context.font="11px verdana",t.context.fillStyle="#ccc",t.context.textBaseline="bottom",t.context.fillText("powered by www.jawsjs.com",t.width/2,t.height-1),t.context.restore())}},t.assets=new t.Assets,t}(jaws||{});if("undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){function e(t){this.size=t,this.values=new Array(this.size),this.value,this.add=function(t){if(this.values.length>this.size){this.values.splice(0,1),this.value=0;for(var e=0;this.values[e];e++)this.value+=this.values[e];this.value=this.value/this.size}return this.values.push(t),this},this.get=function(){return parseInt(this.value)}}return window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,16.666)}}(),t.GameLoop=function(i,s,o){if(!(this instanceof arguments.callee))return new arguments.callee(i,s);this.tick_duration=0,this.fps=0,this.ticks=0;var n,a=!1,r=!1,h=this,c=new e(20);this.runtime=function(){return this.last_tick-this.first_tick},this.start=function(){t.log.info("Game loop start",!0),this.first_tick=(new Date).getTime(),this.current_tick=(new Date).getTime(),this.last_tick=(new Date).getTime(),s.setup!==!1&&i.setup&&i.setup(o),step_delay=1e3/s.fps,60==s.fps?requestAnimFrame(this.loop):n=setInterval(this.loop,step_delay)},this.loop=function(){h.current_tick=(new Date).getTime(),h.tick_duration=h.current_tick-h.last_tick,h.fps=c.add(1e3/h.tick_duration).get(),r||a||(i.update&&i.update(),i.draw&&i.draw(),h.ticks++),60!=s.fps||r||requestAnimFrame(h.loop),h.last_tick=h.current_tick},this.pause=function(){a=!0},this.unpause=function(){a=!1},this.stop=function(){n&&clearInterval(n),r=!0}},t}(jaws||{}),jaws=function(t){var e=0;return t.Rect=function(t,i,s,o){return this instanceof arguments.callee?(this.x=t,this.y=i,this.width=s,this.height=o,this.right=t+s-e,void(this.bottom=i+o-e)):new arguments.callee(t,i,s,o)},t.Rect.prototype.getPosition=function(){return[this.x,this.y]},t.Rect.prototype.move=function(t,e){return this.x+=t,this.y+=e,this.right+=t,this.bottom+=e,this},t.Rect.prototype.moveTo=function(t,i){return this.x=t,this.y=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.resize=function(t,i){return this.width+=t,this.height+=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.clone=function(){return new t.Rect(this.x,this.y,this.width,this.height)},t.Rect.prototype.shrink=function(t,i){return this.x+=t,this.y+=i,this.width-=t+t,this.height-=i+i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.resizeTo=function(t,i){return this.width=t,this.height=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.draw=function(){return t.context.strokeStyle="red",t.context.strokeRect(this.x-.5,this.y-.5,this.width,this.height),this},t.Rect.prototype.collidePoint=function(t,e){return t>=this.x&&t<=this.right&&e>=this.y&&e<=this.bottom},t.Rect.prototype.collideRect=function(t){return(this.x>=t.x&&this.x<=t.right||t.x>=this.x&&t.x<=this.right)&&(this.y>=t.y&&this.y<=t.bottom||t.y>=this.y&&t.y<=this.bottom)},t.Rect.prototype.toString=function(){return"[Rect "+this.x+", "+this.y+", "+this.width+", "+this.height+"]"},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Rect);var jaws=function(jaws){return jaws.Sprite=function(t){return this instanceof arguments.callee?(this.set(t),void(this.context=t.context?t.context:jaws.context)):new arguments.callee(t)},jaws.Sprite.prototype.default_options={x:0,y:0,alpha:1,angle:0,flipped:!1,anchor_x:0,anchor_y:0,image:null,image_path:null,anchor:null,scale_image:null,damping:1,scale_x:1,scale_y:1,scale:1,color:"#ddd",width:16,height:16,_constructor:null,context:null,data:null},jaws.Sprite.prototype.set=function(t){if(jaws.isString(this.image)&&(this.image_path=this.image),jaws.parseOptions(this,t,this.default_options),this.scale&&(this.scale_x=this.scale_y=this.scale),this.image&&this.setImage(this.image),this.scale_image&&this.scaleImage(this.scale_image),this.anchor&&this.setAnchor(this.anchor),!this.image&&this.color&&this.width&&this.height){var e=document.createElement("canvas"),i=e.getContext("2d");e.width=this.width,e.height=this.height,i.fillStyle=this.color,i.fillRect(0,0,this.width,this.height),this.image=e}return this.cacheOffsets(),this},jaws.Sprite.prototype.clone=function(object){var constructor=this._constructor?eval(this._constructor):this.constructor,new_sprite=new constructor(this.attributes());return new_sprite._constructor=this._constructor||this.constructor.name,new_sprite},jaws.Sprite.prototype.setImage=function(t){var e=this;return jaws.isDrawable(t)?(this.image=t,this.cacheOffsets()):(jaws.assets.isLoaded(t)?(this.image=jaws.assets.get(t),this.cacheOffsets()):(jaws.log.warn("Image '"+t+"' not preloaded with jaws.assets.add(). Image and a working sprite.rect() will be delayed."),jaws.assets.load(t,{onload:function(){e.image=jaws.assets.get(t),e.cacheOffsets()}})),this)},jaws.Sprite.prototype.stepToWhile=function(t,e,i){var s=1,o=t<this.x?-s:s,n=e<this.y?-s:s;t=parseInt(t),e=parseInt(e);for(var a=!1,r=!1;;)if(a===!1&&(this.x!=t&&(this.x+=o),i(this)||(this.x-=o,a=!0)),r===!1&&(this.y!=e&&(this.y+=n),i(this)||(this.y-=n,r=!0)),(a||this.x==t)&&(r||this.y==e))return{x:a,y:r}},jaws.Sprite.prototype.stepWhile=function(t,e,i){return this.stepToWhile(this.x+t,this.y+e,i)},jaws.Sprite.prototype.flip=function(){return this.flipped=this.flipped?!1:!0,this},jaws.Sprite.prototype.flipTo=function(t){return this.flipped=t,this},jaws.Sprite.prototype.rotate=function(t){return this.angle+=t,this},jaws.Sprite.prototype.rotateTo=function(t){return this.angle=t,this},jaws.Sprite.prototype.moveTo=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.x=t,this.y=e,this},jaws.Sprite.prototype.move=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),t&&(this.x+=t),e&&(this.y+=e),this},jaws.Sprite.prototype.scaleAll=function(t){return this.scale_x*=t,this.scale_y*=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleTo=function(t){return this.scale_x=this.scale_y=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleWidth=function(t){return this.scale_x*=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleHeight=function(t){return this.scale_y*=t,this.cacheOffsets()},jaws.Sprite.prototype.setX=function(t){return this.x=t,this},jaws.Sprite.prototype.setY=function(t){return this.y=t,this},jaws.Sprite.prototype.setTop=function(t){return this.y=t+this.top_offset,this},jaws.Sprite.prototype.setBottom=function(t){return this.y=t-this.bottom_offset,this},jaws.Sprite.prototype.setLeft=function(t){return this.x=t+this.left_offset,this},jaws.Sprite.prototype.setRight=function(t){return this.x=t-this.right_offset,this},jaws.Sprite.prototype.setWidth=function(t){return this.scale_x=t/this.image.width,this.cacheOffsets()},jaws.Sprite.prototype.setHeight=function(t){return this.scale_y=t/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.resize=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.scale_x=(this.width+t)/this.image.width,this.scale_y=(this.height+e)/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.resizeTo=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.scale_x=t/this.image.width,this.scale_y=e/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.setAnchor=function(t){var e={top_left:[0,0],left_top:[0,0],center_left:[0,.5],left_center:[0,.5],bottom_left:[0,1],left_bottom:[0,1],top_center:[.5,0],center_top:[.5,0],center_center:[.5,.5],center:[.5,.5],bottom_center:[.5,1],center_bottom:[.5,1],top_right:[1,0],right_top:[1,0],center_right:[1,.5],right_center:[1,.5],bottom_right:[1,1],right_bottom:[1,1]};return(a=e[t])&&(this.anchor_x=a[0],this.anchor_y=a[1],this.image&&this.cacheOffsets()),this},jaws.Sprite.prototype.cacheOffsets=function(){return this.image?(this.width=this.image.width*this.scale_x,this.height=this.image.height*this.scale_y,this.left_offset=this.width*this.anchor_x,this.top_offset=this.height*this.anchor_y,this.right_offset=this.width*(1-this.anchor_x),this.bottom_offset=this.height*(1-this.anchor_y),this.cached_rect&&this.cached_rect.resizeTo(this.width,this.height),this):void 0},jaws.Sprite.prototype.rect=function(){return!this.cached_rect&&this.width&&(this.cached_rect=new jaws.Rect(this.x,this.y,this.width,this.height)),this.cached_rect&&this.cached_rect.moveTo(this.x-this.left_offset,this.y-this.top_offset),this.cached_rect},jaws.Sprite.prototype.draw=function(){return this.image?(this.context.save(),this.context.translate(this.x,this.y),0!=this.angle&&jaws.context.rotate(this.angle*Math.PI/180),this.flipped&&this.context.scale(-1,1),this.context.globalAlpha=this.alpha,this.context.translate(-this.left_offset,-this.top_offset),this.context.drawImage(this.image,0,0,this.width,this.height),this.context.restore(),this):this},jaws.Sprite.prototype.scaleImage=function(t){return this.image?(this.setImage(jaws.retroScaleImage(this.image,t)),this):void 0},jaws.Sprite.prototype.asCanvasContext=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");return jaws.context&&(e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled),e.drawImage(this.image,0,0,this.width,this.height),e},jaws.Sprite.prototype.asCanvas=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");return jaws.context&&(e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled),e.drawImage(this.image,0,0,this.width,this.height),t},jaws.Sprite.prototype.toString=function(){return"[Sprite "+this.x.toFixed(2)+", "+this.y.toFixed(2)+", "+this.width+", "+this.height+"]"},jaws.Sprite.prototype.attributes=function(){var t={};return t._constructor=this._constructor||"jaws.Sprite",t.x=parseFloat(this.x.toFixed(2)),t.y=parseFloat(this.y.toFixed(2)),t.image=this.image_path,t.alpha=this.alpha,t.flipped=this.flipped,t.angle=parseFloat(this.angle.toFixed(2)),t.scale_x=this.scale_x,t.scale_y=this.scale_y,t.anchor_x=this.anchor_x,t.anchor_y=this.anchor_y,null!==this.data&&(t.data=jaws.clone(this.data)),t},jaws.Sprite.parse=function(objects){function parseArray(array){array.forEach(function(data){var constructor=data._constructor?eval(data._constructor):data.constructor;if(jaws.isFunction(constructor)){jaws.log.info("Creating "+data._constructor+"("+data.toString()+")",!0);var object=new constructor(data);object._constructor=data._constructor||data.constructor.name,sprites.push(object)}})}var sprites=[];return jaws.isArray(objects)?objects.every(function(t){return t._constructor})?parseArray(objects):sprites=objects:jaws.isString(objects)&&(parseArray(JSON.parse(objects)),jaws.log.info(objects)),sprites},jaws.Sprite.prototype.toJSON=function(){return JSON.stringify(this.attributes())},jaws}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Sprite);var jaws=function(t){function e(t,e,i,s,o){var n=document.createElement("canvas");n.width=s,n.height=o;var a=n.getContext("2d");return a.drawImage(t,e,i,s,o,0,0,n.width,n.height),n}return t.SpriteSheet=function(i){if(!(this instanceof arguments.callee))return new arguments.callee(i);if(t.parseOptions(this,i,this.default_options),t.isString(this.image)&&!i.frame_size){var s=new RegExp("_(\\d+)x(\\d+)","g"),o=s.exec(this.image);this.frame_size=[],this.frame_size[0]=parseInt(o[1]),this.frame_size[1]=parseInt(o[2])}if(this.image=t.isDrawable(this.image)?this.image:t.assets.data[this.image],this.scale_image){var n=t.isDrawable(this.image)?this.image:t.assets.get(this.image);this.frame_size[0]*=this.scale_image,this.frame_size[1]*=this.scale_image,this.image=t.retroScaleImage(n,this.scale_image)}if(this.frames=[],"down"==this.orientation)for(var a=this.offset;a<this.image.width;a+=this.frame_size[0])for(var r=0;r<this.image.height;r+=this.frame_size[1])this.frames.push(e(this.image,a,r,this.frame_size[0],this.frame_size[1]));else for(var r=this.offset;r<this.image.height;r+=this.frame_size[1])for(var a=0;a<this.image.width;a+=this.frame_size[0])this.frames.push(e(this.image,a,r,this.frame_size[0],this.frame_size[1]))},t.SpriteSheet.prototype.default_options={image:null,orientation:"down",frame_size:[32,32],offset:0,scale_image:null},t.SpriteSheet.prototype.toString=function(){return"[SpriteSheet "+this.frames.length+" frames]"},t}(jaws||{}),jaws=function(t){return t.Animation=function(e){if(!(this instanceof arguments.callee))return new arguments.callee(e);if(t.parseOptions(this,e,this.default_options),e.sprite_sheet){var i=new t.SpriteSheet({image:e.sprite_sheet,scale_image:this.scale_image,frame_size:this.frame_size,orientation:this.orientation,offset:this.offset});this.frames=i.frames,this.frame_size=i.frame_size}if(e.scale_image){var s=t.isDrawable(e.sprite_sheet)?e.sprite_sheet:t.assets.get(e.sprite_sheet);this.frame_size[0]*=e.scale_image,this.frame_size[1]*=e.scale_image,e.sprite_sheet=t.retroScaleImage(s,e.scale_image)}if(this.current_tick=(new Date).getTime(),this.last_tick=(new Date).getTime(),this.sum_tick=0,e.subsets){this.subsets={};for(subset in e.subsets)start_stop=e.subsets[subset],this.subsets[subset]=this.slice(start_stop[0],start_stop[1])}},t.Animation.prototype.default_options={frames:[],subsets:[],frame_duration:100,index:0,loop:1,bounce:0,frame_direction:1,frame_size:null,orientation:"down",on_end:null,offset:0,scale_image:null,sprite_sheet:null},t.Animation.prototype.subset=function(t){return this.subsets[t]},t.Animation.prototype.update=function(){return this.current_tick=(new Date).getTime(),this.sum_tick+=this.current_tick-this.last_tick,this.last_tick=this.current_tick,this.sum_tick>this.frame_duration&&(this.index+=this.frame_direction,this.sum_tick=0),(this.index>=this.frames.length||this.index<0)&&(this.bounce?(this.frame_direction=-this.frame_direction,this.index+=2*this.frame_direction):this.loop?this.index=this.frame_direction<0?this.frames.length-1:0:(this.index-=this.frame_direction,this.on_end&&(this.on_end(),this.on_end=null))),this -},t.Animation.prototype.slice=function(e,i){var s={};return s.frame_duration=this.frame_duration,s.loop=this.loop,s.bounce=this.bounce,s.on_end=this.on_end,s.frame_direction=this.frame_direction,s.frames=this.frames.slice().slice(e,i),new t.Animation(s)},t.Animation.prototype.next=function(){return this.update(),this.frames[this.index]},t.Animation.prototype.atLastFrame=function(){return this.index==this.frames.length-1},t.Animation.prototype.atFirstFrame=function(){return 0==this.index},t.Animation.prototype.currentFrame=function(){return this.frames[this.index]},t.Animation.prototype.toString=function(){return"[Animation, "+this.frames.length+" frames]"},t}(jaws||{});if("undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){function e(t,e){for(var i=function(e){return void 0!==t.isSpriteList?t.at(e):t[e]},s=[],o=new Array(e),n=0;e>n;n++)o[n]=n;for(var n=e-1,a=t.length;n>=0;a=t.length){for(s.push(o.map(i));n>=0&&o[n]===a-1;)n--,a--;if(n>=0){o[n]+=1;for(var r=n+1;e>r;r++)o[r]=o[r-1]+1;n=e-1}}return s}return t.collideOneWithOne=function(e,i){return e.radius&&i.radius&&e!==i&&t.collideCircles(e,i)?!0:e.rect&&i.rect&&e!==i&&t.collideRects(e.rect(),i.rect())?!0:!1},t.collideOneWithMany=function(e,i,s){var o=[];if(s){for(var n=0;n<i.length;n++)t.collideOneWithOne(e,i[n])&&(s(e,i[n]),o.push(i[n]));return o}return i.filter(function(i){return t.collideOneWithOne(e,i)})},t.collideManyWithMany=function(i,s,o){var n=[];return i===s?e(i,2).forEach(function(e){t.collideOneWithOne(e[0],e[1])&&(o?o(e[0],e[1]):n.push([e[0],e[1]]))}):i.forEach(function(e){s.forEach(function(i){t.collideOneWithOne(e,i)&&(o?o(e,i):n.push([e,i]))})}),n},t.collideCircles=function(e,i){return t.distanceBetween(e,i)<e.radius+i.radius},t.collideRects=function(t,e){return(t.x>=e.x&&t.x<=e.right||e.x>=t.x&&e.x<=t.right)&&(t.y>=e.y&&t.y<=e.bottom||e.y>=t.y&&e.y<=t.bottom)},t.distanceBetween=function(t,e){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))},t.collide=function(e,i,s){if((e.rect||e.radius)&&i.forEach)return t.collideOneWithMany(e,i,s).length>0;if(e.forEach&&i.forEach)return t.collideManyWithMany(e,i,s).length>0;if(e.forEach&&(i.rect||i.radius))return t.collideOneWithMany(i,e,s).length>0;if(e.rect&&i.rect||e.radius&&i.radius){var o=t.collideOneWithOne(e,i);if(!s||!o)return o;s(e,i)}},t}(jaws||{}),jaws=function(t){return t.PixelMap=function(e){return this instanceof arguments.callee?(this.options=e,this.scale=e.scale||1,this.x=e.x||0,this.y=e.y||0,e.image?(this.setContext(e.image),e.scale_image&&this.setContext(t.retroScaleImage(this.context.canvas,e.scale_image)),this.width=this.context.canvas.width*this.scale,this.height=this.context.canvas.height*this.scale):t.log.warn("PixelMap needs an image to work with"),this.named_colors=[],void this.update()):new arguments.callee(e)},t.PixelMap.prototype.setContext=function(e){var e=t.isDrawable(e)?e:t.assets.get(e);this.context=t.imageToCanvasContext(e)},t.PixelMap.prototype.update=function(t,e,i,s){if((void 0===t||0>t)&&(t=0),(void 0===e||0>e)&&(e=0),(void 0===i||i>this.width)&&(i=this.width),(void 0===s||s>this.height)&&(s=this.height),0==arguments.length)this.data=this.context.getImageData(t,e,i,s).data;else for(var o=this.context.getImageData(t,e,i,s).data,n=0,a=4*this.width,r=e*this.width*4+4*t,h=4*i,c=0;s>c;c++){for(var l=0;h>l;l++)this.data[r+l]=o[n++];r+=a}},t.PixelMap.prototype.draw=function(){t.context.drawImage(this.context.canvas,this.x,this.y,this.width,this.height)},t.PixelMap.prototype.namedColorAtRect=function(t,e){for(var i=t.x,s=t.y;i<t.right-1;i++)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;s<t.bottom-1;s++)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;i>t.x;i--)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;s>t.y;s--)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);return!1},t.PixelMap.prototype.at=function(t,e){t=parseInt(t),e=parseInt(e),0>e&&(e=0);var i=e*this.width*4+4*t,s=this.data[i],o=this.data[i+1],n=this.data[i+2],a=this.data[i+3];return[s,o,n,a]},t.PixelMap.prototype.namedColorAt=function(t,e){for(var i=this.at(t,e),s=0;s<this.named_colors.length;s++){var o=this.named_colors[s].name,n=this.named_colors[s].color;if(n[0]==i[0]&&n[1]==i[1]&&n[2]==i[2]&&n[3]==i[3])return o}},t.PixelMap.prototype.nameColor=function(t,e){this.named_colors.push({name:e,color:t})},t}(jaws||{}),jaws=function(t){return t.Parallax=function(e){return this instanceof arguments.callee?void t.parseOptions(this,e,this.default_options):new arguments.callee(e)},t.Parallax.prototype.default_options={width:function(){return t.width},height:function(){return t.height},scale:1,repeat_x:null,repeat_y:null,camera_x:0,camera_y:0,layers:[]},t.Parallax.prototype.draw=function(){for(var t,e,i=0;i<this.layers.length;i++)for(t=this.layers[i],e=this.repeat_x?-(this.camera_x/t.damping%t.width):-(this.camera_x/t.damping),t.y=this.repeat_y?-(this.camera_y/t.damping%t.height):-(this.camera_y/t.damping),t.x=e;t.y<this.height;){for(;t.x<this.width&&(t.x+t.width>=0&&t.y+t.height>=0&&t.draw(),t.x=t.x+t.width,this.repeat_x););if(t.y=t.y+t.height,t.x=e,!this.repeat_y)break}},t.Parallax.prototype.addLayer=function(e){var i=new t.ParallaxLayer(e);i.scaleAll(this.scale),this.layers.push(i)},t.Parallax.prototype.toString=function(){return"[Parallax "+this.x+", "+this.y+". "+this.layers.length+" layers]"},t.ParallaxLayer=function(e){return this instanceof arguments.callee?(this.damping=e.damping||0,void t.Sprite.call(this,e)):new arguments.callee(e)},t.ParallaxLayer.prototype=t.Sprite.prototype,t}(jaws||{}),jaws=function(jaws){return jaws.Text=function(t){return this instanceof arguments.callee?(this.set(t),t.context&&(this.context=t.context),void(t.context||jaws.context&&(this.context=jaws.context))):new arguments.callee(t)},jaws.Text.prototype.default_options={x:0,y:0,alpha:1,angle:0,anchor_x:0,anchor_y:0,anchor:"top_left",damping:1,style:"normal",fontFace:"serif",fontSize:12,color:"black",textAlign:"start",textBaseline:"alphabetic",text:"",wordWrap:!1,width:function(){return jaws.width},height:function(){return jaws.height},shadowColor:null,shadowBlur:null,shadowOffsetX:null,shadowOffsetY:null,_constructor:null},jaws.Text.prototype.set=function(t){return jaws.parseOptions(this,t,this.default_options),this.anchor&&this.setAnchor(this.anchor),this.cacheOffsets(),this},jaws.Text.prototype.clone=function(){var constructor=this._constructor?eval(this._constructor):this.constructor,new_sprite=new constructor(this.attributes());return new_sprite._constructor=this._constructor||this.constructor.name,new_sprite},jaws.Text.prototype.rotate=function(t){return this.angle+=t,this},jaws.Text.prototype.rotateTo=function(t){return this.angle=t,this},jaws.Text.prototype.moveTo=function(t,e){return this.x=t,this.y=e,this},jaws.Text.prototype.move=function(t,e){return t&&(this.x+=t),e&&(this.y+=e),this},jaws.Text.prototype.setX=function(t){return this.x=t,this},jaws.Text.prototype.setY=function(t){return this.y=t,this},jaws.Text.prototype.setTop=function(t){return this.y=t+this.top_offset,this},jaws.Text.prototype.setBottom=function(t){return this.y=t-this.bottom_offset,this},jaws.Text.prototype.setLeft=function(t){return this.x=t+this.left_offset,this},jaws.Text.prototype.setRight=function(t){return this.x=t-this.right_offset,this},jaws.Text.prototype.setWidth=function(t){return this.width=t,this.cacheOffsets(),this},jaws.Text.prototype.setHeight=function(t){return this.height=t,this.cacheOffsets(),this},jaws.Text.prototype.resize=function(t,e){return this.width+=t,this.height+=e,this.cacheOffsets(),this},jaws.Text.prototype.resizeTo=function(t,e){return this.width=t,this.height=e,this.cacheOffsets(),this},jaws.Text.prototype.setAnchor=function(t){var e={top_left:[0,0],left_top:[0,0],center_left:[0,.5],left_center:[0,.5],bottom_left:[0,1],left_bottom:[0,1],top_center:[.5,0],center_top:[.5,0],center_center:[.5,.5],center:[.5,.5],bottom_center:[.5,1],center_bottom:[.5,1],top_right:[1,0],right_top:[1,0],center_right:[1,.5],right_center:[1,.5],bottom_right:[1,1],right_bottom:[1,1]};return e.hasOwnProperty(t)&&(this.anchor_x=e[t][0],this.anchor_y=e[t][1],this.cacheOffsets()),this},jaws.Text.prototype.cacheOffsets=function(){return this.left_offset=this.width*this.anchor_x,this.top_offset=this.height*this.anchor_y,this.right_offset=this.width*(1-this.anchor_x),this.bottom_offset=this.height*(1-this.anchor_y),this.cached_rect&&this.cached_rect.resizeTo(this.width,this.height),this},jaws.Text.prototype.rect=function(){return!this.cached_rect&&this.width&&(this.cached_rect=new jaws.Rect(this.x,this.y,this.width,this.height)),this.cached_rect&&this.cached_rect.moveTo(this.x-this.left_offset,this.y-this.top_offset),this.cached_rect},jaws.Text.prototype.draw=function(){this.context.save(),0!==this.angle&&this.context.rotate(this.angle*Math.PI/180),this.context.globalAlpha=this.alpha,this.context.translate(-this.left_offset,-this.top_offset),this.context.fillStyle=this.color,this.context.font=this.style+" "+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var t=this.y,e=this.x;if(this.wordWrap)for(var i=this.text.split(" "),s="",o=0;o<i.length;o++){var n=s+i[o]+" ",a=this.context.measureText(n);this.y<t+this.height&&(a.width>this.width?(this.context.fillText(s,this.x,this.y),s=i[o]+" ",this.y+=this.fontSize):s=n,this.context.fillText(s,this.x,this.y))}else if(this.context.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var i=this.text.split(" "),s=" ",o=0;o<i.length;o++){var n=s+i[o]+" ";this.context.measureText(n).width<Math.abs(this.width-this.x)&&(this.context.fillText(n,this.x,this.y),s=i[o]+" ",s=n)}return this.y=t,this.x=e,this.context.restore(),this},jaws.Text.prototype.asCanvasContext=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled,this.context.fillStyle=this.color,this.context.font=this.style+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var i=this.y,s=this.x;if(this.wordWrap)for(var o=this.text.split(" "),n="",a=0;a<o.length;a++){var r=n+o[a]+" ",h=this.context.measureText(r);this.y<i+this.height&&(h.width>this.width?(this.context.fillText(n,this.x,this.y),n=o[a]+" ",this.y+=this.fontSize):n=r,this.context.fillText(n,this.x,this.y))}else if(this.context.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var o=this.text.split(" "),n=" ",a=0;a<o.length;a++){var r=n+o[a]+" ";this.context.measureText(r).width<Math.abs(this.width-this.x)&&(this.context.fillText(r,this.x,this.y),n=o[a]+" ",n=r)}return this.y=i,this.x=s,e},jaws.Text.prototype.asCanvas=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled,this.context.fillStyle=this.color,this.context.font=this.style+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var i=this.y,s=this.x;if(this.wordWrap)for(var o=this.text.split(" "),n="",a=0;a<o.length;a++){var r=n+o[a]+" ",h=e.measureText(r);this.y<i+this.height&&(h.width>this.width?(e.fillText(n,this.x,this.y),n=o[a]+" ",this.y+=this.fontSize):n=r,e.fillText(n,this.x,this.y))}else if(e.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var o=this.text.split(" "),n=" ",a=0;a<o.length;a++){var r=n+o[a]+" ";e.measureText(r).width<Math.abs(this.width-this.x)&&(e.fillText(r,this.x,this.y),n=o[a]+" ",n=r)}return this.y=i,this.x=s,t},jaws.Text.prototype.toString=function(){return"[Text "+this.x.toFixed(2)+", "+this.y.toFixed(2)+", "+this.width+", "+this.height+"]"},jaws.Text.prototype.attributes=function(){var t=this.options;return t._constructor=this._constructor||"jaws.Text",t.x=parseFloat(this.x.toFixed(2)),t.y=parseFloat(this.y.toFixed(2)),t.text=this.text,t.alpha=this.alpha,t.angle=parseFloat(this.angle.toFixed(2)),t.anchor_x=this.anchor_x,t.anchor_y=this.anchor_y,t.style=this.style,t.fontSize=this.fontSize,t.fontFace=this.fontFace,t.color=this.color,t.textAlign=this.textAlign,t.textBaseline=this.textBaseline,t.wordWrap=this.wordWrap,t.width=this.width,t.height=this.height,t},jaws.Text.prototype.toJSON=function(){return JSON.stringify(this.attributes())},jaws}(jaws||{});if("undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Text),"undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){return t.QuadTree=function(e){this.depth=arguments[1]||0,this.bounds=e||new t.Rect(0,0,t.width,t.height),this.nodes=[],this.objects=[]},t.QuadTree.prototype.clear=function(){this.objects=[];for(var t=0;t<this.nodes.length;t++)"undefined"!=typeof this.nodes[t]&&(this.nodes[t].clear(),delete this.nodes[t])},t.QuadTree.prototype.split=function(){var e=Math.round(this.bounds.width/2),i=Math.round(this.bounds.height/2),s=this.bounds.x,o=this.bounds.y;this.nodes[0]=new t.QuadTree(new t.Rect(s+e,o,e,i),this.depth+1),this.nodes[1]=new t.QuadTree(new t.Rect(s,o,e,i),this.depth+1),this.nodes[2]=new t.QuadTree(new t.Rect(s,o+i,e,i),this.depth+1),this.nodes[3]=new t.QuadTree(new t.Rect(s+e,o+i,e,i),this.depth+1)},t.QuadTree.prototype.getIndex=function(t){var e=-1,i=this.bounds.x+this.bounds.width/2,s=this.bounds.y+this.bounds.height/2,o=t.y<s&&t.y+t.height<s,n=t.y>s;return t.x<i&&t.x+t.width<i?o?e=1:n&&(e=2):t.x>i&&(o?e=0:n&&(e=3)),e},t.QuadTree.prototype.insert=function(t){if(t.hasOwnProperty("x")||t.hasOwnProperty("y")||t.hasOwnProperty("width")||t.hasOwnProperty("height")){if("undefined"!=typeof this.nodes[0]){var e=this.getIndex(t);if(-1!==e)return void this.nodes[e].insert(t)}this.objects.push(t),"undefined"==typeof this.nodes[0]&&this.split();for(var i=0;i<this.objects.length;){var e=this.getIndex(this.objects[i]);-1!==e?this.nodes[e].insert(this.objects.splice(i,1)[0]):i++}}},t.QuadTree.prototype.retrieve=function(t){if(t.hasOwnProperty("x")||t.hasOwnProperty("y")||t.hasOwnProperty("width")||t.hasOwnProperty("height")){var e=this.getIndex(t),i=this.objects;if("undefined"!=typeof this.nodes[0])if(-1!==e)i=i.concat(this.nodes[e].retrieve(t));else for(var s=0;s<this.nodes.length;s++)i=i.concat(this.nodes[s].retrieve(t));return i}},t.QuadTree.prototype.collide=function(e,i,s){var o=!1,n=new t.QuadTree,a=[];return e.forEach||(a.push(e),e=a),i.forEach||(a=[],a.push(i),i=a),i.forEach(function(t){n.insert(t)}),e.forEach(function(e){t.collide(e,n.retrieve(e),s)&&(o=!0)}),n.clear(),o},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.QuadTree),window.addEventListener("load",function(){jaws.onload&&jaws.onload()},!1);</script><script type="text/javascript">(function () { - return (function() { - var screen_x1, screen_y1; - screen_x1 = 240; - screen_y1 = 180; - return jaws.onload = function() { - var Demo, game, gameloop; - Demo = (function() { - var AiVaquita, FlippableSprite, HeroVaquita, Sprite, Vaquita, downKey, leftKey, rightKey, spaceKey, upKey, _ref1; - - _ref1 = jaws.keyCodes, leftKey = _ref1.left, rightKey = _ref1.right, upKey = _ref1.up, downKey = _ref1.down, spaceKey = _ref1.space; - - Sprite = (function(_super) { - __extends(_Class, _super); - - function _Class() { - _Class.__super__.constructor.call(this, { - image: this.image, - x: 0, - y: 0, - scale: 2 - }); - } - - return _Class; - - })(jaws.Sprite); - - FlippableSprite = (function(_super) { - __extends(_Class, _super); - - function _Class() { - return _Class.__super__.constructor.apply(this, arguments); - } - - _Class.prototype.draw = function() { - this.flipped = this.lr >= 0; - this.x = (screen_x1 + this.px + this.lr) * 2; - this.y = (screen_y1 + this.py - 5) * 2; - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(Sprite); - - Vaquita = (function(_super) { - __extends(_Class, _super); - - function _Class() { - this.lr = 18; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.draw = function() { - if (this.vx < 0) { - this.lr = -18; - } else if (this.vx > 0) { - this.lr = 18; - } - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(FlippableSprite); - - AiVaquita = (function(_super) { - __extends(_Class, _super); - - function _Class() { - this.image = Math.random() > 0.5 ? pixyvaquita_105 : pixyvaquita; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.draw = function() { - var rx, ry, s, vx, vy, y; - vx = this.vx + Math.floor(Math.random() * 3) - 1; - vy = this.vy + Math.floor(Math.random() * 3) - 1; - x = this.px; - y = this.py; - rx = 0.5 * x / screen_x1; - ry = 0.5 * y / screen_y1; - if ((s = vx * vx + vy * vy * 2) > 6) { - vx = Math.round(vx * 0.9 - rx); - vy = Math.round(vy * 0.9 - ry); - } - this.px += this.vx = vx; - this.py += this.vy = vy; - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(Vaquita); - - HeroVaquita = (function(_super) { - var twist; - - __extends(_Class, _super); - - twist = [pixyvaquita_twist_l, pixyvaquita_twist_r]; - - function _Class() { - this.image = pixyvaquita; - this.time = 0; - _Class.__super__.constructor.call(this); - } - - _Class.prototype.beat_lr = 0; - - _Class.prototype.draw = function() { - this.vx = (jaws.pressed[leftKey] ? -1 : 0) + (jaws.pressed[rightKey] ? 1 : 0); - this.vy = (jaws.pressed[upKey] ? -1 : 0) + (jaws.pressed[downKey] ? 1 : 0); - this.px += this.vx; - this.py += this.vy; - if ((this.time++ % 3) === 0) { - if (this.image !== pixyvaquita) { - this.image = pixyvaquita; - } else if (this.vx !== 0) { - this.image = twist[this.beat_lr++ & 1]; - } - } - return _Class.__super__.draw.call(this); - }; - - return _Class; - - })(Vaquita); - - Demo.prototype.addVaquita = function() { - var angle, v; - angle = Math.random() * 6.28; - v = new AiVaquita; - v.vx = 0; - v.vy = 0; - v.px = Math.floor(Math.sin(angle) * 300); - v.py = Math.floor(Math.cos(angle) * 300); - v.draw(); - return this.vaquitas.push(v); - }; - - function Demo(vaquitas) { - this.vaquitas = vaquitas != null ? vaquitas : []; - } - - Demo.prototype.setup = function() { - var v; - v = new HeroVaquita; - v.px = 0; - v.py = 0; - v.vx = 0; - v.vy = 0; - return this.vaquitas.push(v); - }; - - Demo.prototype.draw = function() { - var v, _j, _len1, _ref2; - jaws.clear(); - if ((!(this.gameloop.ticks & 0x7f) && this.vaquitas.length < 7) || jaws.pressed[spaceKey]) { - this.addVaquita(); - } - _ref2 = this.vaquitas; - for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { - v = _ref2[_j]; - v.draw(); - } - if ((this.gameloop.ticks & 0xff) === 0xff) { - return fps.innerHTML = " - " + this.gameloop.fps + " fps"; - } - }; - - return Demo; - - })(); - if (true) { - jaws.init(); - jaws.setupInput(); - game = new Demo; - gameloop = new jaws.GameLoop(game, { - fps: 24 - }); - return (game.gameloop = gameloop).start(); - } else { - return jaws.start(Demo, { - fps: 25 - }); - } - }; - })(); - })(); -</script></body></html>
\ No newline at end of file diff --git a/game.html.coffee b/game.html.coffee deleted file mode 100644 index 30dc1a4..0000000 --- a/game.html.coffee +++ /dev/null @@ -1,330 +0,0 @@ -# Copyright (c) 2013, 2014 Michele Bini - -# A game featuring a Vaquita, the smallest, most endagered marine cetacean - -# This program is available under the terms of the MIT License - -version = "0.1.63" - -{ htmlcup } = require 'htmlcup' - -htmlcup[x] = htmlcup.compileTag x for x in [ "svg", "rect", "g", "ellipse", "polygon", "line", "image", "defs", "linearGradient", "stop", "use" ] - -title = "Vilma the Vaquita Demo" - -fs = require 'fs' - -datauri = (t, x)-> "data:#{t};base64,#{new Buffer(fs.readFileSync(x)).toString("base64")}" -datauripng = (x)-> datauri "image/png", x -datauriicon = (x)-> datauri "image/x-icon", x - -icon = datauriicon "vaquita.ico" -pixyvaquita = datauripng "vilma.png" -pixyvaquita_v105 = datauripng "pixyvaquita_v105_v.png" -frames = - _: pixyvaquita - _v105: pixyvaquita_v105 - twist_l: datauripng "vilma_twist_l.png" - twist_r: datauripng "vilma_twist_r.png" - -gameName = "#{title} v#{version}" - -htmlcup.jsFile = (f)-> @script type:"text/javascript", (fs.readFileSync(f).toString()) - -genPage = -> - htmlcup.html5Page -> - @head -> - @meta charset:"utf-8" - @link rel:"shortcut icon", href:icon - @title title - @style type: "text/css", - """ - body { - /* background:pink; */ - /* background: #69B2FF; */ - /* background: #21AFF8; */ - /* background: #0286E8; */ - /* background: #1096EE; */ - background:black; - text-align: center; - font-size: 22px; - font-family: Helvetica; - color:white; - color:rgba(255,255,255,0.9); - margin:0; - } - .banner { - border: 5px solid white; - border: 5px solid white rgba(255,255,255,0.9); - box-shadow: 0 2px 4px blue; - margin: 1em; - } - footer, p { - margin-top:0.418em; - margin-bottom:0.418em; - margin-left:auto; - margin-right:auto; - text-shadow: 0 1px 1px blue; - } - p { - width:22em; - max-width:100%; - } - a { - /* - color:rgb(200,255,255); - color:rgba(200,255,255,0.9); - */ - color:white; - color:rgba(255,255,255,0.9); - text-decoration:none; - display: inline-block; - border: 1px solid white; - padding: 0 0.2em; - border-radius: 0.2em; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -ie-border-radius: 0.2em; - } - a:hover { - background-color:rgba(20,70,180,1.0); - } - .petition { - margin:0.418em; - padding:0.618em; - } - .petition a { - font-size:127.2%; - box-shadow: 0 2px 4px blue; - margin:0.3em; - } - .page { - width: 100%; - height: 100%; - margin: 0; - border: 0; - } - .centering { - display: table; - padding: 0; - } - .centered { - display: table-cell; - vertical-align: middle; - text-align: center; - } - .inline-block { - display: inline-block; - } - .dynamic-section { - display: inline-block; - vertical-align:middle; - max-width:100%; - } - .flip-lr { - -moz-transform: scaleX(-1); - -o-transform: scaleX(-1); - -webkit-transform: scaleX(-1); - transform: scaleX(-1); - filter: FlipH; - -ms-filter: "FlipH"; - } - image, .pixelart { - image-rendering:optimizeSpeed; /* Legal fallback */ - image-rendering:-moz-crisp-edges; /* Firefox */ - image-rendering:-o-crisp-edges; /* Opera */ - image-rendering:-webkit-optimize-contrast; /* Safari */ - image-rendering:optimize-contrast; /* CSS3 Proposed */ - image-rendering:crisp-edges; /* CSS4 Proposed */ - image-rendering:pixelated; /* CSS4 Proposed */ - -ms-interpolation-mode:nearest-neighbor; /* IE8+ */ - } - /* - .pixelart { - image-rendering: -moz-crisp-edges; - -ms-interpolation-mode: nearest-neighbor; - image-rendering: pixelated; - image-rendering: crisp-edges; - } - */ - g.flipped { - transform:scale(-1,1); - } - .dim { - opacity: 0.2; - } - .dim:hover { - opacity: 1; - } - """ - @body -> - @div class:"centering page", -> - @div class:"centered", -> - @div style:"visibility:hidden;position:absolute", -> - @img id:"pixyvaquita", src:pixyvaquita - @img id:"pixyvaquita_105", src:frames._v105 - @img id:"pixyvaquita_twist_l", src:frames.twist_l - @img id:"pixyvaquita_twist_r", src:frames.twist_r - @div style:"position:relative", -> - @svg id:"sea-svgroot", width:"960", height:"720", style:"position:absolute;opacity:0.9;z-index:-1000", -> - @defs -> - @linearGradient id:"grad1", x1:"0%", y1:"0%", x2:"0%", y2:"100%", -> - @stop offset:"0%", style:"stop-color:rgb(255,255,255);stop-opacity:1" - @stop offset:"25%", style:"stop-color:rgb(100,200,250);stop-opacity:1" - @stop offset:"50%", style:"stop-color:rgb(0,80,240);stop-opacity:1" - @stop offset:"75%", style:"stop-color:rgb(0,0,180);stop-opacity:1" - @stop offset:"100%", style:"stop-color:rgb(0,0,0);stop-opacity:1" - @rect x:"0", y:"0", width:"960", height:"720", fill:"url(#grad1)" - @canvas width:"960", height:"720", -> - @footer class:"dim", -> - @span gameName - @span " - " - @a target:"_blank", href:"index.html", "Learn about Vaquitas" - @span id:"fps" - gameObjects = null - @script type:"text/javascript", "gameObjects=#{JSON.stringify(gameObjects)};" - @script type:"text/javascript", "__hasProp = {}.hasOwnProperty; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };" - @jsFile "jaws/jaws-min.js" - # @jsFile "jaws-assets-named.js" - @coffeeScript -> do -> - # svgroot = document.getElementById("sea-svgroot") - - # reportErrors = (x)-> - # try - # x() - # catch error - # try - # alert error.toString() - # catch error2 - # alert error - - screen_x1 = 240 - screen_y1 = 180 - - jaws.onload = -> - class Demo - { left: leftKey, right: rightKey, up: upKey, down: downKey, space: spaceKey } = jaws.keyCodes - Sprite = class extends jaws.Sprite - # caller needs to set lr for flip center - constructor: -> - super - image: @image - x: 0 - y: 0 - scale: 2 - FlippableSprite = class extends Sprite - draw: -> - @flipped = @lr >= 0 - @x = (screen_x1 + @px + @lr) * 2 - @y = (screen_y1 + @py - 5) * 2 - super() - Vaquita = class extends FlippableSprite - constructor: -> - @lr = 18 - super() - draw: -> - if @vx < 0 - @lr = - 18 - else if @vx > 0 - @lr = 18 - super() - AiVaquita = class extends Vaquita - constructor: -> - @image = if Math.random() > 0.5 then pixyvaquita_105 else pixyvaquita - super() - draw: -> - vx = @vx + Math.floor(Math.random()*3) - 1 - vy = @vy + Math.floor(Math.random()*3) - 1 - x = @px - y = @py - rx = 0.5 * x / screen_x1 - ry = 0.5 * y / screen_y1 - if (s = vx * vx + vy * vy * 2) > 6 - vx = Math.round(vx * 0.9 - rx) - vy = Math.round(vy * 0.9 - ry) - @px += @vx = vx - @py += @vy = vy - super() - HeroVaquita = class extends Vaquita - twist = [ pixyvaquita_twist_l, pixyvaquita_twist_r ] - constructor: -> - @image = pixyvaquita - @time = 0 - super() - beat_lr: 0 - draw: -> - @vx = (if jaws.pressed[leftKey] then -1 else 0) + (if jaws.pressed[rightKey] then 1 else 0) - @vy = (if jaws.pressed[upKey] then -1 else 0) + (if jaws.pressed[downKey] then 1 else 0) - @px += @vx - @py += @vy - if (@time++ % 3) is 0 - if @image isnt pixyvaquita - @image = pixyvaquita - else if @vx isnt 0 - @image = twist[@beat_lr++ & 1] - super() - addVaquita: -> - # n = v.cloneNode() - # n.setAttribute "opacity", "0.5" - # n.href.baseVal = "#_v105" if Math.random(0) > 0.5 - # n.setAttribute "transform", "" - # sea.appendChild n - angle = Math.random() * 6.28 - v = new AiVaquita - v.vx = 0 - v.vy = 0 - v.px = Math.floor(Math.sin(angle) * 300) - v.py = Math.floor(Math.cos(angle) * 300) - v.draw() - # vaquita.update() - @vaquitas.push v - constructor: (@vaquitas = [])-> - setup: -> - v = new HeroVaquita # jaws.Sprite x:screen_x1*2, y:screen_y1*2, scale:2, image:pixyvaquita - v.px = 0 - v.py = 0 - v.vx = 0 - v.vy = 0 - @vaquitas.push v - draw: -> - jaws.clear() - @addVaquita() if (!(@gameloop.ticks & 0x7f) and @vaquitas.length < 7) or jaws.pressed[spaceKey] - v.draw() for v in @vaquitas - if (@gameloop.ticks & 0xff) is 0xff - fps.innerHTML = " - #{@gameloop.fps} fps" - if true - jaws.init() - jaws.setupInput(); - game = new Demo - gameloop = new jaws.GameLoop(game, { fps:24 }) - (game.gameloop = gameloop).start() - else - jaws.start Demo, fps:25 - - # gameFrame = -> reportErrors -> - # if (time & 0xff) is 0x00 and vaquitas.length < 4 - # addVaquita() - # # s += 0.001 - # x -= vx = pressedKeys[leftKey] - pressedKeys[rightKey] - # y -= pressedKeys[upKey] - pressedKeys[downKey] - # if vx > 0 - # scaleX = 1 - # else if vx < 0 - # scaleX = -1 - # v.setAttribute("transform", "translate(#{x}, #{y}) scale(#{scaleX}, #{scaleY})") - # # transform = v.transform.baseVal.getItem(0) - # # transformMatrix.a = scaleX - # # transformMatrix.e = x - # # transformMatrix.f = y - # if (time % 3) is 0 - # if currentFrame.baseVal is "#twistleft" - # currentFrame .baseVal = "#_" - # else if vx isnt 0 - # currentFrame.baseVal = "#twistleft" - # # transformList.initialize(transform) - # vq.update() for vq in vaquitas - # time++ - - # # setInterval gameFrame, 40 - -genPage() diff --git a/jaws-assets-named.coffee b/jaws-assets-named.coffee deleted file mode 100644 index 4a7a5df..0000000 --- a/jaws-assets-named.coffee +++ /dev/null @@ -1,456 +0,0 @@ -# LGPL-licensed, please consult jawsjs.com -# conversion to coffeescript modification of src_assets.js from 'jawsjs.com' by Michele Bini - -# this supports named assets: like named:ImageName:image.png" - -jaws = ((jaws) -> - - ###* - @fileOverview jaws.assets properties and functions - - Loads and processes image, sound, video, and json assets - (Used internally by JawsJS to create <b>jaws.assets</b>) - - @class Jaws.Assets - @constructor - @property {boolean} bust_cache Add a random argument-string to assets-urls when loading to bypass any cache - @property {boolean} fuchia_to_transparent Convert the color fuchia to transparent when loading .bmp-files - @property {boolean} image_to_canvas Convert all image assets to canvas internally - @property {string} root Rootdir from where all assets are loaded - @property {array} file_type Listing of file postfixes and their associated types - @property {array} can_play Listing of postfixes and (during runtime) populated booleans - ### - - ###* - Returns the length of the resource list - @public - @returns {number} The length of the resource list - ### - - ###* - Set root prefix-path to all assets - - @example - jaws.assets.setRoot("music/").add(["music.mp3", "music.ogg"]).loadAll() - - @public - @param {string} path-prefix for all following assets - @returns {object} self - ### - - ###* - Get one or more resources from their URLs. Supports simple wildcard (you can end a string with "*"). - - @example - jaws.assets.add(["song.mp3", "song.ogg"]) - jaws.assets.get("song.*") // -> Will return song.ogg in firefox and song.mp3 in IE - - @public - @param {string|array} src The resource(s) to retrieve - @returns {array|object} Array or single resource if found in cache. Undefined otherwise. - ### - - # Wildcard? song.*, match against asset-srcs, make sure it's loaded and return content of first match. - - # TODO: self.loaded[src] is false for supported files for some odd reason. - - ###* - Returns if specified resource is currently loading or not - @public - @param {string} src Resource URL - @return {boolean|undefined} If resource is currently loading. Otherwise, undefined. - ### - - ###* - Returns if specified resource is loaded or not - @param src Source URL - @return {boolean|undefined} If specified resource is loaded or not. Otherwise, undefined. - ### - - ###* - Returns lowercase postfix of specified resource - @public - @param {string} src Resource URL - @returns {string} Lowercase postfix of resource - ### - - ###* - Determine type of file (Image, Audio, or Video) from its postfix - @private - @param {string} src Resource URL - @returns {string} Matching type {Image, Audio, Video} or the postfix itself - ### - - ###* - Add URL(s) to asset listing for later loading - @public - @param {string|array|arguments} src The resource URL(s) to add to the asset listing - @example - jaws.assets.add("player.png") - jaws.assets.add(["media/bullet1.png", "media/bullet2.png"]) - jaws.assets.add("foo.png", "bar.png") - jaws.assets.loadAll({onload: start_game}) - ### - - ###* - Iterate through the list of resource URL(s) and load each in turn. - @public - @param {Object} options Object-literal of callback functions - @config {function} [options.onprogress] The function to be called on progress (when one assets of many is loaded) - @config {function} [options.onerror] The function to be called if an error occurs - @config {function} [options.onload] The function to be called when finished - ### - - ###* - Loads a single resource from its given URL - Will attempt to match a resource to known MIME types. - If unknown, loads the file as a blob-object. - - @public - @param {string} src Resource URL - @param {Object} options Object-literal of callback functions - @config {function} [options.onload] Function to be called when assets has loaded - @config {function} [options.onerror] Function to be called if an error occurs - @example - jaws.load("media/foo.png") - jaws.load("http://place.tld/foo.png") - ### - # NOTE: assetLoaded can be called several times during loading. - - #Load everything else as raw blobs... - - # ... But don't load un-supported audio-files. - - # Initial loading callback for all assets for parsing specific filetypes or - # optionally converting images to canvas-objects. - # @private - # @param {EventObject} event The EventObject populated by the calling event - # @see processCallbacks() - - # - # * Only increment load_count ONCE per unique asset. - # * This is needed cause assetLoaded-callback can in certain cases be called several for a single asset... - # * ..and not only Once when it's loaded. - # - - # Called when jaws asset-handler decides that an asset shouldn't be loaded - # For example, an unsupported audio-format won't be loaded. - - # @private - - # Increases the error count and calls processCallbacks with false flag set - # @see processCallbacks() - # @private - # @param {EventObject} event The EventObject populated by the calling event - - # Processes (if set) the callbacks per resource - # @private - # @param {object} asset The asset to be processed - # @param {boolean} ok If an error has occured with the asset loading - # @param {EventObject} event The EventObject populated by the calling event - # @see jaws.start() in core.js - - # Displays the progress of asset handling as an overall percentage of all loading - # (Can be overridden as jaws.assets.displayProgress = function(percent_done) {}) - # @public - # @param {number} percent_done The overall percentage done across all resource handling - - # Make Fuchia (0xFF00FF) transparent (BMPs ONLY) - # @private - # @param {HTMLImageElement} image The Bitmap Image to convert - # @returns {CanvasElement} canvas The translated CanvasElement - - fuchiaToTransparent = (image) -> - return unless jaws.isDrawable(image) - canvas = (if jaws.isImage(image) then jaws.imageToCanvas(image) else image) - context = canvas.getContext("2d") - img_data = context.getImageData(0, 0, canvas.width, canvas.height) - pixels = img_data.data - i = 0 - - while i < pixels.length - # Color: Fuchia - pixels[i + 3] = 0 if pixels[i] is 255 and pixels[i + 1] is 0 and pixels[i + 2] is 255 # Set total see-through transparency - i += 4 - context.putImageData img_data, 0, 0 - canvas - jaws.Assets = Assets = -> - getType = (src) -> - if jaws.isString(src) - postfix = self.getPostfix(src) - (if self.file_type[postfix] then self.file_type[postfix] else postfix) - else - jaws.log.error "jaws.assets.getType: Argument not a String with " + src - return - assetLoaded = (event) -> - asset = @asset - src = asset.src - filetype = getType(asset.src) - try - if filetype is "json" - return if @readyState isnt 4 - self.data[asset.src] = JSON.parse(@responseText) - else if filetype is "image" - new_image = (if self.image_to_canvas then jaws.imageToCanvas(asset.image) else asset.image) - new_image = fuchiaToTransparent(new_image) if self.fuchia_to_transparent and self.getPostfix(asset.src) is "bmp" - self.data[asset.src] = new_image - else if filetype is "audio" and self.can_play[self.getPostfix(asset.src)] - self.data[asset.src] = asset.audio - else if filetype is "video" and self.can_play[self.getPostfix(asset.src)] - self.data[asset.src] = asset.video - else - self.data[asset.src] = @response - catch e - jaws.log.error "Cannot process " + src + " (Message: " + e.message + ", Name: " + e.name + ")" - self.data[asset.src] = null - self.load_count++ unless self.loaded[src] - self.loaded[src] = true - self.loading[src] = false - processCallbacks asset, true, event - return - assetSkipped = (asset) -> - self.loaded[asset.src] = true - self.loading[asset.src] = false - self.load_count++ - processCallbacks asset, true - return - assetError = (event) -> - asset = @asset - self.error_count++ - processCallbacks asset, false, event - return - processCallbacks = (asset, ok, event) -> - percent = parseInt((self.load_count + self.error_count) / self.src_list.length * 100) - if ok - self.onprogress asset.src, percent if self.onprogress - asset.onprogress event if asset.onprogress and event isnt `undefined` - else - self.onerror asset.src, percent if self.onerror - asset.onerror event if asset.onerror and event isnt `undefined` - if percent is 100 - self.onload() if self.onload - self.onprogress = null - self.onerror = null - self.onload = null - return - return new arguments.callee() unless this instanceof arguments.callee - self = this - self.loaded = [] - self.loading = [] - self.src_list = [] - self.data = [] - self.bust_cache = false - self.image_to_canvas = true - self.fuchia_to_transparent = true - self.root = "" - self.file_type = {} - self.file_type["json"] = "json" - self.file_type["wav"] = "audio" - self.file_type["mp3"] = "audio" - self.file_type["ogg"] = "audio" - self.file_type["m4a"] = "audio" - self.file_type["weba"] = "audio" - self.file_type["aac"] = "audio" - self.file_type["mka"] = "audio" - self.file_type["flac"] = "audio" - self.file_type["png"] = "image" - self.file_type["jpg"] = "image" - self.file_type["jpeg"] = "image" - self.file_type["gif"] = "image" - self.file_type["bmp"] = "image" - self.file_type["tiff"] = "image" - self.file_type["mp4"] = "video" - self.file_type["webm"] = "video" - self.file_type["ogv"] = "video" - self.file_type["mkv"] = "video" - self.can_play = {} - try - audioTest = new Audio() - self.can_play["wav"] = !!audioTest.canPlayType("audio/wav; codecs=\"1\"").replace(/^no$/, "") - self.can_play["ogg"] = !!audioTest.canPlayType("audio/ogg; codecs=\"vorbis\"").replace(/^no$/, "") - self.can_play["mp3"] = !!audioTest.canPlayType("audio/mpeg;").replace(/^no$/, "") - self.can_play["m4a"] = !!(audioTest.canPlayType("audio/x-m4a;") or audioTest.canPlayType("audio/aac;")).replace(/^no$/, "") - self.can_play["weba"] = !!audioTest.canPlayType("audio/webm; codecs=\"vorbis\"").replace(/^no$/, "") - self.can_play["aac"] = !!audioTest.canPlayType("audio/aac;").replace(/^no$/, "") - self.can_play["mka"] = !!audioTest.canPlayType("audio/x-matroska;").replace(/^no$/, "") - self.can_play["flac"] = !!audioTest.canPlayType("audio/x-flac;").replace(/^no$/, "") - try - videoTest = document.createElement("video") - self.can_play["mp4"] = !!videoTest.canPlayType("video/mp4;").replace(/^no$/, "") - self.can_play["webm"] = !!videoTest.canPlayType("video/webm; codecs=\"vorbis\"").replace(/^no$/, "") - self.can_play["ogv"] = !!videoTest.canPlayType("video/ogg; codecs=\"vorbis\"").replace(/^no$/, "") - self.can_play["mkv"] = !!videoTest.canPlayType("video/x-matroska;").replace(/^no$/, "") - self.length = -> - self.src_list.length - - self.setRoot = (path) -> - self.root = path - self - - self.get = (src) -> - if jaws.isArray(src) - src.map (i) -> - self.data[i] - - else if jaws.isString(src) - if src[src.length - 1] is "*" - needle = src.replace("*", "") - i = 0 - - while i < self.src_list.length - return self.data[self.src_list[i]] if self.src_list[i].indexOf(needle) is 0 and self.data[self.src_list[i]] - i++ - if self.data[src] - self.data[src] - else - jaws.log.warn "No such asset: " + src, true - else - jaws.log.error "jaws.get: Neither String nor Array. Incorrect URL resource " + src - return - return - - self.isLoading = (src) -> - if jaws.isString(src) - self.loading[src] - else - jaws.log.error "jaws.isLoading: Argument not a String with " + src - return - - self.isLoaded = (src) -> - if jaws.isString(src) - self.loaded[src] - else - jaws.log.error "jaws.isLoaded: Argument not a String with " + src - return - - self.getPostfix = (src) -> - if jaws.isString(src) - src.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1] - else - jaws.log.error "jaws.assets.getPostfix: Argument not a String with " + src - return - - self.add = (src) -> - list = arguments - list = list[0] if list.length is 1 and jaws.isArray(list[0]) - i = 0 - - while i < list.length - if jaws.isArray(list[i]) - self.add list[i] - else - if jaws.isString(list[i]) - self.src_list.push list[i] - else - jaws.log.error "jaws.assets.add: Neither String nor Array. Incorrect URL resource " + src - i++ - self - - self.loadAll = (options) -> - self.load_count = 0 - self.error_count = 0 - self.onprogress = options.onprogress if options.onprogress and jaws.isFunction(options.onprogress) - self.onerror = options.onerror if options.onerror and jaws.isFunction(options.onerror) - self.onload = options.onload if options.onload and jaws.isFunction(options.onload) - self.src_list.forEach (item) -> - self.load item - return - - self - - self.load = (src, options) -> - options = {} unless options - unless jaws.isString(src) - jaws.log.error "jaws.assets.load: Argument not a String with " + src - return - asset = {} - resolved_src = "" - if parts =/^named:([^:]+):/.exec src - asset.name = parts[1] - src = src.substring parts[0].length - asset.src = src - asset.onload = options.onload - asset.onerror = options.onerror - self.loading[src] = true - parser = RegExp("^((f|ht)tp(s)?:)?//") - if /^[a-z][a-z0-9-]*:/.test(src) - resolved_src = asset.src - else - resolved_src = self.root + asset.src - resolved_src += "?" + parseInt(Math.random() * 10000000) if self.bust_cache - type = getType(asset.src) - if type is "image" - try - asset.image = new Image() - asset.image.asset = asset - asset.image.addEventListener "load", assetLoaded - asset.image.addEventListener "error", assetError - asset.image.src = resolved_src - catch e - jaws.log.error "Cannot load Image resource " + resolved_src + " (Message: " + e.message + ", Name: " + e.name + ")" - else if self.can_play[self.getPostfix(asset.src)] - if type is "audio" - try - asset.audio = new Audio() - asset.audio.asset = asset - asset.audio.addEventListener "error", assetError - asset.audio.addEventListener "canplay", assetLoaded - self.data[asset.src] = asset.audio - asset.audio.src = resolved_src - asset.audio.load() - catch e - jaws.log.error "Cannot load Audio resource " + resolved_src + " (Message: " + e.message + ", Name: " + e.name + ")" - else if type is "video" - try - asset.video = document.createElement("video") - asset.video.asset = asset - self.data[asset.src] = asset.video - asset.video.setAttribute "style", "display:none;" - asset.video.addEventListener "error", assetError - asset.video.addEventListener "canplay", assetLoaded - document.body.appendChild asset.video - asset.video.src = resolved_src - asset.video.load() - catch e - jaws.log.error "Cannot load Video resource " + resolved_src + " (Message: " + e.message + ", Name: " + e.name + ")" - else - if type is "audio" and not self.can_play[self.getPostfix(asset.src)] - assetSkipped asset - return self - try - req = new XMLHttpRequest() - req.asset = asset - req.onreadystatechange = assetLoaded - req.onerror = assetError - req.open "GET", resolved_src, true - req.responseType = "blob" if type isnt "json" - req.send null - catch e - jaws.log.error "Cannot load " + resolved_src + " (Message: " + e.message + ", Name: " + e.name + ")" - self - - self.displayProgress = (percent_done) -> - return unless jaws.isNumber(percent_done) - return unless jaws.context - jaws.context.save() - jaws.context.fillStyle = "black" - jaws.context.fillRect 0, 0, jaws.width, jaws.height - jaws.context.fillStyle = "white" - jaws.context.strokeStyle = "white" - jaws.context.textAlign = "center" - jaws.context.strokeRect 50 - 1, (jaws.height / 2) - 30 - 1, jaws.width - 100 + 2, 60 + 2 - jaws.context.fillRect 50, (jaws.height / 2) - 30, ((jaws.width - 100) / 100) * percent_done, 60 - jaws.context.font = "11px verdana" - jaws.context.fillText "Loading... " + percent_done + "%", jaws.width / 2, jaws.height / 2 - 35 - jaws.context.font = "11px verdana" - jaws.context.fillStyle = "#ccc" - jaws.context.textBaseline = "bottom" - jaws.context.fillText "powered by www.jawsjs.com", jaws.width / 2, jaws.height - 1 - jaws.context.restore() - return - - return - - jaws.assets = new jaws.Assets() - jaws -)(jaws or {}) diff --git a/jaws/LICENSE b/jaws/LICENSE deleted file mode 100644 index 65c5ca8..0000000 --- a/jaws/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/jaws/README.rdoc b/jaws/README.rdoc deleted file mode 100644 index 5a84523..0000000 --- a/jaws/README.rdoc +++ /dev/null @@ -1,276 +0,0 @@ -= Jaws - HTML5 Javascript web game development library -{<img src="https://travis-ci.org/ippa/jaws.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/ippa/jaws] - -Depends on JavaScript Edition 5. Works with Chrome 9+, Firefox 3.6+, Safari 5+ & IE9. - -Licensed under LGPL so you're free to use it for commercial projects. - -== Highlights -* Animation(), Sprite(), SpriteSheet(), TileMap(), Assets() and other useful constructors -* Easy and robust game state system to switch between menus, play, high score lists and settings -* JSDOC Documentation & commented examples - -Jaws also: -- Uses 2D canvas to draw stuff on the screen -- Does not depend on any other JavaScript library -- Suple-simple-to-use collision detection, bounding box and circles. -- Doesn't try to force a certain "JS class pattern" on you, just pure JavaScript as mother nature intended it -- Tries to make assets (images, music, json data) in webgames as easy as possible -- Often does object literals as arguments for readabillity (ie. new Sprite({image: "player.png", x: 100, y: 100}) -- Builds on lessons learned from years of developing http://github.com/ippa/chingu (Ruby game lib) - -== Local build / test - npm install - npm test - -== Learn more -- {http://jawsjs.com/docs/index.html}[http://jawsjs.com/docs/index.html] - Jaws documentation -- {http://jawsjs.com/}[http://jawsjs.com/] - Official Jaws homepage. Overview and design choices. -- {http://jawsjs.com/jawsjs/test/index.html}[http://jawsjs.com/jawsjs/test/index.html] - Jaws testsuite (QUnit) - -== Tutorials -- {http://videlais.com/2013/05/17/learning-jawsjs-part-1/}[http://videlais.com/2013/05/17/learning-jawsjs-part-1/] - Intro to sprites, collisions and game states by Dan Cox. -- {http://videlais.com/2013/05/23/learning-jawsjs-part-2/}[http://videlais.com/2013/05/23/learning-jawsjs-part-2/] - SpriteList, collideOneWithMany and more. -- {http://videlais.com/2013/06/01/learning-jawsjs-part-3/}[http://videlais.com/2013/06/01/learning-jawsjs-part-3/] - Viewport & assets. -- {http://videlais.com/2013/06/05/learning-jawsjs-part-4/}[http://videlais.com/2013/06/05/learning-jawsjs-part-4/] - SpriteSheet, Animation and game states. - -== What kind of games can you make with Jaws? -Jaws is well suited for "classic" side/top scrolling games (tile based or not) where you have a number of sprite-sheet-animated sprites. -Jaws comes with basic rect-vs-rect/circle-vs-circle collision detection that works well in most cases. If you have _tons_ of sprites (for example, a bullet hell schmup) you probably want to use a physicslib like Box2D or spatial -hashing like quad trees to speed things up. Jaws use of canvas makes pixel perfect collisions and worms-style terrain relatively easy to develop. If your game is very GUI-heavy you might want to base your game on pure HTML-elements instead of canvas-sprites. - -== Simple examples demonstrating certain features -Check out the sourcecode for comments and explanations: - -* http://jawsjs.com/jawsjs/examples/example0.html - get a Jaws-app working with as little code as possible -* http://jawsjs.com/jawsjs/examples/example1.html - basic example with manual setup of canvas -* http://jawsjs.com/jawsjs/examples/example2.html - jaws game states -* http://jawsjs.com/jawsjs/examples/example3.html - jaws.Animation -* http://jawsjs.com/jawsjs/examples/example4.html - jaws.Rect based collision detection -* http://jawsjs.com/jawsjs/examples/example5.html - the jaws.Sprite({anchor: "..."}) parameter -* http://jawsjs.com/jawsjs/examples/example6.html - jaws.Parallax -* http://jawsjs.com/jawsjs/examples/example7.html - simple DOM-based sprite (DEPRECATED) -* http://jawsjs.com/jawsjs/examples/example8.html - jaws.TileMap -* http://jawsjs.com/jawsjs/examples/example9.html - jaws.Viewport (The basics for a sidescrolling platformer right here) -* http://jawsjs.com/jawsjs/examples/example10.html - jaws.game_states.Edit, a simple map editor -* http://jawsjs.com/jawsjs/examples/example11.html - jaws.gfx.retroScaleImage and Literal Notion game state -* http://jawsjs.com/jawsjs/examples/example12.html - jaws.viewport.drawTileMap() - optimized tile map drawing - -== Games using Jaws -* {http://www.ludumdare.com/compo/ludum-dare-29/?action=preview&uid=21212}[http://www.ludumdare.com/compo/ludum-dare-29/?action=preview&uid=21212] - DrillFish: drill through the levels as a very odd fish - by mezka -* {http://recardona.github.io/BitBot/}[http://recardona.github.io/BitBot/] - BitBot: puzzle, enter commands to the robot to reach the goal -* {http://ld27.pauljoannon.com//}[http://ld27.pauljoannon.com/] - Castle Clocksworth: Steampunk-style puzzle adventure -* {http://ippa.se/webgames/the_adventures_of_10_seconds/}[http://ippa.se/webgames/the_adventures_of_10_seconds/] - Small worms-style terrain puzzle-adventure! -* {http://mcfunkypants.com/LD26/}[http://mcfunkypants.com/LD26/] - Colorful abstract platformer by McFunkypants -* {http://games.subdimension.co.uk/LD26/}[http://games.subdimension.co.uk/LD26/] - Unique painting mini-game by Malphas Wats -* {http://mcfunkypants.com/RubbaRabbit/}[http://mcfunkypants.com/RubbaRabbit/] - Fullscreen platformer by McFunkypants -* {http://mcfunkypants.com/Gwendolynn/}[http://mcfunkypants.com/Gwendolynn/] - Gwendolynn, platformer by McFunkypants -* {http://gamejolt.com/games/arcade/b-w-tennis/13883/}[http://gamejolt.com/games/arcade/b-w-tennis/13883/] - Pong-clone by devpoga -* {http://ricardonakamura.org/game/ld25/}[http://ricardonakamura.org/game/ld25/] - Sword of Truth by http://ricardonakamura.org/ -* {http://gamejolt.com/games/arcade/for-victory/14267/}[http://gamejolt.com/games/arcade/for-victory/14267/] - Minimalistic game by devpoga -* {http://www.castleriot.com/}[http://www.castleriot.com/] - Multiplayer game with guards and thieves. By team @ http://nodeknockout.com/teams/third-times-the-char -* {http://ippa.se/webgames/the_escape/index.html}[http://ippa.se/webgames/the_escape/index.html] - "The Escape" - My Ludum Dare #21 entry. Adventure/Puzzle. -* {http://ippa.se/webgames/unexpected_outcome/index.html}[http://ippa.se/webgames/unexpected_outcome/index.html] - A 10-minute adventure game with pixel-perfect collisions -* {http://ippa.se/webgames/unwaivering/index.html}[http://ippa.se/webgames/unwaivering/index.html] - A Jaws-game for http://www.ludumdare.com/compo/2011/01/25/minild-24/ -* {http://dl.dropbox.com/u/73425602/ld23/index.html}[http://dl.dropbox.com/u/73425602/ld23/index.html] - LD #23 game by Nibelung (http://www.ludumdare.com/compo/author/nibelung/) -* {http://www.twitchtetris.com/}[http://www.twitchtetris.com/] - A tetris game by http://twitchcode.blogspot.com/ -* {http://www.mcfunkypants.com/kart/magma/}[http://www.mcfunkypants.com/kart/magma/] - Another pixel perfect game from MC Funky Pants. Don't touch the rising lava! -* {http://dl.dropbox.com/u/15658925/LD22%20release/game.html}[http://dl.dropbox.com/u/15658925/LD22%20release/game.html] - LD#22 entry by "Char" (http://www.ludumdare.com/compo/ludum-dare-22/?action=preview&uid=3407) -* {http://h31p.com/save-atherton/}[http://h31p.com/save-atherton/] - A political satire mini-game from https://github.com/dalethatcher -* {http://www.mcfunkypants.com/LD22/}[http://www.mcfunkypants.com/LD22/] - a Ludum Dare #22 entry by Christer Kaitila -* {http://nibster.net/LD/}[http://nibster.net/LD/] - a Ludum Dare #22 entry by https://github.com/Nibster -* {http://memetika.com/iron_santa/}[http://memetika.com/iron_santa/] - a Ludum Dare #22 entry by https://github.com/dmitrizagidulin -* {http://markbennett.ca/JAWS-on-a-plane/}[http://markbennett.ca/JAWS-on-a-plane/] - A minigame by https://github.com/MarkBennett -* {http://davidgomes.github.com/paxlure/}[http://davidgomes.github.com/paxlure/] - Zombie blasting minigame by https://github.com/davidgomes -* {http://crystalpart.com/hp/}[http://crystalpart.com/hp/] - A mini game by by Ashan Fernando (http://crystalpart.com/wp/) -* {http://magigames.org/runestone_defense.html}[http://magigames.org/runestone_defense.html] - Runestone, a tower defense game by David Blood -* {http://www.tinycrate.com/ld48/vivere/}[http://www.tinycrate.com/ld48/vivere/] - TinyCrate - An experimental game about mixing DNA by http://www.thank-etc-ok.com/ -* {http://www.tinycrate.com/ld48/volo/}[http://www.tinycrate.com/ld48/volo/] - VOLO - A game by http://www.thank-etc-ok.com -* {http://www.philbgarner.com/vampire/}[http://www.philbgarner.com/vampire/] - Scion of Dracula - Play dracula in search for Van Helsing. By Phil Garner. -* {http://charitygamejam.com/?Parachute%20Champ}[http://charitygamejam.com/?Parachute%20Champ] - Parachute Champ - land your parachute safely. By Nicholas Wrenn. -* {https://www.facebook.com/appcenter/hopeofabug}[https://www.facebook.com/appcenter/hopeofabug] - Facebook minigame by Fahad Mahmood. -... missing your game here? Mail me about it (my email is on my github profilepage). Or create a new issue. - -== Libs using Jaws -* {http://mcfunkypants.com/tower/} - Tower Game Starter Kit -* {http://mcfunkypants.com/ludus/}[http://mcfunkypants.com/ludus/] - Platformer Game Starter Kit -* {https://github.com/deisdestiny/Jaws-TS}[https://github.com/deisdestiny/Jaws-TS] - Jaws TypeScript Definitions - -== Loading Jaws -* jaws.js - includes the whole framework in one easy-to-include file. -* jaws-min.js - same as jaws.js but minified with Googles closure compiler. This is probably what you want to include in your project. -* jaws-dynamic.js - dynamically loads all separate jaws files. Useful for debugging errors when patching jaws since you get exact file/line# of possible errors. Warning, jaws-dynamic.js loads all Jaws-files asynchronously, resulting in Jaws might not be fully loaded when the browser reaches your game.js. You can solve this by hooking into the jaws.onload callback and put your jaws.start() there. - -You can also link to invidual files in your HTML: - <script src="/jawsjs/src/core.js"></script> - <script src="/jawsjs/src/sprite.js"></script> - -NOTE: core.js is always needed but after that you can pick and choose depending on what you need. -A rule of thumb is that a file named "foo.js" will include a constructor named Foo(). - -NOTE #2: jaws.js and jaws-min.js does Not include files from src/extras-dir. -Examples of files that aren't included in the mainbundle are: tile_map_pathfinding.js, pixel_map.js and audio.js (jaws <audio>-wrapper). -If you want to use them, load them as usual: - <script src="/jawsjs/src/extras/tile_map_pathfinding.js"></script> - -== Contribute -Jaws accepts contributions, some simple guidelines: -- Formatting: oneFunction(), OneConstrutor() and one_variable -- 2 spaces for indentation -- Don't patch jaws.js or jaws-min.js, but rather the invidual files in the src/-directory -- Please bundle tests with non-trivial patches, see test/-dir for inspiration -- For bigger patches/feature additions, please contact me beforehand to discuss if/how it fits into Jaws and how to form the API -- Naming shouldn't consist of abbreviations, let's use "context", not "ctx" - -Jaws has gotten bigger contributions from: - -https://github.com/videlais - QuadTree() and other things -https://github.com/dmitrizagidulin - SpriteList() rewrite -https://github.com/gregmax17 - Viewport related stuff -https://github.com/MalphasWats - Viewport pathfinding - -== Issues and bugs -If you find an issue with Jaws githubs issue-tracker is the place to go. Easiest for the developers is if you put your game online. If you don't have any hosting check out http://pages.github.com/. -Pasting your problematic code in the issue-ticket itself will usually mean a lot of hassle with supportcode and assets to actually be able to test the code in question. - -== API-deprecations leading up to ver 1.0 - 2013-09: Remove experimental, buggy DOM-sprite-support from Sprite and Text - 2013-08: SpriteList is no longer included in jaws(-min).js bundle, better off using arrays and jaws.draw/jaws.update - 2013-08: jaws.assets.loadAll() - onfinish -> onload, onload -> onprogress - 2013-08: jaws.assets.load(asset, options) - options can have properties onload and onerror - 2013-03: sprite.anchor() is now sprite.setAnchor() - 2013-03: sprite.scale() is now sprite.scaleAll() - -== Example - <html> - <script src="jaws.js"></script> - <body> - - <canvas width=500 height=300></canvas> - - <script> - function MyGameState() { - var player; - var robot; - - this.setup = function() { - player = new jaws.Sprite({image: "player.png", x: 10, y: 200}); - robot = new jaws.Sprite({x: 200, y: 200}); - robot.animation = new jaws.Animation({sprite_sheet: "images/droid_11x15.png", frame_size: [11,15], frame_duration: 120}); - } - this.update = function() { - if(jaws.pressed("left")) { player.x--; } - if(jaws.pressed("right")) { player.x++; } - robot.setImage( robot.animation.next() ); - } - this.draw = function() { - player.draw(); - robot.draw(); - } - } - - window.onload = function() { - jaws.assets.add("images/droid_11x15.png"); - jaws.assets.add("images/player.png"); - jaws.start(MyGameState); - } - </script> - </body> - </html> - -== The same example with comments - /* - * Jaws provides powerful functions like jaws.start() to quickly get a robust gameloop running. - * It's also possible to do it more manually, fetch your own canvas context and send it to new Sprite(). - * Nothing stops you from using jaws.assets or other jaws.helpers with your own game loop either. - * - * Below code shows the preferred way, letting jaws worry about most of the setup, - * so we can get straight to get game logic. - */ - <html> - <script src="jaws.js"></script> - <body> - - <canvas width=500 height=300></canvas> <!-- don't set width/height of canvas with CSS --> - - <script> - /* - * Jaws encourages the use of game states to separate various parts of your game. - * We send MyGameState to jaws.start() to start with. - * You can later switch to another game state with jaws.switchGameState(OtherGameState) - */ - function MyGameState() { - var player; - var robot; - - /* Put your one-time initializing here. Will get called once each time this game state is activated. */ - this.setup = function() { - /* - * Make a sprite, place it at position 10/200. - * The string "player.png" will resolve to a previously fetched resource. - * If we wanted the sprite to be drawn to a special canvas, we could add the option {context: my_canvas_context}. - * Otherwise jaws will just pick the most likely canvas, which works 99% of the times. - */ - player = new jaws.Sprite({image: "player.png", x: 10, y: 200}); - - /* Let's create an animated robot sprite */ - robot = new jaws.Sprite({x: 200, y: 200}); - - /* Creates an animation from a classic sprite sheet */ - robot.animation = new jaws.Animation({sprite_sheet: "images/droid_11x15.png", frame_size: [11,15], frame_duration: 120}); - } - - /* update() is called each gametick with given FPS. Put your game logic here. */ - this.update = function() { - if(jaws.pressed("left")) { player.x--; } - if(jaws.pressed("right")) { player.x++; } - robot.setImage( robot.animation.next() ); - } - - /* draw() is called each gametick just after update() is done. Put your drawing/canvas stuff here. */ - this.draw = function() { - player.draw(); - robot.draw(); - } - } - - /* Let's start jaws after the document is fully loaded... */ - window.onload = function() { - /* - * Add two images to jaws internal list of assets to be loaded. - * You can load them manually with jaws.assets.loadAll({loaded: myAssetsAreReadyCall}). - * jaws.start() will automatically load all unloaded assets while displaying a progress meter. - * - * Alternative way (nice when you have a lot of assets in the same dir): - * jaws.assets.path = "images/"; - * jaws.assets.add(["droid_11x15.png", "player.png"]) - */ - jaws.assets.add("images/droid_11x15.png"); - jaws.assets.add("images/player.png"); - - /* - * jaws.start(MyGameState) is the easiest way to get something up and running. It will in this order: - * - * 1) - * Call jaws.init() that will detect <canvas> (or create one for you) and set up the 2D context. - * It's then available in jaws.canvas and jaws.context and will be used by all new sprites. - * Also setup keyboard input event listeners with jaws.setupInput(). - * - * 2) - * Pre-load all assets while showing progress meter through with jaws.assets.loadAll(). - * Assets are then available with jaws.assets.get("player.png"). - * - * 3) - * Create an instance of given game state constructor (in this case MyGameState) and call setup() on that instance. - * In setup() you usually initialize the game state, create the gameobjects, sprites and so forth. - * - * 4) - * Loop calls to update() and draw() with given FPS (default 60) until the game ends, - * is paused or another game state is activated. - * - */ - jaws.start(MyGameState); - } - </script> - </body> - </html> diff --git a/jaws/jaws-min.js b/jaws/jaws-min.js deleted file mode 100755 index 79fe719..0000000 --- a/jaws/jaws-min.js +++ /dev/null @@ -1,2 +0,0 @@ -var jaws=function(t){function e(e){t.mouse_x=e.pageX||e.clientX,t.mouse_y=e.pageY||e.clientY;var i=t.canvas?t.canvas:t.dom;t.mouse_x-=i.offsetLeft,t.mouse_y-=i.offsetTop}var i,s;return t.SpriteList=function(){throw"To use SpriteList() you need to include src/extras/sprite_list.js"},t.Audio=function(){throw"To use jaws.Audio() you need to include src/extras/audio.js"},t.title=function(e){return t.isString(e)?e?i.innerHTML=e:i.innerHTML:void t.log.error("jaws.title: Passed in value is not a String.")},t.unpack=function(){var e=["Sprite","SpriteList","Animation","SpriteSheet","Parallax","pressed","QuadTree"];e.forEach(function(e){window[e]?t.log.warn("jaws.unpack: "+e+" already exists in global namespace."):window[e]=t[e]})},t.log=function(e,i){t.isString(e)||(e=JSON.stringify(e)),t.log.on&&(s&&t.log.use_log_element&&(i?s.innerHTML+=e+"<br />":s.innerHTML=e),console.log&&t.log.use_console&&console.log("JawsJS: ",e))},t.log.on=!0,t.log.use_console=!1,t.log.use_log_element=!0,t.log.warn=function(e){console.warn&&t.log.use_console&&t.log.on?console.warn(e):t.log("[WARNING]: "+JSON.stringify(e),!0)},t.log.error=function(e){console.error&&t.log.use_console&&t.log.on?console.error(e):t.log("[ERROR]: "+JSON.stringify(e),!0)},t.log.info=function(e){console.info&&t.log.use_console&&t.log.on?console.info(e):t.log("[INFO]: "+JSON.stringify(e),!0)},t.log.debug=function(e){console.debug&&t.log.use_console&&t.log.on?console.debug(e):t.log("[DEBUG]: "+JSON.stringify(e),!0)},t.log.clear=function(){s&&(s.innerHTML=""),console.clear&&console.clear()},t.init=function(o){i=document.getElementsByTagName("title")[0],t.url_parameters=t.getUrlParameters(),t.canvas=document.getElementsByTagName("canvas")[0],t.canvas||(t.dom=document.getElementById("canvas")),t.canvas?t.context=t.canvas.getContext("2d"):t.dom?t.dom.style.position="relative":(t.canvas=document.createElement("canvas"),t.canvas.width=o.width,t.canvas.height=o.height,t.context=t.canvas.getContext("2d"),document.body.appendChild(t.canvas)),s=document.getElementById("jaws-log"),t.url_parameters.debug&&(s||(s=document.createElement("div"),s.id="jaws-log",s.style.cssText="overflow: auto; color: #aaaaaa; width: 300px; height: 150px; margin: 40px auto 0px auto; padding: 5px; border: #444444 1px solid; clear: both; font: 10px verdana; text-align: left;",document.body.appendChild(s))),t.url_parameters.bust_cache&&(t.log.info("Busting cache when loading assets"),t.assets.bust_cache=!0),t.context&&t.useCrispScaling(),t.width=t.canvas?t.canvas.width:t.dom.offsetWidth,t.height=t.canvas?t.canvas.height:t.dom.offsetHeight,t.mouse_x=0,t.mouse_y=0,window.addEventListener("mousemove",e)},t.useCrispScaling=function(){t.context.imageSmoothingEnabled=!1,t.context.webkitImageSmoothingEnabled=!1,t.context.mozImageSmoothingEnabled=!1},t.useSmoothScaling=function(){t.context.imageSmoothingEnabled=!0,t.context.webkitImageSmoothingEnabled=!0,t.context.mozImageSmoothingEnabled=!0},t.start=function(e,i,s){function o(e,s){t.log.info(s+"%: "+e,!0),i.loading_screen&&t.assets.displayProgress(s)}function n(e,i){t.log.info(i+"%: Error loading asset "+e,!0)}function a(){t.log.info("all assets loaded",!0),t.switchGameState(e||window,{fps:r},s)}i||(i={});var r=i.fps||60;return void 0===i.loading_screen&&(i.loading_screen=!0),i.width||(i.width=500),i.height||(i.height=300),t.init(i),t.isFunction(e)||t.isObject(e)?t.isObject(s)||void 0===s?(i.loading_screen&&t.assets.displayProgress(0),t.log.info("setupInput()",!0),t.setupInput(),t.log.info("assets.loadAll()",!0),void(t.assets.length()>0?t.assets.loadAll({onprogress:o,onerror:n,onload:a}):a())):void t.log.error("jaws.start: The setup options for the game state is not an object."):void t.log.error("jaws.start: Passed in GameState is niether function or object")},t.switchGameState=function(e,i,s){if(void 0===i&&(i={}),t.isFunction(e)&&(e=new e),!t.isObject(e))return void t.log.error("jaws.switchGameState: Passed in GameState should be a Function or an Object.");var o=i&&i.fps||t.game_loop&&t.game_loop.fps||60,n=i.setup;t.game_loop&&t.game_loop.stop(),t.clearKeyCallbacks(),t.previous_game_state=t.game_state,t.game_state=e,t.game_loop=new t.GameLoop(e,{fps:o,setup:n},s),t.game_loop.start()},t.imageToCanvas=function(e){if(t.isCanvas(e))return e;if(!t.isImage(e))return void t.log.error("jaws.imageToCanvas: Passed in object is not an Image.");var i=document.createElement("canvas");i.src=e.src,i.width=e.width,i.height=e.height;var s=i.getContext("2d");return s.drawImage(e,0,0,e.width,e.height),i},t.forceArray=function(t){return Array.isArray(t)?t:[t]},t.clear=function(){t.context.clearRect(0,0,t.width,t.height)},t.fill=function(e){t.context.fillStyle=e,t.context.fillRect(0,0,t.width,t.height)},t.draw=function(){var e=arguments;1==e.length&&t.isArray(e[0])&&(e=e[0]);for(var i=0;i<e.length;i++)t.isArray(e[i])?t.draw(e[i]):e[i].draw&&e[i].draw()},t.update=function(){var e=arguments;1==e.length&&t.isArray(e[0])&&(e=e[0]);for(var i=0;i<e.length;i++)t.isArray(e[i])?t.update(e[i]):e[i].update&&e[i].update()},t.isImage=function(t){return"[object HTMLImageElement]"===Object.prototype.toString.call(t)},t.isCanvas=function(t){return"[object HTMLCanvasElement]"===Object.prototype.toString.call(t)},t.isDrawable=function(e){return t.isImage(e)||t.isCanvas(e)},t.isString=function(t){return"string"==typeof t||"object"==typeof t&&t.constructor===String},t.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},t.isArray=function(t){return t?!(-1===t.constructor.toString().indexOf("Array")):!1},t.isObject=function(t){return null!==t&&"object"==typeof t},t.isFunction=function(t){return"[object Function]"===Object.prototype.toString.call(t)},t.isRegExp=function(t){return t instanceof RegExp},t.isOutsideCanvas=function(e){return e.x&&e.y?e.x<0||e.y<0||e.x>t.width||e.y>t.height:void 0},t.forceInsideCanvas=function(e){e.x&&e.y&&(e.x<0&&(e.x=0),e.x>t.width&&(e.x=t.width),e.y<0&&(e.y=0),e.y>t.height&&(e.y=t.height))},t.getUrlParameters=function(){for(var t,e=[],i=window.location.href.slice(window.location.href.indexOf("?")+1).split("&"),s=0;s<i.length;s++)t=i[s].split("="),e.push(t[0]),e[t[0]]=t[1];return e},t.parseOptions=function(e,i,s){e.options=i;for(var o in i)void 0===s[o]&&t.log.warn("jaws.parseOptions: Unsupported property "+o+"for "+e.constructor);for(var o in s)t.isFunction(s[o])&&(s[o]=s[o]()),e[o]=void 0!==i[o]?i[o]:t.clone(s[o])},t.clone=function(e){return t.isArray(e)?e.slice(0):t.isObject(e)?JSON.parse(JSON.stringify(e)):e},t.imageToCanvasContext=function(e){var i=document.createElement("canvas");i.width=e.width,i.height=e.height;var s=i.getContext("2d");return t.context&&(s.imageSmoothingEnabled=t.context.mozImageSmoothingEnabled,s.webkitImageSmoothingEnabled=t.context.mozImageSmoothingEnabled,s.mozImageSmoothingEnabled=t.context.mozImageSmoothingEnabled),s.drawImage(e,0,0,i.width,i.height),s},t.retroScaleImage=function(e,i){var s=t.isImage(e)?t.imageToCanvas(e):e,o=s.getContext("2d"),n=o.getImageData(0,0,s.width,s.height).data,a=document.createElement("canvas");a.width=e.width*i,a.height=e.height*i;for(var r=a.getContext("2d"),h=r.createImageData(a.width,a.height),c=h.width,l=h.height,u=0;l>u;u+=1)for(var d=Math.floor(u/i),f=u*h.width,p=d*e.width,g=0;c>g;g+=1){var m=Math.floor(g/i),w=4*(f+g),x=4*(p+m);h.data[w]=n[x],h.data[w+1]=n[x+1],h.data[w+2]=n[x+2],h.data[w+3]=n[x+3]}return r.putImageData(h,0,0),a},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws);var jaws=function(t){function e(){for(var t in h)delete h[t]}function i(t){var e=e=t.keyCode;h[e]=!1,d[e]?(d[e](e),t.preventDefault()):g[e]&&t.preventDefault()}function s(t){var e=e=t.keyCode;h[e]=!0,u[e]?(u[e](e),t.preventDefault()):g[e]&&t.preventDefault()}function o(t){var e=f[t.button];"Microsoft Internet Explorer"==navigator.appName&&(e=p[t.button]),h[e]=!0,u[e]&&(u[e](e),t.preventDefault())}function n(t){var e=f[t.button];"Microsoft Internet Explorer"==navigator.appName&&(e=p[t.button]),h[e]=!1,d[e]&&(d[e](e),t.preventDefault())}function a(e){h.left_mouse_button=!0,t.mouse_x=e.touches[0].pageX-t.canvas.offsetLeft,t.mouse_y=e.touches[0].pageY-t.canvas.offsetTop}function r(){h.left_mouse_button=!1,t.mouse_x=void 0,t.mouse_y=void 0}var h={},c={0:"48",1:"49",2:"50",3:"51",4:"52",5:"53",6:"54",7:"55",8:"56",9:"57",backspace:"8",tab:"9",enter:"13",shift:"16",ctrl:"17",alt:"18",pause:"19",caps_lock:"20",esc:"27",space:"32",page_up:"33",page_down:"34",end:"35",home:"36",left:"37",up:"38",right:"39",down:"40",insert:"45","delete":"46",a:"65",b:"66",c:"67",d:"68",e:"69",f:"70",g:"71",h:"72",i:"73",j:"74",k:"75",l:"76",m:"77",n:"78",o:"79",p:"80",q:"81",r:"82",s:"83",t:"84",u:"85",v:"86",w:"87",x:"88",y:"89",z:"90",windows_left:"91",windows_right:"92",select:"93",numpad0:"96",numpad1:"97",numpad2:"98",numpad3:"99",numpad4:"100",numpad5:"101",numpad6:"102",numpad7:"103",numpad8:"104",numpad9:"105",asterisk:"106",plus:"107",minus:"109",decimal_point:"110",divide:"111",f1:"112",f2:"113",f3:"114",f4:"115",f5:"116",f6:"117",f7:"118",f8:"119",f9:"120",numlock:"144",scrollock:"145",semicolon:"186",equals:"187",comma:"188",dash:"189",period:"190",slash:"191",grave_accent:"192",open_bracket:"219",backslash:"220",close_bracket:"221",single_quote:"222"},l={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"caps_lock",27:"esc",32:"space",33:"page_up",34:"page_down",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"delete",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",91:"windows_left",92:"windows_right",93:"select",96:"numpad0",97:"numpad1",98:"numpad2",99:"numpad3",100:"numpad4",101:"numpad5",102:"numpad6",103:"numpad7",104:"numpad8",105:"numpad9",106:"asterisk",107:"plus",109:"minus",110:"decimal_point",111:"divide",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",144:"numlock",145:"scrollock",186:"semicolon",187:"equals",188:"comma",189:"dash",190:"period",191:"slash",192:"grave_accent",219:"open_bracket",220:"backslash",221:"close_bracket",222:"single_quote"},u=[],d=[],f=[],p=[],g=[];return t.setupInput=function(){var h=[];h[0]="left_mouse_button",h[1]="center_mouse_button",h[2]="right_mouse_button";var c=[];c[1]="left_mouse_button",c[2]="right_mouse_button",c[4]="center_mouse_button",f=h,p=c,window.addEventListener("keydown",s),window.addEventListener("keyup",i);var l=t.canvas||t.dom;l.addEventListener("mousedown",o,!1),l.addEventListener("mouseup",n,!1),l.addEventListener("touchstart",a,!1),l.addEventListener("touchend",r,!1),window.addEventListener("blur",e,!1),document.oncontextmenu=function(){return!1}},t.preventDefaultKeys=function(){for(var t=arguments,e=0;e<t.length;e++)g[t[e]]=!0},t.pressed=h,t.keyCodes=c,t.keycodeNames=l,t.on_keydown=function(e,i){if(t.isArray(e))for(var s=0;e[s];s++)u[e[s]]=i;else u[e]=i},t.on_keyup=function(e,i){if(t.isArray(e))for(var s=0;e[s];s++)d[e[s]]=i;else d[e]=i},t.clearKeyCallbacks=function(){d=[],u=[]},t}(jaws||{}),jaws=function(t){function e(e){if(t.isDrawable(e)){for(var i=t.isImage(e)?t.imageToCanvas(e):e,s=i.getContext("2d"),o=s.getImageData(0,0,i.width,i.height),n=o.data,a=0;a<n.length;a+=4)255===n[a]&&0===n[a+1]&&255===n[a+2]&&(n[a+3]=0);return s.putImageData(o,0,0),i}}return t.Assets=function(){function i(e){if(t.isString(e)){var i=r.getPostfix(e);return r.file_type[i]?r.file_type[i]:i}t.log.error("jaws.assets.getType: Argument not a String with "+e)}function s(s){var o=this.asset,n=o.src,h=i(o.src);try{if("json"===h){if(4!==this.readyState)return;r.data[o.src]=JSON.parse(this.responseText)}else if("image"===h){var c=r.image_to_canvas?t.imageToCanvas(o.image):o.image;r.fuchia_to_transparent&&"bmp"===r.getPostfix(o.src)&&(c=e(c)),r.data[o.src]=c}else r.data[o.src]="audio"===h&&r.can_play[r.getPostfix(o.src)]?o.audio:"video"===h&&r.can_play[r.getPostfix(o.src)]?o.video:this.response}catch(l){t.log.error("Cannot process "+n+" (Message: "+l.message+", Name: "+l.name+")"),r.data[o.src]=null}r.loaded[n]||r.load_count++,r.loaded[n]=!0,r.loading[n]=!1,a(o,!0,s)}function o(t){r.loaded[t.src]=!0,r.loading[t.src]=!1,r.load_count++,a(t,!0)}function n(t){var e=this.asset;r.error_count++,a(e,!1,t)}function a(t,e,i){var s=parseInt((r.load_count+r.error_count)/r.src_list.length*100);e?(r.onprogress&&r.onprogress(t.src,s),t.onprogress&&void 0!==i&&t.onprogress(i)):(r.onerror&&r.onerror(t.src,s),t.onerror&&void 0!==i&&t.onerror(i)),100===s&&(r.onload&&r.onload(),r.onprogress=null,r.onerror=null,r.onload=null)}if(!(this instanceof arguments.callee))return new arguments.callee;var r=this;r.loaded=[],r.loading=[],r.src_list=[],r.data=[],r.bust_cache=!1,r.image_to_canvas=!0,r.fuchia_to_transparent=!0,r.root="",r.file_type={},r.file_type.json="json",r.file_type.wav="audio",r.file_type.mp3="audio",r.file_type.ogg="audio",r.file_type.m4a="audio",r.file_type.weba="audio",r.file_type.aac="audio",r.file_type.mka="audio",r.file_type.flac="audio",r.file_type.png="image",r.file_type.jpg="image",r.file_type.jpeg="image",r.file_type.gif="image",r.file_type.bmp="image",r.file_type.tiff="image",r.file_type.mp4="video",r.file_type.webm="video",r.file_type.ogv="video",r.file_type.mkv="video",r.can_play={};try{var h=new Audio;r.can_play.wav=!!h.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),r.can_play.ogg=!!h.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),r.can_play.mp3=!!h.canPlayType("audio/mpeg;").replace(/^no$/,""),r.can_play.m4a=!!(h.canPlayType("audio/x-m4a;")||h.canPlayType("audio/aac;")).replace(/^no$/,""),r.can_play.weba=!!h.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),r.can_play.aac=!!h.canPlayType("audio/aac;").replace(/^no$/,""),r.can_play.mka=!!h.canPlayType("audio/x-matroska;").replace(/^no$/,""),r.can_play.flac=!!h.canPlayType("audio/x-flac;").replace(/^no$/,"")}catch(c){}try{var l=document.createElement("video");r.can_play.mp4=!!l.canPlayType("video/mp4;").replace(/^no$/,""),r.can_play.webm=!!l.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/,""),r.can_play.ogv=!!l.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/,""),r.can_play.mkv=!!l.canPlayType("video/x-matroska;").replace(/^no$/,"")}catch(c){}r.length=function(){return r.src_list.length},r.setRoot=function(t){return r.root=t,r},r.get=function(e){if(t.isArray(e))return e.map(function(t){return r.data[t]});if(!t.isString(e))return void t.log.error("jaws.get: Neither String nor Array. Incorrect URL resource "+e);if("*"===e[e.length-1])for(var i=e.replace("*",""),s=0;s<r.src_list.length;s++)if(0==r.src_list[s].indexOf(i)&&r.data[r.src_list[s]])return r.data[r.src_list[s]];return r.data[e]?r.data[e]:void t.log.warn("No such asset: "+e,!0)},r.isLoading=function(e){return t.isString(e)?r.loading[e]:void t.log.error("jaws.isLoading: Argument not a String with "+e)},r.isLoaded=function(e){return t.isString(e)?r.loaded[e]:void t.log.error("jaws.isLoaded: Argument not a String with "+e)},r.getPostfix=function(e){return t.isString(e)?e.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1]:void t.log.error("jaws.assets.getPostfix: Argument not a String with "+e)},r.add=function(e){var i=arguments;1==i.length&&t.isArray(i[0])&&(i=i[0]);for(var s=0;s<i.length;s++)t.isArray(i[s])?r.add(i[s]):t.isString(i[s])?r.src_list.push(i[s]):t.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource "+e);return r},r.loadAll=function(e){return r.load_count=0,r.error_count=0,e.onprogress&&t.isFunction(e.onprogress)&&(r.onprogress=e.onprogress),e.onerror&&t.isFunction(e.onerror)&&(r.onerror=e.onerror),e.onload&&t.isFunction(e.onload)&&(r.onload=e.onload),r.src_list.forEach(function(t){r.load(t)}),r},r.load=function(e,a){if(a||(a={}),!t.isString(e))return void t.log.error("jaws.assets.load: Argument not a String with "+e);var h={},c="";h.src=e,h.onload=a.onload,h.onerror=a.onerror,r.loading[e]=!0;var l=RegExp("^((f|ht)tp(s)?:)?//");c=l.test(e)?h.src:r.root+h.src,r.bust_cache&&(c+="?"+parseInt(1e7*Math.random()));var u=i(h.src);if("image"===u)try{h.image=new Image,h.image.asset=h,h.image.addEventListener("load",s),h.image.addEventListener("error",n),h.image.src=c}catch(d){t.log.error("Cannot load Image resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}else if(r.can_play[r.getPostfix(h.src)]){if("audio"===u)try{h.audio=new Audio,h.audio.asset=h,h.audio.addEventListener("error",n),h.audio.addEventListener("canplay",s),r.data[h.src]=h.audio,h.audio.src=c,h.audio.load()}catch(d){t.log.error("Cannot load Audio resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}else if("video"===u)try{h.video=document.createElement("video"),h.video.asset=h,r.data[h.src]=h.video,h.video.setAttribute("style","display:none;"),h.video.addEventListener("error",n),h.video.addEventListener("canplay",s),document.body.appendChild(h.video),h.video.src=c,h.video.load()}catch(d){t.log.error("Cannot load Video resource "+c+" (Message: "+d.message+", Name: "+d.name+")")}}else{if("audio"===u&&!r.can_play[r.getPostfix(h.src)])return o(h),r;try{var f=new XMLHttpRequest;f.asset=h,f.onreadystatechange=s,f.onerror=n,f.open("GET",c,!0),"json"!==u&&(f.responseType="blob"),f.send(null)}catch(d){t.log.error("Cannot load "+c+" (Message: "+d.message+", Name: "+d.name+")")}}return r},r.displayProgress=function(e){t.isNumber(e)&&t.context&&(t.context.save(),t.context.fillStyle="black",t.context.fillRect(0,0,t.width,t.height),t.context.fillStyle="white",t.context.strokeStyle="white",t.context.textAlign="center",t.context.strokeRect(49,t.height/2-30-1,t.width-100+2,62),t.context.fillRect(50,t.height/2-30,(t.width-100)/100*e,60),t.context.font="11px verdana",t.context.fillText("Loading... "+e+"%",t.width/2,t.height/2-35),t.context.font="11px verdana",t.context.fillStyle="#ccc",t.context.textBaseline="bottom",t.context.fillText("powered by www.jawsjs.com",t.width/2,t.height-1),t.context.restore())}},t.assets=new t.Assets,t}(jaws||{});if("undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){function e(t){this.size=t,this.values=new Array(this.size),this.value,this.add=function(t){if(this.values.length>this.size){this.values.splice(0,1),this.value=0;for(var e=0;this.values[e];e++)this.value+=this.values[e];this.value=this.value/this.size}return this.values.push(t),this},this.get=function(){return parseInt(this.value)}}return window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,16.666)}}(),t.GameLoop=function(i,s,o){if(!(this instanceof arguments.callee))return new arguments.callee(i,s);this.tick_duration=0,this.fps=0,this.ticks=0;var n,a=!1,r=!1,h=this,c=new e(20);this.runtime=function(){return this.last_tick-this.first_tick},this.start=function(){t.log.info("Game loop start",!0),this.first_tick=(new Date).getTime(),this.current_tick=(new Date).getTime(),this.last_tick=(new Date).getTime(),s.setup!==!1&&i.setup&&i.setup(o),step_delay=1e3/s.fps,60==s.fps?requestAnimFrame(this.loop):n=setInterval(this.loop,step_delay)},this.loop=function(){h.current_tick=(new Date).getTime(),h.tick_duration=h.current_tick-h.last_tick,h.fps=c.add(1e3/h.tick_duration).get(),r||a||(i.update&&i.update(),i.draw&&i.draw(),h.ticks++),60!=s.fps||r||requestAnimFrame(h.loop),h.last_tick=h.current_tick},this.pause=function(){a=!0},this.unpause=function(){a=!1},this.stop=function(){n&&clearInterval(n),r=!0}},t}(jaws||{}),jaws=function(t){var e=0;return t.Rect=function(t,i,s,o){return this instanceof arguments.callee?(this.x=t,this.y=i,this.width=s,this.height=o,this.right=t+s-e,void(this.bottom=i+o-e)):new arguments.callee(t,i,s,o)},t.Rect.prototype.getPosition=function(){return[this.x,this.y]},t.Rect.prototype.move=function(t,e){return this.x+=t,this.y+=e,this.right+=t,this.bottom+=e,this},t.Rect.prototype.moveTo=function(t,i){return this.x=t,this.y=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.resize=function(t,i){return this.width+=t,this.height+=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.clone=function(){return new t.Rect(this.x,this.y,this.width,this.height)},t.Rect.prototype.shrink=function(t,i){return this.x+=t,this.y+=i,this.width-=t+t,this.height-=i+i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.resizeTo=function(t,i){return this.width=t,this.height=i,this.right=this.x+this.width-e,this.bottom=this.y+this.height-e,this},t.Rect.prototype.draw=function(){return t.context.strokeStyle="red",t.context.strokeRect(this.x-.5,this.y-.5,this.width,this.height),this},t.Rect.prototype.collidePoint=function(t,e){return t>=this.x&&t<=this.right&&e>=this.y&&e<=this.bottom},t.Rect.prototype.collideRect=function(t){return(this.x>=t.x&&this.x<=t.right||t.x>=this.x&&t.x<=this.right)&&(this.y>=t.y&&this.y<=t.bottom||t.y>=this.y&&t.y<=this.bottom)},t.Rect.prototype.toString=function(){return"[Rect "+this.x+", "+this.y+", "+this.width+", "+this.height+"]"},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Rect);var jaws=function(jaws){return jaws.Sprite=function(t){return this instanceof arguments.callee?(this.set(t),void(this.context=t.context?t.context:jaws.context)):new arguments.callee(t)},jaws.Sprite.prototype.default_options={x:0,y:0,alpha:1,angle:0,flipped:!1,anchor_x:0,anchor_y:0,image:null,image_path:null,anchor:null,scale_image:null,damping:1,scale_x:1,scale_y:1,scale:1,color:"#ddd",width:16,height:16,_constructor:null,context:null,data:null},jaws.Sprite.prototype.set=function(t){if(jaws.isString(this.image)&&(this.image_path=this.image),jaws.parseOptions(this,t,this.default_options),this.scale&&(this.scale_x=this.scale_y=this.scale),this.image&&this.setImage(this.image),this.scale_image&&this.scaleImage(this.scale_image),this.anchor&&this.setAnchor(this.anchor),!this.image&&this.color&&this.width&&this.height){var e=document.createElement("canvas"),i=e.getContext("2d");e.width=this.width,e.height=this.height,i.fillStyle=this.color,i.fillRect(0,0,this.width,this.height),this.image=e}return this.cacheOffsets(),this},jaws.Sprite.prototype.clone=function(object){var constructor=this._constructor?eval(this._constructor):this.constructor,new_sprite=new constructor(this.attributes());return new_sprite._constructor=this._constructor||this.constructor.name,new_sprite},jaws.Sprite.prototype.setImage=function(t){var e=this;return jaws.isDrawable(t)?(this.image=t,this.cacheOffsets()):(jaws.assets.isLoaded(t)?(this.image=jaws.assets.get(t),this.cacheOffsets()):(jaws.log.warn("Image '"+t+"' not preloaded with jaws.assets.add(). Image and a working sprite.rect() will be delayed."),jaws.assets.load(t,{onload:function(){e.image=jaws.assets.get(t),e.cacheOffsets()}})),this)},jaws.Sprite.prototype.stepToWhile=function(t,e,i){var s=1,o=t<this.x?-s:s,n=e<this.y?-s:s;t=parseInt(t),e=parseInt(e);for(var a=!1,r=!1;;)if(a===!1&&(this.x!=t&&(this.x+=o),i(this)||(this.x-=o,a=!0)),r===!1&&(this.y!=e&&(this.y+=n),i(this)||(this.y-=n,r=!0)),(a||this.x==t)&&(r||this.y==e))return{x:a,y:r}},jaws.Sprite.prototype.stepWhile=function(t,e,i){return this.stepToWhile(this.x+t,this.y+e,i)},jaws.Sprite.prototype.flip=function(){return this.flipped=this.flipped?!1:!0,this},jaws.Sprite.prototype.flipTo=function(t){return this.flipped=t,this},jaws.Sprite.prototype.rotate=function(t){return this.angle+=t,this},jaws.Sprite.prototype.rotateTo=function(t){return this.angle=t,this},jaws.Sprite.prototype.moveTo=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.x=t,this.y=e,this},jaws.Sprite.prototype.move=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),t&&(this.x+=t),e&&(this.y+=e),this},jaws.Sprite.prototype.scaleAll=function(t){return this.scale_x*=t,this.scale_y*=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleTo=function(t){return this.scale_x=this.scale_y=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleWidth=function(t){return this.scale_x*=t,this.cacheOffsets()},jaws.Sprite.prototype.scaleHeight=function(t){return this.scale_y*=t,this.cacheOffsets()},jaws.Sprite.prototype.setX=function(t){return this.x=t,this},jaws.Sprite.prototype.setY=function(t){return this.y=t,this},jaws.Sprite.prototype.setTop=function(t){return this.y=t+this.top_offset,this},jaws.Sprite.prototype.setBottom=function(t){return this.y=t-this.bottom_offset,this},jaws.Sprite.prototype.setLeft=function(t){return this.x=t+this.left_offset,this},jaws.Sprite.prototype.setRight=function(t){return this.x=t-this.right_offset,this},jaws.Sprite.prototype.setWidth=function(t){return this.scale_x=t/this.image.width,this.cacheOffsets()},jaws.Sprite.prototype.setHeight=function(t){return this.scale_y=t/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.resize=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.scale_x=(this.width+t)/this.image.width,this.scale_y=(this.height+e)/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.resizeTo=function(t,e){return jaws.isArray(t)&&void 0===e&&(e=t[1],t=t[0]),this.scale_x=t/this.image.width,this.scale_y=e/this.image.height,this.cacheOffsets()},jaws.Sprite.prototype.setAnchor=function(t){var e={top_left:[0,0],left_top:[0,0],center_left:[0,.5],left_center:[0,.5],bottom_left:[0,1],left_bottom:[0,1],top_center:[.5,0],center_top:[.5,0],center_center:[.5,.5],center:[.5,.5],bottom_center:[.5,1],center_bottom:[.5,1],top_right:[1,0],right_top:[1,0],center_right:[1,.5],right_center:[1,.5],bottom_right:[1,1],right_bottom:[1,1]};return(a=e[t])&&(this.anchor_x=a[0],this.anchor_y=a[1],this.image&&this.cacheOffsets()),this},jaws.Sprite.prototype.cacheOffsets=function(){return this.image?(this.width=this.image.width*this.scale_x,this.height=this.image.height*this.scale_y,this.left_offset=this.width*this.anchor_x,this.top_offset=this.height*this.anchor_y,this.right_offset=this.width*(1-this.anchor_x),this.bottom_offset=this.height*(1-this.anchor_y),this.cached_rect&&this.cached_rect.resizeTo(this.width,this.height),this):void 0},jaws.Sprite.prototype.rect=function(){return!this.cached_rect&&this.width&&(this.cached_rect=new jaws.Rect(this.x,this.y,this.width,this.height)),this.cached_rect&&this.cached_rect.moveTo(this.x-this.left_offset,this.y-this.top_offset),this.cached_rect},jaws.Sprite.prototype.draw=function(){return this.image?(this.context.save(),this.context.translate(this.x,this.y),0!=this.angle&&jaws.context.rotate(this.angle*Math.PI/180),this.flipped&&this.context.scale(-1,1),this.context.globalAlpha=this.alpha,this.context.translate(-this.left_offset,-this.top_offset),this.context.drawImage(this.image,0,0,this.width,this.height),this.context.restore(),this):this},jaws.Sprite.prototype.scaleImage=function(t){return this.image?(this.setImage(jaws.retroScaleImage(this.image,t)),this):void 0},jaws.Sprite.prototype.asCanvasContext=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");return jaws.context&&(e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled),e.drawImage(this.image,0,0,this.width,this.height),e},jaws.Sprite.prototype.asCanvas=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");return jaws.context&&(e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled),e.drawImage(this.image,0,0,this.width,this.height),t},jaws.Sprite.prototype.toString=function(){return"[Sprite "+this.x.toFixed(2)+", "+this.y.toFixed(2)+", "+this.width+", "+this.height+"]"},jaws.Sprite.prototype.attributes=function(){var t={};return t._constructor=this._constructor||"jaws.Sprite",t.x=parseFloat(this.x.toFixed(2)),t.y=parseFloat(this.y.toFixed(2)),t.image=this.image_path,t.alpha=this.alpha,t.flipped=this.flipped,t.angle=parseFloat(this.angle.toFixed(2)),t.scale_x=this.scale_x,t.scale_y=this.scale_y,t.anchor_x=this.anchor_x,t.anchor_y=this.anchor_y,null!==this.data&&(t.data=jaws.clone(this.data)),t},jaws.Sprite.parse=function(objects){function parseArray(array){array.forEach(function(data){var constructor=data._constructor?eval(data._constructor):data.constructor;if(jaws.isFunction(constructor)){jaws.log.info("Creating "+data._constructor+"("+data.toString()+")",!0);var object=new constructor(data);object._constructor=data._constructor||data.constructor.name,sprites.push(object)}})}var sprites=[];return jaws.isArray(objects)?objects.every(function(t){return t._constructor})?parseArray(objects):sprites=objects:jaws.isString(objects)&&(parseArray(JSON.parse(objects)),jaws.log.info(objects)),sprites},jaws.Sprite.prototype.toJSON=function(){return JSON.stringify(this.attributes())},jaws}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Sprite);var jaws=function(t){function e(t,e,i,s,o){var n=document.createElement("canvas");n.width=s,n.height=o;var a=n.getContext("2d");return a.drawImage(t,e,i,s,o,0,0,n.width,n.height),n}return t.SpriteSheet=function(i){if(!(this instanceof arguments.callee))return new arguments.callee(i);if(t.parseOptions(this,i,this.default_options),t.isString(this.image)&&!i.frame_size){var s=new RegExp("_(\\d+)x(\\d+)","g"),o=s.exec(this.image);this.frame_size=[],this.frame_size[0]=parseInt(o[1]),this.frame_size[1]=parseInt(o[2])}if(this.image=t.isDrawable(this.image)?this.image:t.assets.data[this.image],this.scale_image){var n=t.isDrawable(this.image)?this.image:t.assets.get(this.image);this.frame_size[0]*=this.scale_image,this.frame_size[1]*=this.scale_image,this.image=t.retroScaleImage(n,this.scale_image)}if(this.frames=[],"down"==this.orientation)for(var a=this.offset;a<this.image.width;a+=this.frame_size[0])for(var r=0;r<this.image.height;r+=this.frame_size[1])this.frames.push(e(this.image,a,r,this.frame_size[0],this.frame_size[1]));else for(var r=this.offset;r<this.image.height;r+=this.frame_size[1])for(var a=0;a<this.image.width;a+=this.frame_size[0])this.frames.push(e(this.image,a,r,this.frame_size[0],this.frame_size[1]))},t.SpriteSheet.prototype.default_options={image:null,orientation:"down",frame_size:[32,32],offset:0,scale_image:null},t.SpriteSheet.prototype.toString=function(){return"[SpriteSheet "+this.frames.length+" frames]"},t}(jaws||{}),jaws=function(t){return t.Animation=function(e){if(!(this instanceof arguments.callee))return new arguments.callee(e);if(t.parseOptions(this,e,this.default_options),e.sprite_sheet){var i=new t.SpriteSheet({image:e.sprite_sheet,scale_image:this.scale_image,frame_size:this.frame_size,orientation:this.orientation,offset:this.offset});this.frames=i.frames,this.frame_size=i.frame_size}if(e.scale_image){var s=t.isDrawable(e.sprite_sheet)?e.sprite_sheet:t.assets.get(e.sprite_sheet);this.frame_size[0]*=e.scale_image,this.frame_size[1]*=e.scale_image,e.sprite_sheet=t.retroScaleImage(s,e.scale_image)}if(this.current_tick=(new Date).getTime(),this.last_tick=(new Date).getTime(),this.sum_tick=0,e.subsets){this.subsets={};for(subset in e.subsets)start_stop=e.subsets[subset],this.subsets[subset]=this.slice(start_stop[0],start_stop[1])}},t.Animation.prototype.default_options={frames:[],subsets:[],frame_duration:100,index:0,loop:1,bounce:0,frame_direction:1,frame_size:null,orientation:"down",on_end:null,offset:0,scale_image:null,sprite_sheet:null},t.Animation.prototype.subset=function(t){return this.subsets[t]},t.Animation.prototype.update=function(){return this.current_tick=(new Date).getTime(),this.sum_tick+=this.current_tick-this.last_tick,this.last_tick=this.current_tick,this.sum_tick>this.frame_duration&&(this.index+=this.frame_direction,this.sum_tick=0),(this.index>=this.frames.length||this.index<0)&&(this.bounce?(this.frame_direction=-this.frame_direction,this.index+=2*this.frame_direction):this.loop?this.index=this.frame_direction<0?this.frames.length-1:0:(this.index-=this.frame_direction,this.on_end&&(this.on_end(),this.on_end=null))),this -},t.Animation.prototype.slice=function(e,i){var s={};return s.frame_duration=this.frame_duration,s.loop=this.loop,s.bounce=this.bounce,s.on_end=this.on_end,s.frame_direction=this.frame_direction,s.frames=this.frames.slice().slice(e,i),new t.Animation(s)},t.Animation.prototype.next=function(){return this.update(),this.frames[this.index]},t.Animation.prototype.atLastFrame=function(){return this.index==this.frames.length-1},t.Animation.prototype.atFirstFrame=function(){return 0==this.index},t.Animation.prototype.currentFrame=function(){return this.frames[this.index]},t.Animation.prototype.toString=function(){return"[Animation, "+this.frames.length+" frames]"},t}(jaws||{});if("undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){function e(t,e){for(var i=function(e){return void 0!==t.isSpriteList?t.at(e):t[e]},s=[],o=new Array(e),n=0;e>n;n++)o[n]=n;for(var n=e-1,a=t.length;n>=0;a=t.length){for(s.push(o.map(i));n>=0&&o[n]===a-1;)n--,a--;if(n>=0){o[n]+=1;for(var r=n+1;e>r;r++)o[r]=o[r-1]+1;n=e-1}}return s}return t.collideOneWithOne=function(e,i){return e.radius&&i.radius&&e!==i&&t.collideCircles(e,i)?!0:e.rect&&i.rect&&e!==i&&t.collideRects(e.rect(),i.rect())?!0:!1},t.collideOneWithMany=function(e,i,s){var o=[];if(s){for(var n=0;n<i.length;n++)t.collideOneWithOne(e,i[n])&&(s(e,i[n]),o.push(i[n]));return o}return i.filter(function(i){return t.collideOneWithOne(e,i)})},t.collideManyWithMany=function(i,s,o){var n=[];return i===s?e(i,2).forEach(function(e){t.collideOneWithOne(e[0],e[1])&&(o?o(e[0],e[1]):n.push([e[0],e[1]]))}):i.forEach(function(e){s.forEach(function(i){t.collideOneWithOne(e,i)&&(o?o(e,i):n.push([e,i]))})}),n},t.collideCircles=function(e,i){return t.distanceBetween(e,i)<e.radius+i.radius},t.collideRects=function(t,e){return(t.x>=e.x&&t.x<=e.right||e.x>=t.x&&e.x<=t.right)&&(t.y>=e.y&&t.y<=e.bottom||e.y>=t.y&&e.y<=t.bottom)},t.distanceBetween=function(t,e){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))},t.collide=function(e,i,s){if((e.rect||e.radius)&&i.forEach)return t.collideOneWithMany(e,i,s).length>0;if(e.forEach&&i.forEach)return t.collideManyWithMany(e,i,s).length>0;if(e.forEach&&(i.rect||i.radius))return t.collideOneWithMany(i,e,s).length>0;if(e.rect&&i.rect||e.radius&&i.radius){var o=t.collideOneWithOne(e,i);if(!s||!o)return o;s(e,i)}},t}(jaws||{}),jaws=function(t){return t.PixelMap=function(e){return this instanceof arguments.callee?(this.options=e,this.scale=e.scale||1,this.x=e.x||0,this.y=e.y||0,e.image?(this.setContext(e.image),e.scale_image&&this.setContext(t.retroScaleImage(this.context.canvas,e.scale_image)),this.width=this.context.canvas.width*this.scale,this.height=this.context.canvas.height*this.scale):t.log.warn("PixelMap needs an image to work with"),this.named_colors=[],void this.update()):new arguments.callee(e)},t.PixelMap.prototype.setContext=function(e){var e=t.isDrawable(e)?e:t.assets.get(e);this.context=t.imageToCanvasContext(e)},t.PixelMap.prototype.update=function(t,e,i,s){if((void 0===t||0>t)&&(t=0),(void 0===e||0>e)&&(e=0),(void 0===i||i>this.width)&&(i=this.width),(void 0===s||s>this.height)&&(s=this.height),0==arguments.length)this.data=this.context.getImageData(t,e,i,s).data;else for(var o=this.context.getImageData(t,e,i,s).data,n=0,a=4*this.width,r=e*this.width*4+4*t,h=4*i,c=0;s>c;c++){for(var l=0;h>l;l++)this.data[r+l]=o[n++];r+=a}},t.PixelMap.prototype.draw=function(){t.context.drawImage(this.context.canvas,this.x,this.y,this.width,this.height)},t.PixelMap.prototype.namedColorAtRect=function(t,e){for(var i=t.x,s=t.y;i<t.right-1;i++)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;s<t.bottom-1;s++)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;i>t.x;i--)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);for(;s>t.y;s--)if(this.namedColorAt(i,s)==e||void 0===e)return this.namedColorAt(i,s);return!1},t.PixelMap.prototype.at=function(t,e){t=parseInt(t),e=parseInt(e),0>e&&(e=0);var i=e*this.width*4+4*t,s=this.data[i],o=this.data[i+1],n=this.data[i+2],a=this.data[i+3];return[s,o,n,a]},t.PixelMap.prototype.namedColorAt=function(t,e){for(var i=this.at(t,e),s=0;s<this.named_colors.length;s++){var o=this.named_colors[s].name,n=this.named_colors[s].color;if(n[0]==i[0]&&n[1]==i[1]&&n[2]==i[2]&&n[3]==i[3])return o}},t.PixelMap.prototype.nameColor=function(t,e){this.named_colors.push({name:e,color:t})},t}(jaws||{}),jaws=function(t){return t.Parallax=function(e){return this instanceof arguments.callee?void t.parseOptions(this,e,this.default_options):new arguments.callee(e)},t.Parallax.prototype.default_options={width:function(){return t.width},height:function(){return t.height},scale:1,repeat_x:null,repeat_y:null,camera_x:0,camera_y:0,layers:[]},t.Parallax.prototype.draw=function(){for(var t,e,i=0;i<this.layers.length;i++)for(t=this.layers[i],e=this.repeat_x?-(this.camera_x/t.damping%t.width):-(this.camera_x/t.damping),t.y=this.repeat_y?-(this.camera_y/t.damping%t.height):-(this.camera_y/t.damping),t.x=e;t.y<this.height;){for(;t.x<this.width&&(t.x+t.width>=0&&t.y+t.height>=0&&t.draw(),t.x=t.x+t.width,this.repeat_x););if(t.y=t.y+t.height,t.x=e,!this.repeat_y)break}},t.Parallax.prototype.addLayer=function(e){var i=new t.ParallaxLayer(e);i.scaleAll(this.scale),this.layers.push(i)},t.Parallax.prototype.toString=function(){return"[Parallax "+this.x+", "+this.y+". "+this.layers.length+" layers]"},t.ParallaxLayer=function(e){return this instanceof arguments.callee?(this.damping=e.damping||0,void t.Sprite.call(this,e)):new arguments.callee(e)},t.ParallaxLayer.prototype=t.Sprite.prototype,t}(jaws||{}),jaws=function(jaws){return jaws.Text=function(t){return this instanceof arguments.callee?(this.set(t),t.context&&(this.context=t.context),void(t.context||jaws.context&&(this.context=jaws.context))):new arguments.callee(t)},jaws.Text.prototype.default_options={x:0,y:0,alpha:1,angle:0,anchor_x:0,anchor_y:0,anchor:"top_left",damping:1,style:"normal",fontFace:"serif",fontSize:12,color:"black",textAlign:"start",textBaseline:"alphabetic",text:"",wordWrap:!1,width:function(){return jaws.width},height:function(){return jaws.height},shadowColor:null,shadowBlur:null,shadowOffsetX:null,shadowOffsetY:null,_constructor:null},jaws.Text.prototype.set=function(t){return jaws.parseOptions(this,t,this.default_options),this.anchor&&this.setAnchor(this.anchor),this.cacheOffsets(),this},jaws.Text.prototype.clone=function(){var constructor=this._constructor?eval(this._constructor):this.constructor,new_sprite=new constructor(this.attributes());return new_sprite._constructor=this._constructor||this.constructor.name,new_sprite},jaws.Text.prototype.rotate=function(t){return this.angle+=t,this},jaws.Text.prototype.rotateTo=function(t){return this.angle=t,this},jaws.Text.prototype.moveTo=function(t,e){return this.x=t,this.y=e,this},jaws.Text.prototype.move=function(t,e){return t&&(this.x+=t),e&&(this.y+=e),this},jaws.Text.prototype.setX=function(t){return this.x=t,this},jaws.Text.prototype.setY=function(t){return this.y=t,this},jaws.Text.prototype.setTop=function(t){return this.y=t+this.top_offset,this},jaws.Text.prototype.setBottom=function(t){return this.y=t-this.bottom_offset,this},jaws.Text.prototype.setLeft=function(t){return this.x=t+this.left_offset,this},jaws.Text.prototype.setRight=function(t){return this.x=t-this.right_offset,this},jaws.Text.prototype.setWidth=function(t){return this.width=t,this.cacheOffsets(),this},jaws.Text.prototype.setHeight=function(t){return this.height=t,this.cacheOffsets(),this},jaws.Text.prototype.resize=function(t,e){return this.width+=t,this.height+=e,this.cacheOffsets(),this},jaws.Text.prototype.resizeTo=function(t,e){return this.width=t,this.height=e,this.cacheOffsets(),this},jaws.Text.prototype.setAnchor=function(t){var e={top_left:[0,0],left_top:[0,0],center_left:[0,.5],left_center:[0,.5],bottom_left:[0,1],left_bottom:[0,1],top_center:[.5,0],center_top:[.5,0],center_center:[.5,.5],center:[.5,.5],bottom_center:[.5,1],center_bottom:[.5,1],top_right:[1,0],right_top:[1,0],center_right:[1,.5],right_center:[1,.5],bottom_right:[1,1],right_bottom:[1,1]};return e.hasOwnProperty(t)&&(this.anchor_x=e[t][0],this.anchor_y=e[t][1],this.cacheOffsets()),this},jaws.Text.prototype.cacheOffsets=function(){return this.left_offset=this.width*this.anchor_x,this.top_offset=this.height*this.anchor_y,this.right_offset=this.width*(1-this.anchor_x),this.bottom_offset=this.height*(1-this.anchor_y),this.cached_rect&&this.cached_rect.resizeTo(this.width,this.height),this},jaws.Text.prototype.rect=function(){return!this.cached_rect&&this.width&&(this.cached_rect=new jaws.Rect(this.x,this.y,this.width,this.height)),this.cached_rect&&this.cached_rect.moveTo(this.x-this.left_offset,this.y-this.top_offset),this.cached_rect},jaws.Text.prototype.draw=function(){this.context.save(),0!==this.angle&&this.context.rotate(this.angle*Math.PI/180),this.context.globalAlpha=this.alpha,this.context.translate(-this.left_offset,-this.top_offset),this.context.fillStyle=this.color,this.context.font=this.style+" "+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var t=this.y,e=this.x;if(this.wordWrap)for(var i=this.text.split(" "),s="",o=0;o<i.length;o++){var n=s+i[o]+" ",a=this.context.measureText(n);this.y<t+this.height&&(a.width>this.width?(this.context.fillText(s,this.x,this.y),s=i[o]+" ",this.y+=this.fontSize):s=n,this.context.fillText(s,this.x,this.y))}else if(this.context.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var i=this.text.split(" "),s=" ",o=0;o<i.length;o++){var n=s+i[o]+" ";this.context.measureText(n).width<Math.abs(this.width-this.x)&&(this.context.fillText(n,this.x,this.y),s=i[o]+" ",s=n)}return this.y=t,this.x=e,this.context.restore(),this},jaws.Text.prototype.asCanvasContext=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled,this.context.fillStyle=this.color,this.context.font=this.style+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var i=this.y,s=this.x;if(this.wordWrap)for(var o=this.text.split(" "),n="",a=0;a<o.length;a++){var r=n+o[a]+" ",h=this.context.measureText(r);this.y<i+this.height&&(h.width>this.width?(this.context.fillText(n,this.x,this.y),n=o[a]+" ",this.y+=this.fontSize):n=r,this.context.fillText(n,this.x,this.y))}else if(this.context.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var o=this.text.split(" "),n=" ",a=0;a<o.length;a++){var r=n+o[a]+" ";this.context.measureText(r).width<Math.abs(this.width-this.x)&&(this.context.fillText(r,this.x,this.y),n=o[a]+" ",n=r)}return this.y=i,this.x=s,e},jaws.Text.prototype.asCanvas=function(){var t=document.createElement("canvas");t.width=this.width,t.height=this.height;var e=t.getContext("2d");e.mozImageSmoothingEnabled=jaws.context.mozImageSmoothingEnabled,this.context.fillStyle=this.color,this.context.font=this.style+this.fontSize+"px "+this.fontFace,this.context.textBaseline=this.textBaseline,this.context.textAlign=this.textAlign,this.shadowColor&&(this.context.shadowColor=this.shadowColor),this.shadowBlur&&(this.context.shadowBlur=this.shadowBlur),this.shadowOffsetX&&(this.context.shadowOffsetX=this.shadowOffsetX),this.shadowOffsetY&&(this.context.shadowOffsetY=this.shadowOffsetY);var i=this.y,s=this.x;if(this.wordWrap)for(var o=this.text.split(" "),n="",a=0;a<o.length;a++){var r=n+o[a]+" ",h=e.measureText(r);this.y<i+this.height&&(h.width>this.width?(e.fillText(n,this.x,this.y),n=o[a]+" ",this.y+=this.fontSize):n=r,e.fillText(n,this.x,this.y))}else if(e.measureText(this.text).width<this.width)this.context.fillText(this.text,this.x,this.y);else for(var o=this.text.split(" "),n=" ",a=0;a<o.length;a++){var r=n+o[a]+" ";e.measureText(r).width<Math.abs(this.width-this.x)&&(e.fillText(r,this.x,this.y),n=o[a]+" ",n=r)}return this.y=i,this.x=s,t},jaws.Text.prototype.toString=function(){return"[Text "+this.x.toFixed(2)+", "+this.y.toFixed(2)+", "+this.width+", "+this.height+"]"},jaws.Text.prototype.attributes=function(){var t=this.options;return t._constructor=this._constructor||"jaws.Text",t.x=parseFloat(this.x.toFixed(2)),t.y=parseFloat(this.y.toFixed(2)),t.text=this.text,t.alpha=this.alpha,t.angle=parseFloat(this.angle.toFixed(2)),t.anchor_x=this.anchor_x,t.anchor_y=this.anchor_y,t.style=this.style,t.fontSize=this.fontSize,t.fontFace=this.fontFace,t.color=this.color,t.textAlign=this.textAlign,t.textBaseline=this.textBaseline,t.wordWrap=this.wordWrap,t.width=this.width,t.height=this.height,t},jaws.Text.prototype.toJSON=function(){return JSON.stringify(this.attributes())},jaws}(jaws||{});if("undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.Text),"undefined"!=typeof require)var jaws=require("./core.js");var jaws=function(t){return t.QuadTree=function(e){this.depth=arguments[1]||0,this.bounds=e||new t.Rect(0,0,t.width,t.height),this.nodes=[],this.objects=[]},t.QuadTree.prototype.clear=function(){this.objects=[];for(var t=0;t<this.nodes.length;t++)"undefined"!=typeof this.nodes[t]&&(this.nodes[t].clear(),delete this.nodes[t])},t.QuadTree.prototype.split=function(){var e=Math.round(this.bounds.width/2),i=Math.round(this.bounds.height/2),s=this.bounds.x,o=this.bounds.y;this.nodes[0]=new t.QuadTree(new t.Rect(s+e,o,e,i),this.depth+1),this.nodes[1]=new t.QuadTree(new t.Rect(s,o,e,i),this.depth+1),this.nodes[2]=new t.QuadTree(new t.Rect(s,o+i,e,i),this.depth+1),this.nodes[3]=new t.QuadTree(new t.Rect(s+e,o+i,e,i),this.depth+1)},t.QuadTree.prototype.getIndex=function(t){var e=-1,i=this.bounds.x+this.bounds.width/2,s=this.bounds.y+this.bounds.height/2,o=t.y<s&&t.y+t.height<s,n=t.y>s;return t.x<i&&t.x+t.width<i?o?e=1:n&&(e=2):t.x>i&&(o?e=0:n&&(e=3)),e},t.QuadTree.prototype.insert=function(t){if(t.hasOwnProperty("x")||t.hasOwnProperty("y")||t.hasOwnProperty("width")||t.hasOwnProperty("height")){if("undefined"!=typeof this.nodes[0]){var e=this.getIndex(t);if(-1!==e)return void this.nodes[e].insert(t)}this.objects.push(t),"undefined"==typeof this.nodes[0]&&this.split();for(var i=0;i<this.objects.length;){var e=this.getIndex(this.objects[i]);-1!==e?this.nodes[e].insert(this.objects.splice(i,1)[0]):i++}}},t.QuadTree.prototype.retrieve=function(t){if(t.hasOwnProperty("x")||t.hasOwnProperty("y")||t.hasOwnProperty("width")||t.hasOwnProperty("height")){var e=this.getIndex(t),i=this.objects;if("undefined"!=typeof this.nodes[0])if(-1!==e)i=i.concat(this.nodes[e].retrieve(t));else for(var s=0;s<this.nodes.length;s++)i=i.concat(this.nodes[s].retrieve(t));return i}},t.QuadTree.prototype.collide=function(e,i,s){var o=!1,n=new t.QuadTree,a=[];return e.forEach||(a.push(e),e=a),i.forEach||(a=[],a.push(i),i=a),i.forEach(function(t){n.insert(t)}),e.forEach(function(e){t.collide(e,n.retrieve(e),s)&&(o=!0)}),n.clear(),o},t}(jaws||{});"undefined"!=typeof module&&"exports"in module&&(module.exports=jaws.QuadTree),window.addEventListener("load",function(){jaws.onload&&jaws.onload()},!1);
\ No newline at end of file diff --git a/jaws/jaws.js b/jaws/jaws.js deleted file mode 100755 index 2090b4e..0000000 --- a/jaws/jaws.js +++ /dev/null @@ -1,3764 +0,0 @@ -/* Built at: Sun Oct 05 2014 18:48:22 GMT+0200 (CEST) */ -/** - * @namespace JawsJS core functions. - * - * Jaws, a HTML5 canvas/javascript 2D game development framework - * - * Homepage: http://jawsjs.com/ - * Source: http://github.com/ippa/jaws/ - * Documentation: http://jawsjs.com/docs/ - * - * Works with: Chrome 6.0+, Firefox 3.6+, 4+, IE 9+ - * License: LGPL - http://www.gnu.org/licenses/lgpl.html - * - * Jaws uses the "module pattern". - * Adds 1 global, <b>jaws</b>, so plays nice with all other JS libs. - * - * Formating guide: - * jaws.oneFunction() - * jaws.one_variable = 1 - * new jaws.OneConstructor - * - * @property {int} mouse_x Mouse X position with respect to the canvas-element - * @property {int} mouse_y Mouse Y position with respect to the canvas-element - * @property {canvas} canvas The detected/created canvas-element used for the game - * @property {context} context The detected/created canvas 2D-context, used for all draw-operations - * @property {int} width Width of the canvas-element - * @property {int} height Height of the canvas-element - */ -var jaws = (function(jaws) { - - var title; - var log_tag; - - /* - * Placeholders for constructors in extras-dir. We define the constructors here to be able to give ppl better error-msgs. - * When the correct from extras-dir is included, these will be overwritten. - * - */ - //jaws.Parallax = function() { throw("To use jaws.Parallax() you need to include src/extras/parallax.js") } - //jaws.QuadTree = function() { throw("To use QuadTree() you need to include src/extras/quadtree.js") } - //jaws.PixelMap = function() { throw("To use PixelMap() you need to include src/extras/pixel_map.js") } - jaws.SpriteList = function() { throw("To use SpriteList() you need to include src/extras/sprite_list.js") } - jaws.Audio = function() { throw("To use jaws.Audio() you need to include src/extras/audio.js") } - - /** - * Returns or sets contents of title's innerHTML - * @private - * @param {type} value The new value to set the innerHTML of title - * @returns {string} The innerHTML of title - */ - jaws.title = function(value) { - - if (!jaws.isString(value)) { - jaws.log.error("jaws.title: Passed in value is not a String."); - return; - } - - if (value) { - return (title.innerHTML = value); - } - return title.innerHTML; - }; - - /** - * Unpacks Jaws core-constructors into the global namespace. - * If a global property is already taken, a warning will be written to jaws log. - */ - jaws.unpack = function() { - var make_global = ["Sprite", "SpriteList", "Animation", "SpriteSheet", "Parallax", "pressed", "QuadTree"]; - - make_global.forEach(function(item) { - if (window[item]) { - jaws.log.warn("jaws.unpack: " + item + " already exists in global namespace."); - } - else { - window[item] = jaws[item]; - } - }); - }; - - /** - * Writes messages to either log_tag (if set) or console.log (if available) - * @param {string} msg The string to write - * @param {boolean} append If messages should be appended or not - */ - jaws.log = function(msg, append) { - if (!jaws.isString(msg)) { - msg = JSON.stringify(msg); - } - - if (jaws.log.on) { - if (log_tag && jaws.log.use_log_element) { - if (append) { - log_tag.innerHTML += msg + "<br />"; - } - else { - log_tag.innerHTML = msg; - } - } - if (console.log && jaws.log.use_console) { - console.log("JawsJS: ", msg); - } - } - }; - - /** - * If logging should take place or not - * @type {boolean} - */ - jaws.log.on = true; - - /** - * If console.log should be used during log writing - * @type {boolean} - */ - jaws.log.use_console = false; - - /** - * If log_tag should be used during log writing - * @type {boolean} - */ - jaws.log.use_log_element = true; - - /** - * Write messages to console.warn (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.warn = function(msg) { - if (console.warn && jaws.log.use_console && jaws.log.on) { - console.warn(msg); - } else { - jaws.log("[WARNING]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.error (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.error = function(msg) { - if (console.error && jaws.log.use_console && jaws.log.on) { - console.error(msg); - } else { - jaws.log("[ERROR]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.info (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.info = function(msg) { - if (console.info && jaws.log.use_console && jaws.log.on) { - console.info(msg); - } else { - jaws.log("[INFO]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.debug (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.debug = function(msg) { - if (console.debug && jaws.log.use_console && jaws.log.on) { - console.debug(msg); - } else { - jaws.log("[DEBUG]: " + JSON.stringify(msg), true); - } - }; - - /** - * Clears the contents of log_tag element (if set) and console.log (if set) - */ - jaws.log.clear = function() { - if (log_tag) { - log_tag.innerHTML = ""; - } - if (console.clear) { - console.clear(); - } - }; - - /** - * Initalizes jaws{canvas, context, dom, width, height} - * @private - * @param {object} options Object-literal of constructor properties - * @see jaws.url_parameters() - */ - jaws.init = function(options) { - - /* Find <title> tag */ - title = document.getElementsByTagName('title')[0]; - jaws.url_parameters = jaws.getUrlParameters(); - - jaws.canvas = document.getElementsByTagName('canvas')[0]; - if (!jaws.canvas) { - jaws.dom = document.getElementById("canvas"); - } - - // Ordinary <canvas>, get context - if (jaws.canvas) { - jaws.context = jaws.canvas.getContext('2d'); - } - else if (jaws.dom) { - jaws.dom.style.position = "relative"; - } - else { - jaws.canvas = document.createElement("canvas"); - jaws.canvas.width = options.width; - jaws.canvas.height = options.height; - jaws.context = jaws.canvas.getContext('2d'); - document.body.appendChild(jaws.canvas); - } - - /* - * If debug=1 parameter is present in the URL, let's either find <div id="jaws-log"> or create the tag. - * jaws.log(message) will use this div for debug/info output to the gamer or developer - * - */ - log_tag = document.getElementById('jaws-log'); - if (jaws.url_parameters["debug"]) { - if (!log_tag) { - log_tag = document.createElement("div"); - log_tag.id = "jaws-log"; - log_tag.style.cssText = "overflow: auto; color: #aaaaaa; width: 300px; height: 150px; margin: 40px auto 0px auto; padding: 5px; border: #444444 1px solid; clear: both; font: 10px verdana; text-align: left;"; - document.body.appendChild(log_tag); - } - } - - - if(jaws.url_parameters["bust_cache"]) { - jaws.log.info("Busting cache when loading assets") - jaws.assets.bust_cache = true; - } - - /* Let's scale sprites retro-style by default */ - if (jaws.context) - jaws.useCrispScaling(); - - jaws.width = jaws.canvas ? jaws.canvas.width : jaws.dom.offsetWidth; - jaws.height = jaws.canvas ? jaws.canvas.height : jaws.dom.offsetHeight; - - jaws.mouse_x = 0; - jaws.mouse_y = 0; - window.addEventListener("mousemove", saveMousePosition); - }; - - /** - * Use 'retro' crisp scaling when drawing sprites through the canvas API, this is the default - */ - jaws.useCrispScaling = function() { - jaws.context.imageSmoothingEnabled = false; - jaws.context.webkitImageSmoothingEnabled = false; - jaws.context.mozImageSmoothingEnabled = false; - }; - - /** - * Use smooth antialiased scaling when drawing sprites through the canvas API - */ - jaws.useSmoothScaling = function() { - jaws.context.imageSmoothingEnabled = true; - jaws.context.webkitImageSmoothingEnabled = true; - jaws.context.mozImageSmoothingEnabled = true; - }; - - /** - * Keeps updated mouse coordinates in jaws.mouse_x and jaws.mouse_y - * This is called each time event "mousemove" triggers. - * @private - * @param {EventObject} e The EventObject populated by the calling event - */ - function saveMousePosition(e) { - jaws.mouse_x = (e.pageX || e.clientX); - jaws.mouse_y = (e.pageY || e.clientY); - - var game_area = jaws.canvas ? jaws.canvas : jaws.dom; - jaws.mouse_x -= game_area.offsetLeft; - jaws.mouse_y -= game_area.offsetTop; - } - - /** - * 1) Calls jaws.init(), detects or creats a canvas, and sets up the 2D context (jaws.canvas and jaws.context). - * 2) Pre-loads all defined assets with jaws.assets.loadAll(). - * 3) Creates an instance of game_state and calls setup() on that instance. - * 4) Loops calls to update() and draw() with given FPS until game ends or another game state is activated. - * @param {function} game_state The game state function to be started - * @param {object} options Object-literal of game loop properties - * @param {object} game_state_setup_options Object-literal of game state properties and values - * @see jaws.init() - * @see jaws.setupInput() - * @see jaws.assets.loadAll() - * @see jaws.switchGameState() - * @example - * - * jaws.start(MyGame) // Start game state Game() with default options - * jaws.start(MyGame, {fps: 30}) // Start game state Game() with options, in this case jaws will run your game with 30 frames per second. - * jaws.start(window) // Use global functions setup(), update() and draw() if available. Not the recommended way but useful for testing and mini-games. - * - */ - jaws.start = function(game_state, options, game_state_setup_options) { - if (!options) options = {}; - - var fps = options.fps || 60; - if (options.loading_screen === undefined) options.loading_screen = true; - if (!options.width) options.width = 500; - if (!options.height) options.height = 300; - - /* Takes care of finding/creating canvas-element and debug-div */ - jaws.init(options); - - if (!jaws.isFunction(game_state) && !jaws.isObject(game_state)) { - jaws.log.error("jaws.start: Passed in GameState is niether function or object"); - return; - } - if (!jaws.isObject(game_state_setup_options) && game_state_setup_options !== undefined) { - jaws.log.error("jaws.start: The setup options for the game state is not an object."); - return; - } - - if (options.loading_screen) { - jaws.assets.displayProgress(0); - } - - jaws.log.info("setupInput()", true); - jaws.setupInput(); - - /* Callback for when one single asset has been loaded */ - function assetProgress(src, percent_done) { - jaws.log.info(percent_done + "%: " + src, true); - if (options.loading_screen) { - jaws.assets.displayProgress(percent_done); - } - } - - /* Callback for when an asset can't be loaded*/ - function assetError(src, percent_done) { - jaws.log.info(percent_done + "%: Error loading asset " + src, true); - } - - /* Callback for when all assets are loaded */ - function assetsLoaded() { - jaws.log.info("all assets loaded", true); - jaws.switchGameState(game_state || window, {fps: fps}, game_state_setup_options); - } - - jaws.log.info("assets.loadAll()", true); - if (jaws.assets.length() > 0) { - jaws.assets.loadAll({onprogress: assetProgress, onerror: assetError, onload: assetsLoaded}); - } - else { - assetsLoaded(); - } - }; - - /** - * Switchs to a new active game state and saves previous game state in jaws.previous_game_state - * @param {function} game_state The game state function to start - * @param {object} options The object-literal properties to pass to the new game loop - * @param {object} game_state_setup_options The object-literal properties to pass to starting game state - * @example - * - * function MenuState() { - * this.setup = function() { ... } - * this.draw = function() { ... } - * this.update = function() { - * if(pressed("enter")) jaws.switchGameState(GameState); // Start game when Enter is pressed - * } - * } - * - * function GameState() { - * this.setup = function() { ... } - * this.update = function() { ... } - * this.draw = function() { ... } - * } - * - * jaws.start(MenuState) - * - */ - jaws.switchGameState = function(game_state, options, game_state_setup_options) { - if(options === undefined) options = {}; - - if(jaws.isFunction(game_state)) { - game_state = new game_state; - } - if(!jaws.isObject(game_state)) { - jaws.log.error("jaws.switchGameState: Passed in GameState should be a Function or an Object."); - return; - } - - var fps = (options && options.fps) || (jaws.game_loop && jaws.game_loop.fps) || 60; - var setup = options.setup - - jaws.game_loop && jaws.game_loop.stop(); - jaws.clearKeyCallbacks(); - - jaws.previous_game_state = jaws.game_state; - jaws.game_state = game_state; - jaws.game_loop = new jaws.GameLoop(game_state, {fps: fps, setup: setup}, game_state_setup_options); - jaws.game_loop.start(); - }; - - /** - * Creates a new HTMLCanvasElement from a HTMLImageElement - * @param {HTMLImageElement} image The HTMLImageElement to convert to a HTMLCanvasElement - * @returns {HTMLCanvasElement} A HTMLCanvasElement with drawn HTMLImageElement content - */ - jaws.imageToCanvas = function(image) { - if (jaws.isCanvas(image)) return image; - - if (!jaws.isImage(image)) { - jaws.log.error("jaws.imageToCanvas: Passed in object is not an Image."); - return; - } - - var canvas = document.createElement("canvas"); - canvas.src = image.src; - canvas.width = image.width; - canvas.height = image.height; - - var context = canvas.getContext("2d"); - context.drawImage(image, 0, 0, image.width, image.height); - return canvas; - }; - - /** - * Returns object as an array - * @param {object} obj An array or object - * @returns {array} Either an array or the object as an array - * @example - * - * jaws.forceArray(1) // --> [1] - * jaws.forceArray([1,2]) // --> [1,2] - */ - jaws.forceArray = function(obj) { - return Array.isArray(obj) ? obj : [obj]; - }; - - /** - * Clears screen (the canvas-element) through context.clearRect() - */ - jaws.clear = function() { - jaws.context.clearRect(0, 0, jaws.width, jaws.height); - }; - - /** Fills the screen with given fill_style */ - jaws.fill = function(fill_style) { - jaws.context.fillStyle = fill_style; - jaws.context.fillRect(0, 0, jaws.width, jaws.height); - }; - - - /** - * calls draw() on everything you throw on it. Give it arrays, argumentlists, arrays of arrays. - * - */ - jaws.draw = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) jaws.draw(list[i]); - else if(list[i].draw) list[i].draw(); - } - } - - /** - * calls update() on everything you throw on it. Give it arrays, argumentlists, arrays of arrays. - * - */ - jaws.update = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) jaws.update(list[i]); - else if(list[i].update) list[i].update(); - } - } - - /** - * Tests if object is an image or not - * @param {object} obj An Image or image-like object - * @returns {boolean} If object's prototype is "HTMLImageElement" - */ - jaws.isImage = function(obj) { - return Object.prototype.toString.call(obj) === "[object HTMLImageElement]"; - }; - - /** - * Tests if object is a Canvas object - * @param {type} obj A canvas or canvas-like object - * @returns {boolean} If object's prototype is "HTMLCanvasElement" - */ - jaws.isCanvas = function(obj) { - return Object.prototype.toString.call(obj) === "[object HTMLCanvasElement]"; - }; - - /** - * Tests if an object is either a canvas or an image object - * @param {object} obj A canvas or canva-like object - * @returns {boolean} If object isImage or isCanvas - */ - jaws.isDrawable = function(obj) { - return jaws.isImage(obj) || jaws.isCanvas(obj); - }; - - /** - * Tests if an object is a string or not - * @param {object} obj A string or string-like object - * @returns {boolean} The result of typeof and constructor testing - */ - jaws.isString = function(obj) { - return typeof obj === "string" || (typeof obj === "object" && obj.constructor === String); - }; - - /** - * Tests if an object is a number or not - * @param {number} n A number or number-like value - * @returns {boolean} If n passed isNaN() and isFinite() - */ - jaws.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - - /** - * Tests if an object is an Array or not - * @param {object} obj An array or array-like object - * @returns {boolean} If object's constructor is "Array" - */ - jaws.isArray = function(obj) { - if (!obj) - return false; - return !(obj.constructor.toString().indexOf("Array") === -1); - }; - - /** - * Tests if an object is an Object or not - * @param {object} value An object or object-like enitity - * @returns {boolean} If object is not null and typeof 'object' - */ - jaws.isObject = function(value) { - return value !== null && typeof value === 'object'; - }; - - /** - * Tests if an object is a function or not - * @param {object} obj A function or function-like object - * @returns {boolean} If the prototype of the object is "Function" - */ - jaws.isFunction = function(obj) { - return (Object.prototype.toString.call(obj) === "[object Function]"); - }; - - /** - * Tests if an object is a regular expression or not - * @param {object} obj A /regexp/-object - * @returns {boolean} If the object is an instance of RegExp - */ - jaws.isRegExp = function(obj) { - return (obj instanceof RegExp); - }; - - - /** - * Tests if an object is within drawing canvas (jaws.width and jaws.height) - * @param {object} item An object with both x and y properties - * @returns {boolean} If the item's x and y are less than 0 or more than jaws.width or jaws.height - */ - jaws.isOutsideCanvas = function(item) { - if (item.x && item.y) { - return (item.x < 0 || item.y < 0 || item.x > jaws.width || item.y > jaws.height); - } - }; - - /** - * Sets x and y properties to 0 (if less than), or jaws.width or jaws.height (if greater than) - * @param {object} item An object with x and y properties - */ - jaws.forceInsideCanvas = function(item) { - if (item.x && item.y) { - if (item.x < 0) { - item.x = 0; - } - if (item.x > jaws.width) { - item.x = jaws.width; - } - if (item.y < 0) { - item.y = 0; - } - if (item.y > jaws.height) { - item.y = jaws.height; - } - } - }; - - /** - * Parses current window.location for URL parameters and values - * @returns {array} Hash of url-parameters and their values - * @example - * // Given the current URL is <b>http://test.com/?debug=1&foo=bar</b> - * jaws.getUrlParameters() // --> {debug: 1, foo: bar} - */ - jaws.getUrlParameters = function() { - var vars = [], hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split('='); - vars.push(hash[0]); - vars[hash[0]] = hash[1]; - } - return vars; - }; - - /** - * Compares an object's default properties against those sent to its constructor - * @param {object} object The object to compare and assign new values - * @param {object} options Object-literal of constructor properties and new values - * @param {object} defaults Object-literal of properties and their default values - */ - jaws.parseOptions = function(object, options, defaults) { - object["options"] = options; - - for (var option in options) { - if (defaults[option] === undefined) { - jaws.log.warn("jaws.parseOptions: Unsupported property " + option + "for " + object.constructor); - } - } - for (var option in defaults) { - if( jaws.isFunction(defaults[option]) ) defaults[option] = defaults[option](); - object[option] = (options[option] !== undefined) ? options[option] : jaws.clone(defaults[option]); - } - }; - - /** - * Returns a shallow copy of an array or object - * @param {array|object} value The array or object to clone - * @returns {array|object} A copy of an array of object - */ - jaws.clone = function(value) { - if (jaws.isArray(value)) - return value.slice(0); - if (jaws.isObject(value)) - return JSON.parse(JSON.stringify(value)); - return value; - }; - - /* - * Converts image to canvas 2D context. Then you can draw on it :). - */ - jaws.imageToCanvasContext = function(image) { - var canvas = document.createElement("canvas") - canvas.width = image.width - canvas.height = image.height - - var context = canvas.getContext("2d") - if(jaws.context) { - context.imageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - context.webkitImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - } - - context.drawImage(image, 0, 0, canvas.width, canvas.height) - return context - } - - /** - * scale 'image' by factor 'factor'. - * Scaling is done using nearest-neighbor ( retro-blocky-style ). - * Returns a canvas. - */ - jaws.retroScaleImage = function(image, factor) { - var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image - var context = canvas.getContext("2d") - var data = context.getImageData(0,0,canvas.width,canvas.height).data - - // Create new canvas to return - var canvas2 = document.createElement("canvas") - canvas2.width = image.width * factor - canvas2.height = image.height * factor - var context2 = canvas2.getContext("2d") - var to_data = context2.createImageData(canvas2.width, canvas2.height) - - var w2 = to_data.width - var h2 = to_data.height - for (var y=0; y < h2; y += 1) { - var y2 = Math.floor(y / factor) - var y_as_x = y * to_data.width - var y2_as_x = y2 * image.width - - for (var x=0; x < w2; x += 1) { - var x2 = Math.floor(x / factor) - var y_dst = (y_as_x + x) * 4 - var y_src = (y2_as_x + x2) * 4 - - to_data.data[y_dst] = data[y_src]; - to_data.data[y_dst+1] = data[y_src+1]; - to_data.data[y_dst+2] = data[y_src+2]; - to_data.data[y_dst+3] = data[y_src+3]; - } - } - - context2.putImageData(to_data, 0, 0) - - return canvas2 - } - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws } - -var jaws = (function(jaws) { - - var pressed_keys = {} - var previously_pressed_keys = {} - var keyCodes = {"0":"48","1":"49","2":"50","3":"51","4":"52","5":"53","6":"54","7":"55","8":"56","9":"57","backspace":"8","tab":"9","enter":"13","shift":"16","ctrl":"17","alt":"18","pause":"19","caps_lock":"20","esc":"27","space":"32","page_up":"33","page_down":"34","end":"35","home":"36","left":"37","up":"38","right":"39","down":"40","insert":"45","delete":"46","a":"65","b":"66","c":"67","d":"68","e":"69","f":"70","g":"71","h":"72","i":"73","j":"74","k":"75","l":"76","m":"77","n":"78","o":"79","p":"80","q":"81","r":"82","s":"83","t":"84","u":"85","v":"86","w":"87","x":"88","y":"89","z":"90","windows_left":"91","windows_right":"92","select":"93","numpad0":"96","numpad1":"97","numpad2":"98","numpad3":"99","numpad4":"100","numpad5":"101","numpad6":"102","numpad7":"103","numpad8":"104","numpad9":"105","asterisk":"106","plus":"107","minus":"109","decimal_point":"110","divide":"111","f1":"112","f2":"113","f3":"114","f4":"115","f5":"116","f6":"117","f7":"118","f8":"119","f9":"120","numlock":"144","scrollock":"145","semicolon":"186","equals":"187","comma":"188","dash":"189","period":"190","slash":"191","grave_accent":"192","open_bracket":"219","backslash":"220","close_bracket":"221","single_quote":"222"}; - var keycodeNames = {"8":"backspace","9":"tab","13":"enter","16":"shift","17":"ctrl","18":"alt","19":"pause","20":"caps_lock","27":"esc","32":"space","33":"page_up","34":"page_down","35":"end","36":"home","37":"left","38":"up","39":"right","40":"down","45":"insert","46":"delete","48":"0","49":"1","50":"2","51":"3","52":"4","53":"5","54":"6","55":"7","56":"8","57":"9","65":"a","66":"b","67":"c","68":"d","69":"e","70":"f","71":"g","72":"h","73":"i","74":"j","75":"k","76":"l","77":"m","78":"n","79":"o","80":"p","81":"q","82":"r","83":"s","84":"t","85":"u","86":"v","87":"w","88":"x","89":"y","90":"z","91":"windows_left","92":"windows_right","93":"select","96":"numpad0","97":"numpad1","98":"numpad2","99":"numpad3","100":"numpad4","101":"numpad5","102":"numpad6","103":"numpad7","104":"numpad8","105":"numpad9","106":"asterisk","107":"plus","109":"minus","110":"decimal_point","111":"divide","112":"f1","113":"f2","114":"f3","115":"f4","116":"f5","117":"f6","118":"f7","119":"f8","120":"f9","144":"numlock","145":"scrollock","186":"semicolon","187":"equals","188":"comma","189":"dash","190":"period","191":"slash","192":"grave_accent","219":"open_bracket","220":"backslash","221":"close_bracket","222":"single_quote"}; - var on_keydown_callbacks = [] - var on_keyup_callbacks = [] - var mousebuttoncode_to_string = [] - var ie_mousebuttoncode_to_string = [] - var prevent_default_keys = [] - -/** @private - * Map all javascript keycodes to easy-to-remember letters/words - */ -jaws.setupInput = function() { - var m = [] - - m[0] = "left_mouse_button" - m[1] = "center_mouse_button" - m[2] = "right_mouse_button" - - var ie_m = []; - ie_m[1] = "left_mouse_button"; - ie_m[2] = "right_mouse_button"; - ie_m[4] = "center_mouse_button"; - - mousebuttoncode_to_string = m - ie_mousebuttoncode_to_string = ie_m; - - window.addEventListener("keydown", handleKeyDown); - window.addEventListener("keyup", handleKeyUp); - - var jawswindow = jaws.canvas || jaws.dom - jawswindow.addEventListener("mousedown", handleMouseDown, false); - jawswindow.addEventListener("mouseup", handleMouseUp, false); - jawswindow.addEventListener("touchstart", handleTouchStart, false); - jawswindow.addEventListener("touchend", handleTouchEnd, false); - - window.addEventListener("blur", resetPressedKeys, false); - - // this turns off the right click context menu which screws up the mouseup event for button 2 - document.oncontextmenu = function() {return false}; -} - -/** @private - * Reset input-hash. Called when game is blurred so a key-controlled player doesn't keep on moving when the game isn't focused. - */ -function resetPressedKeys(e) { - for (var x in pressed_keys) { - delete pressed_keys[x]; - } -} - -/** @private - * handle event "onkeydown" by remembering what key was pressed - */ -function handleKeyUp(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var code = code = e.keyCode; - pressed_keys[code] = false; - if (on_keyup_callbacks[code]) { - on_keyup_callbacks[code](code) - e.preventDefault() - } else if (prevent_default_keys[code]) { - e.preventDefault(); - } -} - -/** @private - * handle event "onkeydown" by remembering what key was un-pressed - */ -function handleKeyDown(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var code = code = e.keyCode; - pressed_keys[code] = true; - if (on_keydown_callbacks[code]) { - on_keydown_callbacks[code](code); - e.preventDefault(); - } else if (prevent_default_keys[code]) { - e.preventDefault(); - } -} -/** @private - * handle event "onmousedown" by remembering what button was pressed - */ -function handleMouseDown(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var human_name = mousebuttoncode_to_string[e.button]; // 0 1 2 - if (navigator.appName == "Microsoft Internet Explorer"){ - human_name = ie_mousebuttoncode_to_string[e.button]; - } - pressed_keys[human_name] = true - if(on_keydown_callbacks[human_name]) { - on_keydown_callbacks[human_name](human_name); - e.preventDefault(); - } -} - - -/** @private - * handle event "onmouseup" by remembering what button was un-pressed - */ -function handleMouseUp(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var human_name = mousebuttoncode_to_string[e.button] - - if (navigator.appName == "Microsoft Internet Explorer"){ - human_name = ie_mousebuttoncode_to_string[e.button]; - } - pressed_keys[human_name] = false; - if(on_keyup_callbacks[human_name]) { - on_keyup_callbacks[human_name](human_name); - e.preventDefault(); - } -} - -/** @private - * handle event "touchstart" by remembering what button was pressed - */ -function handleTouchStart(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - pressed_keys["left_mouse_button"] = true - jaws.mouse_x = e.touches[0].pageX - jaws.canvas.offsetLeft; - jaws.mouse_y = e.touches[0].pageY - jaws.canvas.offsetTop; - //e.preventDefault() -} - -/** @private - * handle event "touchend" by remembering what button was pressed - */ -function handleTouchEnd(e) { - pressed_keys["left_mouse_button"] = false; - jaws.mouse_x = undefined; - jaws.mouse_y = undefined; -} - -/** - * Prevents default browseraction for given keys. - * @example - * jaws.preventDefaultKeys( ["down"] ) // Stop down-arrow-key from scrolling page down - */ -jaws.preventDefaultKeys = function(array_of_strings) { - var list = arguments; - for(var i=0; i < list.length; i++) { - prevent_default_keys[list[i]] = true; - } -} - -/** - * Array: If a key is currently pressed, the value associated to the key is true, otherwise false or not present. - */ -jaws.pressed = pressed_keys; - -/** - * Mapping key names to codes, and vice-versa - */ -jaws.keyCodes = keyCodes; -jaws.keycodeNames = keycodeNames; - -/** - * sets up a callback for a key (or array of keys) to call when it's pressed down - * - * @example - * // call goLeft() when left arrow key is pressed - * jaws.on_keypress(jaws.keyCode("left"), goLeft) - * - * // call fireWeapon() when SPACE or CTRL is pressed - * jaws.on_keypress([jaws.keyCodes["space"],jaws.keyCodes["ctrl"]], fireWeapon) - */ -jaws.on_keydown = function(key, callback) { - if (jaws.isArray(key)) { - for (var i=0; key[i]; i++) { - on_keydown_callbacks[key[i]] = callback; - } - } else { - on_keydown_callbacks[key] = callback; - } -} - -/** - * sets up a callback when a key (or array of keys) to call when it's released - */ -jaws.on_keyup = function(key, callback) { - if (jaws.isArray(key)) { - for (var i=0; key[i]; i++) { - on_keyup_callbacks[key[i]] = callback; - } - } else { - on_keyup_callbacks[key] = callback; - } -} - -/** @private - * Clean up all callbacks set by on_keydown / on_keyup - */ -jaws.clearKeyCallbacks = function() { - on_keyup_callbacks = []; - on_keydown_callbacks = []; -} - -return jaws; -})(jaws || {}); - -var jaws = (function(jaws) { - /** - * @fileOverview jaws.assets properties and functions - * - * Loads and processes image, sound, video, and json assets - * (Used internally by JawsJS to create <b>jaws.assets</b>) - * - * @class Jaws.Assets - * @constructor - * @property {boolean} bust_cache Add a random argument-string to assets-urls when loading to bypass any cache - * @property {boolean} fuchia_to_transparent Convert the color fuchia to transparent when loading .bmp-files - * @property {boolean} image_to_canvas Convert all image assets to canvas internally - * @property {string} root Rootdir from where all assets are loaded - * @property {array} file_type Listing of file postfixes and their associated types - * @property {array} can_play Listing of postfixes and (during runtime) populated booleans - */ - jaws.Assets = function Assets() { - if (!(this instanceof arguments.callee)) - return new arguments.callee(); - - var self = this; - - self.loaded = []; - self.loading = []; - self.src_list = []; - self.data = []; - - self.bust_cache = false; - self.image_to_canvas = true; - self.fuchia_to_transparent = true; - self.root = ""; - - self.file_type = {}; - self.file_type["json"] = "json"; - self.file_type["wav"] = "audio"; - self.file_type["mp3"] = "audio"; - self.file_type["ogg"] = "audio"; - self.file_type['m4a'] = "audio"; - self.file_type['weba'] = "audio"; - self.file_type['aac'] = "audio"; - self.file_type['mka'] = "audio"; - self.file_type['flac'] = "audio"; - self.file_type["png"] = "image"; - self.file_type["jpg"] = "image"; - self.file_type["jpeg"] = "image"; - self.file_type["gif"] = "image"; - self.file_type["bmp"] = "image"; - self.file_type["tiff"] = "image"; - self.file_type['mp4'] = "video"; - self.file_type['webm'] = "video"; - self.file_type['ogv'] = "video"; - self.file_type['mkv'] = "video"; - - self.can_play = {}; - - try { - var audioTest = new Audio(); - self.can_play["wav"] = !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''); - self.can_play["ogg"] = !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["mp3"] = !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''); - self.can_play["m4a"] = !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''); - self.can_play["weba"] = !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["aac"] = !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''); - self.can_play["mka"] = !!audioTest.canPlayType('audio/x-matroska;').replace(/^no$/, ''); - self.can_play["flac"] = !!audioTest.canPlayType('audio/x-flac;').replace(/^no$/, ''); - } - catch(e) { - } - - try { - var videoTest = document.createElement('video'); - self.can_play["mp4"] = !!videoTest.canPlayType('video/mp4;').replace(/^no$/, ''); - self.can_play["webm"] = !!videoTest.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["ogv"] = !!videoTest.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["mkv"] = !!videoTest.canPlayType('video/x-matroska;').replace(/^no$/, ''); - } - catch(e) { - } - - /** - * Returns the length of the resource list - * @public - * @returns {number} The length of the resource list - */ - self.length = function() { - return self.src_list.length; - }; - - /** - * Set root prefix-path to all assets - * - * @example - * jaws.assets.setRoot("music/").add(["music.mp3", "music.ogg"]).loadAll() - * - * @public - * @param {string} path-prefix for all following assets - * @returns {object} self - */ - self.setRoot = function(path) { - self.root = path - return self - } - - /** - * Get one or more resources from their URLs. Supports simple wildcard (you can end a string with "*"). - * - * @example - * jaws.assets.add(["song.mp3", "song.ogg"]) - * jaws.assets.get("song.*") // -> Will return song.ogg in firefox and song.mp3 in IE - * - * @public - * @param {string|array} src The resource(s) to retrieve - * @returns {array|object} Array or single resource if found in cache. Undefined otherwise. - */ - self.get = function(src) { - if (jaws.isArray(src)) { - return src.map(function(i) { - return self.data[i]; - }); - } - else if (jaws.isString(src)) { - // Wildcard? song.*, match against asset-srcs, make sure it's loaded and return content of first match. - if(src[src.length-1] === "*") { - var needle = src.replace("*", "") - for(var i=0; i < self.src_list.length; i++) { - if(self.src_list[i].indexOf(needle) == 0 && self.data[self.src_list[i]]) - return self.data[self.src_list[i]]; - } - } - - // TODO: self.loaded[src] is false for supported files for some odd reason. - if (self.data[src]) { return self.data[src]; } - else { jaws.log.warn("No such asset: " + src, true); } - } - else { - jaws.log.error("jaws.get: Neither String nor Array. Incorrect URL resource " + src); - return; - } - }; - - /** - * Returns if specified resource is currently loading or not - * @public - * @param {string} src Resource URL - * @return {boolean|undefined} If resource is currently loading. Otherwise, undefined. - */ - self.isLoading = function(src) { - if (jaws.isString(src)) { - return self.loading[src]; - } else { - jaws.log.error("jaws.isLoading: Argument not a String with " + src); - } - }; - - /** - * Returns if specified resource is loaded or not - * @param src Source URL - * @return {boolean|undefined} If specified resource is loaded or not. Otherwise, undefined. - */ - self.isLoaded = function(src) { - if (jaws.isString(src)) { - return self.loaded[src]; - } else { - jaws.log.error("jaws.isLoaded: Argument not a String with " + src); - } - }; - - /** - * Returns lowercase postfix of specified resource - * @public - * @param {string} src Resource URL - * @returns {string} Lowercase postfix of resource - */ - self.getPostfix = function(src) { - if (jaws.isString(src)) { - return src.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1]; - } else { - jaws.log.error("jaws.assets.getPostfix: Argument not a String with " + src); - } - }; - - /** - * Determine type of file (Image, Audio, or Video) from its postfix - * @private - * @param {string} src Resource URL - * @returns {string} Matching type {Image, Audio, Video} or the postfix itself - */ - function getType(src) { - if (jaws.isString(src)) { - var postfix = self.getPostfix(src); - return (self.file_type[postfix] ? self.file_type[postfix] : postfix); - } else { - jaws.log.error("jaws.assets.getType: Argument not a String with " + src); - } - } - - /** - * Add URL(s) to asset listing for later loading - * @public - * @param {string|array|arguments} src The resource URL(s) to add to the asset listing - * @example - * jaws.assets.add("player.png") - * jaws.assets.add(["media/bullet1.png", "media/bullet2.png"]) - * jaws.assets.add("foo.png", "bar.png") - * jaws.assets.loadAll({onload: start_game}) - */ - self.add = function(src) { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) { - self.add(list[i]); - } - else { - if(jaws.isString(list[i])) { self.src_list.push(list[i]) } - else { jaws.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource " + src) } - } - } - - return self; - }; - - /** - * Iterate through the list of resource URL(s) and load each in turn. - * @public - * @param {Object} options Object-literal of callback functions - * @config {function} [options.onprogress] The function to be called on progress (when one assets of many is loaded) - * @config {function} [options.onerror] The function to be called if an error occurs - * @config {function} [options.onload] The function to be called when finished - */ - self.loadAll = function(options) { - self.load_count = 0; - self.error_count = 0; - - if (options.onprogress && jaws.isFunction(options.onprogress)) - self.onprogress = options.onprogress; - - if (options.onerror && jaws.isFunction(options.onerror)) - self.onerror = options.onerror; - - if (options.onload && jaws.isFunction(options.onload)) - self.onload = options.onload; - - self.src_list.forEach(function(item) { - self.load(item); - }); - - return self; - }; - - /** - * Loads a single resource from its given URL - * Will attempt to match a resource to known MIME types. - * If unknown, loads the file as a blob-object. - * - * @public - * @param {string} src Resource URL - * @param {Object} options Object-literal of callback functions - * @config {function} [options.onload] Function to be called when assets has loaded - * @config {function} [options.onerror] Function to be called if an error occurs - * @example - * jaws.load("media/foo.png") - * jaws.load("http://place.tld/foo.png") - */ - self.load = function(src, options) { - if(!options) options = {}; - - if (!jaws.isString(src)) { - jaws.log.error("jaws.assets.load: Argument not a String with " + src); - return; - } - - var asset = {}; - var resolved_src = ""; - asset.src = src; - asset.onload = options.onload; - asset.onerror = options.onerror; - self.loading[src] = true; - var parser = RegExp('^((f|ht)tp(s)?:)?//'); - if (parser.test(src)) { - resolved_src = asset.src; - } else { - resolved_src = self.root + asset.src; - } - if (self.bust_cache) { - resolved_src += "?" + parseInt(Math.random() * 10000000); - } - - var type = getType(asset.src); - if (type === "image") { - try { - asset.image = new Image(); - asset.image.asset = asset; - asset.image.addEventListener('load', assetLoaded); - asset.image.addEventListener('error', assetError); - asset.image.src = resolved_src; - } catch (e) { - jaws.log.error("Cannot load Image resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - else if (self.can_play[self.getPostfix(asset.src)]) { - if (type === "audio") { - try { - asset.audio = new Audio(); - asset.audio.asset = asset; - asset.audio.addEventListener('error', assetError); - asset.audio.addEventListener('canplay', assetLoaded); // NOTE: assetLoaded can be called several times during loading. - self.data[asset.src] = asset.audio; - asset.audio.src = resolved_src; - asset.audio.load(); - } catch (e) { - jaws.log.error("Cannot load Audio resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - else if (type === "video") { - try { - asset.video = document.createElement('video'); - asset.video.asset = asset; - self.data[asset.src] = asset.video; - asset.video.setAttribute("style", "display:none;"); - asset.video.addEventListener('error', assetError); - asset.video.addEventListener('canplay', assetLoaded); - document.body.appendChild(asset.video); - asset.video.src = resolved_src; - asset.video.load(); - } catch (e) { - jaws.log.error("Cannot load Video resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - } - - //Load everything else as raw blobs... - else { - // ... But don't load un-supported audio-files. - if(type === "audio" && !self.can_play[self.getPostfix(asset.src)]) { - assetSkipped(asset); - return self; - } - - try { - var req = new XMLHttpRequest(); - req.asset = asset; - req.onreadystatechange = assetLoaded; - req.onerror = assetError; - req.open('GET', resolved_src, true); - if (type !== "json") - req.responseType = "blob"; - req.send(null); - } catch (e) { - jaws.log.error("Cannot load " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - - return self; - }; - - /** - * Initial loading callback for all assets for parsing specific filetypes or - * optionally converting images to canvas-objects. - * @private - * @param {EventObject} event The EventObject populated by the calling event - * @see processCallbacks() - */ - function assetLoaded(event) { - var asset = this.asset; - var src = asset.src; - var filetype = getType(asset.src); - - try { - if (filetype === "json") { - if (this.readyState !== 4) { - return; - } - self.data[asset.src] = JSON.parse(this.responseText); - } - else if (filetype === "image") { - var new_image = self.image_to_canvas ? jaws.imageToCanvas(asset.image) : asset.image; - if (self.fuchia_to_transparent && self.getPostfix(asset.src) === "bmp") { - new_image = fuchiaToTransparent(new_image); - } - self.data[asset.src] = new_image; - } - else if (filetype === "audio" && self.can_play[self.getPostfix(asset.src)]) { - self.data[asset.src] = asset.audio; - } - else if (filetype === "video" && self.can_play[self.getPostfix(asset.src)]) { - self.data[asset.src] = asset.video; - } else { - self.data[asset.src] = this.response; - } - } catch (e) { - jaws.log.error("Cannot process " + src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - self.data[asset.src] = null; - } - - /* - * Only increment load_count ONCE per unique asset. - * This is needed cause assetLoaded-callback can in certain cases be called several for a single asset... - * ..and not only Once when it's loaded. - */ - if( !self.loaded[src]) self.load_count++; - - self.loaded[src] = true; - self.loading[src] = false; - - processCallbacks(asset, true, event); - } - - /** - * Called when jaws asset-handler decides that an asset shouldn't be loaded - * For example, an unsupported audio-format won't be loaded. - * - * @private - */ - function assetSkipped(asset) { - self.loaded[asset.src] = true; - self.loading[asset.src] = false; - self.load_count++; - processCallbacks(asset, true); - } - - /** - * Increases the error count and calls processCallbacks with false flag set - * @see processCallbacks() - * @private - * @param {EventObject} event The EventObject populated by the calling event - */ - function assetError(event) { - var asset = this.asset; - self.error_count++; - processCallbacks(asset, false, event); - } - - /** - * Processes (if set) the callbacks per resource - * @private - * @param {object} asset The asset to be processed - * @param {boolean} ok If an error has occured with the asset loading - * @param {EventObject} event The EventObject populated by the calling event - * @see jaws.start() in core.js - */ - function processCallbacks(asset, ok, event) { - var percent = parseInt((self.load_count + self.error_count) / self.src_list.length * 100); - - if (ok) { - if(self.onprogress) - self.onprogress(asset.src, percent); - if(asset.onprogress && event !== undefined) - asset.onprogress(event); - } - else { - if(self.onerror) - self.onerror(asset.src, percent); - if(asset.onerror && event !== undefined) - asset.onerror(event); - } - - if (percent === 100) { - if(self.onload) self.onload(); - - self.onprogress = null; - self.onerror = null; - self.onload = null; - } - } - - /** - * Displays the progress of asset handling as an overall percentage of all loading - * (Can be overridden as jaws.assets.displayProgress = function(percent_done) {}) - * @public - * @param {number} percent_done The overall percentage done across all resource handling - */ - self.displayProgress = function(percent_done) { - - if (!jaws.isNumber(percent_done)) - return; - - if (!jaws.context) - return; - - jaws.context.save(); - jaws.context.fillStyle = "black"; - jaws.context.fillRect(0, 0, jaws.width, jaws.height); - - jaws.context.fillStyle = "white"; - jaws.context.strokeStyle = "white"; - jaws.context.textAlign = "center"; - - jaws.context.strokeRect(50 - 1, (jaws.height / 2) - 30 - 1, jaws.width - 100 + 2, 60 + 2); - jaws.context.fillRect(50, (jaws.height / 2) - 30, ((jaws.width - 100) / 100) * percent_done, 60); - - jaws.context.font = "11px verdana"; - jaws.context.fillText("Loading... " + percent_done + "%", jaws.width / 2, jaws.height / 2 - 35); - - jaws.context.font = "11px verdana"; - jaws.context.fillStyle = "#ccc"; - jaws.context.textBaseline = "bottom"; - jaws.context.fillText("powered by www.jawsjs.com", jaws.width / 2, jaws.height - 1); - - jaws.context.restore(); - }; - }; - - /** - * Make Fuchia (0xFF00FF) transparent (BMPs ONLY) - * @private - * @param {HTMLImageElement} image The Bitmap Image to convert - * @returns {CanvasElement} canvas The translated CanvasElement - */ - function fuchiaToTransparent(image) { - if (!jaws.isDrawable(image)) - return; - - var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image; - var context = canvas.getContext("2d"); - var img_data = context.getImageData(0, 0, canvas.width, canvas.height); - var pixels = img_data.data; - for (var i = 0; i < pixels.length; i += 4) { - if (pixels[i] === 255 && pixels[i + 1] === 0 && pixels[i + 2] === 255) { // Color: Fuchia - pixels[i + 3] = 0; // Set total see-through transparency - } - } - - context.putImageData(img_data, 0, 0); - return canvas; - } - - jaws.assets = new jaws.Assets(); - return jaws; -})(jaws || {}); - - -if(typeof require !== "undefined") { var jaws = require("./core.js"); } - -var jaws = (function(jaws) { - -// requestAnim shim layer by Paul Irish -window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(/* function */ callback, /* DOMElement */ element){ - window.setTimeout(callback, 16.666); - }; -})(); - -/** - * @class A classic game loop forever looping calls to update() / draw() with given framerate. "Field Summary" contains options for the GameLoop()-constructor. - * - * @property {int} tick_duration duration in ms between the last 2 ticks (often called dt) - * @property {int} fps the real fps (as opposed to the target fps), smoothed out with a moving average - * @property {int} ticks total amount of ticks since game loops start - * - * @example - * - * game = {} - * draw: function() { ... your stuff executed every 30 FPS ... } - * } - * - * game_loop = new jaws.GameLoop(game, {fps: 30}) - * game_loop.start() - * - * // You can also use the shortcut jaws.start(), it will: - * // 1) Load all assets with jaws.assets.loadAll() - * // 2) Create a GameLoop() and start it - * jaws.start(MyGameState, {fps: 30}) - * - */ -jaws.GameLoop = function GameLoop(game_object, options, game_state_setup_options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( game_object, options ); - - this.tick_duration = 0 - this.fps = 0 - this.ticks = 0 - - var update_id - var paused = false - var stopped = false - var that = this - var mean_value = new MeanValue(20) // let's have a smooth, non-jittery FPS-value - - /** - * returns how game_loop has been active in milliseconds - * does currently not factor in pause-time - */ - this.runtime = function() { - return (this.last_tick - this.first_tick) - } - - /** Start the game loop by calling setup() once and then loop update()/draw() forever with given FPS */ - this.start = function() { - jaws.log.info("Game loop start", true) - - this.first_tick = (new Date()).getTime(); - this.current_tick = (new Date()).getTime(); - this.last_tick = (new Date()).getTime(); - - if(options.setup !== false && game_object.setup) { game_object.setup(game_state_setup_options) } - step_delay = 1000 / options.fps; - - if(options.fps == 60) { - requestAnimFrame(this.loop) - } - else { - update_id = setInterval(this.loop, step_delay); - } - } - - /** The core of the game loop. Calculate a mean FPS and call update()/draw() if game loop is not paused */ - this.loop = function() { - that.current_tick = (new Date()).getTime(); - that.tick_duration = that.current_tick - that.last_tick - that.fps = mean_value.add(1000/that.tick_duration).get() - - if(!stopped && !paused) { - if(game_object.update) { game_object.update() } - if(game_object.draw) { game_object.draw() } - that.ticks++ - } - if(options.fps == 60 && !stopped) requestAnimFrame(that.loop); - that.last_tick = that.current_tick; - } - - /** Pause the game loop. loop() will still get called but not update() / draw() */ - this.pause = function() { paused = true } - - /** unpause the game loop */ - this.unpause = function() { paused = false } - - /** Stop the game loop */ - this.stop = function() { - if(update_id) clearInterval(update_id); - stopped = true; - } -} - -/** @ignore */ -function MeanValue(size) { - this.size = size - this.values = new Array(this.size) - this.value - - this.add = function(value) { - if(this.values.length > this.size) { // is values filled? - this.values.splice(0,1) - this.value = 0 - for(var i=0; this.values[i]; i++) { - this.value += this.values[i] - } - this.value = this.value / this.size - } - this.values.push(value) - - return this - } - - this.get = function() { - return parseInt(this.value) - } - -} - -return jaws; -})(jaws || {}); - - -var jaws = (function(jaws) { - -/* -* 2013-09-28: -* -* For a 10x10 sprite in the topleft corner, should sprite.rect().bottom be 9 or 10? -* There's no right or wrong answer. In some cases 9 makes sense (if checking directly for pixel-values for example). -* In other cases 10 makes sense (bottom = x + height). -* -* The important part is beeing consistent across the lib/game. -* Jaws started out with bottom = x + height so we'll continue with that way until good reasons to change come up. -* Therefore correction = 0 for now. -*/ -var correction = 0; - -/** - @class A Basic rectangle. - @example - rect = new jaws.Rect(5,5,20,20) - rect.right // -> 25 - rect.bottom // -> 25 - rect.move(10,20) - rect.right // -> 35 - rect.bottom // -> 45 - rect.width // -> 20 - rect.height // -> 20 -*/ -jaws.Rect = function Rect(x, y, width, height) { - if( !(this instanceof arguments.callee) ) return new arguments.callee(x, y, width, height); - - this.x = x - this.y = y - this.width = width - this.height = height - this.right = x + width - correction - this.bottom = y + height - correction -} - -/** Return position as [x,y] */ -jaws.Rect.prototype.getPosition = function() { - return [this.x, this.y] -} - -/** Move rect x pixels horizontally and y pixels vertically */ -jaws.Rect.prototype.move = function(x, y) { - this.x += x - this.y += y - this.right += x - this.bottom += y - return this -} - -/** Set rects x/y */ -jaws.Rect.prototype.moveTo = function(x, y) { - this.x = x - this.y = y - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} -/** Modify width and height */ -jaws.Rect.prototype.resize = function(width, height) { - this.width += width - this.height += height - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Returns a new rect witht he same dimensions */ -jaws.Rect.prototype.clone = function() { - return new jaws.Rect(this.x, this.y, this.width, this.height) -} - -/** Shrink rectangle on both axis with given x/y values */ -jaws.Rect.prototype.shrink = function(x, y) { - this.x += x - this.y += y - this.width -= (x+x) - this.height -= (y+y) - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Set width and height */ -jaws.Rect.prototype.resizeTo = function(width, height) { - this.width = width - this.height = height - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Draw rect in color red, useful for debugging */ -jaws.Rect.prototype.draw = function() { - jaws.context.strokeStyle = "red" - jaws.context.strokeRect(this.x-0.5, this.y-0.5, this.width, this.height) - return this -} - -/** Returns true if point at x, y lies within calling rect */ -jaws.Rect.prototype.collidePoint = function(x, y) { - return (x >= this.x && x <= this.right && y >= this.y && y <= this.bottom) -} - -/** Returns true if calling rect overlaps with given rect in any way */ -jaws.Rect.prototype.collideRect = function(rect) { - return ((this.x >= rect.x && this.x <= rect.right) || (rect.x >= this.x && rect.x <= this.right)) && - ((this.y >= rect.y && this.y <= rect.bottom) || (rect.y >= this.y && rect.y <= this.bottom)) -} - -/* -// Possible future functions -jaws.Rect.prototype.collideRightSide = function(rect) { return(this.right >= rect.x && this.x < rect.x) } -jaws.Rect.prototype.collideLeftSide = function(rect) { return(this.x > rect.x && this.x <= rect.right) } -jaws.Rect.prototype.collideTopSide = function(rect) { return(this.y >= rect.y && this.y <= rect.bottom) } -jaws.Rect.prototype.collideBottomSide = function(rect) { return(this.bottom >= rect.y && this.y < rect.y) } -*/ - -jaws.Rect.prototype.toString = function() { return "[Rect " + this.x + ", " + this.y + ", " + this.width + ", " + this.height + "]" } - -return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.Rect } - - -var jaws = (function(jaws) { - -/** -* @class A basic but powerfull sprite for all your onscreen-game objects. "Field Summary" contains options for the Sprite()-constructor. -* @constructor -* -* @property {int} x Horizontal position (0 = furthest left) -* @property {int} y Vertical position (0 = top) -* @property {image} image Image/canvas or string pointing to an asset ("player.png") -* @property {int} alpha Transparency 0=fully transparent, 1=no transperency -* @property {int} angle Angle in degrees (0-360) -* @property {bool} flipped Flip sprite horizontally, usefull for sidescrollers -* @property {string} anchor String stating how to anchor the sprite to canvas, @see Sprite#anchor ("top_left", "center" etc) -* @property {int} scale_image Scale the sprite by this factor -* @property {string,gradient} color If set, draws a rectangle of dimensions rect() with specified color or gradient (linear or radial) -* -* @example -* // create new sprite at top left of the screen, will use jaws.assets.get("foo.png") -* new Sprite({image: "foo.png", x: 0, y: 0}) -* -* // sets anchor to "center" on creation -* new Sprite({image: "topdownspaceship.png", anchor: "center"}) -* -*/ -jaws.Sprite = function Sprite(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - this.set(options) - this.context = options.context ? options.context : jaws.context; // Prefer given canvas-context, fallback to jaws.context -} - -jaws.Sprite.prototype.default_options = { - x: 0, - y: 0, - alpha: 1, - angle: 0, - flipped: false, - anchor_x: 0, - anchor_y: 0, - image: null, - image_path: null, - anchor: null, - scale_image: null, - damping: 1, - scale_x: 1, - scale_y: 1, - scale: 1, - color: "#ddd", - width: 16, - height: 16, - _constructor: null, - context: null, - data: null -} - -/** - * @private - * Call setters from JSON object. Used to parse options. - */ -jaws.Sprite.prototype.set = function(options) { - if(jaws.isString(this.image)) this.image_path = this.image; - jaws.parseOptions(this, options, this.default_options); - - if(this.scale) this.scale_x = this.scale_y = this.scale; - if(this.image) this.setImage(this.image); - if(this.scale_image) this.scaleImage(this.scale_image); - if(this.anchor) this.setAnchor(this.anchor); - - if(!this.image && this.color && this.width && this.height) { - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - canvas.width = this.width; - canvas.height = this.height; - context.fillStyle = this.color; - context.fillRect(0, 0, this.width, this.height); - this.image = canvas; - } - - this.cacheOffsets() - - return this -} - -/** - * @private - * - * Creates a new sprite from current sprites attributes() - * Checks JawsJS magic property '_constructor' when deciding with which constructor to create it - * - */ -jaws.Sprite.prototype.clone = function(object) { - var constructor = this._constructor ? eval(this._constructor) : this.constructor - var new_sprite = new constructor( this.attributes() ); - new_sprite._constructor = this._constructor || this.constructor.name - return new_sprite -} - - -/** - * Sets image from image/canvas or asset-string ("foo.png") - * If asset isn't previously loaded setImage() will try to load it. - */ -jaws.Sprite.prototype.setImage = function(value) { - var that = this - - // An image, great, set this.image and return - if(jaws.isDrawable(value)) { - this.image = value - return this.cacheOffsets() - } - // Not an image, therefore an asset string, i.e. "ship.bmp" - else { - // Assets already loaded? Set this.image - if(jaws.assets.isLoaded(value)) { this.image = jaws.assets.get(value); this.cacheOffsets(); } - - // Not loaded? Load it with callback to set image. - else { - jaws.log.warn("Image '" + value + "' not preloaded with jaws.assets.add(). Image and a working sprite.rect() will be delayed.") - jaws.assets.load(value, {onload: function() { that.image = jaws.assets.get(value); that.cacheOffsets();} } ) - } - } - return this -} - -/** -* Steps 1 pixel towards the given X/Y. Horizontal and vertical steps are done separately between each callback. -* Exits when the continueStep-callback returns true for both vertical and horizontal steps or if target X/Y has been reached. -* -* @returns {object} Object with 2 x/y-properties indicating what plane we moved in when stepToWhile was stopped. -*/ -jaws.Sprite.prototype.stepToWhile = function(target_x, target_y, continueStep) { - var step = 1; - var step_x = (target_x < this.x) ? -step : step; - var step_y = (target_y < this.y) ? -step : step; - - target_x = parseInt(target_x) - target_y = parseInt(target_y) - - var collision_x = false; - var collision_y = false; - - while( true ) { - if(collision_x === false) { - if(this.x != target_x) { this.x += step_x } - if( !continueStep(this) ) { this.x -= step_x; collision_x = true } - } - - if(collision_y === false) { - if(this.y != target_y) { this.y += step_y } - if( !continueStep(this) ) { this.y -= step_y; collision_y = true } - } - - if( (collision_x || this.x == target_x) && (collision_y || this.y == target_y) ) - return {x: collision_x, y: collision_y}; - } -} -/** -* Moves with given vx/vy velocoties by stepping 1 pixel at the time. Horizontal and vertical steps are done separately between each callback. -* Exits when the continueStep-callback returns true for both vertical and horizontal steps or if target X/Y has been reached. -* -* @returns {object} Object with 2 x/y-properties indicating what plane we moved in when stepWhile was stopped. -*/ -jaws.Sprite.prototype.stepWhile = function(vx, vy, continueStep) { - return this.stepToWhile(this.x + vx, this.y + vy, continueStep) -} - -/** Flips image vertically, usefull for sidescrollers when player is walking left/right */ -jaws.Sprite.prototype.flip = function() { this.flipped = this.flipped ? false : true; return this } -jaws.Sprite.prototype.flipTo = function(value) { this.flipped = value; return this } -/** Rotate sprite by value degrees */ -jaws.Sprite.prototype.rotate = function(value) { this.angle += value; return this } -/** Force an rotation-angle on sprite */ -jaws.Sprite.prototype.rotateTo = function(value) { this.angle = value; return this } - -/** Set x/y */ -jaws.Sprite.prototype.moveTo = function(x, y) { - if(jaws.isArray(x) && y === undefined) { - y = x[1] - x = x[0] - } - this.x = x; - this.y = y; - return this; -} -/** Modify x/y */ -jaws.Sprite.prototype.move = function(x, y) { - if(jaws.isArray(x) && y === undefined) { - y = x[1] - x = x[0] - } - - if(x) this.x += x; - if(y) this.y += y; - return this -} -/** -* scale sprite by given factor. 1=don't scale. <1 = scale down. 1>: scale up. -* Modifies width/height. -**/ -jaws.Sprite.prototype.scaleAll = function(value) { this.scale_x *= value; this.scale_y *= value; return this.cacheOffsets() } -/** set scale factor. ie. 2 means a doubling if sprite in both directions. */ -jaws.Sprite.prototype.scaleTo = function(value) { this.scale_x = this.scale_y = value; return this.cacheOffsets() } -/** scale sprite horizontally by scale_factor. Modifies width. */ -jaws.Sprite.prototype.scaleWidth = function(value) { this.scale_x *= value; return this.cacheOffsets() } -/** scale sprite vertically by scale_factor. Modifies height. */ -jaws.Sprite.prototype.scaleHeight = function(value) { this.scale_y *= value; return this.cacheOffsets() } - -/** Sets x */ -jaws.Sprite.prototype.setX = function(value) { this.x = value; return this } -/** Sets y */ -jaws.Sprite.prototype.setY = function(value) { this.y = value; return this } - -/** Position sprites top on the y-axis */ -jaws.Sprite.prototype.setTop = function(value) { this.y = value + this.top_offset; return this } -/** Position sprites bottom on the y-axis */ -jaws.Sprite.prototype.setBottom = function(value) { this.y = value - this.bottom_offset; return this } -/** Position sprites left side on the x-axis */ -jaws.Sprite.prototype.setLeft = function(value) { this.x = value + this.left_offset; return this } -/** Position sprites right side on the x-axis */ -jaws.Sprite.prototype.setRight = function(value) { this.x = value - this.right_offset; return this } - -/** Set new width. Scales sprite. */ -jaws.Sprite.prototype.setWidth = function(value) { this.scale_x = value/this.image.width; return this.cacheOffsets() } -/** Set new height. Scales sprite. */ -jaws.Sprite.prototype.setHeight = function(value) { this.scale_y = value/this.image.height; return this.cacheOffsets() } -/** Resize sprite by adding width */ -jaws.Sprite.prototype.resize = function(width, height) { - if(jaws.isArray(width) && height === undefined) { - height = width[1] - width = width[0] - } - - this.scale_x = (this.width + width) / this.image.width - this.scale_y = (this.height + height) / this.image.height - return this.cacheOffsets() -} -/** - * Resize sprite to exact width/height - */ -jaws.Sprite.prototype.resizeTo = function(width, height) { - if(jaws.isArray(width) && height === undefined) { - height = width[1] - width = width[0] - } - - this.scale_x = width / this.image.width - this.scale_y = height / this.image.height - return this.cacheOffsets() -} - -/** -* The sprites anchor could be describe as "the part of the sprite will be placed at x/y" -* or "when rotating, what point of the of the sprite will it rotate round" -* -* @example -* For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y -* .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y -*/ -jaws.Sprite.prototype.setAnchor = function(value) { - var anchors = { - top_left: [0,0], - left_top: [0,0], - center_left: [0,0.5], - left_center: [0,0.5], - bottom_left: [0,1], - left_bottom: [0,1], - top_center: [0.5,0], - center_top: [0.5,0], - center_center: [0.5,0.5], - center: [0.5,0.5], - bottom_center: [0.5,1], - center_bottom: [0.5,1], - top_right: [1,0], - right_top: [1,0], - center_right: [1,0.5], - right_center: [1,0.5], - bottom_right: [1,1], - right_bottom: [1,1] - } - - if(a = anchors[value]) { - this.anchor_x = a[0] - this.anchor_y = a[1] - if(this.image) this.cacheOffsets(); - } - return this -} - -/** @private */ -jaws.Sprite.prototype.cacheOffsets = function() { - if(!this.image) { return } - - this.width = this.image.width * this.scale_x - this.height = this.image.height * this.scale_y - this.left_offset = this.width * this.anchor_x - this.top_offset = this.height * this.anchor_y - this.right_offset = this.width * (1.0 - this.anchor_x) - this.bottom_offset = this.height * (1.0 - this.anchor_y) - - if(this.cached_rect) this.cached_rect.resizeTo(this.width, this.height); - return this -} - -/** Returns a jaws.Rect() perfectly surrouning sprite. Also cache rect in this.cached_rect. */ -jaws.Sprite.prototype.rect = function() { - if(!this.cached_rect && this.width) this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height); - if(this.cached_rect) this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset); - return this.cached_rect -} - -/** Draw sprite on active canvas */ -jaws.Sprite.prototype.draw = function() { - if(!this.image) { return this } - - this.context.save() - this.context.translate(this.x, this.y) - if(this.angle!=0) { jaws.context.rotate(this.angle * Math.PI / 180) } - this.flipped && this.context.scale(-1, 1) - this.context.globalAlpha = this.alpha - this.context.translate(-this.left_offset, -this.top_offset) // Needs to be separate from above translate call cause of flipped - this.context.drawImage(this.image, 0, 0, this.width, this.height) - this.context.restore() - return this -} - -/** - * Scales image using hard block borders. Useful for that cute, blocky retro-feeling. - * Depends on gfx.js beeing loaded. - */ -jaws.Sprite.prototype.scaleImage = function(factor) { - if(!this.image) return; - this.setImage( jaws.retroScaleImage(this.image, factor) ) - return this -} - -/** - * Returns sprite as a canvas context. - * For certain browsers, a canvas context is faster to work with then a pure image. - */ -jaws.Sprite.prototype.asCanvasContext = function() { - var canvas = document.createElement("canvas") - canvas.width = this.width - canvas.height = this.height - - var context = canvas.getContext("2d") - if(jaws.context) context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - context.drawImage(this.image, 0, 0, this.width, this.height) - return context -} - -/** - * Returns sprite as a canvas - */ -jaws.Sprite.prototype.asCanvas = function() { - var canvas = document.createElement("canvas") - canvas.width = this.width - canvas.height = this.height - - var context = canvas.getContext("2d") - if(jaws.context) context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - context.drawImage(this.image, 0, 0, this.width, this.height) - return canvas -} - -jaws.Sprite.prototype.toString = function() { return "[Sprite " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]" } - -/** returns Sprites state/properties as a pure object */ -jaws.Sprite.prototype.attributes = function() { - var object = {} // Starting with this.options could create circular references through "context" - object["_constructor"] = this._constructor || "jaws.Sprite" - object["x"] = parseFloat(this.x.toFixed(2)) - object["y"] = parseFloat(this.y.toFixed(2)) - object["image"] = this.image_path - object["alpha"] = this.alpha - object["flipped"] = this.flipped - object["angle"] = parseFloat(this.angle.toFixed(2)) - object["scale_x"] = this.scale_x; - object["scale_y"] = this.scale_y; - object["anchor_x"] = this.anchor_x - object["anchor_y"] = this.anchor_y - - if(this.data !== null) object["data"] = jaws.clone(this.data); // For external data (for example added by the editor) that you want serialized - - return object -} -/** - * Load/creates sprites from given data - * - * Argument could either be - * - an array of Sprite objects - * - an array of JSON objects - * - a JSON.stringified string representing an array of JSON objects - * - * @return Array of created sprite -* - */ -jaws.Sprite.parse = function(objects) { - var sprites = [] - - if(jaws.isArray(objects)) { - // If this is an array of JSON representations, parse it - if(objects.every(function(item) { return item._constructor })) { - parseArray(objects) - } else { - // This is already an array of Sprites, load it directly - sprites = objects - } - } - else if(jaws.isString(objects)) { parseArray( JSON.parse(objects) ); jaws.log.info(objects) } - - function parseArray(array) { - array.forEach( function(data) { - var constructor = data._constructor ? eval(data._constructor) : data.constructor - if(jaws.isFunction(constructor)) { - jaws.log.info("Creating " + data._constructor + "(" + data.toString() + ")", true) - var object = new constructor(data) - object._constructor = data._constructor || data.constructor.name - sprites.push(object); - } - }); - } - - return sprites; -} - -/** - * returns a JSON-string representing the state of the Sprite. - * - * Use this to serialize your sprites / game objects, maybe to save in local storage or on a server - * - * jaws.game_states.Edit uses this to export all edited objects. - * - */ -jaws.Sprite.prototype.toJSON = function() { - return JSON.stringify(this.attributes()) -} - -return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.Sprite } - -/* -// Chainable setters under consideration: -jaws.Sprite.prototype.setFlipped = function(value) { this.flipped = value; return this } -jaws.Sprite.prototype.setAlpha = function(value) { this.alpha = value; return this } -jaws.Sprite.prototype.setAnchorX = function(value) { this.anchor_x = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setAnchorY = function(value) { this.anchor_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setAngle = function(value) { this.angle = value; return this } -jaws.Sprite.prototype.setScale = function(value) { this.scale_x = this.scale_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setScaleX = function(value) { this.scale_x = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setScaleY = function(value) { this.scale_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.moveX = function(x) { this.x += x; return this } -jaws.Sprite.prototype.moveXTo = function(x) { this.x = x; return this } -jaws.Sprite.prototype.moveY = function(y) { this.y += y; return this } -jaws.Sprite.prototype.moveYTo = function(y) { this.y = y; return this } -jaws.Sprite.prototype.scaleWidthTo = function(value) { this.scale_x = value; return this.cacheOffsets() } -jaws.Sprite.prototype.scaleHeightTo = function(value) { this.scale_y = value; return this.cachOfffsets() } -*/ - - -var jaws = (function(jaws) { - - -/** - * @class Cut out invidual frames (images) from a larger spritesheet-image. "Field Summary" contains options for the SpriteSheet()-constructor. - * - * @property {image|image} Image/canvas or asset-string to cut up smaller images from - * @property {string} orientation How to cut out invidual images from spritesheet, either "right" or "down" - * @property {array} frame_size width and height of invidual frames in spritesheet - * @property {array} frames all single frames cut out from image - * @property {integer} offset vertical or horizontal offset to start cutting from - * @property {int} scale_image Scale the sprite sheet by this factor before cutting out the frames. frame_size is automatically re-sized too - * -*/ -jaws.SpriteSheet = function SpriteSheet(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - jaws.parseOptions(this, options, this.default_options); - - /* Detect framesize from filename, example: droid_10x16.png means each frame is 10px high and 16px wide */ - if(jaws.isString(this.image) && !options.frame_size) { - var regexp = new RegExp("_(\\d+)x(\\d+)", "g"); - var sizes = regexp.exec(this.image) - this.frame_size = [] - this.frame_size[0] = parseInt(sizes[1]) - this.frame_size[1] = parseInt(sizes[2]) - } - - this.image = jaws.isDrawable(this.image) ? this.image : jaws.assets.data[this.image] - if(this.scale_image) { - var image = (jaws.isDrawable(this.image) ? this.image : jaws.assets.get(this.image)) - this.frame_size[0] *= this.scale_image - this.frame_size[1] *= this.scale_image - this.image = jaws.retroScaleImage(image, this.scale_image) - } - - var index = 0 - this.frames = [] - - // Cut out tiles from Top -> Bottom - if(this.orientation == "down") { - for(var x=this.offset; x < this.image.width; x += this.frame_size[0]) { - for(var y=0; y < this.image.height; y += this.frame_size[1]) { - this.frames.push( cutImage(this.image, x, y, this.frame_size[0], this.frame_size[1]) ) - } - } - } - // Cut out tiles from Left -> Right - else { - for(var y=this.offset; y < this.image.height; y += this.frame_size[1]) { - for(var x=0; x < this.image.width; x += this.frame_size[0]) { - this.frames.push( cutImage(this.image, x, y, this.frame_size[0], this.frame_size[1]) ) - } - } - } -} - -jaws.SpriteSheet.prototype.default_options = { - image: null, - orientation: "down", - frame_size: [32,32], - offset: 0, - scale_image: null -} - -/** @private - * Cut out a rectangular piece of a an image, returns as canvas-element - */ -function cutImage(image, x, y, width, height) { - var cut = document.createElement("canvas") - cut.width = width - cut.height = height - - var ctx = cut.getContext("2d") - ctx.drawImage(image, x, y, width, height, 0, 0, cut.width, cut.height) - - return cut -}; - -jaws.SpriteSheet.prototype.toString = function() { return "[SpriteSheet " + this.frames.length + " frames]" } - -return jaws; -})(jaws || {}); - - -var jaws = (function(jaws) { - -/** - * @class Manages an animation with a given list of frames. "Field Summary" contains options for the Animation()-constructor. - * - * @property {bool} loop Restart animation when end is reached - * @property {bool} bounce Rewind the animation frame by frame when end is reached - * @property {int} index Start on this frame - * @property {array} frames Images/canvaselements - * @property {milliseconds} frame_duration How long should each frame be displayed - * @property {int} frame_direction -1 for backwards animation. 1 is default - * @property {array} frame_size Containing width/height, eg. [32, 32] - * @property {int} offset When cutting out frames from a sprite sheet, start at this frame - * @property {string} orientation How to cut out frames frmo sprite sheet, possible values are "down" or "right" - * @property {function} on_end Function to call when animation ends. triggers only on non-looping, non-bouncing animations - * @property {object} subsets Name specific frames-intervals for easy access later, i.e. {move: [2,4], fire: [4,6]}. Access with animation.subset[name] - * - * @example - * // in setup() - * anim = new jaws.Animation({sprite_sheet: "droid_11x15.png", frame_size: [11,15], frame_duration: 100}) - * player = new jaws.Sprite({y:300, anchor: "center_bottom"}) - * - * // in update() - * player.setImage( anim.next() ) - * - * // in draw() - * player.draw() - * - */ -jaws.Animation = function Animation(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - jaws.parseOptions(this, options, this.default_options); - - if(options.sprite_sheet) { - var sprite_sheet = new jaws.SpriteSheet({image: options.sprite_sheet, scale_image: this.scale_image, frame_size: this.frame_size, orientation: this.orientation, offset: this.offset}) - this.frames = sprite_sheet.frames - this.frame_size = sprite_sheet.frame_size - } - - if(options.scale_image) { - var image = (jaws.isDrawable(options.sprite_sheet) ? options.sprite_sheet : jaws.assets.get(options.sprite_sheet)) - this.frame_size[0] *= options.scale_image - this.frame_size[1] *= options.scale_image - options.sprite_sheet = jaws.retroScaleImage(image, options.scale_image) - } - - /* Initializing timer-stuff */ - this.current_tick = (new Date()).getTime(); - this.last_tick = (new Date()).getTime(); - this.sum_tick = 0 - - if(options.subsets) { - this.subsets = {} - for(subset in options.subsets) { - start_stop = options.subsets[subset] - this.subsets[subset] = this.slice(start_stop[0], start_stop[1]) - } - } -} - -jaws.Animation.prototype.default_options = { - frames: [], - subsets: [], - frame_duration: 100, // default: 100ms between each frameswitch - index: 0, // default: start with the very first frame - loop: 1, - bounce: 0, - frame_direction: 1, - frame_size: null, - orientation: "down", - on_end: null, - offset: 0, - scale_image: null, - sprite_sheet: null -} - -/** - * Return a special animationsubset created with "subset"-parameter when initializing - * - */ -jaws.Animation.prototype.subset = function(subset) { - return this.subsets[subset] -} - -/** - Propells the animation forward by counting milliseconds and changing this.index accordingly - Supports looping and bouncing animations -*/ -jaws.Animation.prototype.update = function() { - this.current_tick = (new Date()).getTime(); - this.sum_tick += (this.current_tick - this.last_tick); - this.last_tick = this.current_tick; - - if(this.sum_tick > this.frame_duration) { - this.index += this.frame_direction - this.sum_tick = 0 - } - if( (this.index >= this.frames.length) || (this.index < 0) ) { - if(this.bounce) { - this.frame_direction = -this.frame_direction - this.index += this.frame_direction * 2 - } - else if(this.loop) { - if(this.frame_direction < 0) { - this.index = this.frames.length -1; - } else { - this.index = 0; - } - } - else { - this.index -= this.frame_direction - if (this.on_end) { - this.on_end() - this.on_end = null - } - } - } - return this -} - -/** - works like Array.slice but returns a new Animation-object with a subset of the frames -*/ -jaws.Animation.prototype.slice = function(start, stop) { - var o = {} - o.frame_duration = this.frame_duration - o.loop = this.loop - o.bounce = this.bounce - o.on_end = this.on_end - o.frame_direction = this.frame_direction - o.frames = this.frames.slice().slice(start, stop) - return new jaws.Animation(o) -}; - -/** - Moves animation forward by calling update() and then return the current frame -*/ -jaws.Animation.prototype.next = function() { - this.update() - return this.frames[this.index] -}; - -/** returns true if animation is at the very last frame */ -jaws.Animation.prototype.atLastFrame = function() { return (this.index == this.frames.length-1) } - -/** returns true if animation is at the very first frame */ -jaws.Animation.prototype.atFirstFrame = function() { return (this.index == 0) } - - -/** - returns the current frame -*/ -jaws.Animation.prototype.currentFrame = function() { - return this.frames[this.index] -}; - -/** - * Debugstring for Animation()-constructor - * @example - * var anim = new Animation(...) - * console.log(anim.toString()) - */ -jaws.Animation.prototype.toString = function() { return "[Animation, " + this.frames.length + " frames]" } - -return jaws; -})(jaws || {}); - - -if(typeof require !== "undefined") { var jaws = require("./core.js"); }
-
-/**
- * @fileOverview Collision Detection
- *
- * Collision detection helpers.
- *
- * @example
- * // collision helper exampels:
- * collideOneWithOne(player, boss) // -> false
- * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
- * collideManyWithMany(bullets, enemies) // -> [ [bullet1, enemy1], [bullet2, enemy2] ]
- * collide(player, boss) // -> false
- * collide(player,
- * bullets,
- * function(player, bullet) {}) // Callback: arguments[0] -> player
- * // arguments[1] -> bullets[i]
- *
- */
-var jaws = (function(jaws) {
-
- /**
- * Collides two objects by reading x, y and either method rect() or property radius.
- * @public
- * @param {object} object1 An object with a 'radius' or 'rect' property
- * @param {object} object2 An object with a 'radius' or 'rect' property
- * @returns {boolean} If the two objects are colliding or not
- */
- jaws.collideOneWithOne = function(object1, object2) {
- if (object1.radius && object2.radius && object1 !== object2 && jaws.collideCircles(object1, object2))
- return true;
-
- if (object1.rect && object2.rect && object1 !== object2 && jaws.collideRects(object1.rect(), object2.rect()))
- return true;
-
- return false;
- };
-
- /**
- * Compares an object against a list, returning those from list that collide with object, and
- * calling 'callback' per collision (if set) with object and item from list.
- * (Note: Will never collide objects with themselves.)
- * @public
- * @param {object} object An object with a 'radius' or 'rect' property
- * @param {array|object} list A collection of objects with a 'length' property
- * @param {function} callback The function to be called per collison detected
- * @returns {array} A collection of items colliding with object from list
- * @example
- * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
- * collideOneWithMany(player, bullets, function(player, bullet) {
- * //player and bullet (bullets[i])
- * });
- */
- jaws.collideOneWithMany = function(object, list, callback) {
- var a = [];
- if (callback) {
- for (var i = 0; i < list.length; i++) {
- if (jaws.collideOneWithOne(object, list[i])) {
- callback(object, list[i]);
- a.push(list[i])
- }
- }
- return a;
- }
- else {
- return list.filter(function(item) {
- return jaws.collideOneWithOne(object, item);
- });
- }
- };
-
- /**
- * Compares two lists, returning those items from each that collide with each other, and
- * calling 'callback' per collision (if set) with item from list1 and item from list2.
- * (Note: Will never collide objects with themselves.)
- * @public
- * @param {array|object} list1 A collection of objects with a 'forEach' property
- * @param {array|object} list2 A collection of objects with a 'forEach' property
- * @param {function} callback The function to be called per collison detected
- * @returns {array} A collection of items colliding with list1 from list2
- * @example
- * jaws.collideManyWithMany(bullets, enemies) // --> [[bullet, enemy], [bullet, enemy]]
- */
- jaws.collideManyWithMany = function(list1, list2, callback) {
- var a = [];
-
- if (list1 === list2) {
- combinations(list1, 2).forEach(function(pair) {
- if (jaws.collideOneWithOne(pair[0], pair[1])) {
- if (callback) {
- callback(pair[0], pair[1]);
- }
- else {
- a.push([pair[0], pair[1]]);
- }
- }
- });
- }
- else {
- list1.forEach(function(item1) {
- list2.forEach(function(item2) {
- if (jaws.collideOneWithOne(item1, item2)) {
- if (callback) {
- callback(item1, item2);
- }
- else {
- a.push([item1, item2]);
- }
- }
- });
- });
- }
-
- return a;
- };
-
- /**
- * Returns if two circle-objects collide with each other
- * @public
- * @param {object} object1 An object with a 'radius' property
- * @param {object} object2 An object with a 'radius' property
- * @returns {boolean} If two circle-objects collide or not
- */
- jaws.collideCircles = function(object1, object2) {
- return (jaws.distanceBetween(object1, object2) < object1.radius + object2.radius);
- };
-
- /**
- * Returns if two Rects collide with each other or not
- * @public
- * @param {object} rect1 An object with 'x', 'y', 'right' and 'bottom' properties
- * @param {object} rect2 An object with 'x', 'y', 'right' and 'bottom' properties
- * @returns {boolean} If two Rects collide with each other or not
- */
- jaws.collideRects = function(rect1, rect2) {
- return ((rect1.x >= rect2.x && rect1.x <= rect2.right) || (rect2.x >= rect1.x && rect2.x <= rect1.right)) &&
- ((rect1.y >= rect2.y && rect1.y <= rect2.bottom) || (rect2.y >= rect1.y && rect2.y <= rect1.bottom));
- };
-
- /**
- * Returns the distance between two objects
- * @public
- * @param {object} object1 An object with 'x' and 'y' properties
- * @param {object} object2 An object with 'x' and 'y' properties
- * @returns {number} The distance between two objects
- */
- jaws.distanceBetween = function(object1, object2) {
- return Math.sqrt(Math.pow(object1.x - object2.x, 2) + Math.pow(object1.y - object2.y, 2));
- };
-
- /**
- * Creates combinations of items from a list of a specific size
- * @private
- * @param {array|object} list An object with a 'length' property
- * @param {number} n The size of the array to return
- * @returns {Array} An array of items having a specific size number of its own entries
- */
- function combinations(list, n) {
- var f = function(i) {
- if (list.isSpriteList !== undefined) {
- return list.at(i);
- } else { // s is an Array
- return list[i];
- }
- };
- var r = [];
- var m = new Array(n);
- for (var i = 0; i < n; i++)
- m[i] = i;
- for (var i = n - 1, sn = list.length; 0 <= i; sn = list.length) {
- r.push(m.map(f));
- while (0 <= i && m[i] === sn - 1) {
- i--;
- sn--;
- }
- if (0 <= i) {
- m[i] += 1;
- for (var j = i + 1; j < n; j++)
- m[j] = m[j - 1] + 1;
- i = n - 1;
- }
- }
- return r;
- }
-
- /**
- * If an object has items or not
- * @private
- * @param {array|object} array An object with a 'length' property
- * @returns {boolean} If the object has items (length > 0)
- */
- function hasItems(array) {
- return (array && array.length > 0);
- }
-
- /**
- * Compares two objects or lists, returning if they collide, and
- * calling 'callback' per collision (if set) between objects or lists.
- * @param {array|object} x An object with either 'rect' or 'forEach' property
- * @param {array|object} x2 An object with either 'rect' or 'forEach' property
- * @param {function} callback
- * @returns {boolean}
- * @examples
- * jaws.collide(player, enemy, function(player, enemy) { ... } )
- * jaws.collide(player, enemies, function(player, enemy) { ... } )
- * jaws.collide(bullets, enemies, function(bullet, enemy) { ... } )
- */
- jaws.collide = function(x, x2, callback) {
- if ((x.rect || x.radius) && x2.forEach)
- return (jaws.collideOneWithMany(x, x2, callback).length > 0);
- if (x.forEach && x2.forEach)
- return (jaws.collideManyWithMany(x, x2, callback).length > 0);
- if (x.forEach && (x2.rect || x2.radius))
- return (jaws.collideOneWithMany(x2, x, callback).length > 0);
- if ((x.rect && x2.rect) || (x.radius && x2.radius)) {
- var result = jaws.collideOneWithOne(x, x2);
- if (callback && result)
- callback(x, x2);
- else
- return result;
- }
- };
-
- return jaws;
-})(jaws || {});
-
- -var jaws = (function(jaws) { -/** -* @class jaws.PixelMap -* @constructor -* -* Worms-style terrain collision detection. Created from a normal image. -* Read out specific pixels. Modify as you would do with a canvas. -* -* @property {string} image the image of the terrain -* @property {int} scale_image Scale the image by this factor -* -* @example -* tile_map = new jaws.Parallax({image: "map.png", scale_image: 4}) // scale_image: 4 for retro blocky feeling! -* tile_map.draw() // draw on canvas -* tile_map.nameColor([0,0,0,255], "ground") // give the color black the name "ground" -* tile_map.namedColorAtRect("ground", player.rect()) // True if players boundingbox is touching any black pixels on tile_map -* -*/ -jaws.PixelMap = function PixelMap(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.options = options - this.scale = options.scale || 1 - this.x = options.x || 0 - this.y = options.y || 0 - - if(options.image) { - this.setContext(options.image); - - if(options.scale_image) { - this.setContext( jaws.retroScaleImage(this.context.canvas, options.scale_image) ) - } - - this.width = this.context.canvas.width * this.scale; - this.height = this.context.canvas.height * this.scale; - } - else { jaws.log.warn("PixelMap needs an image to work with") } - - this.named_colors = []; - this.update(); -} - -/* -* Initiates a drawable context from given image. -* @private -*/ -jaws.PixelMap.prototype.setContext = function(image) { - var image = jaws.isDrawable(image) ? image : jaws.assets.get(image) - this.context = jaws.imageToCanvasContext(image) -} - -/** -* Updates internal pixel-array from the canvas. If we modify the 'terrain' (paint on pixel_map.context) we'll need to call this method. -*/ -jaws.PixelMap.prototype.update = function(x, y, width, height) { - if(x === undefined || x < 0) x = 0; - if(y === undefined || y < 0) y = 0; - if(width === undefined || width > this.width) width = this.width; - if(height === undefined || height > this.height) height = this.height; - - // No arguments? Read whole canvas, replace this.data - if(arguments.length == 0) { - this.data = this.context.getImageData(x, y, width, height).data - } - // Read a rectangle from the canvas, replacing relevant pixels in this.data - else { - var tmp = this.context.getImageData(x, y, width, height).data - var tmp_count = 0; - - // Some precalculation-optimizations - var one_line_down = this.width * 4; - var offset = (y * this.width * 4) + (x*4); - var horizontal_line = width*4; - - for(var y2 = 0; y2 < height; y2++) { - for(var x2 = 0; x2 < horizontal_line; x2++) { - this.data[offset + x2] = tmp[tmp_count++]; - } - offset += one_line_down; - } - } -} - -/** -* Draws the pixel map on the maincanvas -*/ -jaws.PixelMap.prototype.draw = function() { - jaws.context.drawImage(this.context.canvas, this.x, this.y, this.width, this.height) -} - -/** -* Trace the outline of a Rect until a named color found. -* -* @param {object} Rect Instance of jaws.Rect() -* @param {string} Color_Filter Only look for this named color -* -* @return {string} name of found color -*/ -jaws.PixelMap.prototype.namedColorAtRect = function(rect, color) { - var x = rect.x - var y = rect.y - - for(; x < rect.right-1; x++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; y < rect.bottom-1; y++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; x > rect.x; x--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; y > rect.y; y--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - - return false; -} - -/** -* Read current color at given coordinates X/Y -* -* @return {array} 4 integers [R, G, B, A] representing the pixel at x/y -*/ -jaws.PixelMap.prototype.at = function(x, y) { - x = parseInt(x) - y = parseInt(y) - if(y < 0) y = 0; - - var start = (y * this.width * 4) + (x*4); - var R = this.data[start]; - var G = this.data[start + 1]; - var B = this.data[start + 2]; - var A = this.data[start + 3]; - return [R, G, B, A]; -} - -/** -* Get previously named color if it exists at given x/y-coordinates. -* -* @return {string} name or color -*/ -jaws.PixelMap.prototype.namedColorAt = function(x, y) { - var a = this.at(x, y); - for(var i=0; i < this.named_colors.length; i++) { - var name = this.named_colors[i].name; - var c = this.named_colors[i].color; - if(c[0] == a[0] && c[1] == a[1] && c[2] == a[2] && c[3] == a[3]) return name; - } -} - -/** -* Give a RGBA-array a name. Later on we can work with names instead of raw colorvalues. -* -* @example -* pixel_map.nameColor([0,0,0,255], "ground") // Give the color black (with no transparency) the name "ground" -*/ -jaws.PixelMap.prototype.nameColor = function(color, name) { - this.named_colors.push({name: name, color: color}); -} - -return jaws; -})(jaws || {}); - -var jaws = (function(jaws) { - /** - * @class Manage a parallax scroller with different layers. "Field Summary" contains options for the Parallax()-constructor. - * @constructor - * - * @property scale number, scale factor for all layers (2 will double everything and so on) - * @property repeat_x true|false, repeat all parallax layers horizontally - * @property repeat_y true|false, repeat all parallax layers vertically - * @property camera_x number, x-position of "camera". add to camera_x and layers will scroll left. defaults to 0 - * @property camera_y number, y-position of "camera". defaults to 0 - * - * @example - * parallax = new jaws.Parallax({repeat_x: true}) - * parallax.addLayer({image: "parallax_1.png", damping: 100}) - * parallax.addLayer({image: "parallax_2.png", damping: 6}) - * parallax.camera_x += 1 // scroll layers horizontally - * parallax.draw() - * - */ - jaws.Parallax = function Parallax(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - jaws.parseOptions(this, options, this.default_options) - } - - jaws.Parallax.prototype.default_options = { - width: function() { return jaws.width }, - height: function() { return jaws.height }, - scale: 1, - repeat_x: null, - repeat_y: null, - camera_x: 0, - camera_y: 0, - layers: [] - } - - /** Draw all layers in parallax scroller */ - jaws.Parallax.prototype.draw = function(options) { - var layer, numx, numy, initx; - - for(var i=0; i < this.layers.length; i++) { - layer = this.layers[i] - - if(this.repeat_x) { - initx = -((this.camera_x / layer.damping) % layer.width); - } - else { - initx = -(this.camera_x / layer.damping) - } - - if (this.repeat_y) { - layer.y = -((this.camera_y / layer.damping) % layer.height); - } - else { - layer.y = -(this.camera_y / layer.damping); - } - - layer.x = initx; - while (layer.y < this.height) { - while (layer.x < this.width) { - if (layer.x + layer.width >= 0 && layer.y + layer.height >= 0) { //Make sure it's on screen - layer.draw(); //Draw only if actually on screen, for performance reasons - } - layer.x = layer.x + layer.width; - - if (!this.repeat_x) { - break; - } - } - - layer.y = layer.y + layer.height; - layer.x = initx; - if (!this.repeat_y) { - break; - } - } - } - } - /** Add a new layer to the parallax scroller */ - jaws.Parallax.prototype.addLayer = function(options) { - var layer = new jaws.ParallaxLayer(options) - layer.scaleAll(this.scale) - this.layers.push(layer) - } - /** Debugstring for Parallax() */ - jaws.Parallax.prototype.toString = function() { return "[Parallax " + this.x + ", " + this.y + ". " + this.layers.length + " layers]" } - - /** - * @class A single layer that's contained by Parallax() - * - * @property damping number, higher the number, the slower it will scroll with regards to other layers, defaults to 0 - * @constructor - * @extends jaws.Sprite - */ - jaws.ParallaxLayer = function ParallaxLayer(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.damping = options.damping || 0 - jaws.Sprite.call(this, options) - } - jaws.ParallaxLayer.prototype = jaws.Sprite.prototype - - /** Debugstring for ParallaxLayer() */ - // This overwrites Sprites toString, find another sollution. - // jaws.ParallaxLayer.prototype.toString = function() { return "[ParallaxLayer " + this.x + ", " + this.y + "]" } - - return jaws; -})(jaws || {}); - - -/** - * @fileOverview A jaws.Text object with word-wrapping functionality. - * @class jaws.Text - * @property {integer} x Horizontal position (0 = furthest left) - * @property {integer} y Vertical position (0 = top) - * @property {number} alpha Transparency: 0 (fully transparent) to 1 (no transparency) - * @property {number} angle Angle in degrees (0-360) - * @property {string} anchor String stating how to anchor the sprite to canvas; @see Sprite#anchor - * @property {string} text The actual text to be displayed - * @property {string} fontFace A valid font-family - * @property {number} fontSize The size of the text in pixels - * @property {string} textAlign "start", "end", "left", "right", or "center" - * @property {string} textBaseline "top", "bottom", "hanging", "middle", "alphabetic", or "ideographic" - * @property {number} width The width of the rect() containing the text - * @property {number} height The height of the rect() containing the text - * @property {string} style The style to draw the text: "normal", "bold" or italic" - * @property {boolean} wordWrap If word-wrapping should be attempted - * @property {string} shadowColor The color of the shadow for the text - * @property {number} shadowBlur The amount of shadow blur (length away from text) - * @property {number} shadowOffsetX The start of the shadow from initial x - * @property {number} shadowOffsetY The start of the shadow from initial y - * @example - * var text = new Text({text: "Hello world!", x: 10, y: 10}) - */ - -var jaws = (function(jaws) { - - /** - * jaws.Text constructor - * @constructor - * @param {object} options An object-literal collection of constructor values - */ - jaws.Text = function(options) { - if (!(this instanceof arguments.callee)) - return new arguments.callee(options); - - this.set(options); - - if (options.context) { - this.context = options.context; - } - - if (!options.context) { // Defaults to jaws.context - if (jaws.context) - this.context = jaws.context; - } - }; - - /** - * The default values of jaws.Text properties - */ - jaws.Text.prototype.default_options = { - x: 0, - y: 0, - alpha: 1, - angle: 0, - anchor_x: 0, - anchor_y: 0, - anchor: "top_left", - damping: 1, - style: "normal", - fontFace: "serif", - fontSize: 12, - color: "black", - textAlign: "start", - textBaseline: "alphabetic", - text: "", - wordWrap: false, - width: function(){ return jaws.width; }, - height: function() { return jaws.height; }, - shadowColor: null, - shadowBlur: null, - shadowOffsetX: null, - shadowOffsetY: null, - _constructor: null, - }; - - /** - * Overrides constructor values with defaults - * @this {jaws.Text} - * @param {object} options An object-literal collection of constructor values - * @returns {this} - * @see jaws.parseOptions - */ - jaws.Text.prototype.set = function(options) { - - jaws.parseOptions(this, options, this.default_options); - - if (this.anchor) - this.setAnchor(this.anchor); - - this.cacheOffsets(); - - return this; - }; - - /** - * Returns a new instance based on the current jaws.Text object - * @private - * @this {jaws.Text} - * @returns {object} The newly cloned object - */ - jaws.Text.prototype.clone = function() { - var constructor = this._constructor ? eval(this._constructor) : this.constructor; - var new_sprite = new constructor(this.attributes()); - new_sprite._constructor = this._constructor || this.constructor.name; - return new_sprite; - }; - - /** - * Rotate sprite by value degrees - * @this {jaws.Text} - * @param {number} value The amount of the rotation - * @returns {this} Current function scope - */ - jaws.Text.prototype.rotate = function(value) { - this.angle += value; - return this; - }; - - /** - * Forces a rotation-angle on sprite - * @this {jaws.Text} - * @param {number} value The amount of the rotation - * @returns {this} Current function instance - */ - jaws.Text.prototype.rotateTo = function(value) { - this.angle = value; - return this; - }; - - /** - * Move object to position x, y - * @this {jaws.Text} - * @param {number} x The x position to move to - * @param {number} y The y position to move to - * @returns {this} Current function instance - */ - jaws.Text.prototype.moveTo = function(x, y) { - this.x = x; - this.y = y; - return this; - }; - - /** - * Modify x and/or y by a fixed amount - * @this {jaws.Text} - * @param {type} x The additional amount to move x - * @param {type} y The additional amount to move y - * @returns {this} Current function instance - */ - jaws.Text.prototype.move = function(x, y) { - if (x) - this.x += x; - if (y) - this.y += y; - return this; - }; - - /** - * Sets x - * @param {number} value The new x value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setX = function(value) { - this.x = value; - return this; - }; - - /** - * Sets y - * @param {number} value The new y value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setY = function(value) { - this.y = value; - return this; - }; - - /** - * Position sprites top on the y-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setTop = function(value) { - this.y = value + this.top_offset; - return this; - }; - - /** - * Position sprites bottom on the y-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setBottom = function(value) { - this.y = value - this.bottom_offset; - return this; - }; - - /** - * Position sprites left side on the x-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setLeft = function(value) { - this.x = value + this.left_offset; - return this; - }; - - /** - * Position sprites right side on the x-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setRight = function(value) { - this.x = value - this.right_offset; - return this; - }; - - /** - * Set new width. - * @param {number} value The new width - * @returns {this} - */ - jaws.Text.prototype.setWidth = function(value) { - this.width = value; - this.cacheOffsets(); - return this; - }; - - /** - * Set new height. - * @param {number} value The new height - * @returns {this} - */ - jaws.Text.prototype.setHeight = function(value) { - this.height = value; - this.cacheOffsets(); - return this; - }; - - /** - * Resize sprite by adding width or height - * @param {number} width - * @param {number} height - * @returns {this} - */ - jaws.Text.prototype.resize = function(width, height) { - this.width += width; - this.height += height; - this.cacheOffsets(); - return this; - }; - - /** - * Resize sprite to exact width/height - * @this {jaws.Text} - * @param {number} width - * @param {number} height - * @returns {this} - */ - jaws.Text.prototype.resizeTo = function(width, height) { - this.width = width; - this.height = height; - this.cacheOffsets(); - return this; - }; - - /** - * The anchor could be describe as "the part of the text will be placed at x/y" - * or "when rotating, what point of the of the text will it rotate round" - * @param {string} value - * @returns {this} The current function instance - * @example - * For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y - * .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y - */ - jaws.Text.prototype.setAnchor = function(value) { - var anchors = { - top_left: [0, 0], - left_top: [0, 0], - center_left: [0, 0.5], - left_center: [0, 0.5], - bottom_left: [0, 1], - left_bottom: [0, 1], - top_center: [0.5, 0], - center_top: [0.5, 0], - center_center: [0.5, 0.5], - center: [0.5, 0.5], - bottom_center: [0.5, 1], - center_bottom: [0.5, 1], - top_right: [1, 0], - right_top: [1, 0], - center_right: [1, 0.5], - right_center: [1, 0.5], - bottom_right: [1, 1], - right_bottom: [1, 1] - }; - - if (anchors.hasOwnProperty(value)) { - this.anchor_x = anchors[value][0]; - this.anchor_y = anchors[value][1]; - this.cacheOffsets(); - } - return this; - }; - - /** - * Save the object's dimensions - * @private - * @returns {this} The current function instance - */ - jaws.Text.prototype.cacheOffsets = function() { - - this.left_offset = this.width * this.anchor_x; - this.top_offset = this.height * this.anchor_y; - this.right_offset = this.width * (1.0 - this.anchor_x); - this.bottom_offset = this.height * (1.0 - this.anchor_y); - - if (this.cached_rect) - this.cached_rect.resizeTo(this.width, this.height); - return this; - }; - - /** - * Returns a jaws.Rect() perfectly surrouning text. - * @returns {jaws.Rect} - */ - jaws.Text.prototype.rect = function() { - if (!this.cached_rect && this.width) - this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height); - if (this.cached_rect) - this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset); - return this.cached_rect; - }; - - /** - * Draw sprite on active canvas or update its DOM-properties - * @this {jaws.Text} - * @returns {this} The current function instance - */ - jaws.Text.prototype.draw = function() { - this.context.save(); - if (this.angle !== 0) { - this.context.rotate(this.angle * Math.PI / 180); - } - this.context.globalAlpha = this.alpha; - this.context.translate(-this.left_offset, -this.top_offset); // Needs to be separate from above translate call cause of flipped - this.context.fillStyle = this.color; - this.context.font = this.style + " " + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = this.context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - this.context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - this.context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (this.context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (this.context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - this.context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - this.context.restore(); - return this; - }; - - /** - * Returns sprite as a canvas context. - * (For certain browsers, a canvas context is faster to work with then a pure image.) - * @public - * @this {jaws.Text} - */ - jaws.Text.prototype.asCanvasContext = function() { - var canvas = document.createElement("canvas"); - canvas.width = this.width; - canvas.height = this.height; - - var context = canvas.getContext("2d"); - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - this.context.fillStyle = this.color; - this.context.font = this.style + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = this.context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - this.context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - this.context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (this.context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (this.context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - this.context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - return context; - }; - - /** - * Returns text as a canvas - * @this {jaws.Text} - */ - jaws.Text.prototype.asCanvas = function() { - var canvas = document.createElement("canvas"); - canvas.width = this.width; - canvas.height = this.height; - - var context = canvas.getContext("2d"); - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - this.context.fillStyle = this.color; - this.context.font = this.style + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - return canvas; - }; - - /** - * Returns Text's properties as a String - * @returns {string} - */ - jaws.Text.prototype.toString = function() { - return "[Text " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]"; - }; - - /** - * Returns Text's properties as a pure object - * @returns {object} - */ - jaws.Text.prototype.attributes = function() { - var object = this.options; // Start with all creation time properties - object["_constructor"] = this._constructor || "jaws.Text"; - object["x"] = parseFloat(this.x.toFixed(2)); - object["y"] = parseFloat(this.y.toFixed(2)); - object["text"] = this.text; - object["alpha"] = this.alpha; - object["angle"] = parseFloat(this.angle.toFixed(2)); - object["anchor_x"] = this.anchor_x; - object["anchor_y"] = this.anchor_y; - object["style"] = this.style; - object["fontSize"] = this.fontSize; - object["fontFace"] = this.fontFace; - object["color"] = this.color; - object["textAlign"] = this.textAlign; - object["textBaseline"] = this.textBaseline; - object["wordWrap"] = this.wordWrap; - object["width"] = this.width; - object["height"] = this.height; - return object; - }; - - /** - * Returns a JSON-string representing the properties of the Text. - * @returns {string} - */ - jaws.Text.prototype.toJSON = function() { - return JSON.stringify(this.attributes()); - }; - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.Text; -} - -if(typeof require !== "undefined") { var jaws = require("./core.js"); } - -/* - * @class jaws.QuadTree - * @property {jaws.Rect} bounds Rect(x,y,width,height) defining bounds of tree - * @property {number} depth The depth of the root node - * @property {array} nodes The nodes of the root node - * @property {array} objects The objects of the root node - * @example - * setup: - * var quadtree = new jaws.QuadTree(); - * update: - * quadtree.collide(sprite or list, sprite or list, callback function); - */ -var jaws = (function(jaws) { - - /** - * Creates an empty quadtree with optional bounds and starting depth - * @constructor - * @param {jaws.Rect} [bounds] The defining bounds of the tree - * @param {number} [depth] The current depth of the tree - */ - jaws.QuadTree = function(bounds) { - this.depth = arguments[1] || 0; - this.bounds = bounds || new jaws.Rect(0, 0, jaws.width, jaws.height); - this.nodes = []; - this.objects = []; - }; - - /** - * Moves through the nodes and deletes them. - * @this {jaws.QuadTree} - */ - jaws.QuadTree.prototype.clear = function() { - this.objects = []; - - for (var i = 0; i < this.nodes.length; i++) { - if (typeof this.nodes[i] !== 'undefined') { - this.nodes[i].clear(); - delete this.nodes[i]; - } - } - }; - - /** - * Creates four new branches sub-dividing the current node's width and height - * @private - * @this {jaws.QuadTree} - */ - jaws.QuadTree.prototype.split = function() { - var subWidth = Math.round((this.bounds.width / 2)); - var subHeight = Math.round((this.bounds.height / 2)); - var x = this.bounds.x; - var y = this.bounds.y; - - this.nodes[0] = new jaws.QuadTree(new jaws.Rect(x + subWidth, y, subWidth, subHeight), this.depth + 1); - this.nodes[1] = new jaws.QuadTree(new jaws.Rect(x, y, subWidth, subHeight), this.depth + 1); - this.nodes[2] = new jaws.QuadTree(new jaws.Rect(x, y + subHeight, subWidth, subHeight), this.depth + 1); - this.nodes[3] = new jaws.QuadTree(new jaws.Rect(x + subWidth, y + subHeight, subWidth, subHeight), this.depth + 1); - }; - - /** - * Returns the index of a node's branches if passed-in object fits within it - * @private - * @param {object} pRect An object with the properties x, y, width, and height - * @returns {index} The index of nodes[] that matches the dimensions of passed-in object - */ - jaws.QuadTree.prototype.getIndex = function(pRect) { - var index = -1; - var verticalMidpoint = this.bounds.x + (this.bounds.width / 2); - var horizontalMidpoint = this.bounds.y + (this.bounds.height / 2); - - var topQuadrant = (pRect.y < horizontalMidpoint && pRect.y + pRect.height < horizontalMidpoint); - var bottomQuadrant = (pRect.y > horizontalMidpoint); - - if (pRect.x < verticalMidpoint && pRect.x + pRect.width < verticalMidpoint) { - if (topQuadrant) { - index = 1; - } - else if (bottomQuadrant) { - index = 2; - } - } - else if (pRect.x > verticalMidpoint) { - if (topQuadrant) { - index = 0; - } - else if (bottomQuadrant) { - index = 3; - } - } - - return index; - }; - - /** - * Inserts an object into the quadtree, spliting it into new branches if needed - * @param {object} pRect An object with the properties x, y, width, and height - */ - jaws.QuadTree.prototype.insert = function(pRect) { - - if (!pRect.hasOwnProperty("x") && !pRect.hasOwnProperty("y") && - !pRect.hasOwnProperty("width") && !pRect.hasOwnProperty("height")) { - return; - } - - if (typeof this.nodes[0] !== 'undefined') { - var index = this.getIndex(pRect); - - if (index !== -1) { - this.nodes[index].insert(pRect); - return; - } - } - - this.objects.push(pRect); - - if (typeof this.nodes[0] === 'undefined') { - this.split(); - } - - var i = 0; - while (i < this.objects.length) { - var index = this.getIndex(this.objects[i]); - if (index !== -1) { - this.nodes[index].insert(this.objects.splice(i, 1)[0]); - } - else { - i++; - } - } - - }; - - /** - * Returns those objects on the branch matching the position of the passed-in object - * @param {object} pRect An object with properties x, y, width, and height - * @returns {array} The objects on the same branch as the passed-in object - */ - jaws.QuadTree.prototype.retrieve = function(pRect) { - - if (!pRect.hasOwnProperty("x") && !pRect.hasOwnProperty("y") && - !pRect.hasOwnProperty("width") && !pRect.hasOwnProperty("height")) { - return; - } - - var index = this.getIndex(pRect); - var returnObjects = this.objects; - if (typeof this.nodes[0] !== 'undefined') { - if (index !== -1) { - returnObjects = returnObjects.concat(this.nodes[index].retrieve(pRect)); - } else { - for (var i = 0; i < this.nodes.length; i++) { - returnObjects = returnObjects.concat(this.nodes[i].retrieve(pRect)); - } - } - } - return returnObjects; - }; - - /** - * Checks for collisions between objects by creating a quadtree, inserting one or more objects, - * and then comparing the results of a retrieval against another single or set of objects. - * - * With the callback argument, it will call a function and pass the items found colliding - * as the first and second argument. - * - * Without the callback argument, it will return a boolean value if any collisions were found. - * - * @param {object|array} list1 A single or set of objects with properties x, y, width, and height - * @param {object|array} list2 A single or set of objects with properties x, y, width, and height - * @param {function} [callback] The function to call per collision - * @returns {boolean} If the items (or any within their sets) collide with one another - */ - jaws.QuadTree.prototype.collide = function(list1, list2, callback) { - - var overlap = false; - var tree = new jaws.QuadTree(); - var temp = []; - - if (!(list1.forEach)) { - temp.push(list1); - list1 = temp; - } - - if (!(list2.forEach)) { - temp = []; - temp.push(list2); - list2 = temp; - } - - list2.forEach(function(el) { - tree.insert(el); - }); - - list1.forEach(function(el) { - if(jaws.collide(el, tree.retrieve(el), callback)) { - overlap = true; - } - }); - - tree.clear(); - return overlap; - }; - - return jaws; - -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.QuadTree; -} -;window.addEventListener("load", function() { if(jaws.onload) jaws.onload(); }, false);
\ No newline at end of file diff --git a/jaws/package.json b/jaws/package.json deleted file mode 100644 index 1293907..0000000 --- a/jaws/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "JawsJS", - "description": "", - "version": "0.0.1", - "repository": { - "type": "git", - "url": "https://github.com/ippa/jawsjs.git" - }, - "dependencies": { - "gulp": "", - "gulp-concat": "", - "gulp-qunit": "^0.3.3", - "gulp-requirejs": "0.1.x", - "gulp-uglify": "", - "requirejs": "2.1.x" - }, - "devDependencies": { - "gulp-insert": "", - "gulp-qunit": "", - "gulp-shell": "^0.2.9", - "qunitjs": "^1.15.0" - }, - "scripts": { - "test": "gulp test" - } -} diff --git a/jaws/src/animation.js b/jaws/src/animation.js deleted file mode 100755 index 593450f..0000000 --- a/jaws/src/animation.js +++ /dev/null @@ -1,168 +0,0 @@ -var jaws = (function(jaws) { - -/** - * @class Manages an animation with a given list of frames. "Field Summary" contains options for the Animation()-constructor. - * - * @property {bool} loop Restart animation when end is reached - * @property {bool} bounce Rewind the animation frame by frame when end is reached - * @property {int} index Start on this frame - * @property {array} frames Images/canvaselements - * @property {milliseconds} frame_duration How long should each frame be displayed - * @property {int} frame_direction -1 for backwards animation. 1 is default - * @property {array} frame_size Containing width/height, eg. [32, 32] - * @property {int} offset When cutting out frames from a sprite sheet, start at this frame - * @property {string} orientation How to cut out frames frmo sprite sheet, possible values are "down" or "right" - * @property {function} on_end Function to call when animation ends. triggers only on non-looping, non-bouncing animations - * @property {object} subsets Name specific frames-intervals for easy access later, i.e. {move: [2,4], fire: [4,6]}. Access with animation.subset[name] - * - * @example - * // in setup() - * anim = new jaws.Animation({sprite_sheet: "droid_11x15.png", frame_size: [11,15], frame_duration: 100}) - * player = new jaws.Sprite({y:300, anchor: "center_bottom"}) - * - * // in update() - * player.setImage( anim.next() ) - * - * // in draw() - * player.draw() - * - */ -jaws.Animation = function Animation(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - jaws.parseOptions(this, options, this.default_options); - - if(options.sprite_sheet) { - var sprite_sheet = new jaws.SpriteSheet({image: options.sprite_sheet, scale_image: this.scale_image, frame_size: this.frame_size, orientation: this.orientation, offset: this.offset}) - this.frames = sprite_sheet.frames - this.frame_size = sprite_sheet.frame_size - } - - if(options.scale_image) { - var image = (jaws.isDrawable(options.sprite_sheet) ? options.sprite_sheet : jaws.assets.get(options.sprite_sheet)) - this.frame_size[0] *= options.scale_image - this.frame_size[1] *= options.scale_image - options.sprite_sheet = jaws.retroScaleImage(image, options.scale_image) - } - - /* Initializing timer-stuff */ - this.current_tick = (new Date()).getTime(); - this.last_tick = (new Date()).getTime(); - this.sum_tick = 0 - - if(options.subsets) { - this.subsets = {} - for(subset in options.subsets) { - start_stop = options.subsets[subset] - this.subsets[subset] = this.slice(start_stop[0], start_stop[1]) - } - } -} - -jaws.Animation.prototype.default_options = { - frames: [], - subsets: [], - frame_duration: 100, // default: 100ms between each frameswitch - index: 0, // default: start with the very first frame - loop: 1, - bounce: 0, - frame_direction: 1, - frame_size: null, - orientation: "down", - on_end: null, - offset: 0, - scale_image: null, - sprite_sheet: null -} - -/** - * Return a special animationsubset created with "subset"-parameter when initializing - * - */ -jaws.Animation.prototype.subset = function(subset) { - return this.subsets[subset] -} - -/** - Propells the animation forward by counting milliseconds and changing this.index accordingly - Supports looping and bouncing animations -*/ -jaws.Animation.prototype.update = function() { - this.current_tick = (new Date()).getTime(); - this.sum_tick += (this.current_tick - this.last_tick); - this.last_tick = this.current_tick; - - if(this.sum_tick > this.frame_duration) { - this.index += this.frame_direction - this.sum_tick = 0 - } - if( (this.index >= this.frames.length) || (this.index < 0) ) { - if(this.bounce) { - this.frame_direction = -this.frame_direction - this.index += this.frame_direction * 2 - } - else if(this.loop) { - if(this.frame_direction < 0) { - this.index = this.frames.length -1; - } else { - this.index = 0; - } - } - else { - this.index -= this.frame_direction - if (this.on_end) { - this.on_end() - this.on_end = null - } - } - } - return this -} - -/** - works like Array.slice but returns a new Animation-object with a subset of the frames -*/ -jaws.Animation.prototype.slice = function(start, stop) { - var o = {} - o.frame_duration = this.frame_duration - o.loop = this.loop - o.bounce = this.bounce - o.on_end = this.on_end - o.frame_direction = this.frame_direction - o.frames = this.frames.slice().slice(start, stop) - return new jaws.Animation(o) -}; - -/** - Moves animation forward by calling update() and then return the current frame -*/ -jaws.Animation.prototype.next = function() { - this.update() - return this.frames[this.index] -}; - -/** returns true if animation is at the very last frame */ -jaws.Animation.prototype.atLastFrame = function() { return (this.index == this.frames.length-1) } - -/** returns true if animation is at the very first frame */ -jaws.Animation.prototype.atFirstFrame = function() { return (this.index == 0) } - - -/** - returns the current frame -*/ -jaws.Animation.prototype.currentFrame = function() { - return this.frames[this.index] -}; - -/** - * Debugstring for Animation()-constructor - * @example - * var anim = new Animation(...) - * console.log(anim.toString()) - */ -jaws.Animation.prototype.toString = function() { return "[Animation, " + this.frames.length + " frames]" } - -return jaws; -})(jaws || {}); - diff --git a/jaws/src/assets.js b/jaws/src/assets.js deleted file mode 100755 index 6f61947..0000000 --- a/jaws/src/assets.js +++ /dev/null @@ -1,537 +0,0 @@ -var jaws = (function(jaws) { - /** - * @fileOverview jaws.assets properties and functions - * - * Loads and processes image, sound, video, and json assets - * (Used internally by JawsJS to create <b>jaws.assets</b>) - * - * @class Jaws.Assets - * @constructor - * @property {boolean} bust_cache Add a random argument-string to assets-urls when loading to bypass any cache - * @property {boolean} fuchia_to_transparent Convert the color fuchia to transparent when loading .bmp-files - * @property {boolean} image_to_canvas Convert all image assets to canvas internally - * @property {string} root Rootdir from where all assets are loaded - * @property {array} file_type Listing of file postfixes and their associated types - * @property {array} can_play Listing of postfixes and (during runtime) populated booleans - */ - jaws.Assets = function Assets() { - if (!(this instanceof arguments.callee)) - return new arguments.callee(); - - var self = this; - - self.loaded = []; - self.loading = []; - self.src_list = []; - self.data = []; - - self.bust_cache = false; - self.image_to_canvas = true; - self.fuchia_to_transparent = true; - self.root = ""; - - self.file_type = {}; - self.file_type["json"] = "json"; - self.file_type["wav"] = "audio"; - self.file_type["mp3"] = "audio"; - self.file_type["ogg"] = "audio"; - self.file_type['m4a'] = "audio"; - self.file_type['weba'] = "audio"; - self.file_type['aac'] = "audio"; - self.file_type['mka'] = "audio"; - self.file_type['flac'] = "audio"; - self.file_type["png"] = "image"; - self.file_type["jpg"] = "image"; - self.file_type["jpeg"] = "image"; - self.file_type["gif"] = "image"; - self.file_type["bmp"] = "image"; - self.file_type["tiff"] = "image"; - self.file_type['mp4'] = "video"; - self.file_type['webm'] = "video"; - self.file_type['ogv'] = "video"; - self.file_type['mkv'] = "video"; - - self.can_play = {}; - - try { - var audioTest = new Audio(); - self.can_play["wav"] = !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''); - self.can_play["ogg"] = !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["mp3"] = !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''); - self.can_play["m4a"] = !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''); - self.can_play["weba"] = !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["aac"] = !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''); - self.can_play["mka"] = !!audioTest.canPlayType('audio/x-matroska;').replace(/^no$/, ''); - self.can_play["flac"] = !!audioTest.canPlayType('audio/x-flac;').replace(/^no$/, ''); - } - catch(e) { - } - - try { - var videoTest = document.createElement('video'); - self.can_play["mp4"] = !!videoTest.canPlayType('video/mp4;').replace(/^no$/, ''); - self.can_play["webm"] = !!videoTest.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["ogv"] = !!videoTest.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["mkv"] = !!videoTest.canPlayType('video/x-matroska;').replace(/^no$/, ''); - } - catch(e) { - } - - /** - * Returns the length of the resource list - * @public - * @returns {number} The length of the resource list - */ - self.length = function() { - return self.src_list.length; - }; - - /** - * Set root prefix-path to all assets - * - * @example - * jaws.assets.setRoot("music/").add(["music.mp3", "music.ogg"]).loadAll() - * - * @public - * @param {string} path-prefix for all following assets - * @returns {object} self - */ - self.setRoot = function(path) { - self.root = path - return self - } - - /** - * Get one or more resources from their URLs. Supports simple wildcard (you can end a string with "*"). - * - * @example - * jaws.assets.add(["song.mp3", "song.ogg"]) - * jaws.assets.get("song.*") // -> Will return song.ogg in firefox and song.mp3 in IE - * - * @public - * @param {string|array} src The resource(s) to retrieve - * @returns {array|object} Array or single resource if found in cache. Undefined otherwise. - */ - self.get = function(src) { - if (jaws.isArray(src)) { - return src.map(function(i) { - return self.data[i]; - }); - } - else if (jaws.isString(src)) { - // Wildcard? song.*, match against asset-srcs, make sure it's loaded and return content of first match. - if(src[src.length-1] === "*") { - var needle = src.replace("*", "") - for(var i=0; i < self.src_list.length; i++) { - if(self.src_list[i].indexOf(needle) == 0 && self.data[self.src_list[i]]) - return self.data[self.src_list[i]]; - } - } - - // TODO: self.loaded[src] is false for supported files for some odd reason. - if (self.data[src]) { return self.data[src]; } - else { jaws.log.warn("No such asset: " + src, true); } - } - else { - jaws.log.error("jaws.get: Neither String nor Array. Incorrect URL resource " + src); - return; - } - }; - - /** - * Returns if specified resource is currently loading or not - * @public - * @param {string} src Resource URL - * @return {boolean|undefined} If resource is currently loading. Otherwise, undefined. - */ - self.isLoading = function(src) { - if (jaws.isString(src)) { - return self.loading[src]; - } else { - jaws.log.error("jaws.isLoading: Argument not a String with " + src); - } - }; - - /** - * Returns if specified resource is loaded or not - * @param src Source URL - * @return {boolean|undefined} If specified resource is loaded or not. Otherwise, undefined. - */ - self.isLoaded = function(src) { - if (jaws.isString(src)) { - return self.loaded[src]; - } else { - jaws.log.error("jaws.isLoaded: Argument not a String with " + src); - } - }; - - /** - * Returns lowercase postfix of specified resource - * @public - * @param {string} src Resource URL - * @returns {string} Lowercase postfix of resource - */ - self.getPostfix = function(src) { - if (jaws.isString(src)) { - return src.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1]; - } else { - jaws.log.error("jaws.assets.getPostfix: Argument not a String with " + src); - } - }; - - /** - * Determine type of file (Image, Audio, or Video) from its postfix - * @private - * @param {string} src Resource URL - * @returns {string} Matching type {Image, Audio, Video} or the postfix itself - */ - function getType(src) { - if (jaws.isString(src)) { - var postfix = self.getPostfix(src); - return (self.file_type[postfix] ? self.file_type[postfix] : postfix); - } else { - jaws.log.error("jaws.assets.getType: Argument not a String with " + src); - } - } - - /** - * Add URL(s) to asset listing for later loading - * @public - * @param {string|array|arguments} src The resource URL(s) to add to the asset listing - * @example - * jaws.assets.add("player.png") - * jaws.assets.add(["media/bullet1.png", "media/bullet2.png"]) - * jaws.assets.add("foo.png", "bar.png") - * jaws.assets.loadAll({onload: start_game}) - */ - self.add = function(src) { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) { - self.add(list[i]); - } - else { - if(jaws.isString(list[i])) { self.src_list.push(list[i]) } - else { jaws.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource " + src) } - } - } - - return self; - }; - - /** - * Iterate through the list of resource URL(s) and load each in turn. - * @public - * @param {Object} options Object-literal of callback functions - * @config {function} [options.onprogress] The function to be called on progress (when one assets of many is loaded) - * @config {function} [options.onerror] The function to be called if an error occurs - * @config {function} [options.onload] The function to be called when finished - */ - self.loadAll = function(options) { - self.load_count = 0; - self.error_count = 0; - - if (options.onprogress && jaws.isFunction(options.onprogress)) - self.onprogress = options.onprogress; - - if (options.onerror && jaws.isFunction(options.onerror)) - self.onerror = options.onerror; - - if (options.onload && jaws.isFunction(options.onload)) - self.onload = options.onload; - - self.src_list.forEach(function(item) { - self.load(item); - }); - - return self; - }; - - /** - * Loads a single resource from its given URL - * Will attempt to match a resource to known MIME types. - * If unknown, loads the file as a blob-object. - * - * @public - * @param {string} src Resource URL - * @param {Object} options Object-literal of callback functions - * @config {function} [options.onload] Function to be called when assets has loaded - * @config {function} [options.onerror] Function to be called if an error occurs - * @example - * jaws.load("media/foo.png") - * jaws.load("http://place.tld/foo.png") - */ - self.load = function(src, options) { - if(!options) options = {}; - - if (!jaws.isString(src)) { - jaws.log.error("jaws.assets.load: Argument not a String with " + src); - return; - } - - var asset = {}; - var resolved_src = ""; - asset.src = src; - asset.onload = options.onload; - asset.onerror = options.onerror; - self.loading[src] = true; - var parser = RegExp('^((f|ht)tp(s)?:)?//'); - if (parser.test(src)) { - resolved_src = asset.src; - } else { - resolved_src = self.root + asset.src; - } - if (self.bust_cache) { - resolved_src += "?" + parseInt(Math.random() * 10000000); - } - - var type = getType(asset.src); - if (type === "image") { - try { - asset.image = new Image(); - asset.image.asset = asset; - asset.image.addEventListener('load', assetLoaded); - asset.image.addEventListener('error', assetError); - asset.image.src = resolved_src; - } catch (e) { - jaws.log.error("Cannot load Image resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - else if (self.can_play[self.getPostfix(asset.src)]) { - if (type === "audio") { - try { - asset.audio = new Audio(); - asset.audio.asset = asset; - asset.audio.addEventListener('error', assetError); - asset.audio.addEventListener('canplay', assetLoaded); // NOTE: assetLoaded can be called several times during loading. - self.data[asset.src] = asset.audio; - asset.audio.src = resolved_src; - asset.audio.load(); - } catch (e) { - jaws.log.error("Cannot load Audio resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - else if (type === "video") { - try { - asset.video = document.createElement('video'); - asset.video.asset = asset; - self.data[asset.src] = asset.video; - asset.video.setAttribute("style", "display:none;"); - asset.video.addEventListener('error', assetError); - asset.video.addEventListener('canplay', assetLoaded); - document.body.appendChild(asset.video); - asset.video.src = resolved_src; - asset.video.load(); - } catch (e) { - jaws.log.error("Cannot load Video resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - } - - //Load everything else as raw blobs... - else { - // ... But don't load un-supported audio-files. - if(type === "audio" && !self.can_play[self.getPostfix(asset.src)]) { - assetSkipped(asset); - return self; - } - - try { - var req = new XMLHttpRequest(); - req.asset = asset; - req.onreadystatechange = assetLoaded; - req.onerror = assetError; - req.open('GET', resolved_src, true); - if (type !== "json") - req.responseType = "blob"; - req.send(null); - } catch (e) { - jaws.log.error("Cannot load " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - - return self; - }; - - /** - * Initial loading callback for all assets for parsing specific filetypes or - * optionally converting images to canvas-objects. - * @private - * @param {EventObject} event The EventObject populated by the calling event - * @see processCallbacks() - */ - function assetLoaded(event) { - var asset = this.asset; - var src = asset.src; - var filetype = getType(asset.src); - - try { - if (filetype === "json") { - if (this.readyState !== 4) { - return; - } - self.data[asset.src] = JSON.parse(this.responseText); - } - else if (filetype === "image") { - var new_image = self.image_to_canvas ? jaws.imageToCanvas(asset.image) : asset.image; - if (self.fuchia_to_transparent && self.getPostfix(asset.src) === "bmp") { - new_image = fuchiaToTransparent(new_image); - } - self.data[asset.src] = new_image; - } - else if (filetype === "audio" && self.can_play[self.getPostfix(asset.src)]) { - self.data[asset.src] = asset.audio; - } - else if (filetype === "video" && self.can_play[self.getPostfix(asset.src)]) { - self.data[asset.src] = asset.video; - } else { - self.data[asset.src] = this.response; - } - } catch (e) { - jaws.log.error("Cannot process " + src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - self.data[asset.src] = null; - } - - /* - * Only increment load_count ONCE per unique asset. - * This is needed cause assetLoaded-callback can in certain cases be called several for a single asset... - * ..and not only Once when it's loaded. - */ - if( !self.loaded[src]) self.load_count++; - - self.loaded[src] = true; - self.loading[src] = false; - - processCallbacks(asset, true, event); - } - - /** - * Called when jaws asset-handler decides that an asset shouldn't be loaded - * For example, an unsupported audio-format won't be loaded. - * - * @private - */ - function assetSkipped(asset) { - self.loaded[asset.src] = true; - self.loading[asset.src] = false; - self.load_count++; - processCallbacks(asset, true); - } - - /** - * Increases the error count and calls processCallbacks with false flag set - * @see processCallbacks() - * @private - * @param {EventObject} event The EventObject populated by the calling event - */ - function assetError(event) { - var asset = this.asset; - self.error_count++; - processCallbacks(asset, false, event); - } - - /** - * Processes (if set) the callbacks per resource - * @private - * @param {object} asset The asset to be processed - * @param {boolean} ok If an error has occured with the asset loading - * @param {EventObject} event The EventObject populated by the calling event - * @see jaws.start() in core.js - */ - function processCallbacks(asset, ok, event) { - var percent = parseInt((self.load_count + self.error_count) / self.src_list.length * 100); - - if (ok) { - if(self.onprogress) - self.onprogress(asset.src, percent); - if(asset.onprogress && event !== undefined) - asset.onprogress(event); - } - else { - if(self.onerror) - self.onerror(asset.src, percent); - if(asset.onerror && event !== undefined) - asset.onerror(event); - } - - if (percent === 100) { - if(self.onload) self.onload(); - - self.onprogress = null; - self.onerror = null; - self.onload = null; - } - } - - /** - * Displays the progress of asset handling as an overall percentage of all loading - * (Can be overridden as jaws.assets.displayProgress = function(percent_done) {}) - * @public - * @param {number} percent_done The overall percentage done across all resource handling - */ - self.displayProgress = function(percent_done) { - - if (!jaws.isNumber(percent_done)) - return; - - if (!jaws.context) - return; - - jaws.context.save(); - jaws.context.fillStyle = "black"; - jaws.context.fillRect(0, 0, jaws.width, jaws.height); - - jaws.context.fillStyle = "white"; - jaws.context.strokeStyle = "white"; - jaws.context.textAlign = "center"; - - jaws.context.strokeRect(50 - 1, (jaws.height / 2) - 30 - 1, jaws.width - 100 + 2, 60 + 2); - jaws.context.fillRect(50, (jaws.height / 2) - 30, ((jaws.width - 100) / 100) * percent_done, 60); - - jaws.context.font = "11px verdana"; - jaws.context.fillText("Loading... " + percent_done + "%", jaws.width / 2, jaws.height / 2 - 35); - - jaws.context.font = "11px verdana"; - jaws.context.fillStyle = "#ccc"; - jaws.context.textBaseline = "bottom"; - jaws.context.fillText("powered by www.jawsjs.com", jaws.width / 2, jaws.height - 1); - - jaws.context.restore(); - }; - }; - - /** - * Make Fuchia (0xFF00FF) transparent (BMPs ONLY) - * @private - * @param {HTMLImageElement} image The Bitmap Image to convert - * @returns {CanvasElement} canvas The translated CanvasElement - */ - function fuchiaToTransparent(image) { - if (!jaws.isDrawable(image)) - return; - - var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image; - var context = canvas.getContext("2d"); - var img_data = context.getImageData(0, 0, canvas.width, canvas.height); - var pixels = img_data.data; - for (var i = 0; i < pixels.length; i += 4) { - if (pixels[i] === 255 && pixels[i + 1] === 0 && pixels[i + 2] === 255) { // Color: Fuchia - pixels[i + 3] = 0; // Set total see-through transparency - } - } - - context.putImageData(img_data, 0, 0); - return canvas; - } - - jaws.assets = new jaws.Assets(); - return jaws; -})(jaws || {}); - diff --git a/jaws/src/collision_detection.js b/jaws/src/collision_detection.js deleted file mode 100755 index 2f4588c..0000000 --- a/jaws/src/collision_detection.js +++ /dev/null @@ -1,226 +0,0 @@ -if(typeof require !== "undefined") { var jaws = require("./core.js"); }
-
-/**
- * @fileOverview Collision Detection
- *
- * Collision detection helpers.
- *
- * @example
- * // collision helper exampels:
- * collideOneWithOne(player, boss) // -> false
- * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
- * collideManyWithMany(bullets, enemies) // -> [ [bullet1, enemy1], [bullet2, enemy2] ]
- * collide(player, boss) // -> false
- * collide(player,
- * bullets,
- * function(player, bullet) {}) // Callback: arguments[0] -> player
- * // arguments[1] -> bullets[i]
- *
- */
-var jaws = (function(jaws) {
-
- /**
- * Collides two objects by reading x, y and either method rect() or property radius.
- * @public
- * @param {object} object1 An object with a 'radius' or 'rect' property
- * @param {object} object2 An object with a 'radius' or 'rect' property
- * @returns {boolean} If the two objects are colliding or not
- */
- jaws.collideOneWithOne = function(object1, object2) {
- if (object1.radius && object2.radius && object1 !== object2 && jaws.collideCircles(object1, object2))
- return true;
-
- if (object1.rect && object2.rect && object1 !== object2 && jaws.collideRects(object1.rect(), object2.rect()))
- return true;
-
- return false;
- };
-
- /**
- * Compares an object against a list, returning those from list that collide with object, and
- * calling 'callback' per collision (if set) with object and item from list.
- * (Note: Will never collide objects with themselves.)
- * @public
- * @param {object} object An object with a 'radius' or 'rect' property
- * @param {array|object} list A collection of objects with a 'length' property
- * @param {function} callback The function to be called per collison detected
- * @returns {array} A collection of items colliding with object from list
- * @example
- * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
- * collideOneWithMany(player, bullets, function(player, bullet) {
- * //player and bullet (bullets[i])
- * });
- */
- jaws.collideOneWithMany = function(object, list, callback) {
- var a = [];
- if (callback) {
- for (var i = 0; i < list.length; i++) {
- if (jaws.collideOneWithOne(object, list[i])) {
- callback(object, list[i]);
- a.push(list[i])
- }
- }
- return a;
- }
- else {
- return list.filter(function(item) {
- return jaws.collideOneWithOne(object, item);
- });
- }
- };
-
- /**
- * Compares two lists, returning those items from each that collide with each other, and
- * calling 'callback' per collision (if set) with item from list1 and item from list2.
- * (Note: Will never collide objects with themselves.)
- * @public
- * @param {array|object} list1 A collection of objects with a 'forEach' property
- * @param {array|object} list2 A collection of objects with a 'forEach' property
- * @param {function} callback The function to be called per collison detected
- * @returns {array} A collection of items colliding with list1 from list2
- * @example
- * jaws.collideManyWithMany(bullets, enemies) // --> [[bullet, enemy], [bullet, enemy]]
- */
- jaws.collideManyWithMany = function(list1, list2, callback) {
- var a = [];
-
- if (list1 === list2) {
- combinations(list1, 2).forEach(function(pair) {
- if (jaws.collideOneWithOne(pair[0], pair[1])) {
- if (callback) {
- callback(pair[0], pair[1]);
- }
- else {
- a.push([pair[0], pair[1]]);
- }
- }
- });
- }
- else {
- list1.forEach(function(item1) {
- list2.forEach(function(item2) {
- if (jaws.collideOneWithOne(item1, item2)) {
- if (callback) {
- callback(item1, item2);
- }
- else {
- a.push([item1, item2]);
- }
- }
- });
- });
- }
-
- return a;
- };
-
- /**
- * Returns if two circle-objects collide with each other
- * @public
- * @param {object} object1 An object with a 'radius' property
- * @param {object} object2 An object with a 'radius' property
- * @returns {boolean} If two circle-objects collide or not
- */
- jaws.collideCircles = function(object1, object2) {
- return (jaws.distanceBetween(object1, object2) < object1.radius + object2.radius);
- };
-
- /**
- * Returns if two Rects collide with each other or not
- * @public
- * @param {object} rect1 An object with 'x', 'y', 'right' and 'bottom' properties
- * @param {object} rect2 An object with 'x', 'y', 'right' and 'bottom' properties
- * @returns {boolean} If two Rects collide with each other or not
- */
- jaws.collideRects = function(rect1, rect2) {
- return ((rect1.x >= rect2.x && rect1.x <= rect2.right) || (rect2.x >= rect1.x && rect2.x <= rect1.right)) &&
- ((rect1.y >= rect2.y && rect1.y <= rect2.bottom) || (rect2.y >= rect1.y && rect2.y <= rect1.bottom));
- };
-
- /**
- * Returns the distance between two objects
- * @public
- * @param {object} object1 An object with 'x' and 'y' properties
- * @param {object} object2 An object with 'x' and 'y' properties
- * @returns {number} The distance between two objects
- */
- jaws.distanceBetween = function(object1, object2) {
- return Math.sqrt(Math.pow(object1.x - object2.x, 2) + Math.pow(object1.y - object2.y, 2));
- };
-
- /**
- * Creates combinations of items from a list of a specific size
- * @private
- * @param {array|object} list An object with a 'length' property
- * @param {number} n The size of the array to return
- * @returns {Array} An array of items having a specific size number of its own entries
- */
- function combinations(list, n) {
- var f = function(i) {
- if (list.isSpriteList !== undefined) {
- return list.at(i);
- } else { // s is an Array
- return list[i];
- }
- };
- var r = [];
- var m = new Array(n);
- for (var i = 0; i < n; i++)
- m[i] = i;
- for (var i = n - 1, sn = list.length; 0 <= i; sn = list.length) {
- r.push(m.map(f));
- while (0 <= i && m[i] === sn - 1) {
- i--;
- sn--;
- }
- if (0 <= i) {
- m[i] += 1;
- for (var j = i + 1; j < n; j++)
- m[j] = m[j - 1] + 1;
- i = n - 1;
- }
- }
- return r;
- }
-
- /**
- * If an object has items or not
- * @private
- * @param {array|object} array An object with a 'length' property
- * @returns {boolean} If the object has items (length > 0)
- */
- function hasItems(array) {
- return (array && array.length > 0);
- }
-
- /**
- * Compares two objects or lists, returning if they collide, and
- * calling 'callback' per collision (if set) between objects or lists.
- * @param {array|object} x An object with either 'rect' or 'forEach' property
- * @param {array|object} x2 An object with either 'rect' or 'forEach' property
- * @param {function} callback
- * @returns {boolean}
- * @examples
- * jaws.collide(player, enemy, function(player, enemy) { ... } )
- * jaws.collide(player, enemies, function(player, enemy) { ... } )
- * jaws.collide(bullets, enemies, function(bullet, enemy) { ... } )
- */
- jaws.collide = function(x, x2, callback) {
- if ((x.rect || x.radius) && x2.forEach)
- return (jaws.collideOneWithMany(x, x2, callback).length > 0);
- if (x.forEach && x2.forEach)
- return (jaws.collideManyWithMany(x, x2, callback).length > 0);
- if (x.forEach && (x2.rect || x2.radius))
- return (jaws.collideOneWithMany(x2, x, callback).length > 0);
- if ((x.rect && x2.rect) || (x.radius && x2.radius)) {
- var result = jaws.collideOneWithOne(x, x2);
- if (callback && result)
- callback(x, x2);
- else
- return result;
- }
- };
-
- return jaws;
-})(jaws || {});
-
diff --git a/jaws/src/core.js b/jaws/src/core.js deleted file mode 100755 index 5a02dd1..0000000 --- a/jaws/src/core.js +++ /dev/null @@ -1,713 +0,0 @@ -/** - * @namespace JawsJS core functions. - * - * Jaws, a HTML5 canvas/javascript 2D game development framework - * - * Homepage: http://jawsjs.com/ - * Source: http://github.com/ippa/jaws/ - * Documentation: http://jawsjs.com/docs/ - * - * Works with: Chrome 6.0+, Firefox 3.6+, 4+, IE 9+ - * License: LGPL - http://www.gnu.org/licenses/lgpl.html - * - * Jaws uses the "module pattern". - * Adds 1 global, <b>jaws</b>, so plays nice with all other JS libs. - * - * Formating guide: - * jaws.oneFunction() - * jaws.one_variable = 1 - * new jaws.OneConstructor - * - * @property {int} mouse_x Mouse X position with respect to the canvas-element - * @property {int} mouse_y Mouse Y position with respect to the canvas-element - * @property {canvas} canvas The detected/created canvas-element used for the game - * @property {context} context The detected/created canvas 2D-context, used for all draw-operations - * @property {int} width Width of the canvas-element - * @property {int} height Height of the canvas-element - */ -var jaws = (function(jaws) { - - var title; - var log_tag; - - /* - * Placeholders for constructors in extras-dir. We define the constructors here to be able to give ppl better error-msgs. - * When the correct from extras-dir is included, these will be overwritten. - * - */ - //jaws.Parallax = function() { throw("To use jaws.Parallax() you need to include src/extras/parallax.js") } - //jaws.QuadTree = function() { throw("To use QuadTree() you need to include src/extras/quadtree.js") } - //jaws.PixelMap = function() { throw("To use PixelMap() you need to include src/extras/pixel_map.js") } - jaws.SpriteList = function() { throw("To use SpriteList() you need to include src/extras/sprite_list.js") } - jaws.Audio = function() { throw("To use jaws.Audio() you need to include src/extras/audio.js") } - - /** - * Returns or sets contents of title's innerHTML - * @private - * @param {type} value The new value to set the innerHTML of title - * @returns {string} The innerHTML of title - */ - jaws.title = function(value) { - - if (!jaws.isString(value)) { - jaws.log.error("jaws.title: Passed in value is not a String."); - return; - } - - if (value) { - return (title.innerHTML = value); - } - return title.innerHTML; - }; - - /** - * Unpacks Jaws core-constructors into the global namespace. - * If a global property is already taken, a warning will be written to jaws log. - */ - jaws.unpack = function() { - var make_global = ["Sprite", "SpriteList", "Animation", "SpriteSheet", "Parallax", "pressed", "QuadTree"]; - - make_global.forEach(function(item) { - if (window[item]) { - jaws.log.warn("jaws.unpack: " + item + " already exists in global namespace."); - } - else { - window[item] = jaws[item]; - } - }); - }; - - /** - * Writes messages to either log_tag (if set) or console.log (if available) - * @param {string} msg The string to write - * @param {boolean} append If messages should be appended or not - */ - jaws.log = function(msg, append) { - if (!jaws.isString(msg)) { - msg = JSON.stringify(msg); - } - - if (jaws.log.on) { - if (log_tag && jaws.log.use_log_element) { - if (append) { - log_tag.innerHTML += msg + "<br />"; - } - else { - log_tag.innerHTML = msg; - } - } - if (console.log && jaws.log.use_console) { - console.log("JawsJS: ", msg); - } - } - }; - - /** - * If logging should take place or not - * @type {boolean} - */ - jaws.log.on = true; - - /** - * If console.log should be used during log writing - * @type {boolean} - */ - jaws.log.use_console = false; - - /** - * If log_tag should be used during log writing - * @type {boolean} - */ - jaws.log.use_log_element = true; - - /** - * Write messages to console.warn (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.warn = function(msg) { - if (console.warn && jaws.log.use_console && jaws.log.on) { - console.warn(msg); - } else { - jaws.log("[WARNING]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.error (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.error = function(msg) { - if (console.error && jaws.log.use_console && jaws.log.on) { - console.error(msg); - } else { - jaws.log("[ERROR]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.info (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.info = function(msg) { - if (console.info && jaws.log.use_console && jaws.log.on) { - console.info(msg); - } else { - jaws.log("[INFO]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.debug (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.debug = function(msg) { - if (console.debug && jaws.log.use_console && jaws.log.on) { - console.debug(msg); - } else { - jaws.log("[DEBUG]: " + JSON.stringify(msg), true); - } - }; - - /** - * Clears the contents of log_tag element (if set) and console.log (if set) - */ - jaws.log.clear = function() { - if (log_tag) { - log_tag.innerHTML = ""; - } - if (console.clear) { - console.clear(); - } - }; - - /** - * Initalizes jaws{canvas, context, dom, width, height} - * @private - * @param {object} options Object-literal of constructor properties - * @see jaws.url_parameters() - */ - jaws.init = function(options) { - - /* Find <title> tag */ - title = document.getElementsByTagName('title')[0]; - jaws.url_parameters = jaws.getUrlParameters(); - - jaws.canvas = document.getElementsByTagName('canvas')[0]; - if (!jaws.canvas) { - jaws.dom = document.getElementById("canvas"); - } - - // Ordinary <canvas>, get context - if (jaws.canvas) { - jaws.context = jaws.canvas.getContext('2d'); - } - else if (jaws.dom) { - jaws.dom.style.position = "relative"; - } - else { - jaws.canvas = document.createElement("canvas"); - jaws.canvas.width = options.width; - jaws.canvas.height = options.height; - jaws.context = jaws.canvas.getContext('2d'); - document.body.appendChild(jaws.canvas); - } - - /* - * If debug=1 parameter is present in the URL, let's either find <div id="jaws-log"> or create the tag. - * jaws.log(message) will use this div for debug/info output to the gamer or developer - * - */ - log_tag = document.getElementById('jaws-log'); - if (jaws.url_parameters["debug"]) { - if (!log_tag) { - log_tag = document.createElement("div"); - log_tag.id = "jaws-log"; - log_tag.style.cssText = "overflow: auto; color: #aaaaaa; width: 300px; height: 150px; margin: 40px auto 0px auto; padding: 5px; border: #444444 1px solid; clear: both; font: 10px verdana; text-align: left;"; - document.body.appendChild(log_tag); - } - } - - - if(jaws.url_parameters["bust_cache"]) { - jaws.log.info("Busting cache when loading assets") - jaws.assets.bust_cache = true; - } - - /* Let's scale sprites retro-style by default */ - if (jaws.context) - jaws.useCrispScaling(); - - jaws.width = jaws.canvas ? jaws.canvas.width : jaws.dom.offsetWidth; - jaws.height = jaws.canvas ? jaws.canvas.height : jaws.dom.offsetHeight; - - jaws.mouse_x = 0; - jaws.mouse_y = 0; - window.addEventListener("mousemove", saveMousePosition); - }; - - /** - * Use 'retro' crisp scaling when drawing sprites through the canvas API, this is the default - */ - jaws.useCrispScaling = function() { - jaws.context.imageSmoothingEnabled = false; - jaws.context.webkitImageSmoothingEnabled = false; - jaws.context.mozImageSmoothingEnabled = false; - }; - - /** - * Use smooth antialiased scaling when drawing sprites through the canvas API - */ - jaws.useSmoothScaling = function() { - jaws.context.imageSmoothingEnabled = true; - jaws.context.webkitImageSmoothingEnabled = true; - jaws.context.mozImageSmoothingEnabled = true; - }; - - /** - * Keeps updated mouse coordinates in jaws.mouse_x and jaws.mouse_y - * This is called each time event "mousemove" triggers. - * @private - * @param {EventObject} e The EventObject populated by the calling event - */ - function saveMousePosition(e) { - jaws.mouse_x = (e.pageX || e.clientX); - jaws.mouse_y = (e.pageY || e.clientY); - - var game_area = jaws.canvas ? jaws.canvas : jaws.dom; - jaws.mouse_x -= game_area.offsetLeft; - jaws.mouse_y -= game_area.offsetTop; - } - - /** - * 1) Calls jaws.init(), detects or creats a canvas, and sets up the 2D context (jaws.canvas and jaws.context). - * 2) Pre-loads all defined assets with jaws.assets.loadAll(). - * 3) Creates an instance of game_state and calls setup() on that instance. - * 4) Loops calls to update() and draw() with given FPS until game ends or another game state is activated. - * @param {function} game_state The game state function to be started - * @param {object} options Object-literal of game loop properties - * @param {object} game_state_setup_options Object-literal of game state properties and values - * @see jaws.init() - * @see jaws.setupInput() - * @see jaws.assets.loadAll() - * @see jaws.switchGameState() - * @example - * - * jaws.start(MyGame) // Start game state Game() with default options - * jaws.start(MyGame, {fps: 30}) // Start game state Game() with options, in this case jaws will run your game with 30 frames per second. - * jaws.start(window) // Use global functions setup(), update() and draw() if available. Not the recommended way but useful for testing and mini-games. - * - */ - jaws.start = function(game_state, options, game_state_setup_options) { - if (!options) options = {}; - - var fps = options.fps || 60; - if (options.loading_screen === undefined) options.loading_screen = true; - if (!options.width) options.width = 500; - if (!options.height) options.height = 300; - - /* Takes care of finding/creating canvas-element and debug-div */ - jaws.init(options); - - if (!jaws.isFunction(game_state) && !jaws.isObject(game_state)) { - jaws.log.error("jaws.start: Passed in GameState is niether function or object"); - return; - } - if (!jaws.isObject(game_state_setup_options) && game_state_setup_options !== undefined) { - jaws.log.error("jaws.start: The setup options for the game state is not an object."); - return; - } - - if (options.loading_screen) { - jaws.assets.displayProgress(0); - } - - jaws.log.info("setupInput()", true); - jaws.setupInput(); - - /* Callback for when one single asset has been loaded */ - function assetProgress(src, percent_done) { - jaws.log.info(percent_done + "%: " + src, true); - if (options.loading_screen) { - jaws.assets.displayProgress(percent_done); - } - } - - /* Callback for when an asset can't be loaded*/ - function assetError(src, percent_done) { - jaws.log.info(percent_done + "%: Error loading asset " + src, true); - } - - /* Callback for when all assets are loaded */ - function assetsLoaded() { - jaws.log.info("all assets loaded", true); - jaws.switchGameState(game_state || window, {fps: fps}, game_state_setup_options); - } - - jaws.log.info("assets.loadAll()", true); - if (jaws.assets.length() > 0) { - jaws.assets.loadAll({onprogress: assetProgress, onerror: assetError, onload: assetsLoaded}); - } - else { - assetsLoaded(); - } - }; - - /** - * Switchs to a new active game state and saves previous game state in jaws.previous_game_state - * @param {function} game_state The game state function to start - * @param {object} options The object-literal properties to pass to the new game loop - * @param {object} game_state_setup_options The object-literal properties to pass to starting game state - * @example - * - * function MenuState() { - * this.setup = function() { ... } - * this.draw = function() { ... } - * this.update = function() { - * if(pressed("enter")) jaws.switchGameState(GameState); // Start game when Enter is pressed - * } - * } - * - * function GameState() { - * this.setup = function() { ... } - * this.update = function() { ... } - * this.draw = function() { ... } - * } - * - * jaws.start(MenuState) - * - */ - jaws.switchGameState = function(game_state, options, game_state_setup_options) { - if(options === undefined) options = {}; - - if(jaws.isFunction(game_state)) { - game_state = new game_state; - } - if(!jaws.isObject(game_state)) { - jaws.log.error("jaws.switchGameState: Passed in GameState should be a Function or an Object."); - return; - } - - var fps = (options && options.fps) || (jaws.game_loop && jaws.game_loop.fps) || 60; - var setup = options.setup - - jaws.game_loop && jaws.game_loop.stop(); - jaws.clearKeyCallbacks(); - - jaws.previous_game_state = jaws.game_state; - jaws.game_state = game_state; - jaws.game_loop = new jaws.GameLoop(game_state, {fps: fps, setup: setup}, game_state_setup_options); - jaws.game_loop.start(); - }; - - /** - * Creates a new HTMLCanvasElement from a HTMLImageElement - * @param {HTMLImageElement} image The HTMLImageElement to convert to a HTMLCanvasElement - * @returns {HTMLCanvasElement} A HTMLCanvasElement with drawn HTMLImageElement content - */ - jaws.imageToCanvas = function(image) { - if (jaws.isCanvas(image)) return image; - - if (!jaws.isImage(image)) { - jaws.log.error("jaws.imageToCanvas: Passed in object is not an Image."); - return; - } - - var canvas = document.createElement("canvas"); - canvas.src = image.src; - canvas.width = image.width; - canvas.height = image.height; - - var context = canvas.getContext("2d"); - context.drawImage(image, 0, 0, image.width, image.height); - return canvas; - }; - - /** - * Returns object as an array - * @param {object} obj An array or object - * @returns {array} Either an array or the object as an array - * @example - * - * jaws.forceArray(1) // --> [1] - * jaws.forceArray([1,2]) // --> [1,2] - */ - jaws.forceArray = function(obj) { - return Array.isArray(obj) ? obj : [obj]; - }; - - /** - * Clears screen (the canvas-element) through context.clearRect() - */ - jaws.clear = function() { - jaws.context.clearRect(0, 0, jaws.width, jaws.height); - }; - - /** Fills the screen with given fill_style */ - jaws.fill = function(fill_style) { - jaws.context.fillStyle = fill_style; - jaws.context.fillRect(0, 0, jaws.width, jaws.height); - }; - - - /** - * calls draw() on everything you throw on it. Give it arrays, argumentlists, arrays of arrays. - * - */ - jaws.draw = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) jaws.draw(list[i]); - else if(list[i].draw) list[i].draw(); - } - } - - /** - * calls update() on everything you throw on it. Give it arrays, argumentlists, arrays of arrays. - * - */ - jaws.update = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) jaws.update(list[i]); - else if(list[i].update) list[i].update(); - } - } - - /** - * Tests if object is an image or not - * @param {object} obj An Image or image-like object - * @returns {boolean} If object's prototype is "HTMLImageElement" - */ - jaws.isImage = function(obj) { - return Object.prototype.toString.call(obj) === "[object HTMLImageElement]"; - }; - - /** - * Tests if object is a Canvas object - * @param {type} obj A canvas or canvas-like object - * @returns {boolean} If object's prototype is "HTMLCanvasElement" - */ - jaws.isCanvas = function(obj) { - return Object.prototype.toString.call(obj) === "[object HTMLCanvasElement]"; - }; - - /** - * Tests if an object is either a canvas or an image object - * @param {object} obj A canvas or canva-like object - * @returns {boolean} If object isImage or isCanvas - */ - jaws.isDrawable = function(obj) { - return jaws.isImage(obj) || jaws.isCanvas(obj); - }; - - /** - * Tests if an object is a string or not - * @param {object} obj A string or string-like object - * @returns {boolean} The result of typeof and constructor testing - */ - jaws.isString = function(obj) { - return typeof obj === "string" || (typeof obj === "object" && obj.constructor === String); - }; - - /** - * Tests if an object is a number or not - * @param {number} n A number or number-like value - * @returns {boolean} If n passed isNaN() and isFinite() - */ - jaws.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - - /** - * Tests if an object is an Array or not - * @param {object} obj An array or array-like object - * @returns {boolean} If object's constructor is "Array" - */ - jaws.isArray = function(obj) { - if (!obj) - return false; - return !(obj.constructor.toString().indexOf("Array") === -1); - }; - - /** - * Tests if an object is an Object or not - * @param {object} value An object or object-like enitity - * @returns {boolean} If object is not null and typeof 'object' - */ - jaws.isObject = function(value) { - return value !== null && typeof value === 'object'; - }; - - /** - * Tests if an object is a function or not - * @param {object} obj A function or function-like object - * @returns {boolean} If the prototype of the object is "Function" - */ - jaws.isFunction = function(obj) { - return (Object.prototype.toString.call(obj) === "[object Function]"); - }; - - /** - * Tests if an object is a regular expression or not - * @param {object} obj A /regexp/-object - * @returns {boolean} If the object is an instance of RegExp - */ - jaws.isRegExp = function(obj) { - return (obj instanceof RegExp); - }; - - - /** - * Tests if an object is within drawing canvas (jaws.width and jaws.height) - * @param {object} item An object with both x and y properties - * @returns {boolean} If the item's x and y are less than 0 or more than jaws.width or jaws.height - */ - jaws.isOutsideCanvas = function(item) { - if (item.x && item.y) { - return (item.x < 0 || item.y < 0 || item.x > jaws.width || item.y > jaws.height); - } - }; - - /** - * Sets x and y properties to 0 (if less than), or jaws.width or jaws.height (if greater than) - * @param {object} item An object with x and y properties - */ - jaws.forceInsideCanvas = function(item) { - if (item.x && item.y) { - if (item.x < 0) { - item.x = 0; - } - if (item.x > jaws.width) { - item.x = jaws.width; - } - if (item.y < 0) { - item.y = 0; - } - if (item.y > jaws.height) { - item.y = jaws.height; - } - } - }; - - /** - * Parses current window.location for URL parameters and values - * @returns {array} Hash of url-parameters and their values - * @example - * // Given the current URL is <b>http://test.com/?debug=1&foo=bar</b> - * jaws.getUrlParameters() // --> {debug: 1, foo: bar} - */ - jaws.getUrlParameters = function() { - var vars = [], hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split('='); - vars.push(hash[0]); - vars[hash[0]] = hash[1]; - } - return vars; - }; - - /** - * Compares an object's default properties against those sent to its constructor - * @param {object} object The object to compare and assign new values - * @param {object} options Object-literal of constructor properties and new values - * @param {object} defaults Object-literal of properties and their default values - */ - jaws.parseOptions = function(object, options, defaults) { - object["options"] = options; - - for (var option in options) { - if (defaults[option] === undefined) { - jaws.log.warn("jaws.parseOptions: Unsupported property " + option + "for " + object.constructor); - } - } - for (var option in defaults) { - if( jaws.isFunction(defaults[option]) ) defaults[option] = defaults[option](); - object[option] = (options[option] !== undefined) ? options[option] : jaws.clone(defaults[option]); - } - }; - - /** - * Returns a shallow copy of an array or object - * @param {array|object} value The array or object to clone - * @returns {array|object} A copy of an array of object - */ - jaws.clone = function(value) { - if (jaws.isArray(value)) - return value.slice(0); - if (jaws.isObject(value)) - return JSON.parse(JSON.stringify(value)); - return value; - }; - - /* - * Converts image to canvas 2D context. Then you can draw on it :). - */ - jaws.imageToCanvasContext = function(image) { - var canvas = document.createElement("canvas") - canvas.width = image.width - canvas.height = image.height - - var context = canvas.getContext("2d") - if(jaws.context) { - context.imageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - context.webkitImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - } - - context.drawImage(image, 0, 0, canvas.width, canvas.height) - return context - } - - /** - * scale 'image' by factor 'factor'. - * Scaling is done using nearest-neighbor ( retro-blocky-style ). - * Returns a canvas. - */ - jaws.retroScaleImage = function(image, factor) { - var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image - var context = canvas.getContext("2d") - var data = context.getImageData(0,0,canvas.width,canvas.height).data - - // Create new canvas to return - var canvas2 = document.createElement("canvas") - canvas2.width = image.width * factor - canvas2.height = image.height * factor - var context2 = canvas2.getContext("2d") - var to_data = context2.createImageData(canvas2.width, canvas2.height) - - var w2 = to_data.width - var h2 = to_data.height - for (var y=0; y < h2; y += 1) { - var y2 = Math.floor(y / factor) - var y_as_x = y * to_data.width - var y2_as_x = y2 * image.width - - for (var x=0; x < w2; x += 1) { - var x2 = Math.floor(x / factor) - var y_dst = (y_as_x + x) * 4 - var y_src = (y2_as_x + x2) * 4 - - to_data.data[y_dst] = data[y_src]; - to_data.data[y_dst+1] = data[y_src+1]; - to_data.data[y_dst+2] = data[y_src+2]; - to_data.data[y_dst+3] = data[y_src+3]; - } - } - - context2.putImageData(to_data, 0, 0) - - return canvas2 - } - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws } diff --git a/jaws/src/extras/audio.js b/jaws/src/extras/audio.js deleted file mode 100755 index ccb1065..0000000 --- a/jaws/src/extras/audio.js +++ /dev/null @@ -1,351 +0,0 @@ -/** - * @fileOverview A jaws.Audio object that detects and loads supported filetypes - * - * The jaws.Audio object uses jaws.assets to determine if a particular - * reported MIME audio filetype can be played within the current enviroment. - * - * Note: If loading fails at any point or no supported types were found, - * the 'audio' property is set to null. - * - * @see jaws.assets.file_type - * @see jaws.assets.can_play - * - * @class jaws.Audio - * @property {array|string|Audio|null} audio A string or array of file locations initally; - * replaced with an Audio object if loading was successful - * - * @example - * var audio = new jaws.Audio({audio: ["file.mp3", "file.ogg"], volume: 1.0}); - * - * audio.play() //Assuming either MP3 or OGG is supported - * - * //Because play(), stop(), and other audio functions will log an error - * // if 'audio' is not an Audio object, its load status can be checked too. - * - * if(audio.isLoaded()) { - * audio.play(); - * } - * - */ -var jaws = (function(jaws) { - - /** - * jaws.Audio object - * @constructor - * @param {object} options Object-literal of constructor properties - * @param {bool} [options.loop] Whether to initially loop the playing or not - * @param {bool} [options.mute] Whether to initially mute the audio or not - * @param {bool} [options.pause] Whether to initially pause the audio or not - * @param {bool} [options.autoplay] Whether to start playing after loading - * @param {int} [options.volume] Initial audio volume, between 0.1 and 1.0 - * @param {function} [options.onend] Initial callback on "end" event for audio - * @param {function} [options.onplay] Initial callback on "play" event for audio - * @param {function} [options.onpause] Initial callback on "pause" event for audio - * @see jaws.audio.set() - */ - jaws.Audio = function Audio(options) { - if (!(this instanceof arguments.callee)) - return new arguments.callee(options); - - if (typeof Audio !== "undefined") { - this.set(options); - } else { - jaws.log.error("jaws.Audio (constructor): 'Audio' object does not exist."); - this.audio = null; - } - }; - - /** - * The default properties and their values - * @type {object} - */ - jaws.Audio.prototype.default_options = { - audio: null, - autoplay: false, - loop: false, - volume: 0, - onend: null, - onplay: null, - onpause: null, - _constructor: null - }; - - /** - * Compares default_options to the constructor options and loads an audio resource if able - * @param {object} options Object-literal of constructor properties - * @this {jaws.Audio} - * @returns {object} The 'this' scope of the calling instance of an jaws.Audio object - */ - jaws.Audio.prototype.set = function(options) { - - jaws.parseOptions(this, options, this.default_options); - - if (this.audio) { - if (jaws.isString(this.audio)) { - var type = jaws.assets.getPostfix(this.audio); - if (jaws.assets.file_type[type] && jaws.assets.can_play[type]) { - this.setAudio(this.audio); - } else { - jaws.log.warn("jaws.Audio.set: Unknown or unplayable MIME filetype."); - this.audio = null; - } - } else if (jaws.isArray(this.audio)) { - for (var i = 0; i < this.audio.length; i++) { - if (jaws.isString(this.audio[i])) { - var type = jaws.assets.getPostfix(this.audio[i]); - if (jaws.assets.file_type[type] && jaws.assets.can_play[type]) { - this.setAudio(this.audio[i]); - break; - } - } - } - - if (!(this.audio instanceof Audio)) { - jaws.log.warn("jaws.Audio.set: No known or playable MIME filetypes were found."); - this.audio = null; - } - - } else { - jaws.log.error("jaws.Audio.set: Passed in 'audio' property is neither a String nor Array"); - this.audio = null; - } - } - return this; - }; - - /** - * Loads a reference from the jaws.assets cache or requests that an Audio resource URL be loaded - * @param {type} value - * @this {jaws.Audio} - * @returns {object} The 'this' scope of the calling instance of an jaws.Audio object - */ - jaws.Audio.prototype.setAudio = function(value) { - var self = this; - if (jaws.assets.isLoaded(value)) { - var audio = jaws.assets.get(value); - if (audio instanceof Audio) { - this.audio = audio; - - if (this.volume >= 0 && this.volume <= 1.0 && jaws.isNumber(this.volume)) - this.audio.volume = this.volume; - - if (this.loop) - this.audio.loop = this.loop; - - if (this.onend && jaws.isFunction(this.onend)) { - this.audio.addEventListener('end', this.onend); - } - - if (this.onplay && jaws.isFunction(this.onplay)) { - this.audio.addEventListener('play', this.onplay); - } - - if (this.onpause && jaws.isFunction(this.onplay)) { - this.audio.addEventListener('pause', this.onpause); - } - - if (this.autoplay) - this.audio.autoplay = this.autoplay; - - } else { - this.audio = null; - } - } else { - jaws.log.warn("jaws.Audio.setAudio: Audio '" + value + "' not preloaded with jaws.assets.add()."); - jaws.assets.load(value, function() { - var audio = jaws.assets.get(value); - if (audio instanceof Audio) { - self.audio = audio; - - if (self.volume >= 0 && self.volume <= 1.0 && jaws.isNumber(self.volume)) - self.audio.volume = self.volume; - - if (self.loop) - self.audio.loop = self.loop; - - if (self.hasOwnProperty("onend") && jaws.isFunction(self.onend)) { - self.audio.addEventListener('end', self.onend); - } - - if (self.hasOwnProperty("onplay") && jaws.isFunction(self.onplay)) { - self.audio.addEventListener('play', self.onplay); - } - - if (self.hasOwnProperty("onpause") && jaws.isFunction(self.onplay)) { - self.audio.addEventListener('pause', self.onpause); - } - - if (self.autoplay) - self.audio.autoplay = self.autoplay; - - } else { - self.audio = null; - } - }, function() { - jaws.log.error("jaws.Audio.setAudio: Could not load Audio resource URL " + value); - self.audio = null; - }); - } - return this; - }; - - /** - * Plays audio - * @this {jaws.Audio} - */ - jaws.Audio.prototype.play = function() { - if (this.audio instanceof Audio) { - this.audio.play(); - } else { - jaws.log.error("jaws.Audio.play: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Stops audio - * @this {jaws.Audio} - */ - jaws.Audio.prototype.stop = function() { - if (this.audio instanceof Audio) { - this.audio.pause(); - this.audio.currentTime = 0; - } else { - jaws.log.error("jaws.Audio.stop: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Pauses audio - * @this {jaws.Audio} - */ - jaws.Audio.prototype.pause = function() { - if (this.audio instanceof Audio) { - this.audio.pause(); - } else { - jaws.log.error("jaws.Audio.pause: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Mutes the audio - * @this {jaws.Audio} - */ - jaws.Audio.prototype.mute = function() { - if (this.audio instanceof Audio) { - this.audio.mute = true; - } else { - jaws.log.error("jaws.Audio.mute: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Unmute the audio - * @this {jaws.Audio} - */ - jaws.Audio.prototype.unmute = function() { - if (this.audio instanceof Audio) { - this.audio.mute = false; - } else { - jaws.log.error("jaws.Audio.unmute: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Seeks to a position in audio - * @param {type} value - * @this {jaws.Audio} - */ - jaws.Audio.prototype.seekTo = function(value) { - if (this.audio instanceof Audio) { - if (jaws.isNumber(value)) { - if (value <= this.audio.duration) { - this.audio.currentTime = value; - } else { - this.audio.currentTime = this.audio.duration; - } - } - } else { - jaws.log.warn("jaws.Audio.seekTo: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Sets the volume of the audio - * @param {type} value The new volume within the range 0.0 to 1.0 - * @this {jaws.Audio} - */ - jaws.Audio.prototype.setVolume = function(value) { - if (this.audio instanceof Audio) { - if (jaws.isNumber(value) && value <= 1.0 && value >= 0) { - this.audio.volume = value; - } - } else { - jaws.log.warn("jaws.Audio: jaws.setVolume: Either 'audio' was loaded incorrectly or does not exist"); - } - }; - - /** - * Returns if 'audio' is actually an Audio object - */ - jaws.Audio.prototype.isLoaded = function() { - return (this.audio instanceof Audio); - }; - - /** - * Returns a String containing value properties - * @this {jaws.Audio} - * @returns {String} - */ - jaws.Audio.prototype.toString = function() { - var properties = "[Audio "; - if (this.audio instanceof Audio) { - properties += this.audio.src + ", "; - properties += this.audio.currentTime + ", "; - properties += this.audio.duration + ", "; - properties += this.audio.volume + " ]"; - } else { - properties += null + " ]"; - } - return properties; - }; - - /** - * Returns an object created with values from 'this' properties - * @this {jaws.Audio} - * @returns {object} - */ - jaws.Audio.prototype.attributes = function() { - var object = this.options; - object["_constructor"] = this._constructor || "jaws.Audio"; - - if (this.audio instanceof Audio) { - object["audio"] = this.audio.src; - object["loop"] = this.loop; - object["muted"] = this.audio.muted; - object["volume"] = this.audio.volume; - } else { - object["audio"] = null; - } - - if (this.hasOwnProperty("autoplay")) - object["autoplay"] = this.autoplay; - - return object; - }; - - /** - * Return the properties of the current object as a JSON-encoded string - * @this {jaws.Audio} - * @returns {string} The properties of the jaws.Audio object as a JSON-encoded string - */ - jaws.Audio.prototype.toJSON = function() { - return JSON.stringify(this.attributes()); - }; - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.Audio; -}
\ No newline at end of file diff --git a/jaws/src/extras/sprite_list.js b/jaws/src/extras/sprite_list.js deleted file mode 100755 index 5fe2d0e..0000000 --- a/jaws/src/extras/sprite_list.js +++ /dev/null @@ -1,366 +0,0 @@ -var jaws = (function(jaws) { -/** - * @class Manages all your Sprites in lists. Makes easy mass-draw() / update() possible among others. Implements Array API. "Field Summary" contains options for the SpriteList()-constructor. - * - * Sprites (your bullets, aliens, enemies, players etc) will need to be - * updated, draw, deleted. Often in various orders and based on different conditions. - * This is where SpriteList() comes in: - * - * @example - * // create 100 enemies - * var enemies = new SpriteList() - * for(i=0; i < 100; i++) { - * enemies.push(new Sprite({image: "enemy.png", x: i, y: 200})) - * } - * enemies.draw() // calls draw() on all enemies - * enemies.update() // calls update() on all enemies - * enemies.removeIf(isOutsideCanvas) // removes each item in enemies that returns true when isOutsideCanvas(item) is called - * enemies.drawIf(isInsideViewport) // only call draw() on items that returns true when isInsideViewport is called with item as argument - * - * @param {Object} [options] Currently used to pass in a literal list of sprites. See {@link SpriteList#load} for details - */ -jaws.SpriteList = function SpriteList(options) { - // Make both sprite_list = new SpriteList() and sprite_list = SpriteList() work - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.sprites = [] - this.length = 0 - - if(options) this.load(options); -} -/** -* Adds one or more sprites to sprite_list -*/ -jaws.SpriteList.prototype.add = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - - if(list.length > 1) { - for(var i=0; i < list.length; i++) { - this.sprites.push(list[i]) - } - } - else { - this.sprites.push(arguments) - } - - this.updateLength() - return this; -} - -/** - * Return the sprite at the specified index. - * Replaces the array [] notation. - * So: - * my_sprite_list.at(1) is equivalent to my_array[1] - * - * @param {Number} index - * @returns {Object} Sprite at index - */ -jaws.SpriteList.prototype.at = function(index) { - return this.sprites[index] -} - -// Implement the Array API functions - -/** - * Concatenate this sprite list and another array. Does not modify original. - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/concat - * @return {Object} A new SpriteList comprised of this one joined with other lists. - */ -jaws.SpriteList.prototype.concat = function() { - return this.sprites.concat.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf - * @param {Object} searchElement - * @param {Number} fromIndex - * @returns {Number} - */ -jaws.SpriteList.prototype.indexOf = function(searchElement, fromIndex) { - return this.sprites.indexOf(searchElement, fromIndex) -} - -/** - * Joins the contents of the sprite list into a string. - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/join - * - * Implemented mostly for an easy verbose way to display the sprites - * inside the sprite list. - * @param {String} [separator] String to separate each array element. If ommitted, defaults to comma. - */ -jaws.SpriteList.prototype.join = function(separator) { - return this.sprites.join(separator) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf - */ -jaws.SpriteList.prototype.lastIndexOf = function() { - return this.sprites.lastIndexOf.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop - * @returns {Object} Last sprite in the list - */ -jaws.SpriteList.prototype.pop = function() { - var element = this.sprites.pop() - this.updateLength() - return element -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push - * @returns {Number} New length of the sprite list - */ -jaws.SpriteList.prototype.push = function() { - this.sprites.push.apply(this.sprites, arguments) - this.updateLength() - return this.length -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reverse - */ -jaws.SpriteList.prototype.reverse = function() { - this.sprites.reverse() -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/shift - * @returns {Object} First sprite in the list - */ -jaws.SpriteList.prototype.shift = function() { - var element = this.sprites.shift() - this.updateLength() - return element -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/slice - * @param {Number} start - * @param {Number} end - * @returns {Object} A new array containing sprites (a section of the sprites array defined by start and end) - * - * @todo Fix it to return SpriteList instead of array - */ -jaws.SpriteList.prototype.slice = function(start, end) { - return this.sprites.slice(start, end) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort - */ -jaws.SpriteList.prototype.sort = function() { - this.sprites.sort.apply(this.sprites, arguments) -} - -/** - * Add or remove sprites from the list. - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice - * @return {Array} Array containing removed sprites - */ -jaws.SpriteList.prototype.splice = function() { - var removedElements = this.sprites.splice.apply(this.sprites, arguments) - this.updateLength() - return removedElements -} - -/** - * Add one or more sprites to the front of the list - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift - * @returns {Number} New length of the sprite list - */ -jaws.SpriteList.prototype.unshift = function() { - this.sprites.unshift.apply(this.sprites, arguments) - this.updateLength() - return this.length -} - -/** - * Update the length of the sprite list. - * Since we're delegating array operations to sprites array, this is not done automatically - */ -jaws.SpriteList.prototype.updateLength = function() { - this.length = this.sprites.length -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/ValueOf - * @return {String} Literal string representation (currently, just the value of toString() ) - */ -jaws.SpriteList.prototype.valueOf = function() { - return this.toString() -} - -// Implement "extras" / standardized Array functions -// See http://dev.opera.com/articles/view/javascript-array-extras-in-detail/ for discussion, browser compatibility - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter - * @return {Array} - */ -jaws.SpriteList.prototype.filter = function() { - return this.sprites.filter.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach - */ -jaws.SpriteList.prototype.forEach = function() { - this.sprites.forEach.apply(this.sprites, arguments) - this.updateLength() // in case the forEach operation changes the sprites array -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every - * @returns {Boolean} - */ -jaws.SpriteList.prototype.every = function() { - return this.sprites.every.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map - * @returns {Array} - */ -jaws.SpriteList.prototype.map = function() { - return this.sprites.map.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Reduce - * @returns {Object|Number|String} - */ -jaws.SpriteList.prototype.reduce = function() { - return this.sprites.reduce.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/ReduceRight - * @returns {Object|Number|String} - */ -jaws.SpriteList.prototype.reduceRight = function() { - return this.sprites.reduceRight.apply(this.sprites, arguments) -} - -/** - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some - * @returns {Boolean} - */ -jaws.SpriteList.prototype.some = function() { - return this.sprites.some.apply(this.sprites, arguments) -} - -/** - * Returns true if this object is a sprite lsit. - * Used to tell SpriteLists and Arrays apart - * @returns {Boolean} - */ -jaws.SpriteList.prototype.isSpriteList = function() { - return true; -} - -/** - * Load sprites into sprite list. - * - * Argument could either be - * - an array of Sprite objects - * - an array of JSON objects - * - a JSON.stringified string representing an array of JSON objects - * - */ -jaws.SpriteList.prototype.load = function(objects) { - var that = this; // Since forEach changes this into DOMWindow.. hm, lame. - if(jaws.isArray(objects)) { - // If this is an array of JSON representations, parse it - if(objects.every(function(item) { return item._constructor })) { - parseArray(objects) - } else { - // This is an array of Sprites, load it directly - this.sprites = objects - } - } - else if(jaws.isString(objects)) { parseArray( JSON.parse(objects) ); jaws.log.info(objects) } - this.updateLength() - - function parseArray(array) { - array.forEach( function(data) { - var constructor = data._constructor ? eval(data._constructor) : data.constructor - if(jaws.isFunction(constructor)) { - jaws.log.info("Creating " + data._constructor + "(" + data.toString() + ")", true) - var object = new constructor(data) - object._constructor = data._constructor || data.constructor.name - that.push(object); - } - }); - } -} - -/** - * Removes the first occurrence of obj from list - */ -jaws.SpriteList.prototype.remove = function(obj) { - var index = this.indexOf(obj) - if(index > -1) { this.splice(index, 1) } - this.updateLength() -} - -/** - * Invoke draw() on each element of the sprite list - */ -jaws.SpriteList.prototype.draw = function() { - this.forEach(function(ea) { - ea.draw() - }) -} - -/** Draw sprites in spritelist where condition(sprite) returns true */ -jaws.SpriteList.prototype.drawIf = function(condition) { - this.forEach(function(ea) { - if( condition(ea) ) { - ea.draw() - } - }) -} - -/** Call update() on all sprites in spritelist */ -jaws.SpriteList.prototype.update = function() { - this.forEach(function(ea) { - ea.update() - }) -} - -/** Call update() on sprites in spritelist where condition(sprite) returns true */ -jaws.SpriteList.prototype.updateIf = function(condition) { - this.forEach(function(ea) { - if( condition(ea) ) { - ea.update() - } - }) -} - -/** - * Delete sprites in spritelist where condition(sprite) returns true. - * Alias for removeIf() - * @deprecated - */ -jaws.SpriteList.prototype.deleteIf = function(condition) { - this.removeIf(condition) -} - -/** Remove sprites in spritelist where condition(sprite) returns true */ -jaws.SpriteList.prototype.removeIf = function(condition) { - this.sprites = this.filter(function(ea) { - return !condition(ea) - }) - this.updateLength() -} - -jaws.SpriteList.prototype.toString = function() { return "[SpriteList " + this.length + " sprites]" } - -return jaws; -})(jaws || {}); - diff --git a/jaws/src/game_loop.js b/jaws/src/game_loop.js deleted file mode 100755 index aa773e5..0000000 --- a/jaws/src/game_loop.js +++ /dev/null @@ -1,135 +0,0 @@ -if(typeof require !== "undefined") { var jaws = require("./core.js"); } - -var jaws = (function(jaws) { - -// requestAnim shim layer by Paul Irish -window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(/* function */ callback, /* DOMElement */ element){ - window.setTimeout(callback, 16.666); - }; -})(); - -/** - * @class A classic game loop forever looping calls to update() / draw() with given framerate. "Field Summary" contains options for the GameLoop()-constructor. - * - * @property {int} tick_duration duration in ms between the last 2 ticks (often called dt) - * @property {int} fps the real fps (as opposed to the target fps), smoothed out with a moving average - * @property {int} ticks total amount of ticks since game loops start - * - * @example - * - * game = {} - * draw: function() { ... your stuff executed every 30 FPS ... } - * } - * - * game_loop = new jaws.GameLoop(game, {fps: 30}) - * game_loop.start() - * - * // You can also use the shortcut jaws.start(), it will: - * // 1) Load all assets with jaws.assets.loadAll() - * // 2) Create a GameLoop() and start it - * jaws.start(MyGameState, {fps: 30}) - * - */ -jaws.GameLoop = function GameLoop(game_object, options, game_state_setup_options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( game_object, options ); - - this.tick_duration = 0 - this.fps = 0 - this.ticks = 0 - - var update_id - var paused = false - var stopped = false - var that = this - var mean_value = new MeanValue(20) // let's have a smooth, non-jittery FPS-value - - /** - * returns how game_loop has been active in milliseconds - * does currently not factor in pause-time - */ - this.runtime = function() { - return (this.last_tick - this.first_tick) - } - - /** Start the game loop by calling setup() once and then loop update()/draw() forever with given FPS */ - this.start = function() { - jaws.log.info("Game loop start", true) - - this.first_tick = (new Date()).getTime(); - this.current_tick = (new Date()).getTime(); - this.last_tick = (new Date()).getTime(); - - if(options.setup !== false && game_object.setup) { game_object.setup(game_state_setup_options) } - step_delay = 1000 / options.fps; - - if(options.fps == 60) { - requestAnimFrame(this.loop) - } - else { - update_id = setInterval(this.loop, step_delay); - } - } - - /** The core of the game loop. Calculate a mean FPS and call update()/draw() if game loop is not paused */ - this.loop = function() { - that.current_tick = (new Date()).getTime(); - that.tick_duration = that.current_tick - that.last_tick - that.fps = mean_value.add(1000/that.tick_duration).get() - - if(!stopped && !paused) { - if(game_object.update) { game_object.update() } - if(game_object.draw) { game_object.draw() } - that.ticks++ - } - if(options.fps == 60 && !stopped) requestAnimFrame(that.loop); - that.last_tick = that.current_tick; - } - - /** Pause the game loop. loop() will still get called but not update() / draw() */ - this.pause = function() { paused = true } - - /** unpause the game loop */ - this.unpause = function() { paused = false } - - /** Stop the game loop */ - this.stop = function() { - if(update_id) clearInterval(update_id); - stopped = true; - } -} - -/** @ignore */ -function MeanValue(size) { - this.size = size - this.values = new Array(this.size) - this.value - - this.add = function(value) { - if(this.values.length > this.size) { // is values filled? - this.values.splice(0,1) - this.value = 0 - for(var i=0; this.values[i]; i++) { - this.value += this.values[i] - } - this.value = this.value / this.size - } - this.values.push(value) - - return this - } - - this.get = function() { - return parseInt(this.value) - } - -} - -return jaws; -})(jaws || {}); - diff --git a/jaws/src/input.js b/jaws/src/input.js deleted file mode 100755 index fd3195f..0000000 --- a/jaws/src/input.js +++ /dev/null @@ -1,203 +0,0 @@ -var jaws = (function(jaws) { - - var pressed_keys = {} - var previously_pressed_keys = {} - var keyCodes = {"0":"48","1":"49","2":"50","3":"51","4":"52","5":"53","6":"54","7":"55","8":"56","9":"57","backspace":"8","tab":"9","enter":"13","shift":"16","ctrl":"17","alt":"18","pause":"19","caps_lock":"20","esc":"27","space":"32","page_up":"33","page_down":"34","end":"35","home":"36","left":"37","up":"38","right":"39","down":"40","insert":"45","delete":"46","a":"65","b":"66","c":"67","d":"68","e":"69","f":"70","g":"71","h":"72","i":"73","j":"74","k":"75","l":"76","m":"77","n":"78","o":"79","p":"80","q":"81","r":"82","s":"83","t":"84","u":"85","v":"86","w":"87","x":"88","y":"89","z":"90","windows_left":"91","windows_right":"92","select":"93","numpad0":"96","numpad1":"97","numpad2":"98","numpad3":"99","numpad4":"100","numpad5":"101","numpad6":"102","numpad7":"103","numpad8":"104","numpad9":"105","asterisk":"106","plus":"107","minus":"109","decimal_point":"110","divide":"111","f1":"112","f2":"113","f3":"114","f4":"115","f5":"116","f6":"117","f7":"118","f8":"119","f9":"120","numlock":"144","scrollock":"145","semicolon":"186","equals":"187","comma":"188","dash":"189","period":"190","slash":"191","grave_accent":"192","open_bracket":"219","backslash":"220","close_bracket":"221","single_quote":"222"}; - var keycodeNames = {"8":"backspace","9":"tab","13":"enter","16":"shift","17":"ctrl","18":"alt","19":"pause","20":"caps_lock","27":"esc","32":"space","33":"page_up","34":"page_down","35":"end","36":"home","37":"left","38":"up","39":"right","40":"down","45":"insert","46":"delete","48":"0","49":"1","50":"2","51":"3","52":"4","53":"5","54":"6","55":"7","56":"8","57":"9","65":"a","66":"b","67":"c","68":"d","69":"e","70":"f","71":"g","72":"h","73":"i","74":"j","75":"k","76":"l","77":"m","78":"n","79":"o","80":"p","81":"q","82":"r","83":"s","84":"t","85":"u","86":"v","87":"w","88":"x","89":"y","90":"z","91":"windows_left","92":"windows_right","93":"select","96":"numpad0","97":"numpad1","98":"numpad2","99":"numpad3","100":"numpad4","101":"numpad5","102":"numpad6","103":"numpad7","104":"numpad8","105":"numpad9","106":"asterisk","107":"plus","109":"minus","110":"decimal_point","111":"divide","112":"f1","113":"f2","114":"f3","115":"f4","116":"f5","117":"f6","118":"f7","119":"f8","120":"f9","144":"numlock","145":"scrollock","186":"semicolon","187":"equals","188":"comma","189":"dash","190":"period","191":"slash","192":"grave_accent","219":"open_bracket","220":"backslash","221":"close_bracket","222":"single_quote"}; - var on_keydown_callbacks = [] - var on_keyup_callbacks = [] - var mousebuttoncode_to_string = [] - var ie_mousebuttoncode_to_string = [] - var prevent_default_keys = [] - -/** @private - * Map all javascript keycodes to easy-to-remember letters/words - */ -jaws.setupInput = function() { - var m = [] - - m[0] = "left_mouse_button" - m[1] = "center_mouse_button" - m[2] = "right_mouse_button" - - var ie_m = []; - ie_m[1] = "left_mouse_button"; - ie_m[2] = "right_mouse_button"; - ie_m[4] = "center_mouse_button"; - - mousebuttoncode_to_string = m - ie_mousebuttoncode_to_string = ie_m; - - window.addEventListener("keydown", handleKeyDown); - window.addEventListener("keyup", handleKeyUp); - - var jawswindow = jaws.canvas || jaws.dom - jawswindow.addEventListener("mousedown", handleMouseDown, false); - jawswindow.addEventListener("mouseup", handleMouseUp, false); - jawswindow.addEventListener("touchstart", handleTouchStart, false); - jawswindow.addEventListener("touchend", handleTouchEnd, false); - - window.addEventListener("blur", resetPressedKeys, false); - - // this turns off the right click context menu which screws up the mouseup event for button 2 - document.oncontextmenu = function() {return false}; -} - -/** @private - * Reset input-hash. Called when game is blurred so a key-controlled player doesn't keep on moving when the game isn't focused. - */ -function resetPressedKeys(e) { - for (var x in pressed_keys) { - delete pressed_keys[x]; - } -} - -/** @private - * handle event "onkeydown" by remembering what key was pressed - */ -function handleKeyUp(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var code = code = e.keyCode; - pressed_keys[code] = false; - if (on_keyup_callbacks[code]) { - on_keyup_callbacks[code](code) - e.preventDefault() - } else if (prevent_default_keys[code]) { - e.preventDefault(); - } -} - -/** @private - * handle event "onkeydown" by remembering what key was un-pressed - */ -function handleKeyDown(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var code = code = e.keyCode; - pressed_keys[code] = true; - if (on_keydown_callbacks[code]) { - on_keydown_callbacks[code](code); - e.preventDefault(); - } else if (prevent_default_keys[code]) { - e.preventDefault(); - } -} -/** @private - * handle event "onmousedown" by remembering what button was pressed - */ -function handleMouseDown(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var human_name = mousebuttoncode_to_string[e.button]; // 0 1 2 - if (navigator.appName == "Microsoft Internet Explorer"){ - human_name = ie_mousebuttoncode_to_string[e.button]; - } - pressed_keys[human_name] = true - if(on_keydown_callbacks[human_name]) { - on_keydown_callbacks[human_name](human_name); - e.preventDefault(); - } -} - - -/** @private - * handle event "onmouseup" by remembering what button was un-pressed - */ -function handleMouseUp(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var human_name = mousebuttoncode_to_string[e.button] - - if (navigator.appName == "Microsoft Internet Explorer"){ - human_name = ie_mousebuttoncode_to_string[e.button]; - } - pressed_keys[human_name] = false; - if(on_keyup_callbacks[human_name]) { - on_keyup_callbacks[human_name](human_name); - e.preventDefault(); - } -} - -/** @private - * handle event "touchstart" by remembering what button was pressed - */ -function handleTouchStart(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - pressed_keys["left_mouse_button"] = true - jaws.mouse_x = e.touches[0].pageX - jaws.canvas.offsetLeft; - jaws.mouse_y = e.touches[0].pageY - jaws.canvas.offsetTop; - //e.preventDefault() -} - -/** @private - * handle event "touchend" by remembering what button was pressed - */ -function handleTouchEnd(e) { - pressed_keys["left_mouse_button"] = false; - jaws.mouse_x = undefined; - jaws.mouse_y = undefined; -} - -/** - * Prevents default browseraction for given keys. - * @example - * jaws.preventDefaultKeys( ["down"] ) // Stop down-arrow-key from scrolling page down - */ -jaws.preventDefaultKeys = function(array_of_strings) { - var list = arguments; - for(var i=0; i < list.length; i++) { - prevent_default_keys[list[i]] = true; - } -} - -/** - * Array: If a key is currently pressed, the value associated to the key is true, otherwise false or not present. - */ -jaws.pressed = pressed_keys; - -/** - * Mapping key names to codes, and vice-versa - */ -jaws.keyCodes = keyCodes; -jaws.keycodeNames = keycodeNames; - -/** - * sets up a callback for a key (or array of keys) to call when it's pressed down - * - * @example - * // call goLeft() when left arrow key is pressed - * jaws.on_keypress(jaws.keyCode("left"), goLeft) - * - * // call fireWeapon() when SPACE or CTRL is pressed - * jaws.on_keypress([jaws.keyCodes["space"],jaws.keyCodes["ctrl"]], fireWeapon) - */ -jaws.on_keydown = function(key, callback) { - if (jaws.isArray(key)) { - for (var i=0; key[i]; i++) { - on_keydown_callbacks[key[i]] = callback; - } - } else { - on_keydown_callbacks[key] = callback; - } -} - -/** - * sets up a callback when a key (or array of keys) to call when it's released - */ -jaws.on_keyup = function(key, callback) { - if (jaws.isArray(key)) { - for (var i=0; key[i]; i++) { - on_keyup_callbacks[key[i]] = callback; - } - } else { - on_keyup_callbacks[key] = callback; - } -} - -/** @private - * Clean up all callbacks set by on_keydown / on_keyup - */ -jaws.clearKeyCallbacks = function() { - on_keyup_callbacks = []; - on_keydown_callbacks = []; -} - -return jaws; -})(jaws || {}); diff --git a/jaws/src/parallax.js b/jaws/src/parallax.js deleted file mode 100755 index 759624e..0000000 --- a/jaws/src/parallax.js +++ /dev/null @@ -1,108 +0,0 @@ -var jaws = (function(jaws) { - /** - * @class Manage a parallax scroller with different layers. "Field Summary" contains options for the Parallax()-constructor. - * @constructor - * - * @property scale number, scale factor for all layers (2 will double everything and so on) - * @property repeat_x true|false, repeat all parallax layers horizontally - * @property repeat_y true|false, repeat all parallax layers vertically - * @property camera_x number, x-position of "camera". add to camera_x and layers will scroll left. defaults to 0 - * @property camera_y number, y-position of "camera". defaults to 0 - * - * @example - * parallax = new jaws.Parallax({repeat_x: true}) - * parallax.addLayer({image: "parallax_1.png", damping: 100}) - * parallax.addLayer({image: "parallax_2.png", damping: 6}) - * parallax.camera_x += 1 // scroll layers horizontally - * parallax.draw() - * - */ - jaws.Parallax = function Parallax(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - jaws.parseOptions(this, options, this.default_options) - } - - jaws.Parallax.prototype.default_options = { - width: function() { return jaws.width }, - height: function() { return jaws.height }, - scale: 1, - repeat_x: null, - repeat_y: null, - camera_x: 0, - camera_y: 0, - layers: [] - } - - /** Draw all layers in parallax scroller */ - jaws.Parallax.prototype.draw = function(options) { - var layer, numx, numy, initx; - - for(var i=0; i < this.layers.length; i++) { - layer = this.layers[i] - - if(this.repeat_x) { - initx = -((this.camera_x / layer.damping) % layer.width); - } - else { - initx = -(this.camera_x / layer.damping) - } - - if (this.repeat_y) { - layer.y = -((this.camera_y / layer.damping) % layer.height); - } - else { - layer.y = -(this.camera_y / layer.damping); - } - - layer.x = initx; - while (layer.y < this.height) { - while (layer.x < this.width) { - if (layer.x + layer.width >= 0 && layer.y + layer.height >= 0) { //Make sure it's on screen - layer.draw(); //Draw only if actually on screen, for performance reasons - } - layer.x = layer.x + layer.width; - - if (!this.repeat_x) { - break; - } - } - - layer.y = layer.y + layer.height; - layer.x = initx; - if (!this.repeat_y) { - break; - } - } - } - } - /** Add a new layer to the parallax scroller */ - jaws.Parallax.prototype.addLayer = function(options) { - var layer = new jaws.ParallaxLayer(options) - layer.scaleAll(this.scale) - this.layers.push(layer) - } - /** Debugstring for Parallax() */ - jaws.Parallax.prototype.toString = function() { return "[Parallax " + this.x + ", " + this.y + ". " + this.layers.length + " layers]" } - - /** - * @class A single layer that's contained by Parallax() - * - * @property damping number, higher the number, the slower it will scroll with regards to other layers, defaults to 0 - * @constructor - * @extends jaws.Sprite - */ - jaws.ParallaxLayer = function ParallaxLayer(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.damping = options.damping || 0 - jaws.Sprite.call(this, options) - } - jaws.ParallaxLayer.prototype = jaws.Sprite.prototype - - /** Debugstring for ParallaxLayer() */ - // This overwrites Sprites toString, find another sollution. - // jaws.ParallaxLayer.prototype.toString = function() { return "[ParallaxLayer " + this.x + ", " + this.y + "]" } - - return jaws; -})(jaws || {}); - diff --git a/jaws/src/pixel_map.js b/jaws/src/pixel_map.js deleted file mode 100755 index 782942a..0000000 --- a/jaws/src/pixel_map.js +++ /dev/null @@ -1,154 +0,0 @@ -var jaws = (function(jaws) { -/** -* @class jaws.PixelMap -* @constructor -* -* Worms-style terrain collision detection. Created from a normal image. -* Read out specific pixels. Modify as you would do with a canvas. -* -* @property {string} image the image of the terrain -* @property {int} scale_image Scale the image by this factor -* -* @example -* tile_map = new jaws.Parallax({image: "map.png", scale_image: 4}) // scale_image: 4 for retro blocky feeling! -* tile_map.draw() // draw on canvas -* tile_map.nameColor([0,0,0,255], "ground") // give the color black the name "ground" -* tile_map.namedColorAtRect("ground", player.rect()) // True if players boundingbox is touching any black pixels on tile_map -* -*/ -jaws.PixelMap = function PixelMap(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.options = options - this.scale = options.scale || 1 - this.x = options.x || 0 - this.y = options.y || 0 - - if(options.image) { - this.setContext(options.image); - - if(options.scale_image) { - this.setContext( jaws.retroScaleImage(this.context.canvas, options.scale_image) ) - } - - this.width = this.context.canvas.width * this.scale; - this.height = this.context.canvas.height * this.scale; - } - else { jaws.log.warn("PixelMap needs an image to work with") } - - this.named_colors = []; - this.update(); -} - -/* -* Initiates a drawable context from given image. -* @private -*/ -jaws.PixelMap.prototype.setContext = function(image) { - var image = jaws.isDrawable(image) ? image : jaws.assets.get(image) - this.context = jaws.imageToCanvasContext(image) -} - -/** -* Updates internal pixel-array from the canvas. If we modify the 'terrain' (paint on pixel_map.context) we'll need to call this method. -*/ -jaws.PixelMap.prototype.update = function(x, y, width, height) { - if(x === undefined || x < 0) x = 0; - if(y === undefined || y < 0) y = 0; - if(width === undefined || width > this.width) width = this.width; - if(height === undefined || height > this.height) height = this.height; - - // No arguments? Read whole canvas, replace this.data - if(arguments.length == 0) { - this.data = this.context.getImageData(x, y, width, height).data - } - // Read a rectangle from the canvas, replacing relevant pixels in this.data - else { - var tmp = this.context.getImageData(x, y, width, height).data - var tmp_count = 0; - - // Some precalculation-optimizations - var one_line_down = this.width * 4; - var offset = (y * this.width * 4) + (x*4); - var horizontal_line = width*4; - - for(var y2 = 0; y2 < height; y2++) { - for(var x2 = 0; x2 < horizontal_line; x2++) { - this.data[offset + x2] = tmp[tmp_count++]; - } - offset += one_line_down; - } - } -} - -/** -* Draws the pixel map on the maincanvas -*/ -jaws.PixelMap.prototype.draw = function() { - jaws.context.drawImage(this.context.canvas, this.x, this.y, this.width, this.height) -} - -/** -* Trace the outline of a Rect until a named color found. -* -* @param {object} Rect Instance of jaws.Rect() -* @param {string} Color_Filter Only look for this named color -* -* @return {string} name of found color -*/ -jaws.PixelMap.prototype.namedColorAtRect = function(rect, color) { - var x = rect.x - var y = rect.y - - for(; x < rect.right-1; x++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; y < rect.bottom-1; y++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; x > rect.x; x--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; y > rect.y; y--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - - return false; -} - -/** -* Read current color at given coordinates X/Y -* -* @return {array} 4 integers [R, G, B, A] representing the pixel at x/y -*/ -jaws.PixelMap.prototype.at = function(x, y) { - x = parseInt(x) - y = parseInt(y) - if(y < 0) y = 0; - - var start = (y * this.width * 4) + (x*4); - var R = this.data[start]; - var G = this.data[start + 1]; - var B = this.data[start + 2]; - var A = this.data[start + 3]; - return [R, G, B, A]; -} - -/** -* Get previously named color if it exists at given x/y-coordinates. -* -* @return {string} name or color -*/ -jaws.PixelMap.prototype.namedColorAt = function(x, y) { - var a = this.at(x, y); - for(var i=0; i < this.named_colors.length; i++) { - var name = this.named_colors[i].name; - var c = this.named_colors[i].color; - if(c[0] == a[0] && c[1] == a[1] && c[2] == a[2] && c[3] == a[3]) return name; - } -} - -/** -* Give a RGBA-array a name. Later on we can work with names instead of raw colorvalues. -* -* @example -* pixel_map.nameColor([0,0,0,255], "ground") // Give the color black (with no transparency) the name "ground" -*/ -jaws.PixelMap.prototype.nameColor = function(color, name) { - this.named_colors.push({name: name, color: color}); -} - -return jaws; -})(jaws || {}); diff --git a/jaws/src/quadtree.js b/jaws/src/quadtree.js deleted file mode 100755 index 0dd25ab..0000000 --- a/jaws/src/quadtree.js +++ /dev/null @@ -1,213 +0,0 @@ -if(typeof require !== "undefined") { var jaws = require("./core.js"); } - -/* - * @class jaws.QuadTree - * @property {jaws.Rect} bounds Rect(x,y,width,height) defining bounds of tree - * @property {number} depth The depth of the root node - * @property {array} nodes The nodes of the root node - * @property {array} objects The objects of the root node - * @example - * setup: - * var quadtree = new jaws.QuadTree(); - * update: - * quadtree.collide(sprite or list, sprite or list, callback function); - */ -var jaws = (function(jaws) { - - /** - * Creates an empty quadtree with optional bounds and starting depth - * @constructor - * @param {jaws.Rect} [bounds] The defining bounds of the tree - * @param {number} [depth] The current depth of the tree - */ - jaws.QuadTree = function(bounds) { - this.depth = arguments[1] || 0; - this.bounds = bounds || new jaws.Rect(0, 0, jaws.width, jaws.height); - this.nodes = []; - this.objects = []; - }; - - /** - * Moves through the nodes and deletes them. - * @this {jaws.QuadTree} - */ - jaws.QuadTree.prototype.clear = function() { - this.objects = []; - - for (var i = 0; i < this.nodes.length; i++) { - if (typeof this.nodes[i] !== 'undefined') { - this.nodes[i].clear(); - delete this.nodes[i]; - } - } - }; - - /** - * Creates four new branches sub-dividing the current node's width and height - * @private - * @this {jaws.QuadTree} - */ - jaws.QuadTree.prototype.split = function() { - var subWidth = Math.round((this.bounds.width / 2)); - var subHeight = Math.round((this.bounds.height / 2)); - var x = this.bounds.x; - var y = this.bounds.y; - - this.nodes[0] = new jaws.QuadTree(new jaws.Rect(x + subWidth, y, subWidth, subHeight), this.depth + 1); - this.nodes[1] = new jaws.QuadTree(new jaws.Rect(x, y, subWidth, subHeight), this.depth + 1); - this.nodes[2] = new jaws.QuadTree(new jaws.Rect(x, y + subHeight, subWidth, subHeight), this.depth + 1); - this.nodes[3] = new jaws.QuadTree(new jaws.Rect(x + subWidth, y + subHeight, subWidth, subHeight), this.depth + 1); - }; - - /** - * Returns the index of a node's branches if passed-in object fits within it - * @private - * @param {object} pRect An object with the properties x, y, width, and height - * @returns {index} The index of nodes[] that matches the dimensions of passed-in object - */ - jaws.QuadTree.prototype.getIndex = function(pRect) { - var index = -1; - var verticalMidpoint = this.bounds.x + (this.bounds.width / 2); - var horizontalMidpoint = this.bounds.y + (this.bounds.height / 2); - - var topQuadrant = (pRect.y < horizontalMidpoint && pRect.y + pRect.height < horizontalMidpoint); - var bottomQuadrant = (pRect.y > horizontalMidpoint); - - if (pRect.x < verticalMidpoint && pRect.x + pRect.width < verticalMidpoint) { - if (topQuadrant) { - index = 1; - } - else if (bottomQuadrant) { - index = 2; - } - } - else if (pRect.x > verticalMidpoint) { - if (topQuadrant) { - index = 0; - } - else if (bottomQuadrant) { - index = 3; - } - } - - return index; - }; - - /** - * Inserts an object into the quadtree, spliting it into new branches if needed - * @param {object} pRect An object with the properties x, y, width, and height - */ - jaws.QuadTree.prototype.insert = function(pRect) { - - if (!pRect.hasOwnProperty("x") && !pRect.hasOwnProperty("y") && - !pRect.hasOwnProperty("width") && !pRect.hasOwnProperty("height")) { - return; - } - - if (typeof this.nodes[0] !== 'undefined') { - var index = this.getIndex(pRect); - - if (index !== -1) { - this.nodes[index].insert(pRect); - return; - } - } - - this.objects.push(pRect); - - if (typeof this.nodes[0] === 'undefined') { - this.split(); - } - - var i = 0; - while (i < this.objects.length) { - var index = this.getIndex(this.objects[i]); - if (index !== -1) { - this.nodes[index].insert(this.objects.splice(i, 1)[0]); - } - else { - i++; - } - } - - }; - - /** - * Returns those objects on the branch matching the position of the passed-in object - * @param {object} pRect An object with properties x, y, width, and height - * @returns {array} The objects on the same branch as the passed-in object - */ - jaws.QuadTree.prototype.retrieve = function(pRect) { - - if (!pRect.hasOwnProperty("x") && !pRect.hasOwnProperty("y") && - !pRect.hasOwnProperty("width") && !pRect.hasOwnProperty("height")) { - return; - } - - var index = this.getIndex(pRect); - var returnObjects = this.objects; - if (typeof this.nodes[0] !== 'undefined') { - if (index !== -1) { - returnObjects = returnObjects.concat(this.nodes[index].retrieve(pRect)); - } else { - for (var i = 0; i < this.nodes.length; i++) { - returnObjects = returnObjects.concat(this.nodes[i].retrieve(pRect)); - } - } - } - return returnObjects; - }; - - /** - * Checks for collisions between objects by creating a quadtree, inserting one or more objects, - * and then comparing the results of a retrieval against another single or set of objects. - * - * With the callback argument, it will call a function and pass the items found colliding - * as the first and second argument. - * - * Without the callback argument, it will return a boolean value if any collisions were found. - * - * @param {object|array} list1 A single or set of objects with properties x, y, width, and height - * @param {object|array} list2 A single or set of objects with properties x, y, width, and height - * @param {function} [callback] The function to call per collision - * @returns {boolean} If the items (or any within their sets) collide with one another - */ - jaws.QuadTree.prototype.collide = function(list1, list2, callback) { - - var overlap = false; - var tree = new jaws.QuadTree(); - var temp = []; - - if (!(list1.forEach)) { - temp.push(list1); - list1 = temp; - } - - if (!(list2.forEach)) { - temp = []; - temp.push(list2); - list2 = temp; - } - - list2.forEach(function(el) { - tree.insert(el); - }); - - list1.forEach(function(el) { - if(jaws.collide(el, tree.retrieve(el), callback)) { - overlap = true; - } - }); - - tree.clear(); - return overlap; - }; - - return jaws; - -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.QuadTree; -} diff --git a/jaws/src/rect.js b/jaws/src/rect.js deleted file mode 100755 index 92a54a4..0000000 --- a/jaws/src/rect.js +++ /dev/null @@ -1,128 +0,0 @@ -var jaws = (function(jaws) { - -/* -* 2013-09-28: -* -* For a 10x10 sprite in the topleft corner, should sprite.rect().bottom be 9 or 10? -* There's no right or wrong answer. In some cases 9 makes sense (if checking directly for pixel-values for example). -* In other cases 10 makes sense (bottom = x + height). -* -* The important part is beeing consistent across the lib/game. -* Jaws started out with bottom = x + height so we'll continue with that way until good reasons to change come up. -* Therefore correction = 0 for now. -*/ -var correction = 0; - -/** - @class A Basic rectangle. - @example - rect = new jaws.Rect(5,5,20,20) - rect.right // -> 25 - rect.bottom // -> 25 - rect.move(10,20) - rect.right // -> 35 - rect.bottom // -> 45 - rect.width // -> 20 - rect.height // -> 20 -*/ -jaws.Rect = function Rect(x, y, width, height) { - if( !(this instanceof arguments.callee) ) return new arguments.callee(x, y, width, height); - - this.x = x - this.y = y - this.width = width - this.height = height - this.right = x + width - correction - this.bottom = y + height - correction -} - -/** Return position as [x,y] */ -jaws.Rect.prototype.getPosition = function() { - return [this.x, this.y] -} - -/** Move rect x pixels horizontally and y pixels vertically */ -jaws.Rect.prototype.move = function(x, y) { - this.x += x - this.y += y - this.right += x - this.bottom += y - return this -} - -/** Set rects x/y */ -jaws.Rect.prototype.moveTo = function(x, y) { - this.x = x - this.y = y - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} -/** Modify width and height */ -jaws.Rect.prototype.resize = function(width, height) { - this.width += width - this.height += height - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Returns a new rect witht he same dimensions */ -jaws.Rect.prototype.clone = function() { - return new jaws.Rect(this.x, this.y, this.width, this.height) -} - -/** Shrink rectangle on both axis with given x/y values */ -jaws.Rect.prototype.shrink = function(x, y) { - this.x += x - this.y += y - this.width -= (x+x) - this.height -= (y+y) - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Set width and height */ -jaws.Rect.prototype.resizeTo = function(width, height) { - this.width = width - this.height = height - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Draw rect in color red, useful for debugging */ -jaws.Rect.prototype.draw = function() { - jaws.context.strokeStyle = "red" - jaws.context.strokeRect(this.x-0.5, this.y-0.5, this.width, this.height) - return this -} - -/** Returns true if point at x, y lies within calling rect */ -jaws.Rect.prototype.collidePoint = function(x, y) { - return (x >= this.x && x <= this.right && y >= this.y && y <= this.bottom) -} - -/** Returns true if calling rect overlaps with given rect in any way */ -jaws.Rect.prototype.collideRect = function(rect) { - return ((this.x >= rect.x && this.x <= rect.right) || (rect.x >= this.x && rect.x <= this.right)) && - ((this.y >= rect.y && this.y <= rect.bottom) || (rect.y >= this.y && rect.y <= this.bottom)) -} - -/* -// Possible future functions -jaws.Rect.prototype.collideRightSide = function(rect) { return(this.right >= rect.x && this.x < rect.x) } -jaws.Rect.prototype.collideLeftSide = function(rect) { return(this.x > rect.x && this.x <= rect.right) } -jaws.Rect.prototype.collideTopSide = function(rect) { return(this.y >= rect.y && this.y <= rect.bottom) } -jaws.Rect.prototype.collideBottomSide = function(rect) { return(this.bottom >= rect.y && this.y < rect.y) } -*/ - -jaws.Rect.prototype.toString = function() { return "[Rect " + this.x + ", " + this.y + ", " + this.width + ", " + this.height + "]" } - -return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.Rect } - diff --git a/jaws/src/sprite.js b/jaws/src/sprite.js deleted file mode 100755 index e05dc71..0000000 --- a/jaws/src/sprite.js +++ /dev/null @@ -1,462 +0,0 @@ -var jaws = (function(jaws) { - -/** -* @class A basic but powerfull sprite for all your onscreen-game objects. "Field Summary" contains options for the Sprite()-constructor. -* @constructor -* -* @property {int} x Horizontal position (0 = furthest left) -* @property {int} y Vertical position (0 = top) -* @property {image} image Image/canvas or string pointing to an asset ("player.png") -* @property {int} alpha Transparency 0=fully transparent, 1=no transperency -* @property {int} angle Angle in degrees (0-360) -* @property {bool} flipped Flip sprite horizontally, usefull for sidescrollers -* @property {string} anchor String stating how to anchor the sprite to canvas, @see Sprite#anchor ("top_left", "center" etc) -* @property {int} scale_image Scale the sprite by this factor -* @property {string,gradient} color If set, draws a rectangle of dimensions rect() with specified color or gradient (linear or radial) -* -* @example -* // create new sprite at top left of the screen, will use jaws.assets.get("foo.png") -* new Sprite({image: "foo.png", x: 0, y: 0}) -* -* // sets anchor to "center" on creation -* new Sprite({image: "topdownspaceship.png", anchor: "center"}) -* -*/ -jaws.Sprite = function Sprite(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - this.set(options) - this.context = options.context ? options.context : jaws.context; // Prefer given canvas-context, fallback to jaws.context -} - -jaws.Sprite.prototype.default_options = { - x: 0, - y: 0, - alpha: 1, - angle: 0, - flipped: false, - anchor_x: 0, - anchor_y: 0, - image: null, - image_path: null, - anchor: null, - scale_image: null, - damping: 1, - scale_x: 1, - scale_y: 1, - scale: 1, - color: "#ddd", - width: 16, - height: 16, - _constructor: null, - context: null, - data: null -} - -/** - * @private - * Call setters from JSON object. Used to parse options. - */ -jaws.Sprite.prototype.set = function(options) { - if(jaws.isString(this.image)) this.image_path = this.image; - jaws.parseOptions(this, options, this.default_options); - - if(this.scale) this.scale_x = this.scale_y = this.scale; - if(this.image) this.setImage(this.image); - if(this.scale_image) this.scaleImage(this.scale_image); - if(this.anchor) this.setAnchor(this.anchor); - - if(!this.image && this.color && this.width && this.height) { - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - canvas.width = this.width; - canvas.height = this.height; - context.fillStyle = this.color; - context.fillRect(0, 0, this.width, this.height); - this.image = canvas; - } - - this.cacheOffsets() - - return this -} - -/** - * @private - * - * Creates a new sprite from current sprites attributes() - * Checks JawsJS magic property '_constructor' when deciding with which constructor to create it - * - */ -jaws.Sprite.prototype.clone = function(object) { - var constructor = this._constructor ? eval(this._constructor) : this.constructor - var new_sprite = new constructor( this.attributes() ); - new_sprite._constructor = this._constructor || this.constructor.name - return new_sprite -} - - -/** - * Sets image from image/canvas or asset-string ("foo.png") - * If asset isn't previously loaded setImage() will try to load it. - */ -jaws.Sprite.prototype.setImage = function(value) { - var that = this - - // An image, great, set this.image and return - if(jaws.isDrawable(value)) { - this.image = value - return this.cacheOffsets() - } - // Not an image, therefore an asset string, i.e. "ship.bmp" - else { - // Assets already loaded? Set this.image - if(jaws.assets.isLoaded(value)) { this.image = jaws.assets.get(value); this.cacheOffsets(); } - - // Not loaded? Load it with callback to set image. - else { - jaws.log.warn("Image '" + value + "' not preloaded with jaws.assets.add(). Image and a working sprite.rect() will be delayed.") - jaws.assets.load(value, {onload: function() { that.image = jaws.assets.get(value); that.cacheOffsets();} } ) - } - } - return this -} - -/** -* Steps 1 pixel towards the given X/Y. Horizontal and vertical steps are done separately between each callback. -* Exits when the continueStep-callback returns true for both vertical and horizontal steps or if target X/Y has been reached. -* -* @returns {object} Object with 2 x/y-properties indicating what plane we moved in when stepToWhile was stopped. -*/ -jaws.Sprite.prototype.stepToWhile = function(target_x, target_y, continueStep) { - var step = 1; - var step_x = (target_x < this.x) ? -step : step; - var step_y = (target_y < this.y) ? -step : step; - - target_x = parseInt(target_x) - target_y = parseInt(target_y) - - var collision_x = false; - var collision_y = false; - - while( true ) { - if(collision_x === false) { - if(this.x != target_x) { this.x += step_x } - if( !continueStep(this) ) { this.x -= step_x; collision_x = true } - } - - if(collision_y === false) { - if(this.y != target_y) { this.y += step_y } - if( !continueStep(this) ) { this.y -= step_y; collision_y = true } - } - - if( (collision_x || this.x == target_x) && (collision_y || this.y == target_y) ) - return {x: collision_x, y: collision_y}; - } -} -/** -* Moves with given vx/vy velocoties by stepping 1 pixel at the time. Horizontal and vertical steps are done separately between each callback. -* Exits when the continueStep-callback returns true for both vertical and horizontal steps or if target X/Y has been reached. -* -* @returns {object} Object with 2 x/y-properties indicating what plane we moved in when stepWhile was stopped. -*/ -jaws.Sprite.prototype.stepWhile = function(vx, vy, continueStep) { - return this.stepToWhile(this.x + vx, this.y + vy, continueStep) -} - -/** Flips image vertically, usefull for sidescrollers when player is walking left/right */ -jaws.Sprite.prototype.flip = function() { this.flipped = this.flipped ? false : true; return this } -jaws.Sprite.prototype.flipTo = function(value) { this.flipped = value; return this } -/** Rotate sprite by value degrees */ -jaws.Sprite.prototype.rotate = function(value) { this.angle += value; return this } -/** Force an rotation-angle on sprite */ -jaws.Sprite.prototype.rotateTo = function(value) { this.angle = value; return this } - -/** Set x/y */ -jaws.Sprite.prototype.moveTo = function(x, y) { - if(jaws.isArray(x) && y === undefined) { - y = x[1] - x = x[0] - } - this.x = x; - this.y = y; - return this; -} -/** Modify x/y */ -jaws.Sprite.prototype.move = function(x, y) { - if(jaws.isArray(x) && y === undefined) { - y = x[1] - x = x[0] - } - - if(x) this.x += x; - if(y) this.y += y; - return this -} -/** -* scale sprite by given factor. 1=don't scale. <1 = scale down. 1>: scale up. -* Modifies width/height. -**/ -jaws.Sprite.prototype.scaleAll = function(value) { this.scale_x *= value; this.scale_y *= value; return this.cacheOffsets() } -/** set scale factor. ie. 2 means a doubling if sprite in both directions. */ -jaws.Sprite.prototype.scaleTo = function(value) { this.scale_x = this.scale_y = value; return this.cacheOffsets() } -/** scale sprite horizontally by scale_factor. Modifies width. */ -jaws.Sprite.prototype.scaleWidth = function(value) { this.scale_x *= value; return this.cacheOffsets() } -/** scale sprite vertically by scale_factor. Modifies height. */ -jaws.Sprite.prototype.scaleHeight = function(value) { this.scale_y *= value; return this.cacheOffsets() } - -/** Sets x */ -jaws.Sprite.prototype.setX = function(value) { this.x = value; return this } -/** Sets y */ -jaws.Sprite.prototype.setY = function(value) { this.y = value; return this } - -/** Position sprites top on the y-axis */ -jaws.Sprite.prototype.setTop = function(value) { this.y = value + this.top_offset; return this } -/** Position sprites bottom on the y-axis */ -jaws.Sprite.prototype.setBottom = function(value) { this.y = value - this.bottom_offset; return this } -/** Position sprites left side on the x-axis */ -jaws.Sprite.prototype.setLeft = function(value) { this.x = value + this.left_offset; return this } -/** Position sprites right side on the x-axis */ -jaws.Sprite.prototype.setRight = function(value) { this.x = value - this.right_offset; return this } - -/** Set new width. Scales sprite. */ -jaws.Sprite.prototype.setWidth = function(value) { this.scale_x = value/this.image.width; return this.cacheOffsets() } -/** Set new height. Scales sprite. */ -jaws.Sprite.prototype.setHeight = function(value) { this.scale_y = value/this.image.height; return this.cacheOffsets() } -/** Resize sprite by adding width */ -jaws.Sprite.prototype.resize = function(width, height) { - if(jaws.isArray(width) && height === undefined) { - height = width[1] - width = width[0] - } - - this.scale_x = (this.width + width) / this.image.width - this.scale_y = (this.height + height) / this.image.height - return this.cacheOffsets() -} -/** - * Resize sprite to exact width/height - */ -jaws.Sprite.prototype.resizeTo = function(width, height) { - if(jaws.isArray(width) && height === undefined) { - height = width[1] - width = width[0] - } - - this.scale_x = width / this.image.width - this.scale_y = height / this.image.height - return this.cacheOffsets() -} - -/** -* The sprites anchor could be describe as "the part of the sprite will be placed at x/y" -* or "when rotating, what point of the of the sprite will it rotate round" -* -* @example -* For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y -* .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y -*/ -jaws.Sprite.prototype.setAnchor = function(value) { - var anchors = { - top_left: [0,0], - left_top: [0,0], - center_left: [0,0.5], - left_center: [0,0.5], - bottom_left: [0,1], - left_bottom: [0,1], - top_center: [0.5,0], - center_top: [0.5,0], - center_center: [0.5,0.5], - center: [0.5,0.5], - bottom_center: [0.5,1], - center_bottom: [0.5,1], - top_right: [1,0], - right_top: [1,0], - center_right: [1,0.5], - right_center: [1,0.5], - bottom_right: [1,1], - right_bottom: [1,1] - } - - if(a = anchors[value]) { - this.anchor_x = a[0] - this.anchor_y = a[1] - if(this.image) this.cacheOffsets(); - } - return this -} - -/** @private */ -jaws.Sprite.prototype.cacheOffsets = function() { - if(!this.image) { return } - - this.width = this.image.width * this.scale_x - this.height = this.image.height * this.scale_y - this.left_offset = this.width * this.anchor_x - this.top_offset = this.height * this.anchor_y - this.right_offset = this.width * (1.0 - this.anchor_x) - this.bottom_offset = this.height * (1.0 - this.anchor_y) - - if(this.cached_rect) this.cached_rect.resizeTo(this.width, this.height); - return this -} - -/** Returns a jaws.Rect() perfectly surrouning sprite. Also cache rect in this.cached_rect. */ -jaws.Sprite.prototype.rect = function() { - if(!this.cached_rect && this.width) this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height); - if(this.cached_rect) this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset); - return this.cached_rect -} - -/** Draw sprite on active canvas */ -jaws.Sprite.prototype.draw = function() { - if(!this.image) { return this } - - this.context.save() - this.context.translate(this.x, this.y) - if(this.angle!=0) { jaws.context.rotate(this.angle * Math.PI / 180) } - this.flipped && this.context.scale(-1, 1) - this.context.globalAlpha = this.alpha - this.context.translate(-this.left_offset, -this.top_offset) // Needs to be separate from above translate call cause of flipped - this.context.drawImage(this.image, 0, 0, this.width, this.height) - this.context.restore() - return this -} - -/** - * Scales image using hard block borders. Useful for that cute, blocky retro-feeling. - * Depends on gfx.js beeing loaded. - */ -jaws.Sprite.prototype.scaleImage = function(factor) { - if(!this.image) return; - this.setImage( jaws.retroScaleImage(this.image, factor) ) - return this -} - -/** - * Returns sprite as a canvas context. - * For certain browsers, a canvas context is faster to work with then a pure image. - */ -jaws.Sprite.prototype.asCanvasContext = function() { - var canvas = document.createElement("canvas") - canvas.width = this.width - canvas.height = this.height - - var context = canvas.getContext("2d") - if(jaws.context) context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - context.drawImage(this.image, 0, 0, this.width, this.height) - return context -} - -/** - * Returns sprite as a canvas - */ -jaws.Sprite.prototype.asCanvas = function() { - var canvas = document.createElement("canvas") - canvas.width = this.width - canvas.height = this.height - - var context = canvas.getContext("2d") - if(jaws.context) context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - context.drawImage(this.image, 0, 0, this.width, this.height) - return canvas -} - -jaws.Sprite.prototype.toString = function() { return "[Sprite " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]" } - -/** returns Sprites state/properties as a pure object */ -jaws.Sprite.prototype.attributes = function() { - var object = {} // Starting with this.options could create circular references through "context" - object["_constructor"] = this._constructor || "jaws.Sprite" - object["x"] = parseFloat(this.x.toFixed(2)) - object["y"] = parseFloat(this.y.toFixed(2)) - object["image"] = this.image_path - object["alpha"] = this.alpha - object["flipped"] = this.flipped - object["angle"] = parseFloat(this.angle.toFixed(2)) - object["scale_x"] = this.scale_x; - object["scale_y"] = this.scale_y; - object["anchor_x"] = this.anchor_x - object["anchor_y"] = this.anchor_y - - if(this.data !== null) object["data"] = jaws.clone(this.data); // For external data (for example added by the editor) that you want serialized - - return object -} -/** - * Load/creates sprites from given data - * - * Argument could either be - * - an array of Sprite objects - * - an array of JSON objects - * - a JSON.stringified string representing an array of JSON objects - * - * @return Array of created sprite -* - */ -jaws.Sprite.parse = function(objects) { - var sprites = [] - - if(jaws.isArray(objects)) { - // If this is an array of JSON representations, parse it - if(objects.every(function(item) { return item._constructor })) { - parseArray(objects) - } else { - // This is already an array of Sprites, load it directly - sprites = objects - } - } - else if(jaws.isString(objects)) { parseArray( JSON.parse(objects) ); jaws.log.info(objects) } - - function parseArray(array) { - array.forEach( function(data) { - var constructor = data._constructor ? eval(data._constructor) : data.constructor - if(jaws.isFunction(constructor)) { - jaws.log.info("Creating " + data._constructor + "(" + data.toString() + ")", true) - var object = new constructor(data) - object._constructor = data._constructor || data.constructor.name - sprites.push(object); - } - }); - } - - return sprites; -} - -/** - * returns a JSON-string representing the state of the Sprite. - * - * Use this to serialize your sprites / game objects, maybe to save in local storage or on a server - * - * jaws.game_states.Edit uses this to export all edited objects. - * - */ -jaws.Sprite.prototype.toJSON = function() { - return JSON.stringify(this.attributes()) -} - -return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.Sprite } - -/* -// Chainable setters under consideration: -jaws.Sprite.prototype.setFlipped = function(value) { this.flipped = value; return this } -jaws.Sprite.prototype.setAlpha = function(value) { this.alpha = value; return this } -jaws.Sprite.prototype.setAnchorX = function(value) { this.anchor_x = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setAnchorY = function(value) { this.anchor_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setAngle = function(value) { this.angle = value; return this } -jaws.Sprite.prototype.setScale = function(value) { this.scale_x = this.scale_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setScaleX = function(value) { this.scale_x = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setScaleY = function(value) { this.scale_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.moveX = function(x) { this.x += x; return this } -jaws.Sprite.prototype.moveXTo = function(x) { this.x = x; return this } -jaws.Sprite.prototype.moveY = function(y) { this.y += y; return this } -jaws.Sprite.prototype.moveYTo = function(y) { this.y = y; return this } -jaws.Sprite.prototype.scaleWidthTo = function(value) { this.scale_x = value; return this.cacheOffsets() } -jaws.Sprite.prototype.scaleHeightTo = function(value) { this.scale_y = value; return this.cachOfffsets() } -*/ - diff --git a/jaws/src/sprite_sheet.js b/jaws/src/sprite_sheet.js deleted file mode 100755 index 756b814..0000000 --- a/jaws/src/sprite_sheet.js +++ /dev/null @@ -1,84 +0,0 @@ -var jaws = (function(jaws) { - - -/** - * @class Cut out invidual frames (images) from a larger spritesheet-image. "Field Summary" contains options for the SpriteSheet()-constructor. - * - * @property {image|image} Image/canvas or asset-string to cut up smaller images from - * @property {string} orientation How to cut out invidual images from spritesheet, either "right" or "down" - * @property {array} frame_size width and height of invidual frames in spritesheet - * @property {array} frames all single frames cut out from image - * @property {integer} offset vertical or horizontal offset to start cutting from - * @property {int} scale_image Scale the sprite sheet by this factor before cutting out the frames. frame_size is automatically re-sized too - * -*/ -jaws.SpriteSheet = function SpriteSheet(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - jaws.parseOptions(this, options, this.default_options); - - /* Detect framesize from filename, example: droid_10x16.png means each frame is 10px high and 16px wide */ - if(jaws.isString(this.image) && !options.frame_size) { - var regexp = new RegExp("_(\\d+)x(\\d+)", "g"); - var sizes = regexp.exec(this.image) - this.frame_size = [] - this.frame_size[0] = parseInt(sizes[1]) - this.frame_size[1] = parseInt(sizes[2]) - } - - this.image = jaws.isDrawable(this.image) ? this.image : jaws.assets.data[this.image] - if(this.scale_image) { - var image = (jaws.isDrawable(this.image) ? this.image : jaws.assets.get(this.image)) - this.frame_size[0] *= this.scale_image - this.frame_size[1] *= this.scale_image - this.image = jaws.retroScaleImage(image, this.scale_image) - } - - var index = 0 - this.frames = [] - - // Cut out tiles from Top -> Bottom - if(this.orientation == "down") { - for(var x=this.offset; x < this.image.width; x += this.frame_size[0]) { - for(var y=0; y < this.image.height; y += this.frame_size[1]) { - this.frames.push( cutImage(this.image, x, y, this.frame_size[0], this.frame_size[1]) ) - } - } - } - // Cut out tiles from Left -> Right - else { - for(var y=this.offset; y < this.image.height; y += this.frame_size[1]) { - for(var x=0; x < this.image.width; x += this.frame_size[0]) { - this.frames.push( cutImage(this.image, x, y, this.frame_size[0], this.frame_size[1]) ) - } - } - } -} - -jaws.SpriteSheet.prototype.default_options = { - image: null, - orientation: "down", - frame_size: [32,32], - offset: 0, - scale_image: null -} - -/** @private - * Cut out a rectangular piece of a an image, returns as canvas-element - */ -function cutImage(image, x, y, width, height) { - var cut = document.createElement("canvas") - cut.width = width - cut.height = height - - var ctx = cut.getContext("2d") - ctx.drawImage(image, x, y, width, height, 0, 0, cut.width, cut.height) - - return cut -}; - -jaws.SpriteSheet.prototype.toString = function() { return "[SpriteSheet " + this.frames.length + " frames]" } - -return jaws; -})(jaws || {}); - diff --git a/jaws/src/text.js b/jaws/src/text.js deleted file mode 100755 index 5856b75..0000000 --- a/jaws/src/text.js +++ /dev/null @@ -1,619 +0,0 @@ -/** - * @fileOverview A jaws.Text object with word-wrapping functionality. - * @class jaws.Text - * @property {integer} x Horizontal position (0 = furthest left) - * @property {integer} y Vertical position (0 = top) - * @property {number} alpha Transparency: 0 (fully transparent) to 1 (no transparency) - * @property {number} angle Angle in degrees (0-360) - * @property {string} anchor String stating how to anchor the sprite to canvas; @see Sprite#anchor - * @property {string} text The actual text to be displayed - * @property {string} fontFace A valid font-family - * @property {number} fontSize The size of the text in pixels - * @property {string} textAlign "start", "end", "left", "right", or "center" - * @property {string} textBaseline "top", "bottom", "hanging", "middle", "alphabetic", or "ideographic" - * @property {number} width The width of the rect() containing the text - * @property {number} height The height of the rect() containing the text - * @property {string} style The style to draw the text: "normal", "bold" or italic" - * @property {boolean} wordWrap If word-wrapping should be attempted - * @property {string} shadowColor The color of the shadow for the text - * @property {number} shadowBlur The amount of shadow blur (length away from text) - * @property {number} shadowOffsetX The start of the shadow from initial x - * @property {number} shadowOffsetY The start of the shadow from initial y - * @example - * var text = new Text({text: "Hello world!", x: 10, y: 10}) - */ - -var jaws = (function(jaws) { - - /** - * jaws.Text constructor - * @constructor - * @param {object} options An object-literal collection of constructor values - */ - jaws.Text = function(options) { - if (!(this instanceof arguments.callee)) - return new arguments.callee(options); - - this.set(options); - - if (options.context) { - this.context = options.context; - } - - if (!options.context) { // Defaults to jaws.context - if (jaws.context) - this.context = jaws.context; - } - }; - - /** - * The default values of jaws.Text properties - */ - jaws.Text.prototype.default_options = { - x: 0, - y: 0, - alpha: 1, - angle: 0, - anchor_x: 0, - anchor_y: 0, - anchor: "top_left", - damping: 1, - style: "normal", - fontFace: "serif", - fontSize: 12, - color: "black", - textAlign: "start", - textBaseline: "alphabetic", - text: "", - wordWrap: false, - width: function(){ return jaws.width; }, - height: function() { return jaws.height; }, - shadowColor: null, - shadowBlur: null, - shadowOffsetX: null, - shadowOffsetY: null, - _constructor: null, - }; - - /** - * Overrides constructor values with defaults - * @this {jaws.Text} - * @param {object} options An object-literal collection of constructor values - * @returns {this} - * @see jaws.parseOptions - */ - jaws.Text.prototype.set = function(options) { - - jaws.parseOptions(this, options, this.default_options); - - if (this.anchor) - this.setAnchor(this.anchor); - - this.cacheOffsets(); - - return this; - }; - - /** - * Returns a new instance based on the current jaws.Text object - * @private - * @this {jaws.Text} - * @returns {object} The newly cloned object - */ - jaws.Text.prototype.clone = function() { - var constructor = this._constructor ? eval(this._constructor) : this.constructor; - var new_sprite = new constructor(this.attributes()); - new_sprite._constructor = this._constructor || this.constructor.name; - return new_sprite; - }; - - /** - * Rotate sprite by value degrees - * @this {jaws.Text} - * @param {number} value The amount of the rotation - * @returns {this} Current function scope - */ - jaws.Text.prototype.rotate = function(value) { - this.angle += value; - return this; - }; - - /** - * Forces a rotation-angle on sprite - * @this {jaws.Text} - * @param {number} value The amount of the rotation - * @returns {this} Current function instance - */ - jaws.Text.prototype.rotateTo = function(value) { - this.angle = value; - return this; - }; - - /** - * Move object to position x, y - * @this {jaws.Text} - * @param {number} x The x position to move to - * @param {number} y The y position to move to - * @returns {this} Current function instance - */ - jaws.Text.prototype.moveTo = function(x, y) { - this.x = x; - this.y = y; - return this; - }; - - /** - * Modify x and/or y by a fixed amount - * @this {jaws.Text} - * @param {type} x The additional amount to move x - * @param {type} y The additional amount to move y - * @returns {this} Current function instance - */ - jaws.Text.prototype.move = function(x, y) { - if (x) - this.x += x; - if (y) - this.y += y; - return this; - }; - - /** - * Sets x - * @param {number} value The new x value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setX = function(value) { - this.x = value; - return this; - }; - - /** - * Sets y - * @param {number} value The new y value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setY = function(value) { - this.y = value; - return this; - }; - - /** - * Position sprites top on the y-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setTop = function(value) { - this.y = value + this.top_offset; - return this; - }; - - /** - * Position sprites bottom on the y-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setBottom = function(value) { - this.y = value - this.bottom_offset; - return this; - }; - - /** - * Position sprites left side on the x-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setLeft = function(value) { - this.x = value + this.left_offset; - return this; - }; - - /** - * Position sprites right side on the x-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setRight = function(value) { - this.x = value - this.right_offset; - return this; - }; - - /** - * Set new width. - * @param {number} value The new width - * @returns {this} - */ - jaws.Text.prototype.setWidth = function(value) { - this.width = value; - this.cacheOffsets(); - return this; - }; - - /** - * Set new height. - * @param {number} value The new height - * @returns {this} - */ - jaws.Text.prototype.setHeight = function(value) { - this.height = value; - this.cacheOffsets(); - return this; - }; - - /** - * Resize sprite by adding width or height - * @param {number} width - * @param {number} height - * @returns {this} - */ - jaws.Text.prototype.resize = function(width, height) { - this.width += width; - this.height += height; - this.cacheOffsets(); - return this; - }; - - /** - * Resize sprite to exact width/height - * @this {jaws.Text} - * @param {number} width - * @param {number} height - * @returns {this} - */ - jaws.Text.prototype.resizeTo = function(width, height) { - this.width = width; - this.height = height; - this.cacheOffsets(); - return this; - }; - - /** - * The anchor could be describe as "the part of the text will be placed at x/y" - * or "when rotating, what point of the of the text will it rotate round" - * @param {string} value - * @returns {this} The current function instance - * @example - * For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y - * .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y - */ - jaws.Text.prototype.setAnchor = function(value) { - var anchors = { - top_left: [0, 0], - left_top: [0, 0], - center_left: [0, 0.5], - left_center: [0, 0.5], - bottom_left: [0, 1], - left_bottom: [0, 1], - top_center: [0.5, 0], - center_top: [0.5, 0], - center_center: [0.5, 0.5], - center: [0.5, 0.5], - bottom_center: [0.5, 1], - center_bottom: [0.5, 1], - top_right: [1, 0], - right_top: [1, 0], - center_right: [1, 0.5], - right_center: [1, 0.5], - bottom_right: [1, 1], - right_bottom: [1, 1] - }; - - if (anchors.hasOwnProperty(value)) { - this.anchor_x = anchors[value][0]; - this.anchor_y = anchors[value][1]; - this.cacheOffsets(); - } - return this; - }; - - /** - * Save the object's dimensions - * @private - * @returns {this} The current function instance - */ - jaws.Text.prototype.cacheOffsets = function() { - - this.left_offset = this.width * this.anchor_x; - this.top_offset = this.height * this.anchor_y; - this.right_offset = this.width * (1.0 - this.anchor_x); - this.bottom_offset = this.height * (1.0 - this.anchor_y); - - if (this.cached_rect) - this.cached_rect.resizeTo(this.width, this.height); - return this; - }; - - /** - * Returns a jaws.Rect() perfectly surrouning text. - * @returns {jaws.Rect} - */ - jaws.Text.prototype.rect = function() { - if (!this.cached_rect && this.width) - this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height); - if (this.cached_rect) - this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset); - return this.cached_rect; - }; - - /** - * Draw sprite on active canvas or update its DOM-properties - * @this {jaws.Text} - * @returns {this} The current function instance - */ - jaws.Text.prototype.draw = function() { - this.context.save(); - if (this.angle !== 0) { - this.context.rotate(this.angle * Math.PI / 180); - } - this.context.globalAlpha = this.alpha; - this.context.translate(-this.left_offset, -this.top_offset); // Needs to be separate from above translate call cause of flipped - this.context.fillStyle = this.color; - this.context.font = this.style + " " + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = this.context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - this.context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - this.context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (this.context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (this.context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - this.context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - this.context.restore(); - return this; - }; - - /** - * Returns sprite as a canvas context. - * (For certain browsers, a canvas context is faster to work with then a pure image.) - * @public - * @this {jaws.Text} - */ - jaws.Text.prototype.asCanvasContext = function() { - var canvas = document.createElement("canvas"); - canvas.width = this.width; - canvas.height = this.height; - - var context = canvas.getContext("2d"); - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - this.context.fillStyle = this.color; - this.context.font = this.style + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = this.context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - this.context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - this.context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (this.context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (this.context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - this.context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - return context; - }; - - /** - * Returns text as a canvas - * @this {jaws.Text} - */ - jaws.Text.prototype.asCanvas = function() { - var canvas = document.createElement("canvas"); - canvas.width = this.width; - canvas.height = this.height; - - var context = canvas.getContext("2d"); - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - this.context.fillStyle = this.color; - this.context.font = this.style + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - return canvas; - }; - - /** - * Returns Text's properties as a String - * @returns {string} - */ - jaws.Text.prototype.toString = function() { - return "[Text " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]"; - }; - - /** - * Returns Text's properties as a pure object - * @returns {object} - */ - jaws.Text.prototype.attributes = function() { - var object = this.options; // Start with all creation time properties - object["_constructor"] = this._constructor || "jaws.Text"; - object["x"] = parseFloat(this.x.toFixed(2)); - object["y"] = parseFloat(this.y.toFixed(2)); - object["text"] = this.text; - object["alpha"] = this.alpha; - object["angle"] = parseFloat(this.angle.toFixed(2)); - object["anchor_x"] = this.anchor_x; - object["anchor_y"] = this.anchor_y; - object["style"] = this.style; - object["fontSize"] = this.fontSize; - object["fontFace"] = this.fontFace; - object["color"] = this.color; - object["textAlign"] = this.textAlign; - object["textBaseline"] = this.textBaseline; - object["wordWrap"] = this.wordWrap; - object["width"] = this.width; - object["height"] = this.height; - return object; - }; - - /** - * Returns a JSON-string representing the properties of the Text. - * @returns {string} - */ - jaws.Text.prototype.toJSON = function() { - return JSON.stringify(this.attributes()); - }; - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.Text; -} |
