diff --git a/Games/Contra-Shooter/Contra-Shooter.png b/Games/Contra-Shooter/Contra-Shooter.png
new file mode 100644
index 0000000000..dde2203b9c
Binary files /dev/null and b/Games/Contra-Shooter/Contra-Shooter.png differ
diff --git a/Games/Contra-Shooter/Gruntfile.js b/Games/Contra-Shooter/Gruntfile.js
new file mode 100644
index 0000000000..3c5b505722
--- /dev/null
+++ b/Games/Contra-Shooter/Gruntfile.js
@@ -0,0 +1,188 @@
+module.exports = function(grunt) {
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+
+ concat: {
+ dist: {
+ src: [
+ 'lib/melonJS.js',
+ 'lib/plugins/*.js',
+ 'js/game.js',
+ 'build/js/resources.js',
+ 'js/**/*.js',
+ ],
+ dest: 'build/js/app.js'
+ }
+ },
+
+ copy: {
+ dist: {
+ files: [{
+ src: 'index.css',
+ dest: 'build/index.css'
+ },{
+ src: 'main.js',
+ dest: 'build/main.js'
+ },{
+ src: 'manifest.json',
+ dest: 'build/manifest.json'
+ },{
+ src: 'package.json',
+ dest: 'build/package.json'
+ },{
+ src: 'data/**/*',
+ dest: 'build/',
+ expand: true
+ },{
+ src: 'icons/*',
+ dest: 'build/',
+ expand: true
+ }]
+ }
+ },
+
+ clean: {
+ app: ['build/js/app.js'],
+ dist: ['build/','bin/'],
+ },
+
+ processhtml: {
+ dist: {
+ options: {
+ process: true,
+ data: {
+ title: '<%= pkg.name %>',
+ }
+ },
+ files: {
+ 'build/index.html': ['index.html']
+ }
+ }
+ },
+
+ replace : {
+ dist : {
+ options : {
+ usePrefix : false,
+ force : true,
+ patterns : [
+ {
+ match : /this\._super\(\s*([\w\.]+)\s*,\s*["'](\w+)["']\s*(,\s*)?/g,
+ replacement : '$1.prototype.$2.apply(this$3'
+ },
+ ],
+ },
+ files : [
+ {
+ src : [ 'build/js/app.js' ],
+ dest : 'build/js/app.js'
+ }
+ ]
+ },
+ },
+
+ uglify: {
+ options: {
+ report: 'min',
+ preserveComments: 'some'
+ },
+ dist: {
+ files: {
+ 'build/js/app.min.js': [
+ 'build/js/app.js'
+ ]
+ }
+ }
+ },
+
+ connect: {
+ server: {
+ options: {
+ port: 3000,
+ keepalive: false
+ }
+ }
+ },
+
+ 'download-electron': {
+ version: '1.4.6',
+ outputDir: 'bin',
+ rebuild: false,
+ },
+
+ asar: {
+ dist: {
+ cwd: 'build',
+ src: ['**/*', '!js/app.js'],
+ expand: true,
+ dest: 'bin/' + (
+ process.platform === 'darwin'
+ ? 'Electron.app/Contents/Resources/'
+ : 'resources/'
+ ) + 'app.asar'
+ },
+ },
+
+ resources: {
+ dist: {
+ options: {
+ dest: 'build/js/resources.js',
+ varname: 'game.resources',
+ },
+ files: [{
+ src: ['data/bgm/**/*', 'data/sfx/**/*'],
+ type: 'audio'
+ },{
+ src: ['data/img/**/*.png'],
+ type: 'image'
+ },{
+ src: ['data/img/**/*.json'],
+ type: 'json'
+ },{
+ src: ['data/map/**/*.tmx', 'data/map/**/*.json'],
+ type: 'tmx'
+ },{
+ src: ['data/map/**/*.tsx'],
+ type: 'tsx'
+ }]
+ }
+ },
+
+ watch: {
+ resources: {
+ files: ['data/**/*'],
+ tasks: ['resources'],
+ options: {
+ spawn: false,
+ },
+ },
+ },
+
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-copy');
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-processhtml');
+ grunt.loadNpmTasks("grunt-replace");
+ grunt.loadNpmTasks('grunt-contrib-connect');
+ grunt.loadNpmTasks('grunt-download-electron');
+ grunt.loadNpmTasks('grunt-asar');
+
+ // Custom Tasks
+ grunt.loadTasks('tasks');
+
+ grunt.registerTask('default', [
+ 'resources',
+ 'concat',
+ 'replace',
+ 'uglify',
+ 'copy',
+ 'processhtml',
+ 'clean:app',
+ ]);
+ grunt.registerTask('dist', ['default', 'download-electron', 'asar']);
+ grunt.registerTask('serve', ['resources', 'connect', 'watch']);
+}
diff --git a/Games/Contra-Shooter/README.md b/Games/Contra-Shooter/README.md
new file mode 100644
index 0000000000..1f750ac09a
--- /dev/null
+++ b/Games/Contra-Shooter/README.md
@@ -0,0 +1,45 @@
+# **Contra-Shooter**
+
+---
+
+
+
+## **Description đ**
+
+- Contra is an action-packed run-and-gun shooter game where players battle through various levels filled with enemies and obstacles. The game offers a mix of intense combat and strategic gameplay, challenging players to use their skills and reflexes to overcome each stage.
+
+## **Functionalities đŽ**
+
+- Single-player and co-op multiplayer modes.
+- Various weapons and power-ups.
+- Multiple levels with unique environments and enemies.
+- Boss battles at the end of each level.
+- Save and load game progress.
+- Customizable controls and settings.
+
+
+
+## **How to play? đšī¸**
+
+- Start the game and choose your mode (single-player or co-op multiplayer).
+- Use the arrow keys or joystick to move your character.
+- Press the shoot button to fire your weapon at enemies.
+- Collect power-ups and weapons to enhance your abilities.
+- Navigate through levels, avoid or destroy obstacles, and defeat enemies.
+- Face the boss at the end of each level and defeat it to progress.
+- Use the pause menu to access settings, save/load game, and more.
+
+
+
+## **Screenshots đ¸**
+
+
+
+
+![Contra-Shooter](https://github.com/VesperAkshay/GameZone/assets/118452811/f0d2261f-33ee-488d-8fb9-d320a334d708)
+
+
+
+
+## **Working video đš**
+
diff --git a/Games/Contra-Shooter/build/js/resources.js b/Games/Contra-Shooter/build/js/resources.js
new file mode 100644
index 0000000000..3684b93d62
--- /dev/null
+++ b/Games/Contra-Shooter/build/js/resources.js
@@ -0,0 +1,107 @@
+game.resources = [
+ {
+ "name": "dst-inertexponent",
+ "type": "audio",
+ "src": "data/bgm/"
+ },
+ {
+ "name": "cling",
+ "type": "audio",
+ "src": "data/sfx/"
+ },
+ {
+ "name": "jump",
+ "type": "audio",
+ "src": "data/sfx/"
+ },
+ {
+ "name": "stomp",
+ "type": "audio",
+ "src": "data/sfx/"
+ },
+ {
+ "name": "icon_badge_24_48",
+ "type": "image",
+ "src": "data/img/gui/icon_badge_24_48.png"
+ },
+ {
+ "name": "icon_select_30_25",
+ "type": "image",
+ "src": "data/img/gui/icon_select_30_25.png"
+ },
+ {
+ "name": "img_captured",
+ "type": "image",
+ "src": "data/img/gui/img_captured.png"
+ },
+ {
+ "name": "img_neslogo",
+ "type": "image",
+ "src": "data/img/gui/img_neslogo.png"
+ },
+ {
+ "name": "img_splashpage",
+ "type": "image",
+ "src": "data/img/gui/img_splashpage.png"
+ },
+ {
+ "name": "contra-tile-map",
+ "type": "image",
+ "src": "data/img/map/contra-tile-map.png"
+ },
+ {
+ "name": "bad-guys",
+ "type": "image",
+ "src": "data/img/sprite/bad-guys.png"
+ },
+ {
+ "name": "bill-lance",
+ "type": "image",
+ "src": "data/img/sprite/bill-lance.png"
+ },
+ {
+ "name": "explosion-sprite",
+ "type": "image",
+ "src": "data/img/sprite/explosion-sprite.png"
+ },
+ {
+ "name": "power-ups-sprite",
+ "type": "image",
+ "src": "data/img/sprite/power-ups-sprite.png"
+ },
+ {
+ "name": "rock_platform-sprite",
+ "type": "image",
+ "src": "data/img/sprite/rock_platform-sprite.png"
+ },
+ {
+ "name": "bad-guys",
+ "type": "json",
+ "src": "data/img/sprite/bad-guys.json"
+ },
+ {
+ "name": "bill-lance",
+ "type": "json",
+ "src": "data/img/sprite/bill-lance.json"
+ },
+ {
+ "name": "contra-lvl-0",
+ "type": "tmx",
+ "src": "data/map/contra-lvl-0.tmx"
+ },
+ {
+ "name": "contra-lvl-1",
+ "type": "tmx",
+ "src": "data/map/contra-lvl-1.tmx"
+ },
+ {
+ "name": "contra-tileset-1",
+ "type": "tmx",
+ "src": "data/map/contra-tileset-1.tmx"
+ },
+ {
+ "name": "contra-tileset-2",
+ "type": "tmx",
+ "src": "data/map/contra-tileset-2.tmx"
+ }
+];
\ No newline at end of file
diff --git a/Games/Contra-Shooter/data/bgm/.placeholder b/Games/Contra-Shooter/data/bgm/.placeholder
new file mode 100644
index 0000000000..60fc579a73
--- /dev/null
+++ b/Games/Contra-Shooter/data/bgm/.placeholder
@@ -0,0 +1,2 @@
+docs will be placed here when built
+
diff --git a/Games/Contra-Shooter/data/bgm/dst-inertexponent.mp3 b/Games/Contra-Shooter/data/bgm/dst-inertexponent.mp3
new file mode 100644
index 0000000000..3a3361c0cd
Binary files /dev/null and b/Games/Contra-Shooter/data/bgm/dst-inertexponent.mp3 differ
diff --git a/Games/Contra-Shooter/data/bgm/dst-inertexponent.ogg b/Games/Contra-Shooter/data/bgm/dst-inertexponent.ogg
new file mode 100644
index 0000000000..8310b52729
Binary files /dev/null and b/Games/Contra-Shooter/data/bgm/dst-inertexponent.ogg differ
diff --git a/Games/Contra-Shooter/data/fnt/LICENSE.txt b/Games/Contra-Shooter/data/fnt/LICENSE.txt
new file mode 100644
index 0000000000..141e49f4d0
--- /dev/null
+++ b/Games/Contra-Shooter/data/fnt/LICENSE.txt
@@ -0,0 +1,94 @@
+Copyright (c) 2011, Cody "CodeMan38" Boisclair (cody@zone38.net),
+with Reserved Font Name "Press Start".
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/Games/Contra-Shooter/data/fnt/PressStart2P.fnt b/Games/Contra-Shooter/data/fnt/PressStart2P.fnt
new file mode 100644
index 0000000000..e37ac831bd
--- /dev/null
+++ b/Games/Contra-Shooter/data/fnt/PressStart2P.fnt
@@ -0,0 +1,100 @@
+info face=PressStart2P size=24 bold=0 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=0,0 outline=0
+common lineHeight=24 base=24 scaleW=256 scaleH=512 pages=1 packed=0
+page id=0 file="PressStart2P.png"
+chars count=95
+char id=32 x=1 y=1 width=0 height=0 xoffset=0 yoffset=24 xadvance=22 page=0 chnl=15
+char id=33 x=1 y=2 width=9 height=21 xoffset=6 yoffset=0 xadvance=22 page=0 chnl=15
+char id=34 x=1 y=24 width=15 height=9 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=35 x=11 y=1 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=36 x=1 y=34 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=37 x=1 y=56 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=38 x=1 y=78 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=39 x=17 y=23 width=6 height=9 xoffset=6 yoffset=0 xadvance=22 page=0 chnl=15
+char id=40 x=1 y=100 width=12 height=21 xoffset=6 yoffset=0 xadvance=22 page=0 chnl=15
+char id=41 x=1 y=122 width=12 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=42 x=1 y=144 width=21 height=15 xoffset=0 yoffset=3 xadvance=22 page=0 chnl=15
+char id=43 x=14 y=100 width=18 height=15 xoffset=3 yoffset=3 xadvance=22 page=0 chnl=15
+char id=44 x=14 y=116 width=9 height=9 xoffset=3 yoffset=15 xadvance=22 page=0 chnl=15
+char id=45 x=14 y=126 width=18 height=3 xoffset=3 yoffset=9 xadvance=22 page=0 chnl=15
+char id=46 x=24 y=116 width=6 height=6 xoffset=6 yoffset=15 xadvance=22 page=0 chnl=15
+char id=47 x=23 y=33 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=48 x=33 y=1 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=49 x=23 y=55 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=50 x=23 y=77 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=51 x=42 y=55 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=52 x=45 y=23 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=53 x=55 y=1 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=54 x=1 y=160 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=55 x=1 y=182 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=56 x=1 y=204 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=57 x=1 y=226 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=58 x=1 y=248 width=6 height=15 xoffset=6 yoffset=3 xadvance=22 page=0 chnl=15
+char id=59 x=1 y=264 width=9 height=18 xoffset=3 yoffset=3 xadvance=22 page=0 chnl=15
+char id=60 x=1 y=283 width=15 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=61 x=45 y=45 width=21 height=9 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=62 x=11 y=248 width=15 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=63 x=23 y=130 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=64 x=33 y=99 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=65 x=45 y=77 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=66 x=64 y=55 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=67 x=67 y=23 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=68 x=77 y=1 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=69 x=23 y=152 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=70 x=23 y=174 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=71 x=23 y=196 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=72 x=23 y=218 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=73 x=1 y=305 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=74 x=17 y=270 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=75 x=27 y=240 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=76 x=45 y=121 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=77 x=55 y=99 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=78 x=67 y=77 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=79 x=86 y=45 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=80 x=89 y=23 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=81 x=99 y=1 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=82 x=45 y=143 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=83 x=64 y=121 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=84 x=77 y=99 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=85 x=89 y=67 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=86 x=108 y=45 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=87 x=111 y=23 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=88 x=121 y=1 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=89 x=45 y=165 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=90 x=45 y=187 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=91 x=64 y=165 width=12 height=21 xoffset=6 yoffset=0 xadvance=22 page=0 chnl=15
+char id=92 x=67 y=143 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=93 x=86 y=121 width=12 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=94 x=27 y=262 width=15 height=6 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=95 x=89 y=89 width=21 height=3 xoffset=0 yoffset=21 xadvance=22 page=0 chnl=15
+char id=96 x=33 y=121 width=6 height=6 xoffset=9 yoffset=0 xadvance=22 page=0 chnl=15
+char id=97 x=96 y=93 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=98 x=111 y=67 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=99 x=130 y=45 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=100 x=133 y=23 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=101 x=143 y=1 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=102 x=45 y=209 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=103 x=1 y=327 width=21 height=18 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=104 x=20 y=292 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=105 x=39 y=269 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=106 x=49 y=231 width=15 height=24 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=107 x=64 y=209 width=21 height=21 xoffset=0 yoffset=0 xadvance=22 page=0 chnl=15
+char id=108 x=67 y=187 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=109 x=77 y=165 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=110 x=89 y=143 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=111 x=99 y=109 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=112 x=118 y=89 width=21 height=18 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=113 x=133 y=61 width=21 height=18 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=114 x=152 y=45 width=18 height=15 xoffset=3 yoffset=6 xadvance=22 page=0 chnl=15
+char id=115 x=99 y=125 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=116 x=155 y=17 width=18 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=117 x=165 y=1 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=118 x=1 y=346 width=18 height=15 xoffset=3 yoffset=6 xadvance=22 page=0 chnl=15
+char id=119 x=1 y=362 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=120 x=20 y=346 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=121 x=23 y=314 width=21 height=18 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=122 x=42 y=291 width=21 height=15 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=123 x=58 y=256 width=12 height=21 xoffset=6 yoffset=0 xadvance=22 page=0 chnl=15
+char id=124 x=65 y=231 width=6 height=21 xoffset=9 yoffset=0 xadvance=22 page=0 chnl=15
+char id=125 x=1 y=378 width=12 height=21 xoffset=3 yoffset=0 xadvance=22 page=0 chnl=15
+char id=126 x=58 y=278 width=21 height=9 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
+char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=6 xadvance=22 page=0 chnl=15
\ No newline at end of file
diff --git a/Games/Contra-Shooter/data/fnt/PressStart2P.ltr b/Games/Contra-Shooter/data/fnt/PressStart2P.ltr
new file mode 100644
index 0000000000..78c4f30213
--- /dev/null
+++ b/Games/Contra-Shooter/data/fnt/PressStart2P.ltr
@@ -0,0 +1 @@
+{"settings":{"scalings":"1;","filename":"PressStart2P","postfixes":""},"glyphs":{"canvasWidth":"Auto","roundValues":true,"glyphs":" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~","powerOfTwo":true,"packMethod":1,"padding":1,"canvasHeight":"Auto","descriptionFormat":1},"font":{"spacing":-2,"data":"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OwEdATsBBTUrAR0BKQE9ASsBgAEAAQBAQEBAgIBAQP7A/sBAQEBAQEBAQAIAwMBAQICA/wCAgAEAAQCAgAPAQEBAgIBAQICAQEBAQICAQECAgICAQEBAQMBAgIBAQAAAAAIAAACAA4AEAAAnAC8AABM1KQEdATsBGQErAR0BKwEdASkBPQE7AT0BOwE9ASkBPQErAT0BOwEFNSsBHQE7AYABQAFAQEBAQEBA/wD/AMDAQED/AP8AQEBAQAIAwMDAwAPAQEBA/wD/AEBAQEBAQEBAQEBAQICAgICAgAACAQABAAIAA4AABwAPAAABNTsBHQErARE1OwEdASsBAQCAgICAgICAgAGAgICAAgCAgIAAAgCAAIACAAOAAA8AFwAAATU7AR0BKwEdASsBPQE7ARE1OwEdASsBAQCAgEBAgIBAQICAgIABgICAgEBAQEACAICAgAABAIAAgAMABAAANwAAATU7AR0BKwEdASsBHQErAR0BOwEdATsBHQE7AR0BKwE9ASsBPQErAT0BKwE9ATsBPQE7AT0BOwECAICAQEBAQEBAQEBAQEBAgIBAQEBAQEBAQEBAQEADwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAACAAABgAOAAwAABwAPAAARNSkBHQEpARE1KQEdASkBAcABwP5A/kABwAHA/kD+QAHAQEBAAUBAQEAAAAABAIAAgAMABAAANwAAEzU7AR0BOwEdATsBHQE7AR0BKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BOwE9ASsBPQErAT0BKwGAgIBAQEBAQEBAQEBAQECAgEBAQEBAQEBAQEBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAIAAACAA4AEAAAHAC8AACU1OwEdASsBAzUpAR0BOwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwEdASsBPQE7AQEAwMDAwIABQAFAQEBAQEBAwMCAgEBAwMCAgEBAwEBAQANAQEBAgIBAQEBAQEBAQEBAQECAgAAAAAIAAACAA4AEAAAnAC8AABM1KQEdATsBGQEpAT0BOwEdATsBPQEpARkBKQEdASkBPQErARkBOwEBNSsBHQE7AYABQAFAQED+wP7AwMBAQP7A/sABQAFA/sD+wEBAQEABgEBAQEADwEBAQP8A/wDAwICAwMD+wP7A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KwE9ATsBAYCAgEBA/wD/AEBAQEBAQAPAQMDAQEBAQEBAQEAAAAIAgAIAAwAEAAAXAB8AAAE1OwEdATsBHQErAR0BKwE9ASsBPQE7AQU1KwEdATsBAQDAwEBAQEDAwEBAQEABAEBAQEADwEBAQICAQEBAQICAgICAgAAAAAACAAABAAOAA4AANwBPAAARNTsBHQE7AT0BOwEdATsBHQE7AR0BKwEdASsBHQErAT0BKwEdASsBPQE7AT0BOwE9ASsBPQErAQU1KwEdATsBHQErAR0BOwE9ATsBPQErAYCAQECAgEBAQEBAQEBAgIBAQICAQEBAQEBAQEACAEBAQEBAQEBAQEBAQANAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAACAAAAgAOABAAAEwBbAAABNTsBGQErAT0BKwE9ATsBPQE7AQE1OwEZATsBPQE7AT0BOwE9ATsBPQE7AR0BKwEdASsBHQErAR0BKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwE9ASsBPQE7AQMAQEBAQICAQEBAQP2AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAkBA/wD/AEBAQEBAQAHAQP8A/wBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAgIBAQAAAAAEAAACAA4AEAABjAAATNTsBGQE7AT0BOwE9ATsBPQE7AT0BOwEdASsBHQErAR0BOwEdASsBHQE7AR0BKwE9ATsBPQE7AT0BKwE9ASsBHQErAR0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwE9ATsBgEBAQEBAQEBAQEBAQEBAQECAgEBAQEDAwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAA8BA/wD/AEBAQEBAQEBAQEBAQEBAgIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQEAAAAADAAAAgAOABAAAEwBjAGsAAAE1OwEZASsBPQErAT0BOwE9ATsBATU7AR0BKwEdATsBHQE7AT0BOwE9ATsBPQE7AR0BKwEdASsBHQErAR0BKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwE9ATsBPQErAT0BKwEBNSsBHQE7AQMAQEBAQICAQEBAQP0AwMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA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AQDAwEBAQEBAQEBAgICAgEBAQEDAwICAQEBAQAEAQEBAQMDAQEADwEBAQEBAwMBAQEBAQEBAQEBAgIBAQMDAQEBAQEBAQEBAQAAAAAACAAAAAAOABAAAMwA7AAATNSkBHQE7ARkBKwEdASsBHQE7AR0BKwE9ASsBPQE7AT0BKwE9ASsBPQE7AT0BKQE9ASkBATUrAR0BOwGAAUABQEBAwMBAQICAgIBAQEBAgIBAQEBAAQABAP8A/wACAMDAwMADwEBAQP8A/wBAQEBAQEBAQEBAQEBAQEBAQEBAQP7AQEBAAAAAAAEAAACAA4AEAABHAAABNTsBHQErAR0BOwEdATsBHQErAT0BKwEdASsBHQE7AR0BOwE9ATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBPQE7AT0BOwECAEBAQECAgEBAgICAgEBAQECAgICAQED/AP8AQEBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAQAAAIADgAQAACcAAAE1OwEdASsBHQE7AR0BKQEdASkBHQEpAT0BKwE9ATsBPQE7AT0BOwECAEBAQEDAwP7A/sABQAFA/oD+gEBAQECAgEBAA8BAQEBAQEBAwMBAQEBAwMBAQEBAAAACAAAAgAOABAAAQwBLAAABNTsBHQE7AR0BOwEdASsBPQErAR0BKwEdATsBHQE7AT0BOwEdASsBHQEpAT0BKwE9ASsBPQE7AT0BOwE9ASsBPQE7AQU1KwEdATsBAQDAwEBAQECAgICAQEBAQICAgIBAQP8A/wBAQEBAQEBAQEBAQEABAEBAQEADwEBAQICAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAIAAACAA4AEAAAjACsAAAE1OwEdATsBHQE7AR0BKQEdASkBHQEpAT0BKwE9ATsBPQE7AQU1KwEdATsBAQDAwEBAQED+wP7AAUABQP6A/oBAQEBAQEABAEBAQEADwEBAQEBAQEDAwEBAQEDAwICAQEBAQAAAAgAAAIADgAQAADcAPwAAATUpAR0BOwEdASsBPQErAR0BKwEdATsBHQE7AT0BOwEdASsBHQEpAT0BKwE9ASsBPQE7AT0BOwETNTsBHQErAQEAAQABAEBAgICAgEBAQECAgICAQED/AP8AQEBAQEBA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QEBAQEBAQEBAQEBAQEBAQAABAAAAgAOABAAANwAAATU7AR0BOwEdASsBHQErAR0BOwEdASsBPQErAT0BKwEdASsBHQErAT0BOwE9ASsBPQErAT0BOwEBgEBAwMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAwMADgICAgEBAQEBAQICAQEBAQEBAQECAgEBAQEBAQAAAAAACAAAAgAOABAAALwBHAAABNTsBHQE7AR0BKwEdASsBHQE7AR0BKwE9ASsBHQErAT0BOwE9ASsBPQErAT0BOwEXNSsBHQErAR0BOwEdATsBPQE7AT0BKwEBgEBAwMBAQEBAQEBAQMDAQEBAQEBAQEDAwIBAQEBAQEBAQEBAQEADgICAgEBAQEBAQICAQEBAQICAQEBAQEBAQEBAQEBAQEBAQEBAAAEAAACAA4AEAAAvAAABNTsBHQE7AR0BOwEdATsBHQErAR0BOwEdASkBPQE7AT0BKwE9ATsBPQE7AT0BOwEBgEBAQEBAQEBAgIBAQP7A/sBAQICAQEBAQEBAA8BAQEBAQEBAgIBAQEBAQEBAQICAQEBAQAAAAgCAAIADgAQAAB8AJwAAATU7AR0BOwEdASsBHQE7AR0BKQE9ATsBPQErAT0BOwEFNSsBHQE7AQGAgICAgICAQED/AP8AQECAgICAAQCAgICAA4CAgICAgICAQEBAQICAgICAgICAAAABAAAAgAOABAAALwAAEzU7AR0BOwE9ATsBHQE7AR0BKwEdASsBHQErAR0BKwE9ASsBPQErAT0BKwE9ATsBgICAQECAgEBAQEBAQEBAQEBAQEBAQEBAQAPAQEBAQEBAQMDAQEBAQEBAQEBAQEBAwMAAAQAAAIADgAQAADcAAAE1OwEdATsBHQE7AR0BOwEdASsBHQErAR0BKwEdASsBPQErAT0BKwE9ASsBPQE7AT0BOwE9ATsBAYBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAQAAAIADgAQAADcAAAEROwEdATsBHQE7AR0BOwEdASsBHQErAT0BOwE9ASsBPQErARkBKwEdASsBPQErAT0BOwE9ATsBAYBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQEBAQICAAwABAEBAQEBAQEBAQEBAQEBAQED/AP8AQEBAQEBAQEAAAwCAAIADgAQAAAcAFwAfAAATESkBGQEpAQE1KQEdATsBHQErAR0BOwEFNSsBHQE7AYABgAGA/oD+gAKA/wD/AMDAgIDAwP8AQEBAQAJAAcD+QP5AAkDAQEBAQEBAwEBAQAAAAAEAgACAAwABgAALAAABNTsBHQEpAT0BOwEBgMDA/sD+wICAAUBAgIBAQAAAAwAAAIAEAAQAAC8ANwBHAAABNTsBHQE7AR0BOwEdASsBHQErAR0BKwE9ASsBHQErAT0BKwE9ASsBPQE7AT0BOwEXNSsBHQE7AQU1KwEdASsBHQE7AT0BOwECAEBAgIBAQEBAQECAgEBAQEBAQEBAQEDAwIBAQEBAAQBAQEBAQEBAQAPAQEBAQEDAwEBAQEBAQEBAQEBAQMDAQEBAQEBAgICAgEBAQEAAAQAAAIADgAQAACMAAAE1OwEdASsBHQEpARkBKwEZASsBGQErARkBKwE9ATsBPQE7AQEAwMCAgAEAAQCAgICAgIBAQEBAQEADwEBAQEBA/sD+wAEAAQD/AP8AAQABAEBAQEAAAAIAAACAA4AEAAAbACMAAAE1KQEZASsBGQErARkBKwEZASsBPQE7AT0BOwEFNSsBHQE7AQEAAUABQICAgICAgEBAQEBAQAGAgICAgAPAQP5A/kABAAEA/wD/AAEAAQBAQEBAQEBAQAAAAAASAN4AAQAAAAAAAABOAJ4AAQAAAAAAAQAOAQsAAQAAAAAAAgAHASoAAQAAAAAAAwAqAYgAAQAAAAAABAAOAdEAAQAAAAAABQANAfwAAQAAAAAABgAMAiQAAQAAAAAADRE1JJ0AAQAAAAAADgAaNgkAAwABBAkAAACcAAAAAwABBAkAAQAcAO0AAwABBAkAAgAOARoAAwABBAkAAwBUATIAAwABBAkABAAcAbMAAwABBAkABQAaAeAAAwABBAkABgAYAgoAAwABBAkADSJqAjEAAwABBAkADgA0NdMAKABjACkAIAAyADAAMQAxACAAQwBvAGQAeQAgACIAQwBvAGQAZQBNAGEAbgAzADgAIgAgAEIAbwBpAHMAYwBsAGEAaQByAC4AIABSAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAFMASQBMACAATwBwAGUAbgAgAEYAbwBuAHQAIABMAGkAYwBlAG4AcwBlAC4AAChjKSAyMDExIENvZHkgIkNvZGVNYW4zOCIgQm9pc2NsYWlyLiBSZWxlYXNlZCB1bmRlciB0aGUgU0lMIE9wZW4gRm9udCBMaWNlbnNlLgAAUAByAGUAcwBzACAAUwB0AGEAcgB0ACAAMgBQAABQcmVzcyBTdGFydCAyUAAAUgBlAGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAAyAC4AMAAgADoAIABQAHIAZQBzAHMAIABTAHQAYQByAHQAIAAyAFAAIAA6ACAAMQAyAC0ANgAtADIAMAAxADEAAEZvbnRGb3JnZSAyLjAgOiBQcmVzcyBTdGFydCAyUCA6IDEyLTYtMjAxMQAAUAByAGUAcwBzACAAUwB0AGEAcgB0ACAAMgBQAABQcmVzcyBTdGFydCAyUAAAVgBlAHIAcwBpAG8AbgAgADIALgAxADQAIAAAVmVyc2lvbiAyLjE0IAAAUAByAGUAcwBzAFMAdABhAHIAdAAyAFAAAFByZXNzU3RhcnQyUAAAQwBvAHAAeQByAGkAZwBoAHQAIAAoAGMAKQAgADIAMAAxADEALAAgAEMAbwBkAHkAIAAiAEMAbwBkAGUATQBhAG4AMwA4ACIAIABCAG8AaQBzAGMAbABhAGkAcgAgACgAYwBvAGQAeQBAAHoAbwBuAGUAMwA4AC4AbgBlAHQAKQAsAAoAdwBpAHQAaAAgAFIAZQBzAGUAcgB2AGUAZAAgAEYAbwBuAHQAIABOAGEAbQBlACAAUAByAGUAcwBzACAAUwB0AGEAcgB0AC4ACgAKAFQAaABpAHMAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAaQBzACAAbABpAGMAZQBuAHMAZQBkACAAdQBuAGQAZQByACAAdABoAGUAIABTAEkATAAgAE8AcABlAG4AIABGAG8AbgB0ACAATABpAGMAZQBuAHMAZQAsACAAVgBlAHIAcwBpAG8AbgAgADEALgAxAC4ACgBUAGgAaQBzACAAbABpAGMAZQBuAHMAZQAgAGkAcwAgAGMAbwBwAGkAZQBkACAAYgBlAGwAbwB3ACwAIABhAG4AZAAgAGkAcwAgAGEAbABzAG8AIABhAHYAYQBpAGwAYQBiAGwAZQAgAHcAaQB0AGgAIABhACAARgBBAFEAIABhAHQAOgAKAGgAdAB0AHAAOgAvAC8AcwBjAHIAaQBwAHQAcwAuAHMAaQBsAC4AbwByAGcALwBPAEYATAAKAAoACgAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ACgBTAEkATAAgAE8AUABFAE4AIABGAE8ATgBUACAATABJAEMARQBOAFMARQAgAFYAZQByAHMAaQBvAG4AIAAxAC4AMQAgAC0AIAAyADYAIABGAGUAYgByAHUAYQByAHkAIAAyADAAMAA3AAoALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAAoACgBQAFIARQBBAE0AQgBMAEUACgBUAGgAZQAgAGcAbwBhAGwAcwAgAG8AZgAgAHQAaABlACAATwBwAGUAbgAgAEYAbwBuAHQAIABMAGkAYwBlAG4AcwBlACAAKABPAEYATAApACAAYQByAGUAIAB0AG8AIABzAHQAaQBtAHUAbABhAHQAZQAgAHcAbwByAGwAZAB3AGkAZABlAAoAZABlAHYAZQBsAG8AcABtAGUAbgB0ACAAbwBmACAAYwBvAGwAbABhAGIAbwByAGEAdABpAHYAZQAgAGYAbwBuAHQAIABwAHIAbwBqAGUAYwB0AHMALAAgAHQAbwAgAHMAdQBwAHAAbwByAHQAIAB0AGgAZQAgAGYAbwBuAHQAIABjAHIAZQBhAHQAaQBvAG4ACgBlAGYAZgBvAHIAdABzACAAbwBmACAAYQBjAGEAZABlAG0AaQBjACAAYQBuAGQAIABsAGkAbgBnAHUAaQBzAHQAaQBjACAAYwBvAG0AbQB1AG4AaQB0AGkAZQBzACwAIABhAG4AZAAgAHQAbwAgAHAAcgBvAHYAaQBkAGUAIABhACAAZgByAGUAZQAgAGEAbgBkAAoAbwBwAGUAbgAgAGYAcgBhAG0AZQB3AG8AcgBrACAAaQBuACAAdwBoAGkAYwBoACAAZgBvAG4AdABzACAAbQBhAHkAIABiAGUAIABzAGgAYQByAGUAZAAgAGEAbgBkACAAaQBtAHAAcgBvAHYAZQBkACAAaQBuACAAcABhAHIAdABuAGUAcgBzAGgAaQBwAAoAdwBpAHQAaAAgAG8AdABoAGUAcgBzAC4ACgAKAFQAaABlACAATwBGAEwAIABhAGwAbABvAHcAcwAgAHQAaABlACAAbABpAGMAZQBuAHMAZQBkACAAZgBvAG4AdABzACAAdABvACAAYgBlACAAdQBzAGUAZAAsACAAcwB0AHUAZABpAGUAZAAsACAAbQBvAGQAaQBmAGkAZQBkACAAYQBuAGQACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAZgByAGUAZQBsAHkAIABhAHMAIABsAG8AbgBnACAAYQBzACAAdABoAGUAeQAgAGEAcgBlACAAbgBvAHQAIABzAG8AbABkACAAYgB5ACAAdABoAGUAbQBzAGUAbAB2AGUAcwAuACAAVABoAGUACgBmAG8AbgB0AHMALAAgAGkAbgBjAGwAdQBkAGkAbgBnACAAYQBuAHkAIABkAGUAcgBpAHYAYQB0AGkAdgBlACAAdwBvAHIAawBzACwAIABjAGEAbgAgAGIAZQAgAGIAdQBuAGQAbABlAGQALAAgAGUAbQBiAGUAZABkAGUAZAAsACAACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAYQBuAGQALwBvAHIAIABzAG8AbABkACAAdwBpAHQAaAAgAGEAbgB5ACAAcwBvAGYAdAB3AGEAcgBlACAAcAByAG8AdgBpAGQAZQBkACAAdABoAGEAdAAgAGEAbgB5ACAAcgBlAHMAZQByAHYAZQBkAAoAbgBhAG0AZQBzACAAYQByAGUAIABuAG8AdAAgAHUAcwBlAGQAIABiAHkAIABkAGUAcgBpAHYAYQB0AGkAdgBlACAAdwBvAHIAawBzAC4AIABUAGgAZQAgAGYAbwBuAHQAcwAgAGEAbgBkACAAZABlAHIAaQB2AGEAdABpAHYAZQBzACwACgBoAG8AdwBlAHYAZQByACwAIABjAGEAbgBuAG8AdAAgAGIAZQAgAHIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAGEAbgB5ACAAbwB0AGgAZQByACAAdAB5AHAAZQAgAG8AZgAgAGwAaQBjAGUAbgBzAGUALgAgAFQAaABlAAoAcgBlAHEAdQBpAHIAZQBtAGUAbgB0ACAAZgBvAHIAIABmAG8AbgB0AHMAIAB0AG8AIAByAGUAbQBhAGkAbgAgAHUAbgBkAGUAcgAgAHQAaABpAHMAIABsAGkAYwBlAG4AcwBlACAAZABvAGUAcwAgAG4AbwB0ACAAYQBwAHAAbAB5AAoAdABvACAAYQBuAHkAIABkAG8AYwB1AG0AZQBuAHQAIABjAHIAZQBhAHQAZQBkACAAdQBzAGkAbgBnACAAdABoAGUAIABmAG8AbgB0AHMAIABvAHIAIAB0AGgAZQBpAHIAIABkAGUAcgBpAHYAYQB0AGkAdgBlAHMALgAKAAoARABFAEYASQBOAEkAVABJAE8ATgBTAAoAIgBGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACIAIAByAGUAZgBlAHIAcwAgAHQAbwAgAHQAaABlACAAcwBlAHQAIABvAGYAIABmAGkAbABlAHMAIAByAGUAbABlAGEAcwBlAGQAIABiAHkAIAB0AGgAZQAgAEMAbwBwAHkAcgBpAGcAaAB0AAoASABvAGwAZABlAHIAKABzACkAIAB1AG4AZABlAHIAIAB0AGgAaQBzACAAbABpAGMAZQBuAHMAZQAgAGEAbgBkACAAYwBsAGUAYQByAGwAeQAgAG0AYQByAGsAZQBkACAAYQBzACAAcwB1AGMAaAAuACAAVABoAGkAcwAgAG0AYQB5AAoAaQBuAGMAbAB1AGQAZQAgAHMAbwB1AHIAYwBlACAAZgBpAGwAZQBzACwAIABiAHUAaQBsAGQAIABzAGMAcgBpAHAAdABzACAAYQBuAGQAIABkAG8AYwB1AG0AZQBuAHQAYQB0AGkAbwBuAC4ACgAKACIAUgBlAHMAZQByAHYAZQBkACAARgBvAG4AdAAgAE4AYQBtAGUAIgAgAHIAZQBmAGUAcgBzACAAdABvACAAYQBuAHkAIABuAGEAbQBlAHMAIABzAHAAZQBjAGkAZgBpAGUAZAAgAGEAcwAgAHMAdQBjAGgAIABhAGYAdABlAHIAIAB0AGgAZQAKAGMAbwBwAHkAcgBpAGcAaAB0ACAAcwB0AGEAdABlAG0AZQBuAHQAKABzACkALgAKAAoAIgBPAHIAaQBnAGkAbgBhAGwAIABWAGUAcgBzAGkAbwBuACIAIAByAGUAZgBlAHIAcwAgAHQAbwAgAHQAaABlACAAYwBvAGwAbABlAGMAdABpAG8AbgAgAG8AZgAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUAIABjAG8AbQBwAG8AbgBlAG4AdABzACAAYQBzAAoAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAYgB5ACAAdABoAGUAIABDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByACgAcwApAC4ACgAKACIATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgAiACAAcgBlAGYAZQByAHMAIAB0AG8AIABhAG4AeQAgAGQAZQByAGkAdgBhAHQAaQB2AGUAIABtAGEAZABlACAAYgB5ACAAYQBkAGQAaQBuAGcAIAB0AG8ALAAgAGQAZQBsAGUAdABpAG4AZwAsAAoAbwByACAAcwB1AGIAcwB0AGkAdAB1AHQAaQBuAGcAIAAtAC0AIABpAG4AIABwAGEAcgB0ACAAbwByACAAaQBuACAAdwBoAG8AbABlACAALQAtACAAYQBuAHkAIABvAGYAIAB0AGgAZQAgAGMAbwBtAHAAbwBuAGUAbgB0AHMAIABvAGYAIAB0AGgAZQAKAE8AcgBpAGcAaQBuAGEAbAAgAFYAZQByAHMAaQBvAG4ALAAgAGIAeQAgAGMAaABhAG4AZwBpAG4AZwAgAGYAbwByAG0AYQB0AHMAIABvAHIAIABiAHkAIABwAG8AcgB0AGkAbgBnACAAdABoAGUAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAdABvACAAYQAKAG4AZQB3ACAAZQBuAHYAaQByAG8AbgBtAGUAbgB0AC4ACgAKACIAQQB1AHQAaABvAHIAIgAgAHIAZQBmAGUAcgBzACAAdABvACAAYQBuAHkAIABkAGUAcwBpAGcAbgBlAHIALAAgAGUAbgBnAGkAbgBlAGUAcgAsACAAcAByAG8AZwByAGEAbQBtAGUAcgAsACAAdABlAGMAaABuAGkAYwBhAGwACgB3AHIAaQB0AGUAcgAgAG8AcgAgAG8AdABoAGUAcgAgAHAAZQByAHMAbwBuACAAdwBoAG8AIABjAG8AbgB0AHIAaQBiAHUAdABlAGQAIAB0AG8AIAB0AGgAZQAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUALgAKAAoAUABFAFIATQBJAFMAUwBJAE8ATgAgACYAIABDAE8ATgBEAEkAVABJAE8ATgBTAAoAUABlAHIAbQBpAHMAcwBpAG8AbgAgAGkAcwAgAGgAZQByAGUAYgB5ACAAZwByAGEAbgB0AGUAZAAsACAAZgByAGUAZQAgAG8AZgAgAGMAaABhAHIAZwBlACwAIAB0AG8AIABhAG4AeQAgAHAAZQByAHMAbwBuACAAbwBiAHQAYQBpAG4AaQBuAGcACgBhACAAYwBvAHAAeQAgAG8AZgAgAHQAaABlACAARgBvAG4AdAAgAFMAbwBmAHQAdwBhAHIAZQAsACAAdABvACAAdQBzAGUALAAgAHMAdAB1AGQAeQAsACAAYwBvAHAAeQAsACAAbQBlAHIAZwBlACwAIABlAG0AYgBlAGQALAAgAG0AbwBkAGkAZgB5ACwACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQAsACAAYQBuAGQAIABzAGUAbABsACAAbQBvAGQAaQBmAGkAZQBkACAAYQBuAGQAIAB1AG4AbQBvAGQAaQBmAGkAZQBkACAAYwBvAHAAaQBlAHMAIABvAGYAIAB0AGgAZQAgAEYAbwBuAHQACgBTAG8AZgB0AHcAYQByAGUALAAgAHMAdQBiAGoAZQBjAHQAIAB0AG8AIAB0AGgAZQAgAGYAbwBsAGwAbwB3AGkAbgBnACAAYwBvAG4AZABpAHQAaQBvAG4AcwA6AAoACgAxACkAIABOAGUAaQB0AGgAZQByACAAdABoAGUAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAbgBvAHIAIABhAG4AeQAgAG8AZgAgAGkAdABzACAAaQBuAGQAaQB2AGkAZAB1AGEAbAAgAGMAbwBtAHAAbwBuAGUAbgB0AHMALAAKAGkAbgAgAE8AcgBpAGcAaQBuAGEAbAAgAG8AcgAgAE0AbwBkAGkAZgBpAGUAZAAgAFYAZQByAHMAaQBvAG4AcwAsACAAbQBhAHkAIABiAGUAIABzAG8AbABkACAAYgB5ACAAaQB0AHMAZQBsAGYALgAKAAoAMgApACAATwByAGkAZwBpAG4AYQBsACAAbwByACAATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgBzACAAbwBmACAAdABoAGUAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAbQBhAHkAIABiAGUAIABiAHUAbgBkAGwAZQBkACwACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAYQBuAGQALwBvAHIAIABzAG8AbABkACAAdwBpAHQAaAAgAGEAbgB5ACAAcwBvAGYAdAB3AGEAcgBlACwAIABwAHIAbwB2AGkAZABlAGQAIAB0AGgAYQB0ACAAZQBhAGMAaAAgAGMAbwBwAHkACgBjAG8AbgB0AGEAaQBuAHMAIAB0AGgAZQAgAGEAYgBvAHYAZQAgAGMAbwBwAHkAcgBpAGcAaAB0ACAAbgBvAHQAaQBjAGUAIABhAG4AZAAgAHQAaABpAHMAIABsAGkAYwBlAG4AcwBlAC4AIABUAGgAZQBzAGUAIABjAGEAbgAgAGIAZQAKAGkAbgBjAGwAdQBkAGUAZAAgAGUAaQB0AGgAZQByACAAYQBzACAAcwB0AGEAbgBkAC0AYQBsAG8AbgBlACAAdABlAHgAdAAgAGYAaQBsAGUAcwAsACAAaAB1AG0AYQBuAC0AcgBlAGEAZABhAGIAbABlACAAaABlAGEAZABlAHIAcwAgAG8AcgAKAGkAbgAgAHQAaABlACAAYQBwAHAAcgBvAHAAcgBpAGEAdABlACAAbQBhAGMAaABpAG4AZQAtAHIAZQBhAGQAYQBiAGwAZQAgAG0AZQB0AGEAZABhAHQAYQAgAGYAaQBlAGwAZABzACAAdwBpAHQAaABpAG4AIAB0AGUAeAB0ACAAbwByAAoAYgBpAG4AYQByAHkAIABmAGkAbABlAHMAIABhAHMAIABsAG8AbgBnACAAYQBzACAAdABoAG8AcwBlACAAZgBpAGUAbABkAHMAIABjAGEAbgAgAGIAZQAgAGUAYQBzAGkAbAB5ACAAdgBpAGUAdwBlAGQAIABiAHkAIAB0AGgAZQAgAHUAcwBlAHIALgAKAAoAMwApACAATgBvACAATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgAgAG8AZgAgAHQAaABlACAARgBvAG4AdAAgAFMAbwBmAHQAdwBhAHIAZQAgAG0AYQB5ACAAdQBzAGUAIAB0AGgAZQAgAFIAZQBzAGUAcgB2AGUAZAAgAEYAbwBuAHQACgBOAGEAbQBlACgAcwApACAAdQBuAGwAZQBzAHMAIABlAHgAcABsAGkAYwBpAHQAIAB3AHIAaQB0AHQAZQBuACAAcABlAHIAbQBpAHMAcwBpAG8AbgAgAGkAcwAgAGcAcgBhAG4AdABlAGQAIABiAHkAIAB0AGgAZQAgAGMAbwByAHIAZQBzAHAAbwBuAGQAaQBuAGcACgBDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByAC4AIABUAGgAaQBzACAAcgBlAHMAdAByAGkAYwB0AGkAbwBuACAAbwBuAGwAeQAgAGEAcABwAGwAaQBlAHMAIAB0AG8AIAB0AGgAZQAgAHAAcgBpAG0AYQByAHkAIABmAG8AbgB0ACAAbgBhAG0AZQAgAGEAcwAKAHAAcgBlAHMAZQBuAHQAZQBkACAAdABvACAAdABoAGUAIAB1AHMAZQByAHMALgAKAAoANAApACAAVABoAGUAIABuAGEAbQBlACgAcwApACAAbwBmACAAdABoAGUAIABDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByACgAcwApACAAbwByACAAdABoAGUAIABBAHUAdABoAG8AcgAoAHMAKQAgAG8AZgAgAHQAaABlACAARgBvAG4AdAAKAFMAbwBmAHQAdwBhAHIAZQAgAHMAaABhAGwAbAAgAG4AbwB0ACAAYgBlACAAdQBzAGUAZAAgAHQAbwAgAHAAcgBvAG0AbwB0AGUALAAgAGUAbgBkAG8AcgBzAGUAIABvAHIAIABhAGQAdgBlAHIAdABpAHMAZQAgAGEAbgB5AAoATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgAsACAAZQB4AGMAZQBwAHQAIAB0AG8AIABhAGMAawBuAG8AdwBsAGUAZABnAGUAIAB0AGgAZQAgAGMAbwBuAHQAcgBpAGIAdQB0AGkAbwBuACgAcwApACAAbwBmACAAdABoAGUACgBDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByACgAcwApACAAYQBuAGQAIAB0AGgAZQAgAEEAdQB0AGgAbwByACgAcwApACAAbwByACAAdwBpAHQAaAAgAHQAaABlAGkAcgAgAGUAeABwAGwAaQBjAGkAdAAgAHcAcgBpAHQAdABlAG4ACgBwAGUAcgBtAGkAcwBzAGkAbwBuAC4ACgAKADUAKQAgAFQAaABlACAARgBvAG4AdAAgAFMAbwBmAHQAdwBhAHIAZQAsACAAbQBvAGQAaQBmAGkAZQBkACAAbwByACAAdQBuAG0AbwBkAGkAZgBpAGUAZAAsACAAaQBuACAAcABhAHIAdAAgAG8AcgAgAGkAbgAgAHcAaABvAGwAZQAsAAoAbQB1AHMAdAAgAGIAZQAgAGQAaQBzAHQAcgBpAGIAdQB0AGUAZAAgAGUAbgB0AGkAcgBlAGwAeQAgAHUAbgBkAGUAcgAgAHQAaABpAHMAIABsAGkAYwBlAG4AcwBlACwAIABhAG4AZAAgAG0AdQBzAHQAIABuAG8AdAAgAGIAZQAKAGQAaQBzAHQAcgBpAGIAdQB0AGUAZAAgAHUAbgBkAGUAcgAgAGEAbgB5ACAAbwB0AGgAZQByACAAbABpAGMAZQBuAHMAZQAuACAAVABoAGUAIAByAGUAcQB1AGkAcgBlAG0AZQBuAHQAIABmAG8AcgAgAGYAbwBuAHQAcwAgAHQAbwAKAHIAZQBtAGEAaQBuACAAdQBuAGQAZQByACAAdABoAGkAcwAgAGwAaQBjAGUAbgBzAGUAIABkAG8AZQBzACAAbgBvAHQAIABhAHAAcABsAHkAIAB0AG8AIABhAG4AeQAgAGQAbwBjAHUAbQBlAG4AdAAgAGMAcgBlAGEAdABlAGQACgB1AHMAaQBuAGcAIAB0AGgAZQAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUALgAKAAoAVABFAFIATQBJAE4AQQBUAEkATwBOAAoAVABoAGkAcwAgAGwAaQBjAGUAbgBzAGUAIABiAGUAYwBvAG0AZQBzACAAbgB1AGwAbAAgAGEAbgBkACAAdgBvAGkAZAAgAGkAZgAgAGEAbgB5ACAAbwBmACAAdABoAGUAIABhAGIAbwB2AGUAIABjAG8AbgBkAGkAdABpAG8AbgBzACAAYQByAGUACgBuAG8AdAAgAG0AZQB0AC4ACgAKAEQASQBTAEMATABBAEkATQBFAFIACgBUAEgARQAgAEYATwBOAFQAIABTAE8ARgBUAFcAQQBSAEUAIABJAFMAIABQAFIATwBWAEkARABFAEQAIAAiAEEAUwAgAEkAUwAiACwAIABXAEkAVABIAE8AVQBUACAAVwBBAFIAUgBBAE4AVABZACAATwBGACAAQQBOAFkAIABLAEkATgBEACwACgBFAFgAUABSAEUAUwBTACAATwBSACAASQBNAFAATABJAEUARAAsACAASQBOAEMATABVAEQASQBOAEcAIABCAFUAVAAgAE4ATwBUACAATABJAE0ASQBUAEUARAAgAFQATwAgAEEATgBZACAAVwBBAFIAUgBBAE4AVABJAEUAUwAgAE8ARgAKAE0ARQBSAEMASABBAE4AVABBAEIASQBMAEkAVABZACwAIABGAEkAVABOAEUAUwBTACAARgBPAFIAIABBACAAUABBAFIAVABJAEMAVQBMAEEAUgAgAFAAVQBSAFAATwBTAEUAIABBAE4ARAAgAE4ATwBOAEkATgBGAFIASQBOAEcARQBNAEUATgBUAAoATwBGACAAQwBPAFAAWQBSAEkARwBIAFQALAAgAFAAQQBUAEUATgBUACwAIABUAFIAQQBEAEUATQBBAFIASwAsACAATwBSACAATwBUAEgARQBSACAAUgBJAEcASABUAC4AIABJAE4AIABOAE8AIABFAFYARQBOAFQAIABTAEgAQQBMAEwAIABUAEgARQAKAEMATwBQAFkAUgBJAEcASABUACAASABPAEwARABFAFIAIABCAEUAIABMAEkAQQBCAEwARQAgAEYATwBSACAAQQBOAFkAIABDAEwAQQBJAE0ALAAgAEQAQQBNAEEARwBFAFMAIABPAFIAIABPAFQASABFAFIAIABMAEkAQQBCAEkATABJAFQAWQAsAAoASQBOAEMATABVAEQASQBOAEcAIABBAE4AWQAgAEcARQBOAEUAUgBBAEwALAAgAFMAUABFAEMASQBBAEwALAAgAEkATgBEAEkAUgBFAEMAVAAsACAASQBOAEMASQBEAEUATgBUAEEATAAsACAATwBSACAAQwBPAE4AUwBFAFEAVQBFAE4AVABJAEEATAAKAEQAQQBNAEEARwBFAFMALAAgAFcASABFAFQASABFAFIAIABJAE4AIABBAE4AIABBAEMAVABJAE8ATgAgAE8ARgAgAEMATwBOAFQAUgBBAEMAVAAsACAAVABPAFIAVAAgAE8AUgAgAE8AVABIAEUAUgBXAEkAUwBFACwAIABBAFIASQBTAEkATgBHAAoARgBSAE8ATQAsACAATwBVAFQAIABPAEYAIABUAEgARQAgAFUAUwBFACAATwBSACAASQBOAEEAQgBJAEwASQBUAFkAIABUAE8AIABVAFMARQAgAFQASABFACAARgBPAE4AVAAgAFMATwBGAFQAVwBBAFIARQAgAE8AUgAgAEYAUgBPAE0ACgBPAFQASABFAFIAIABEAEUAQQBMAEkATgBHAFMAIABJAE4AIABUAEgARQAgAEYATwBOAFQAIABTAE8ARgBUAFcAQQBSAEUALgAAQ29weXJpZ2h0IChjKSAyMDExLCBDb2R5ICJDb2RlTWFuMzgiIEJvaXNjbGFpciAoY29keUB6b25lMzgubmV0KSwKd2l0aCBSZXNlcnZlZCBGb250IE5hbWUgUHJlc3MgU3RhcnQuCgpUaGlzIEZvbnQgU29mdHdhcmUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIFNJTCBPcGVuIEZvbnQgTGljZW5zZSwgVmVyc2lvbiAxLjEuClRoaXMgbGljZW5zZSBpcyBjb3BpZWQgYmVsb3csIGFuZCBpcyBhbHNvIGF2YWlsYWJsZSB3aXRoIGEgRkFRIGF0OgpodHRwOi8vc2NyaXB0cy5zaWwub3JnL09GTAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClNJTCBPUEVOIEZPTlQgTElDRU5TRSBWZXJzaW9uIDEuMSAtIDI2IEZlYnJ1YXJ5IDIwMDcKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClBSRUFNQkxFClRoZSBnb2FscyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UgKE9GTCkgYXJlIHRvIHN0aW11bGF0ZSB3b3JsZHdpZGUKZGV2ZWxvcG1lbnQgb2YgY29sbGFib3JhdGl2ZSBmb250IHByb2plY3RzLCB0byBzdXBwb3J0IHRoZSBmb250IGNyZWF0aW9uCmVmZm9ydHMgb2YgYWNhZGVtaWMgYW5kIGxpbmd1aXN0aWMgY29tbXVuaXRpZXMsIGFuZCB0byBwcm92aWRlIGEgZnJlZSBhbmQKb3BlbiBmcmFtZXdvcmsgaW4gd2hpY2ggZm9udHMgbWF5IGJlIHNoYXJlZCBhbmQgaW1wcm92ZWQgaW4gcGFydG5lcnNoaXAKd2l0aCBvdGhlcnMuCgpUaGUgT0ZMIGFsbG93cyB0aGUgbGljZW5zZWQgZm9udHMgdG8gYmUgdXNlZCwgc3R1ZGllZCwgbW9kaWZpZWQgYW5kCnJlZGlzdHJpYnV0ZWQgZnJlZWx5IGFzIGxvbmcgYXMgdGhleSBhcmUgbm90IHNvbGQgYnkgdGhlbXNlbHZlcy4gVGhlCmZvbnRzLCBpbmNsdWRpbmcgYW55IGRlcml2YXRpdmUgd29ya3MsIGNhbiBiZSBidW5kbGVkLCBlbWJlZGRlZCwgCnJlZGlzdHJpYnV0ZWQgYW5kL29yIHNvbGQgd2l0aCBhbnkgc29mdHdhcmUgcHJvdmlkZWQgdGhhdCBhbnkgcmVzZXJ2ZWQKbmFtZXMgYXJlIG5vdCB1c2VkIGJ5IGRlcml2YXRpdmUgd29ya3MuIFRoZSBmb250cyBhbmQgZGVyaXZhdGl2ZXMsCmhvd2V2ZXIsIGNhbm5vdCBiZSByZWxlYXNlZCB1bmRlciBhbnkgb3RoZXIgdHlwZSBvZiBsaWNlbnNlLiBUaGUKcmVxdWlyZW1lbnQgZm9yIGZvbnRzIHRvIHJlbWFpbiB1bmRlciB0aGlzIGxpY2Vuc2UgZG9lcyBub3QgYXBwbHkKdG8gYW55IGRvY3VtZW50IGNyZWF0ZWQgdXNpbmcgdGhlIGZvbnRzIG9yIHRoZWlyIGRlcml2YXRpdmVzLgoKREVGSU5JVElPTlMKIkZvbnQgU29mdHdhcmUiIHJlZmVycyB0byB0aGUgc2V0IG9mIGZpbGVzIHJlbGVhc2VkIGJ5IHRoZSBDb3B5cmlnaHQKSG9sZGVyKHMpIHVuZGVyIHRoaXMgbGljZW5zZSBhbmQgY2xlYXJseSBtYXJrZWQgYXMgc3VjaC4gVGhpcyBtYXkKaW5jbHVkZSBzb3VyY2UgZmlsZXMsIGJ1aWxkIHNjcmlwdHMgYW5kIGRvY3VtZW50YXRpb24uCgoiUmVzZXJ2ZWQgRm9udCBOYW1lIiByZWZlcnMgdG8gYW55IG5hbWVzIHNwZWNpZmllZCBhcyBzdWNoIGFmdGVyIHRoZQpjb3B5cmlnaHQgc3RhdGVtZW50KHMpLgoKIk9yaWdpbmFsIFZlcnNpb24iIHJlZmVycyB0byB0aGUgY29sbGVjdGlvbiBvZiBGb250IFNvZnR3YXJlIGNvbXBvbmVudHMgYXMKZGlzdHJpYnV0ZWQgYnkgdGhlIENvcHlyaWdodCBIb2xkZXIocykuCgoiTW9kaWZpZWQgVmVyc2lvbiIgcmVmZXJzIHRvIGFueSBkZXJpdmF0aXZlIG1hZGUgYnkgYWRkaW5nIHRvLCBkZWxldGluZywKb3Igc3Vic3RpdHV0aW5nIC0tIGluIHBhcnQgb3IgaW4gd2hvbGUgLS0gYW55IG9mIHRoZSBjb21wb25lbnRzIG9mIHRoZQpPcmlnaW5hbCBWZXJzaW9uLCBieSBjaGFuZ2luZyBmb3JtYXRzIG9yIGJ5IHBvcnRpbmcgdGhlIEZvbnQgU29mdHdhcmUgdG8gYQpuZXcgZW52aXJvbm1lbnQuCgoiQXV0aG9yIiByZWZlcnMgdG8gYW55IGRlc2lnbmVyLCBlbmdpbmVlciwgcHJvZ3JhbW1lciwgdGVjaG5pY2FsCndyaXRlciBvciBvdGhlciBwZXJzb24gd2hvIGNvbnRyaWJ1dGVkIHRvIHRoZSBGb250IFNvZnR3YXJlLgoKUEVSTUlTU0lPTiAmIENPTkRJVElPTlMKUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nCmEgY29weSBvZiB0aGUgRm9udCBTb2Z0d2FyZSwgdG8gdXNlLCBzdHVkeSwgY29weSwgbWVyZ2UsIGVtYmVkLCBtb2RpZnksCnJlZGlzdHJpYnV0ZSwgYW5kIHNlbGwgbW9kaWZpZWQgYW5kIHVubW9kaWZpZWQgY29waWVzIG9mIHRoZSBGb250ClNvZnR3YXJlLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCjEpIE5laXRoZXIgdGhlIEZvbnQgU29mdHdhcmUgbm9yIGFueSBvZiBpdHMgaW5kaXZpZHVhbCBjb21wb25lbnRzLAppbiBPcmlnaW5hbCBvciBNb2RpZmllZCBWZXJzaW9ucywgbWF5IGJlIHNvbGQgYnkgaXRzZWxmLgoKMikgT3JpZ2luYWwgb3IgTW9kaWZpZWQgVmVyc2lvbnMgb2YgdGhlIEZvbnQgU29mdHdhcmUgbWF5IGJlIGJ1bmRsZWQsCnJlZGlzdHJpYnV0ZWQgYW5kL29yIHNvbGQgd2l0aCBhbnkgc29mdHdhcmUsIHByb3ZpZGVkIHRoYXQgZWFjaCBjb3B5CmNvbnRhaW5zIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIGxpY2Vuc2UuIFRoZXNlIGNhbiBiZQppbmNsdWRlZCBlaXRoZXIgYXMgc3RhbmQtYWxvbmUgdGV4dCBmaWxlcywgaHVtYW4tcmVhZGFibGUgaGVhZGVycyBvcgppbiB0aGUgYXBwcm9wcmlhdGUgbWFjaGluZS1yZWFkYWJsZSBtZXRhZGF0YSBmaWVsZHMgd2l0aGluIHRleHQgb3IKYmluYXJ5IGZpbGVzIGFzIGxvbmcgYXMgdGhvc2UgZmllbGRzIGNhbiBiZSBlYXNpbHkgdmlld2VkIGJ5IHRoZSB1c2VyLgoKMykgTm8gTW9kaWZpZWQgVmVyc2lvbiBvZiB0aGUgRm9udCBTb2Z0d2FyZSBtYXkgdXNlIHRoZSBSZXNlcnZlZCBGb250Ck5hbWUocykgdW5sZXNzIGV4cGxpY2l0IHdyaXR0ZW4gcGVybWlzc2lvbiBpcyBncmFudGVkIGJ5IHRoZSBjb3JyZXNwb25kaW5nCkNvcHlyaWdodCBIb2xkZXIuIFRoaXMgcmVzdHJpY3Rpb24gb25seSBhcHBsaWVzIHRvIHRoZSBwcmltYXJ5IGZvbnQgbmFtZSBhcwpwcmVzZW50ZWQgdG8gdGhlIHVzZXJzLgoKNCkgVGhlIG5hbWUocykgb2YgdGhlIENvcHlyaWdodCBIb2xkZXIocykgb3IgdGhlIEF1dGhvcihzKSBvZiB0aGUgRm9udApTb2Z0d2FyZSBzaGFsbCBub3QgYmUgdXNlZCB0byBwcm9tb3RlLCBlbmRvcnNlIG9yIGFkdmVydGlzZSBhbnkKTW9kaWZpZWQgVmVyc2lvbiwgZXhjZXB0IHRvIGFja25vd2xlZGdlIHRoZSBjb250cmlidXRpb24ocykgb2YgdGhlCkNvcHlyaWdodCBIb2xkZXIocykgYW5kIHRoZSBBdXRob3Iocykgb3Igd2l0aCB0aGVpciBleHBsaWNpdCB3cml0dGVuCnBlcm1pc3Npb24uCgo1KSBUaGUgRm9udCBTb2Z0d2FyZSwgbW9kaWZpZWQgb3IgdW5tb2RpZmllZCwgaW4gcGFydCBvciBpbiB3aG9sZSwKbXVzdCBiZSBkaXN0cmlidXRlZCBlbnRpcmVseSB1bmRlciB0aGlzIGxpY2Vuc2UsIGFuZCBtdXN0IG5vdCBiZQpkaXN0cmlidXRlZCB1bmRlciBhbnkgb3RoZXIgbGljZW5zZS4gVGhlIHJlcXVpcmVtZW50IGZvciBmb250cyB0bwpyZW1haW4gdW5kZXIgdGhpcyBsaWNlbnNlIGRvZXMgbm90IGFwcGx5IHRvIGFueSBkb2N1bWVudCBjcmVhdGVkCnVzaW5nIHRoZSBGb250IFNvZnR3YXJlLgoKVEVSTUlOQVRJT04KVGhpcyBsaWNlbnNlIGJlY29tZXMgbnVsbCBhbmQgdm9pZCBpZiBhbnkgb2YgdGhlIGFib3ZlIGNvbmRpdGlvbnMgYXJlCm5vdCBtZXQuCgpESVNDTEFJTUVSClRIRSBGT05UIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsCkVYUFJFU1MgT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBBTlkgV0FSUkFOVElFUyBPRgpNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQKT0YgQ09QWVJJR0hULCBQQVRFTlQsIFRSQURFTUFSSywgT1IgT1RIRVIgUklHSFQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwKSU5DTFVESU5HIEFOWSBHRU5FUkFMLCBTUEVDSUFMLCBJTkRJUkVDVCwgSU5DSURFTlRBTCwgT1IgQ09OU0VRVUVOVElBTApEQU1BR0VTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcKRlJPTSwgT1VUIE9GIFRIRSBVU0UgT1IgSU5BQklMSVRZIFRPIFVTRSBUSEUgRk9OVCBTT0ZUV0FSRSBPUiBGUk9NCk9USEVSIERFQUxJTkdTIElOIFRIRSBGT05UIFNPRlRXQVJFLgAAaAB0AHQAcAA6AC8ALwBzAGMAcgBpAHAAdABzAC4AcwBpAGwALgBvAHIAZwAvAE8ARgBMAABodHRwOi8vc2NyaXB0cy5zaWwub3JnL09GTAAAAAACAAAAAAAA/+kAMwAAAAEAAAAAAAAAAAAAAAAAAAAAAi8AAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAQIBAwCjAIQAhQC9AJYA6ACGAI4AiwCdAKkApAEEAIoA2gCDAJMBBQEGAI0BBwCIAMMA3gEIAJ4AqgD1APQA9gCiAK0AyQDHAK4AYgBjAJAAZADLAGUAyADKAM8AzADNAM4A6QBmANMA0ADRAK8AZwDwAJEA1gDUANUAaADrAO0AiQBqAGkAawBtAGwAbgCgAG8AcQBwAHIAcwB1AHQAdgB3AOoAeAB6AHkAewB9AHwAuAChAH8AfgCAAIEA7ADuALoBCQEKAQsBDAENAQ4A/QD+AQ8BEAERARIA/wEAARMBFAEVAQEBFgEXARgBGQEaARsBHAEdAR4BHwEgASEA+AD5ASIBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEA+gDXATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAOIA4wFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwCwALEBUAFRAVIBUwFUAVUBVgFXAVgBWQD7APwA5ADlAVoBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8AuwFwAXEBcgFzAOYA5wF0AKYA2ADhAXUBdgF3AXgA2wDcAN0A4ADZAN8BeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiACoAYkBigGLAYwBjQGOAY8BkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsAnwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQCXAa4BrwGwAJsBsQGyAbMBtAG1AbYBtwG4AbkBugG7AbwBvQG+Ab8BwAHBAcIBwwHEAcUBxgHHAcgByQHKAcsBzAHNAc4BzwHQAdEB0gHTAdQB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMB5AHlAeYB5wHoAekB6gHrAewB7QHuAe8B8AHxAfIB8wH0AfUB9gH3AfgB+QH6AfsB/AH9Af4B/wIAAgECAgIDAgQCBQIGAgcCCAIJAgoCCwIMAg0CDgIPAhACEQISAhMCFAIVAhYCFwIYAhkCGgIbAhwCHQIeALIAswIfALYAtwDEALQAtQDFAIIAwgCHAKsAxgC+AL8AvAIgAiECIgCMAiMCJAIlAiYAmAInAJoAmQClAJIAnACnAI8AlACVAigCKQIqAisAuQIsAi0CLgIvAjACMQIyAjMCNAI1AjYCNwd1bmkwMDdGB3VuaTAwQTAHdW5pMDBBRAd1bmkwMEIyB3VuaTAwQjMHdW5pMDBCNQd1bmkwMEI5B0FtYWNyb24HYW1hY3JvbgZBYnJldmUGYWJyZXZlB0FvZ29uZWsHYW9nb25lawtDY2lyY3VtZmxleAtjY2lyY3VtZmxleApDZG90YWNjZW50CmNkb3RhY2NlbnQGRGNhcm9uBmRjYXJvbgZEY3JvYXQHRW1hY3JvbgdlbWFjcm9uBkVicmV2ZQZlYnJldmUKRWRvdGFjY2VudAplZG90YWNjZW50B0VvZ29uZWsHZW9nb25lawZFY2Fyb24GZWNhcm9uC0djaXJjdW1mbGV4C2djaXJjdW1mbGV4Ckdkb3RhY2NlbnQKZ2RvdGFjY2VudAxHY29tbWFhY2NlbnQMZ2NvbW1hYWNjZW50C0hjaXJjdW1mbGV4C2hjaXJjdW1mbGV4BEhiYXIEaGJhcgZJdGlsZGUGaXRpbGRlB0ltYWNyb24HaW1hY3JvbgZJYnJldmUGaWJyZXZlB0lvZ29uZWsHaW9nb25lawJJSgJpagtKY2lyY3VtZmxleAtqY2lyY3VtZmxleAxLY29tbWFhY2NlbnQMa2NvbW1hYWNjZW50DGtncmVlbmxhbmRpYwZMYWN1dGUGbGFjdXRlDExjb21tYWFjY2VudAxsY29tbWFhY2NlbnQGTGNhcm9uBmxjYXJvbgRMZG90BGxkb3QGTmFjdXRlBm5hY3V0ZQxOY29tbWFhY2NlbnQMbmNvbW1hYWNjZW50Bk5jYXJvbgZuY2Fyb24LbmFwb3N0cm9waGUDRW5nA2VuZwdPbWFjcm9uB29tYWNyb24GT2JyZXZlBm9icmV2ZQ1PaHVuZ2FydW1sYXV0DW9odW5nYXJ1bWxhdXQGUmFjdXRlBnJhY3V0ZQxSY29tbWFhY2NlbnQMcmNvbW1hYWNjZW50BlJjYXJvbgZyY2Fyb24GU2FjdXRlBnNhY3V0ZQtTY2lyY3VtZmxleAtzY2lyY3VtZmxleAxUY29tbWFhY2NlbnQMdGNvbW1hYWNjZW50BlRjYXJvbgZ0Y2Fyb24EVGJhcgR0YmFyBlV0aWxkZQZ1dGlsZGUHVW1hY3Jvbgd1bWFjcm9uBlVicmV2ZQZ1YnJldmUFVXJpbmcFdXJpbmcNVWh1bmdhcnVtbGF1dA11aHVuZ2FydW1sYXV0B1VvZ29uZWsHdW9nb25lawtXY2lyY3VtZmxleAt3Y2lyY3VtZmxleAtZY2lyY3VtZmxleAt5Y2lyY3VtZmxleAZaYWN1dGUGemFjdXRlClpkb3RhY2NlbnQKemRvdGFjY2VudAVsb25ncwd1bmkwMkM5B3VuaTAyQ0EHdW5pMDJDQgd1bmkwMkQ3B3VuaTAzN0EHdW5pMDM3RQV0b25vcw1kaWVyZXNpc3Rvbm9zCkFscGhhdG9ub3MJYW5vdGVsZWlhDEVwc2lsb250b25vcwhFdGF0b25vcwlJb3RhdG9ub3MMT21pY3JvbnRvbm9zDFVwc2lsb250b25vcwpPbWVnYXRvbm9zEWlvdGFkaWVyZXNpc3Rvbm9zBUFscGhhBEJldGEFR2FtbWEHRXBzaWxvbgRaZXRhA0V0YQVUaGV0YQRJb3RhBUthcHBhBkxhbWJkYQJNdQJOdQJYaQdPbWljcm9uAlBpA1JobwVTaWdtYQNUYXUHVXBzaWxvbgNQaGkDQ2hpA1BzaQxJb3RhZGllcmVzaXMPVXBzaWxvbmRpZXJlc2lzCmFscGhhdG9ub3MMZXBzaWxvbnRvbm9zCGV0YXRvbm9zCWlvdGF0b25vcxR1cHNpbG9uZGllcmVzaXN0b25vcwVhbHBoYQRiZXRhBWdhbW1hBWRlbHRhB2Vwc2lsb24EemV0YQNldGEFdGhldGEEaW90YQVrYXBwYQZsYW1iZGECbnUCeGkHb21pY3JvbgNyaG8Gc2lnbWExBXNpZ21hA3RhdQd1cHNpbG9uA3BoaQNjaGkDcHNpBW9tZWdhDGlvdGFkaWVyZXNpcw91cHNpbG9uZGllcmVzaXMMb21pY3JvbnRvbm9zDHVwc2lsb250b25vcwpvbWVnYXRvbm9zB3VuaTA0MDAJYWZpaTEwMDIzCWFmaWkxMDA1MQlhZmlpMTAwNTIJYWZpaTEwMDUzCWFmaWkxMDA1NAlhZmlpMTAwNTUJYWZpaTEwMDU2CWFmaWkxMDA1NwlhZmlpMTAwNTgJYWZpaTEwMDU5CWFmaWkxMDA2MAlhZmlpMTAwNjEHdW5pMDQwRAlhZmlpMTAwNjIJYWZpaTEwMTQ1CWFmaWkxMDAxNwlhZmlpMTAwMTgJYWZpaTEwMDE5CWFmaWkxMDAyMAlhZmlpMTAwMjEJYWZpaTEwMDIyCWFmaWkxMDAyNAlhZmlpMTAwMjUJYWZpaTEwMDI2CWFmaWkxMDAyNwlhZmlpMTAwMjgJYWZpaTEwMDI5CWFmaWkxMDAzMAlhZmlpMTAwMzEJYWZpaTEwMDMyCWFmaWkxMDAzMwlhZmlpMTAwMzQJYWZpaTEwMDM1CWFmaWkxMDAzNglhZmlpMTAwMzcJYWZpaTEwMDM4CWFmaWkxMDAzOQlhZmlpMTAwNDAJYWZpaTEwMDQxCWFmaWkxMDA0MglhZmlpMTAwNDMJYWZpaTEwMDQ0CWFmaWkxMDA0NQlhZmlpMTAwNDYJYWZpaTEwMDQ3CWFmaWkxMDA0OAlhZmlpMTAwNDkJYWZpaTEwMDY1CWFmaWkxMDA2NglhZmlpMTAwNjcJYWZpaTEwMDY4CWFmaWkxMDA2OQlhZmlpMTAwNzAJYWZpaTEwMDcyCWFmaWkxMDA3MwlhZmlpMTAwNzQJYWZpaTEwMDc1CWFmaWkxMDA3NglhZmlpMTAwNzcJYWZpaTEwMDc4CWFmaWkxMDA3OQlhZmlpMTAwODAJYWZpaTEwMDgxCWFmaWkxMDA4MglhZmlpMTAwODMJYWZpaTEwMDg0CWFmaWkxMDA4NQlhZmlpMTAwODYJYWZpaTEwMDg3CWFmaWkxMDA4OAlhZmlpMTAwODkJYWZpaTEwMDkwCWFmaWkxMDA5MQlhZmlpMTAwOTIJYWZpaTEwMDkzCWFmaWkxMDA5NAlhZmlpMTAwOTUJYWZpaTEwMDk2CWFmaWkxMDA5Nwd1bmkwNDUwCWFmaWkxMDA3MQlhZmlpMTAwOTkJYWZpaTEwMTAwCWFmaWkxMDEwMQlhZmlpMTAxMDIJYWZpaTEwMTAzCWFmaWkxMDEwNAlhZmlpMTAxMDUJYWZpaTEwMTA2CWFmaWkxMDEwNwlhZmlpMTAxMDgJYWZpaTEwMTA5B3VuaTA0NUQJYWZpaTEwMTEwCWFmaWkxMDE5MwlhZmlpMDAyMDgERXVybwd1bmkyMEFGCWFmaWk2MTM1MglhcnJvd2xlZnQHYXJyb3d1cAphcnJvd3JpZ2h0CWFycm93ZG93bgd1bmkyMjA2B3RyaWFndXAHdW5pMjVCNgd0cmlhZ2RuB3VuaTI1QzAHdW5pMjYwNQd1bmkyNjA2BXNwYWRlBGNsdWIFaGVhcnQHZGlhbW9uZAttdXNpY2Fsbm90ZQd1bmlGMTAwB3VuaUYxMDEHdW5pRjhGRgd1bmlGQjAxB3VuaUZCMDIAAAAAAf//AAIAAQAAAA4AAAAYACAAAAACAAEAAQIuAAEABAAAAAIAAAABAAAAAQAAAAAAAQAAAADMPaLPAAAAAMobHN4AAAAAzLYldw==","size":24},"glow":{"ratios":[0,255],"distance":0,"blurX":2,"quality":3,"blurY":2,"strength":2,"colors":[16711680,16711680],"angle":45,"alphas":[0,1],"glowEnabled":false},"stroke":{"pixelHinting":false,"jointStyle":"miter","gradientType":"linear","miterLimit":1,"gradientAlphas":[1,1,1,1],"gradientColors":[8872704,5651456,5651456,5651456],"xOffset":0,"gradientRotation":90,"yOffset":0,"fillType":"gradientFill","textureScale":1,"strokeEnabled":false,"gradientRatios":[0,250.2336448598131,250.2336448598131,255],"size":2},"fill":{"distanceFieldSpread":16,"distanceFieldType":"Type 1","gradientRatios":[0,255],"gradientType":"linear","gradientAlphas":[1,1],"gradientColors":[16777215,16777215],"xOffset":0,"gradientRotation":90,"yOffset":0,"fillType":"gradientFill","distanceFieldEnabled":false,"textureScale":1,"distanceFieldColor":16777215,"distanceFieldDownscale":4},"shadow":{"distance":4,"alpha":1,"blurX":4,"quality":1,"blurY":4,"color":0,"strength":1,"shadowEnabled":false,"angle":45},"background":{"color":16777215,"alpha":0},"bevel":{"strength":1,"highlightColor":16777215,"highlightAlpha":1,"shadowColor":0,"shadowAlpha":1,"distance":4,"blurX":4,"quality":1,"blurY":4,"type":"inner","bevelEnabled":false,"angle":45}}
\ No newline at end of file
diff --git a/Games/Contra-Shooter/data/fnt/PressStart2P.png b/Games/Contra-Shooter/data/fnt/PressStart2P.png
new file mode 100644
index 0000000000..73766690aa
Binary files /dev/null and b/Games/Contra-Shooter/data/fnt/PressStart2P.png differ
diff --git a/Games/Contra-Shooter/data/fnt/PressStart2P.ttf b/Games/Contra-Shooter/data/fnt/PressStart2P.ttf
new file mode 100644
index 0000000000..98044e935e
Binary files /dev/null and b/Games/Contra-Shooter/data/fnt/PressStart2P.ttf differ
diff --git a/Games/Contra-Shooter/data/img/gui/icon_badge_24_48.png b/Games/Contra-Shooter/data/img/gui/icon_badge_24_48.png
new file mode 100644
index 0000000000..871702a3bd
Binary files /dev/null and b/Games/Contra-Shooter/data/img/gui/icon_badge_24_48.png differ
diff --git a/Games/Contra-Shooter/data/img/gui/icon_select_30_25.png b/Games/Contra-Shooter/data/img/gui/icon_select_30_25.png
new file mode 100644
index 0000000000..597987de88
Binary files /dev/null and b/Games/Contra-Shooter/data/img/gui/icon_select_30_25.png differ
diff --git a/Games/Contra-Shooter/data/img/gui/img_captured.png b/Games/Contra-Shooter/data/img/gui/img_captured.png
new file mode 100644
index 0000000000..629990969d
Binary files /dev/null and b/Games/Contra-Shooter/data/img/gui/img_captured.png differ
diff --git a/Games/Contra-Shooter/data/img/gui/img_neslogo.png b/Games/Contra-Shooter/data/img/gui/img_neslogo.png
new file mode 100644
index 0000000000..914c130fb9
Binary files /dev/null and b/Games/Contra-Shooter/data/img/gui/img_neslogo.png differ
diff --git a/Games/Contra-Shooter/data/img/gui/img_splashpage.png b/Games/Contra-Shooter/data/img/gui/img_splashpage.png
new file mode 100644
index 0000000000..6b110ca5a2
Binary files /dev/null and b/Games/Contra-Shooter/data/img/gui/img_splashpage.png differ
diff --git a/Games/Contra-Shooter/data/img/map/contra-tile-map.png b/Games/Contra-Shooter/data/img/map/contra-tile-map.png
new file mode 100644
index 0000000000..6182a13966
Binary files /dev/null and b/Games/Contra-Shooter/data/img/map/contra-tile-map.png differ
diff --git a/Games/Contra-Shooter/data/img/sprite/bad-guys.json b/Games/Contra-Shooter/data/img/sprite/bad-guys.json
new file mode 100644
index 0000000000..3e3f66aa06
--- /dev/null
+++ b/Games/Contra-Shooter/data/img/sprite/bad-guys.json
@@ -0,0 +1,121 @@
+{"frames": [
+ {
+ "filename": "BadGuys_01.png",
+ "frame": {"x":33,"y":0,"w":24,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":31},
+ "sourceSize": {"w":24,"h":31}
+ },
+ {
+ "filename": "BadGuys_02.png",
+ "frame": {"x":30,"y":68,"w":24,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":31},
+ "sourceSize": {"w":24,"h":31}
+ },
+ {
+ "filename": "BadGuys_03.png",
+ "frame": {"x":31,"y":34,"w":24,"h":32},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":32},
+ "sourceSize": {"w":24,"h":32}
+ },
+ {
+ "filename": "BadGuys_04.png",
+ "frame": {"x":77,"y":0,"w":18,"h":38},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":38},
+ "sourceSize": {"w":18,"h":38}
+ },
+ {
+ "filename": "BadGuys_05.png",
+ "frame": {"x":76,"y":68,"w":18,"h":39},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":39},
+ "sourceSize": {"w":18,"h":39}
+ },
+ {
+ "filename": "BadGuys_17.png",
+ "frame": {"x":96,"y":40,"w":17,"h":32},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":17,"h":32},
+ "sourceSize": {"w":17,"h":32}
+ },
+ {
+ "filename": "BadGuys_18.png",
+ "frame": {"x":57,"y":33,"w":18,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":33},
+ "sourceSize": {"w":18,"h":33}
+ },
+ {
+ "filename": "BadGuys_19.png",
+ "frame": {"x":96,"y":74,"w":17,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":17,"h":31},
+ "sourceSize": {"w":17,"h":31}
+ },
+ {
+ "filename": "BadGuys_20.png",
+ "frame": {"x":97,"y":0,"w":16,"h":32},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":32},
+ "sourceSize": {"w":16,"h":32}
+ },
+ {
+ "filename": "BadGuys_21.png",
+ "frame": {"x":56,"y":68,"w":18,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":33},
+ "sourceSize": {"w":18,"h":33}
+ },
+ {
+ "filename": "BadGuys_22.png",
+ "frame": {"x":0,"y":96,"w":18,"h":28},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":28},
+ "sourceSize": {"w":18,"h":28}
+ },
+ {
+ "filename": "BadGuys_93.png",
+ "frame": {"x":0,"y":34,"w":29,"h":32},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":29,"h":32},
+ "sourceSize": {"w":29,"h":32}
+ },
+ {
+ "filename": "BadGuys_94.png",
+ "frame": {"x":0,"y":0,"w":31,"h":32},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":31,"h":32},
+ "sourceSize": {"w":31,"h":32}
+ },
+ {
+ "filename": "BadGuys_95.png",
+ "frame": {"x":0,"y":68,"w":28,"h":26},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":28,"h":26},
+ "sourceSize": {"w":28,"h":26}
+ },
+ {"EOL":"true"}
+],
+"meta": {
+ "app": "ShoeBox",
+ "exporter": "melonJS",
+ "size": {"w":128,"h":128}
+}
+}
\ No newline at end of file
diff --git a/Games/Contra-Shooter/data/img/sprite/bad-guys.png b/Games/Contra-Shooter/data/img/sprite/bad-guys.png
new file mode 100644
index 0000000000..bad714b4bc
Binary files /dev/null and b/Games/Contra-Shooter/data/img/sprite/bad-guys.png differ
diff --git a/Games/Contra-Shooter/data/img/sprite/bill-lance.json b/Games/Contra-Shooter/data/img/sprite/bill-lance.json
new file mode 100644
index 0000000000..d2bf2df607
--- /dev/null
+++ b/Games/Contra-Shooter/data/img/sprite/bill-lance.json
@@ -0,0 +1,969 @@
+{"frames": [
+ {
+ "filename": "Bill_01.png",
+ "frame": {"x":335,"y":47,"w":14,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":45},
+ "sourceSize": {"w":14,"h":45}
+ },
+ {
+ "filename": "Bill_02.png",
+ "frame": {"x":337,"y":94,"w":14,"h":46},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":46},
+ "sourceSize": {"w":14,"h":46}
+ },
+ {
+ "filename": "Bill_03.png",
+ "frame": {"x":52,"y":200,"w":24,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":33},
+ "sourceSize": {"w":24,"h":33}
+ },
+ {
+ "filename": "Bill_04.png",
+ "frame": {"x":27,"y":164,"w":24,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":34},
+ "sourceSize": {"w":24,"h":34}
+ },
+ {
+ "filename": "Bill_05.png",
+ "frame": {"x":36,"y":0,"w":24,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":34},
+ "sourceSize": {"w":24,"h":34}
+ },
+ {
+ "filename": "Bill_06.png",
+ "frame": {"x":36,"y":72,"w":24,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":33},
+ "sourceSize": {"w":24,"h":33}
+ },
+ {
+ "filename": "Bill_07.png",
+ "frame": {"x":321,"y":130,"w":14,"h":46},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":46},
+ "sourceSize": {"w":14,"h":46}
+ },
+ {
+ "filename": "Bill_08.png",
+ "frame": {"x":336,"y":178,"w":14,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":45},
+ "sourceSize": {"w":14,"h":45}
+ },
+ {
+ "filename": "Bill_09.png",
+ "frame": {"x":129,"y":121,"w":21,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":34},
+ "sourceSize": {"w":21,"h":34}
+ },
+ {
+ "filename": "Bill_10.png",
+ "frame": {"x":136,"y":37,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Bill_11.png",
+ "frame": {"x":283,"y":133,"w":17,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":17,"h":36},
+ "sourceSize": {"w":17,"h":36}
+ },
+ {
+ "filename": "Bill_12.png",
+ "frame": {"x":302,"y":133,"w":17,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":17,"h":36},
+ "sourceSize": {"w":17,"h":36}
+ },
+ {
+ "filename": "Bill_13.png",
+ "frame": {"x":152,"y":159,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Bill_14.png",
+ "frame": {"x":152,"y":197,"w":21,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":34},
+ "sourceSize": {"w":21,"h":34}
+ },
+ {
+ "filename": "Bill_15.png",
+ "frame": {"x":104,"y":126,"w":23,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":33},
+ "sourceSize": {"w":23,"h":33}
+ },
+ {
+ "filename": "Bill_16.png",
+ "frame": {"x":78,"y":213,"w":23,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":35},
+ "sourceSize": {"w":23,"h":35}
+ },
+ {
+ "filename": "Bill_17.png",
+ "frame": {"x":176,"y":73,"w":21,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":35},
+ "sourceSize": {"w":21,"h":35}
+ },
+ {
+ "filename": "Bill_18.png",
+ "frame": {"x":182,"y":36,"w":21,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":35},
+ "sourceSize": {"w":21,"h":35}
+ },
+ {
+ "filename": "Bill_19.png",
+ "frame": {"x":103,"y":169,"w":23,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":35},
+ "sourceSize": {"w":23,"h":35}
+ },
+ {
+ "filename": "Bill_20.png",
+ "frame": {"x":88,"y":0,"w":23,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":33},
+ "sourceSize": {"w":23,"h":33}
+ },
+ {
+ "filename": "Bill_21.png",
+ "frame": {"x":282,"y":196,"w":18,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":36},
+ "sourceSize": {"w":18,"h":36}
+ },
+ {
+ "filename": "Bill_22.png",
+ "frame": {"x":198,"y":110,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Bill_23.png",
+ "frame": {"x":128,"y":161,"w":22,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":22,"h":31},
+ "sourceSize": {"w":22,"h":31}
+ },
+ {
+ "filename": "Bill_24.png",
+ "frame": {"x":283,"y":72,"w":18,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":34},
+ "sourceSize": {"w":18,"h":34}
+ },
+ {
+ "filename": "Bill_25.png",
+ "frame": {"x":199,"y":73,"w":20,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":34},
+ "sourceSize": {"w":20,"h":34}
+ },
+ {
+ "filename": "Bill_26.png",
+ "frame": {"x":220,"y":186,"w":20,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":34},
+ "sourceSize": {"w":20,"h":34}
+ },
+ {
+ "filename": "Bill_27.png",
+ "frame": {"x":269,"y":36,"w":18,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":34},
+ "sourceSize": {"w":18,"h":34}
+ },
+ {
+ "filename": "Bill_28.png",
+ "frame": {"x":129,"y":88,"w":22,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":22,"h":31},
+ "sourceSize": {"w":22,"h":31}
+ },
+ {
+ "filename": "Bill_29.png",
+ "frame": {"x":198,"y":148,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Bill_30.png",
+ "frame": {"x":263,"y":108,"w":18,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":36},
+ "sourceSize": {"w":18,"h":36}
+ },
+ {
+ "filename": "Bill_31.png",
+ "frame": {"x":283,"y":171,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Bill_32.png",
+ "frame": {"x":194,"y":229,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Bill_33.png",
+ "frame": {"x":300,"y":234,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Bill_34.png",
+ "frame": {"x":205,"y":36,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Bill_35.png",
+ "frame": {"x":0,"y":26,"w":34,"h":17},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":17},
+ "sourceSize": {"w":34,"h":17}
+ },
+ {
+ "filename": "Bill_36.png",
+ "frame": {"x":0,"y":45,"w":34,"h":17},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":17},
+ "sourceSize": {"w":34,"h":17}
+ },
+ {
+ "filename": "Bill_37.png",
+ "frame": {"x":216,"y":229,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Bill_38.png",
+ "frame": {"x":221,"y":162,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Bill_39.png",
+ "frame": {"x":205,"y":54,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Bill_40.png",
+ "frame": {"x":282,"y":234,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Bill_41.png",
+ "frame": {"x":0,"y":0,"w":34,"h":11},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":11},
+ "sourceSize": {"w":34,"h":11}
+ },
+ {
+ "filename": "Bill_42.png",
+ "frame": {"x":247,"y":47,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Bill_43.png",
+ "frame": {"x":79,"y":109,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Bill_44.png",
+ "frame": {"x":263,"y":146,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Bill_45.png",
+ "frame": {"x":79,"y":129,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Bill_46.png",
+ "frame": {"x":79,"y":149,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Bill_47.png",
+ "frame": {"x":263,"y":171,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Bill_48.png",
+ "frame": {"x":88,"y":35,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Bill_49.png",
+ "frame": {"x":262,"y":197,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Bill_50.png",
+ "frame": {"x":0,"y":115,"w":34,"h":11},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":11},
+ "sourceSize": {"w":34,"h":11}
+ },
+ {
+ "filename": "Bill_51.png",
+ "frame": {"x":87,"y":71,"w":23,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":16},
+ "sourceSize": {"w":23,"h":16}
+ },
+ {
+ "filename": "Bill_52.png",
+ "frame": {"x":62,"y":89,"w":12,"h":13},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":12,"h":13},
+ "sourceSize": {"w":12,"h":13}
+ },
+ {
+ "filename": "Bill_53.png",
+ "frame": {"x":36,"y":107,"w":12,"h":13},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":12,"h":13},
+ "sourceSize": {"w":12,"h":13}
+ },
+ {
+ "filename": "Bill_54.png",
+ "frame": {"x":62,"y":71,"w":23,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":16},
+ "sourceSize": {"w":23,"h":16}
+ },
+ {
+ "filename": "Bill_55.png",
+ "frame": {"x":221,"y":115,"w":19,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":19,"h":45},
+ "sourceSize": {"w":19,"h":45}
+ },
+ {
+ "filename": "Bill_56.png",
+ "frame": {"x":0,"y":128,"w":25,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":25,"h":45},
+ "sourceSize": {"w":25,"h":45}
+ },
+ {
+ "filename": "Bill_57.png",
+ "frame": {"x":242,"y":115,"w":19,"h":39},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":19,"h":39},
+ "sourceSize": {"w":19,"h":39}
+ },
+ {
+ "filename": "Bill_58.png",
+ "frame": {"x":198,"y":186,"w":20,"h":41},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":41},
+ "sourceSize": {"w":20,"h":41}
+ },
+ {
+ "filename": "Bill_59.png",
+ "frame": {"x":26,"y":222,"w":24,"h":22},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":22},
+ "sourceSize": {"w":24,"h":22}
+ },
+ {
+ "filename": "Bill_60.png",
+ "frame": {"x":36,"y":36,"w":24,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":34},
+ "sourceSize": {"w":24,"h":34}
+ },
+ {
+ "filename": "Lance_01.png",
+ "frame": {"x":325,"y":0,"w":14,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":45},
+ "sourceSize": {"w":14,"h":45}
+ },
+ {
+ "filename": "Lance_02.png",
+ "frame": {"x":319,"y":82,"w":14,"h":46},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":46},
+ "sourceSize": {"w":14,"h":46}
+ },
+ {
+ "filename": "Lance_03.png",
+ "frame": {"x":62,"y":36,"w":24,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":33},
+ "sourceSize": {"w":24,"h":33}
+ },
+ {
+ "filename": "Lance_04.png",
+ "frame": {"x":53,"y":142,"w":24,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":34},
+ "sourceSize": {"w":24,"h":34}
+ },
+ {
+ "filename": "Lance_05.png",
+ "frame": {"x":62,"y":0,"w":24,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":34},
+ "sourceSize": {"w":24,"h":34}
+ },
+ {
+ "filename": "Lance_06.png",
+ "frame": {"x":53,"y":107,"w":24,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":33},
+ "sourceSize": {"w":24,"h":33}
+ },
+ {
+ "filename": "Lance_07.png",
+ "frame": {"x":303,"y":82,"w":14,"h":46},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":46},
+ "sourceSize": {"w":14,"h":46}
+ },
+ {
+ "filename": "Lance_08.png",
+ "frame": {"x":320,"y":209,"w":14,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":45},
+ "sourceSize": {"w":14,"h":45}
+ },
+ {
+ "filename": "Lance_09.png",
+ "frame": {"x":138,"y":0,"w":21,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":34},
+ "sourceSize": {"w":21,"h":34}
+ },
+ {
+ "filename": "Lance_10.png",
+ "frame": {"x":152,"y":121,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Lance_11.png",
+ "frame": {"x":289,"y":0,"w":17,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":17,"h":36},
+ "sourceSize": {"w":17,"h":36}
+ },
+ {
+ "filename": "Lance_12.png",
+ "frame": {"x":302,"y":171,"w":17,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":17,"h":36},
+ "sourceSize": {"w":17,"h":36}
+ },
+ {
+ "filename": "Lance_13.png",
+ "frame": {"x":153,"y":75,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Lance_14.png",
+ "frame": {"x":161,"y":0,"w":21,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":34},
+ "sourceSize": {"w":21,"h":34}
+ },
+ {
+ "filename": "Lance_15.png",
+ "frame": {"x":78,"y":178,"w":23,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":33},
+ "sourceSize": {"w":23,"h":33}
+ },
+ {
+ "filename": "Lance_16.png",
+ "frame": {"x":113,"y":0,"w":23,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":35},
+ "sourceSize": {"w":23,"h":35}
+ },
+ {
+ "filename": "Lance_17.png",
+ "frame": {"x":159,"y":36,"w":21,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":35},
+ "sourceSize": {"w":21,"h":35}
+ },
+ {
+ "filename": "Lance_18.png",
+ "frame": {"x":175,"y":113,"w":21,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":35},
+ "sourceSize": {"w":21,"h":35}
+ },
+ {
+ "filename": "Lance_19.png",
+ "frame": {"x":104,"y":89,"w":23,"h":35},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":35},
+ "sourceSize": {"w":23,"h":35}
+ },
+ {
+ "filename": "Lance_20.png",
+ "frame": {"x":103,"y":206,"w":23,"h":33},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":33},
+ "sourceSize": {"w":23,"h":33}
+ },
+ {
+ "filename": "Lance_21.png",
+ "frame": {"x":249,"y":0,"w":18,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":36},
+ "sourceSize": {"w":18,"h":36}
+ },
+ {
+ "filename": "Lance_22.png",
+ "frame": {"x":175,"y":150,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Lance_23.png",
+ "frame": {"x":112,"y":55,"w":22,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":22,"h":31},
+ "sourceSize": {"w":22,"h":31}
+ },
+ {
+ "filename": "Lance_24.png",
+ "frame": {"x":263,"y":72,"w":18,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":34},
+ "sourceSize": {"w":18,"h":34}
+ },
+ {
+ "filename": "Lance_25.png",
+ "frame": {"x":206,"y":0,"w":20,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":34},
+ "sourceSize": {"w":20,"h":34}
+ },
+ {
+ "filename": "Lance_26.png",
+ "frame": {"x":184,"y":0,"w":20,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":34},
+ "sourceSize": {"w":20,"h":34}
+ },
+ {
+ "filename": "Lance_27.png",
+ "frame": {"x":269,"y":0,"w":18,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":34},
+ "sourceSize": {"w":18,"h":34}
+ },
+ {
+ "filename": "Lance_28.png",
+ "frame": {"x":128,"y":194,"w":22,"h":31},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":22,"h":31},
+ "sourceSize": {"w":22,"h":31}
+ },
+ {
+ "filename": "Lance_29.png",
+ "frame": {"x":175,"y":188,"w":21,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":21,"h":36},
+ "sourceSize": {"w":21,"h":36}
+ },
+ {
+ "filename": "Lance_30.png",
+ "frame": {"x":242,"y":197,"w":18,"h":36},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":36},
+ "sourceSize": {"w":18,"h":36}
+ },
+ {
+ "filename": "Lance_31.png",
+ "frame": {"x":289,"y":38,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Lance_32.png",
+ "frame": {"x":172,"y":233,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Lance_33.png",
+ "frame": {"x":303,"y":60,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Lance_34.png",
+ "frame": {"x":113,"y":37,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Lance_35.png",
+ "frame": {"x":0,"y":77,"w":34,"h":17},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":17},
+ "sourceSize": {"w":34,"h":17}
+ },
+ {
+ "filename": "Lance_36.png",
+ "frame": {"x":0,"y":96,"w":34,"h":17},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":17},
+ "sourceSize": {"w":34,"h":17}
+ },
+ {
+ "filename": "Lance_37.png",
+ "frame": {"x":150,"y":233,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Lance_38.png",
+ "frame": {"x":302,"y":209,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Lance_39.png",
+ "frame": {"x":128,"y":227,"w":20,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":16},
+ "sourceSize": {"w":20,"h":16}
+ },
+ {
+ "filename": "Lance_40.png",
+ "frame": {"x":307,"y":38,"w":16,"h":20},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":20},
+ "sourceSize": {"w":16,"h":20}
+ },
+ {
+ "filename": "Lance_41.png",
+ "frame": {"x":0,"y":64,"w":34,"h":11},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":11},
+ "sourceSize": {"w":34,"h":11}
+ },
+ {
+ "filename": "Lance_42.png",
+ "frame": {"x":283,"y":108,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Lance_43.png",
+ "frame": {"x":52,"y":235,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Lance_44.png",
+ "frame": {"x":243,"y":72,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Lance_45.png",
+ "frame": {"x":27,"y":200,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Lance_46.png",
+ "frame": {"x":53,"y":178,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Lance_47.png",
+ "frame": {"x":227,"y":47,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Lance_48.png",
+ "frame": {"x":79,"y":89,"w":23,"h":18},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":18},
+ "sourceSize": {"w":23,"h":18}
+ },
+ {
+ "filename": "Lance_49.png",
+ "frame": {"x":262,"y":222,"w":18,"h":23},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":18,"h":23},
+ "sourceSize": {"w":18,"h":23}
+ },
+ {
+ "filename": "Lance_50.png",
+ "frame": {"x":0,"y":13,"w":34,"h":11},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":34,"h":11},
+ "sourceSize": {"w":34,"h":11}
+ },
+ {
+ "filename": "Lance_51.png",
+ "frame": {"x":87,"y":71,"w":23,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":16},
+ "sourceSize": {"w":23,"h":16}
+ },
+ {
+ "filename": "Lance_52.png",
+ "frame": {"x":62,"y":89,"w":12,"h":13},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":12,"h":13},
+ "sourceSize": {"w":12,"h":13}
+ },
+ {
+ "filename": "Lance_53.png",
+ "frame": {"x":36,"y":107,"w":12,"h":13},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":12,"h":13},
+ "sourceSize": {"w":12,"h":13}
+ },
+ {
+ "filename": "Lance_54.png",
+ "frame": {"x":62,"y":71,"w":23,"h":16},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":23,"h":16},
+ "sourceSize": {"w":23,"h":16}
+ },
+ {
+ "filename": "Lance_55.png",
+ "frame": {"x":228,"y":0,"w":19,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":19,"h":45},
+ "sourceSize": {"w":19,"h":45}
+ },
+ {
+ "filename": "Lance_56.png",
+ "frame": {"x":0,"y":175,"w":25,"h":45},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":25,"h":45},
+ "sourceSize": {"w":25,"h":45}
+ },
+ {
+ "filename": "Lance_57.png",
+ "frame": {"x":242,"y":156,"w":19,"h":39},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":19,"h":39},
+ "sourceSize": {"w":19,"h":39}
+ },
+ {
+ "filename": "Lance_58.png",
+ "frame": {"x":221,"y":72,"w":20,"h":41},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":20,"h":41},
+ "sourceSize": {"w":20,"h":41}
+ },
+ {
+ "filename": "Lance_59.png",
+ "frame": {"x":0,"y":222,"w":24,"h":22},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":22},
+ "sourceSize": {"w":24,"h":22}
+ },
+ {
+ "filename": "Lance_60.png",
+ "frame": {"x":27,"y":128,"w":24,"h":34},
+ "rotated": false,
+ "trimmed": true,
+ "spriteSourceSize": {"x":0,"y":0,"w":24,"h":34},
+ "sourceSize": {"w":24,"h":34}
+ },
+ {"EOL":"true"}
+],
+"meta": {
+ "app": "ShoeBox",
+ "exporter": "melonJS",
+ "size": {"w":512,"h":256}
+}
+}
\ No newline at end of file
diff --git a/Games/Contra-Shooter/data/img/sprite/bill-lance.png b/Games/Contra-Shooter/data/img/sprite/bill-lance.png
new file mode 100644
index 0000000000..7e66088d5e
Binary files /dev/null and b/Games/Contra-Shooter/data/img/sprite/bill-lance.png differ
diff --git a/Games/Contra-Shooter/data/img/sprite/explosion-sprite.png b/Games/Contra-Shooter/data/img/sprite/explosion-sprite.png
new file mode 100644
index 0000000000..118beb66f1
Binary files /dev/null and b/Games/Contra-Shooter/data/img/sprite/explosion-sprite.png differ
diff --git a/Games/Contra-Shooter/data/img/sprite/power-ups-sprite.png b/Games/Contra-Shooter/data/img/sprite/power-ups-sprite.png
new file mode 100644
index 0000000000..fd417d8bae
Binary files /dev/null and b/Games/Contra-Shooter/data/img/sprite/power-ups-sprite.png differ
diff --git a/Games/Contra-Shooter/data/img/sprite/rock_platform-sprite.png b/Games/Contra-Shooter/data/img/sprite/rock_platform-sprite.png
new file mode 100644
index 0000000000..c66433d45f
Binary files /dev/null and b/Games/Contra-Shooter/data/img/sprite/rock_platform-sprite.png differ
diff --git a/Games/Contra-Shooter/data/map/contra-lvl-0.tmx b/Games/Contra-Shooter/data/map/contra-lvl-0.tmx
new file mode 100644
index 0000000000..f9bea17a53
--- /dev/null
+++ b/Games/Contra-Shooter/data/map/contra-lvl-0.tmx
@@ -0,0 +1,66 @@
+
+
diff --git a/Games/Contra-Shooter/data/map/contra-lvl-1.tmx b/Games/Contra-Shooter/data/map/contra-lvl-1.tmx
new file mode 100644
index 0000000000..6aaae7095c
--- /dev/null
+++ b/Games/Contra-Shooter/data/map/contra-lvl-1.tmx
@@ -0,0 +1,68 @@
+
+
diff --git a/Games/Contra-Shooter/data/map/contra-tileset-1.tmx b/Games/Contra-Shooter/data/map/contra-tileset-1.tmx
new file mode 100644
index 0000000000..2d41291607
--- /dev/null
+++ b/Games/Contra-Shooter/data/map/contra-tileset-1.tmx
@@ -0,0 +1,25 @@
+
+
diff --git a/Games/Contra-Shooter/data/map/contra-tileset-2.tmx b/Games/Contra-Shooter/data/map/contra-tileset-2.tmx
new file mode 100644
index 0000000000..202312e61b
--- /dev/null
+++ b/Games/Contra-Shooter/data/map/contra-tileset-2.tmx
@@ -0,0 +1,34 @@
+
+
diff --git a/Games/Contra-Shooter/data/sfx/cling.mp3 b/Games/Contra-Shooter/data/sfx/cling.mp3
new file mode 100644
index 0000000000..a879cf68e0
Binary files /dev/null and b/Games/Contra-Shooter/data/sfx/cling.mp3 differ
diff --git a/Games/Contra-Shooter/data/sfx/cling.ogg b/Games/Contra-Shooter/data/sfx/cling.ogg
new file mode 100644
index 0000000000..c345a4c0a7
Binary files /dev/null and b/Games/Contra-Shooter/data/sfx/cling.ogg differ
diff --git a/Games/Contra-Shooter/data/sfx/jump.mp3 b/Games/Contra-Shooter/data/sfx/jump.mp3
new file mode 100644
index 0000000000..1e8aebff88
Binary files /dev/null and b/Games/Contra-Shooter/data/sfx/jump.mp3 differ
diff --git a/Games/Contra-Shooter/data/sfx/jump.ogg b/Games/Contra-Shooter/data/sfx/jump.ogg
new file mode 100644
index 0000000000..a4cf2c1b1f
Binary files /dev/null and b/Games/Contra-Shooter/data/sfx/jump.ogg differ
diff --git a/Games/Contra-Shooter/data/sfx/stomp.mp3 b/Games/Contra-Shooter/data/sfx/stomp.mp3
new file mode 100644
index 0000000000..3e6d0a2a24
Binary files /dev/null and b/Games/Contra-Shooter/data/sfx/stomp.mp3 differ
diff --git a/Games/Contra-Shooter/data/sfx/stomp.ogg b/Games/Contra-Shooter/data/sfx/stomp.ogg
new file mode 100644
index 0000000000..0fdd4c3edd
Binary files /dev/null and b/Games/Contra-Shooter/data/sfx/stomp.ogg differ
diff --git a/Games/Contra-Shooter/icons/touch-icon-ipad-76x76.png b/Games/Contra-Shooter/icons/touch-icon-ipad-76x76.png
new file mode 100644
index 0000000000..142f22f241
Binary files /dev/null and b/Games/Contra-Shooter/icons/touch-icon-ipad-76x76.png differ
diff --git a/Games/Contra-Shooter/icons/touch-icon-ipad-retina-152x152.png b/Games/Contra-Shooter/icons/touch-icon-ipad-retina-152x152.png
new file mode 100644
index 0000000000..960fae6c33
Binary files /dev/null and b/Games/Contra-Shooter/icons/touch-icon-ipad-retina-152x152.png differ
diff --git a/Games/Contra-Shooter/icons/touch-icon-iphone-60x60.png b/Games/Contra-Shooter/icons/touch-icon-iphone-60x60.png
new file mode 100644
index 0000000000..9453e148ed
Binary files /dev/null and b/Games/Contra-Shooter/icons/touch-icon-iphone-60x60.png differ
diff --git a/Games/Contra-Shooter/icons/touch-icon-iphone-retina-120x120.png b/Games/Contra-Shooter/icons/touch-icon-iphone-retina-120x120.png
new file mode 100644
index 0000000000..9c27b0856c
Binary files /dev/null and b/Games/Contra-Shooter/icons/touch-icon-iphone-retina-120x120.png differ
diff --git a/Games/Contra-Shooter/icons/touch-icon-marketplace-128x128.png b/Games/Contra-Shooter/icons/touch-icon-marketplace-128x128.png
new file mode 100644
index 0000000000..e6796420d7
Binary files /dev/null and b/Games/Contra-Shooter/icons/touch-icon-marketplace-128x128.png differ
diff --git a/Games/Contra-Shooter/index.css b/Games/Contra-Shooter/index.css
new file mode 100644
index 0000000000..6ffeae8b90
--- /dev/null
+++ b/Games/Contra-Shooter/index.css
@@ -0,0 +1,53 @@
+body {
+ background-color: #4d4d4d;
+ color: #fff;
+
+ /* Allow mouse dragging. */
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+
+ /* disable touch panning/zooming */
+ -ms-touch-action: none;
+ touch-action: none;
+
+ /* Allow canvas to hit the edges of the browser viewport. */
+ margin: 0;
+}
+
+#screen canvas {
+ margin: auto;
+ display: block;
+
+ /* disable scaling interpolation */
+ image-rendering: optimizeSpeed;
+ image-rendering: -moz-crisp-edges;
+ image-rendering: -o-crisp-edges;
+ image-rendering: -webkit-optimize-contrast;
+ image-rendering: optimize-contrast;
+ image-rendering: pixelated;
+ -ms-interpolation-mode: nearest-neighbor;
+}
+
+.html-space {
+
+ position: absolute;
+ margin: auto;
+ z-index: 2;
+ transform: translate(50%, -50%);
+ color: white;
+}
+
+.html-flex{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.html-input {
+
+ display: block;
+ z-index: 2;
+}
\ No newline at end of file
diff --git a/Games/Contra-Shooter/index.html b/Games/Contra-Shooter/index.html
new file mode 100644
index 0000000000..64b18b123a
--- /dev/null
+++ b/Games/Contra-Shooter/index.html
@@ -0,0 +1,80 @@
+
+
+
You generally should not add new properties to this namespace as it may be + * overwritten in future versions.
+ * @name me + * @namespace + */ + window.me = window.me || {}; + + /* + * DOM loading stuff + */ + var readyBound = false, isReady = false, readyList = []; + + // Handle when the DOM is ready + function domReady() { + // Make sure that the DOM is not already loaded + if (!isReady) { + // be sure document.body is there + if (!document.body) { + return setTimeout(domReady, 13); + } + + // clean up loading event + if (document.removeEventListener) { + document.removeEventListener( + "DOMContentLoaded", + domReady, + false + ); + } + // remove the event on window.onload (always added in `onReady`) + window.removeEventListener("load", domReady, false); + + // execute all callbacks + while (readyList.length){ + readyList.shift().call(window, []); + } + + // Remember that the DOM is ready + isReady = true; + + /* + * Add support for AMD (Asynchronous Module Definition) libraries + * such as require.js. + */ + if (typeof define === "function" && define.amd) { + define("me", [], function () { + return me; + }); + } + } + } + + /** + * Specify a function to execute when the DOM is fully loaded + * @memberOf external:window# + * @alias onReady + * @param {Function} fn A function to execute after the DOM is ready. + * @example + * // small main skeleton + * var game = { + * // Initialize the game + * // called by the window.onReady function + * onload : function () { + * // init video + * if (!me.video.init('screen', 640, 480, true)) { + * alert("Sorry but your browser does not support html 5 canvas."); + * return; + * } + * + * // initialize the "audio" + * me.audio.init("mp3,ogg"); + * + * // set callback for ressources loaded event + * me.loader.onload = this.loaded.bind(this); + * + * // set all ressources to be loaded + * me.loader.preload(game.resources); + * + * // load everything & display a loading screen + * me.state.change(me.state.LOADING); + * }, + * + * // callback when everything is loaded + * loaded : function () { + * // define stuff + * // .... + * + * // change to the menu screen + * me.state.change(me.state.MENU); + * } + * }; // game + * + * // "bootstrap" + * window.onReady(function () { + * game.onload(); + * }); + */ + window.onReady = function (fn) { + // If the DOM is already ready + if (isReady) { + // Execute the function immediately + fn.call(window, []); + } + else { + // Add the function to the wait list + readyList.push(fn); + + // attach listeners if not yet done + if (!readyBound) { + // directly call domReady if document is already "ready" + if (document.readyState === "complete") { + // defer the fn call to ensure our script is fully loaded + window.setTimeout(domReady, 0); + } + else { + if (document.addEventListener) { + // Use the handy event callback + document.addEventListener("DOMContentLoaded", domReady, false); + } + // A fallback to window.onload, that will always work + window.addEventListener("load", domReady, false); + } + readyBound = true; + } + } + }; + + // call the library init function when ready + // (this should not be here?) + if (me.skipAutoInit !== true) { + window.onReady(function () { + me.boot(); + }); + } + else { + /** + * @ignore + */ + me.init = function () { + me.boot(); + domReady(); + }; + } + + if (!window.throttle) { + /** + * a simple throttle function + * use same fct signature as the one in prototype + * in case it's already defined before + * @ignore + */ + window.throttle = function (delay, no_trailing, callback) { + var last = window.performance.now(), deferTimer; + // `no_trailing` defaults to false. + if (typeof no_trailing !== "boolean") { + no_trailing = false; + } + return function () { + var now = window.performance.now(); + var elasped = now - last; + var args = arguments; + if (elasped < delay) { + if (no_trailing === false) { + // hold on to it + clearTimeout(deferTimer); + deferTimer = setTimeout(function () { + last = now; + return callback.apply(null, args); + }, elasped); + } + } + else { + last = now; + return callback.apply(null, args); + } + }; + }; + } + + if (typeof console === "undefined") { + /** + * Dummy console.log to avoid crash + * in case the browser does not support it + * @ignore + */ + console = { // jshint ignore:line + log : function () {}, + info : function () {}, + error : function () { + alert(Array.prototype.slice.call(arguments).join(", ")); + } + }; + } + +})(); +/* eslint-enable space-before-blocks, no-global-assign, no-native-reassign */ + +/** + * MelonJS Game Engine + * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod + * http://www.melonjs.org + */ + +/* eslint-disable no-extend-native */ + +/** + * The built in Function Object + * @external Function + * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function|Function} + */ +if (!Function.prototype.bind) { + /** @ignore */ + var Empty = function () {}; + + /** + * Binds this function to the given context by wrapping it in another function and returning the wrapper.+ * Whenever the resulting "bound" function is called, it will call the original ensuring that this is set to context.
+ * Also optionally curries arguments for the function.
+ * @memberof! external:Function#
+ * @alias bind
+ * @param {Object} that the object to bind to.
+ * @param {} [arguments...] Optional additional arguments to curry for the function.
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind|Function.bind}
+ * @example
+ * // Ensure that our callback is triggered with the right object context (this):
+ * myObject.onComplete(this.callback.bind(this));
+ */
+ Function.prototype.bind = function bind(that) {
+ // ECMAScript 5 compliant implementation
+ // http://es5.github.com/#x15.3.4.5
+ // from https://github.com/kriskowal/es5-shim
+ var target = this;
+ if (typeof target !== "function") {
+ throw new TypeError("Function.prototype.bind called on incompatible " + target);
+ }
+ var args = Array.prototype.slice.call(arguments, 1);
+ var bound = function () {
+ if (this instanceof bound) {
+ var result = target.apply(this, args.concat(Array.prototype.slice.call(arguments)));
+ if (Object(result) === result) {
+ return result;
+ }
+ return this;
+ }
+ else {
+ return target.apply(that, args.concat(Array.prototype.slice.call(arguments)));
+ }
+ };
+ if (target.prototype) {
+ Empty.prototype = target.prototype;
+ bound.prototype = new Empty();
+ Empty.prototype = null;
+ }
+ return bound;
+ };
+}
+
+/**
+ * Executes a function as soon as the interpreter is idle (stack empty).
+ * @memberof! external:Function#
+ * @alias defer
+ * @param {Object} context The execution context of the deferred function.
+ * @param {} [arguments...] Optional additional arguments to carry for the
+ * function.
+ * @return {Number} id that can be used to clear the deferred function using
+ * clearTimeout
+ * @example
+ * // execute myFunc() when the stack is empty,
+ * // with the current context and 'myArgument' as parameter
+ * myFunc.defer(this, 'myArgument');
+ */
+Function.prototype.defer = function () {
+ return setTimeout(this.bind.apply(this, arguments), 0.01);
+};
+/* eslint-enable no-extend-native */
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+/**
+ * The built in Object object.
+ * @external Object
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object|Object}
+ */
+
+/* eslint-disable no-self-compare */
+
+if (!Object.defineProperty) {
+ /**
+ * simple defineProperty function definition (if not supported by the browser)
+ * if defineProperty is redefined, internally use __defineGetter__/__defineSetter__ as fallback
+ * @param {Object} obj The object on which to define the property.
+ * @param {string} prop The name of the property to be defined or modified.
+ * @param {Object} desc The descriptor for the property being defined or modified.
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty|Object.defineProperty}
+ */
+ Object.defineProperty = function (obj, prop, desc) {
+ // check if Object support __defineGetter function
+ if (obj.__defineGetter__) {
+ if (desc.get) {
+ obj.__defineGetter__(prop, desc.get);
+ }
+ if (desc.set) {
+ obj.__defineSetter__(prop, desc.set);
+ }
+ } else {
+ // we should never reach this point....
+ throw new TypeError("Object.defineProperty not supported");
+ }
+ };
+}
+
+if (!Object.create) {
+ /**
+ * Prototypal Inheritance Create Helper
+ * @name create
+ * @memberOf external:Object#
+ * @function
+ * @param {Object} o
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create|Object.create}
+ * @example
+ * // declare oldObject
+ * oldObject = new Object();
+ * // make some crazy stuff with oldObject (adding functions, etc...)
+ * // ...
+ *
+ * // make newObject inherits from oldObject
+ * newObject = Object.create(oldObject);
+ */
+ Object.create = function (o) {
+ var Fn = function () {};
+ Fn.prototype = o;
+ return new Fn();
+ };
+}
+
+if (!Object.is) {
+ /**
+ * The Object.is() method determines whether two values are the same value.
+ * @name is
+ * @memberOf external:Object#
+ * @function
+ * @param {Object} a The first value to compare
+ * @param {Object} b The second value to compare
+ * @return {Boolean}
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is|Object.is}
+ * @example
+ * // Strings are equal
+ * var s = "foo";
+ * Object.is(s, "foo"); //> true
+ *
+ * // 0 and -0 are not equal
+ * Object.is(0, -0); //>false
+ *
+ * // NaN and NaN are equal
+ * Object.is(NaN, NaN); //> true
+ *
+ * // Two object references are not equal
+ * Object.is({}, {}); //> false
+ *
+ * // Two vars referencing one object are equal
+ * var a = {}, b = a;
+ * Object.is(a, b); //> true
+ */
+ Object.is = function(a, b) {
+ // SameValue algorithm
+ if (a === b) { // Steps 1-5, 7-10
+ // Steps 6.b-6.e: +0 != -0
+ return a !== 0 || 1 / a === 1 / b;
+ } else {
+ // Step 6.a: NaN == NaN
+ return a !== a && b !== b;
+ }
+ };
+}
+
+if (!Object.assign) {
+ (function () {
+ /**
+ * The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
+ * The Object.assign method only copies enumerable and own properties from a source object to a target object.
+ * It uses [[Get]] on the source and [[Put]] on the target, so it will invoke getters and setters.
+ * Therefore it assigns properties versus just copying or defining new properties.
+ * This may make it unsuitable for merging new properties into a prototype if the merge sources contain getters.
+ * For copying propertiy definitions, including their enumerability, into prototypes Object.getOwnPropertyDescriptor and Object.defineProperty should be used instead.
+ * @name assign
+ * @memberOf external:Object#
+ * @function
+ * @param {Object} target The target object.
+ * @param {Object[]} sources The source object(s).
+ * @return {Object} The target object gets returned.
+ * @see {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign}
+ * @example
+ * // Merging objects
+ * var o1 = { a: 1 };
+ * var o2 = { b: 2 };
+ * var o3 = { c: 3 };
+ *
+ * var obj = Object.assign(o1, o2, o3);
+ * console.log(obj);
+ * // { a: 1, b: 2, c: 3 }
+ */
+ Object.assign = function (target) {
+ "use strict";
+ // We must check against these specific cases.
+ if (target === undefined || target === null) {
+ throw new TypeError("Cannot convert undefined or null to object");
+ }
+
+ var output = Object(target);
+ for (var index = 1; index < arguments.length; index++) {
+ var source = arguments[index];
+ if (source !== undefined && source !== null) {
+ for (var nextKey in source) {
+ if (source.hasOwnProperty(nextKey)) {
+ output[nextKey] = source[nextKey];
+ }
+ }
+ }
+ }
+ return output;
+ };
+ })();
+}
+
+/**
+ * Extend a class prototype with the provided mixin descriptors.
+ * Designed as a faster replacement for John Resig's Simple Inheritance.
+ * @name extend
+ * @memberOf me.Object
+ * @function
+ * @param {Object[]} mixins... Each mixin is a dictionary of functions, or a
+ * previously extended class whose methods will be applied to the target class
+ * prototype.
+ * @return {Object}
+ * @example
+ * var Person = me.Object.extend({
+ * "init" : function (isDancing) {
+ * this.dancing = isDancing;
+ * },
+ * "dance" : function () {
+ * return this.dancing;
+ * }
+ * });
+ *
+ * var Ninja = Person.extend({
+ * "init" : function () {
+ * // Call the super constructor, passing a single argument
+ * Person.prototype.init.apply(this, [false]);
+ * },
+ * "dance" : function () {
+ * // Call the overridden dance() method
+ * return Person.prototype.dance.apply(this);
+ * },
+ * "swingSword" : function () {
+ * return true;
+ * }
+ * });
+ *
+ * var Pirate = Person.extend(Ninja, {
+ * "init" : function () {
+ * // Call the super constructor, passing a single argument
+ * Person.prototype.init.apply(this, [true]);
+ * }
+ * });
+ *
+ * var p = new Person(true);
+ * console.log(p.dance()); // => true
+ *
+ * var n = new Ninja();
+ * console.log(n.dance()); // => false
+ * console.log(n.swingSword()); // => true
+ *
+ * var r = new Pirate();
+ * console.log(r.dance()); // => true
+ * console.log(r.swingSword()); // => true
+ *
+ * console.log(
+ * p instanceof Person &&
+ * n instanceof Ninja &&
+ * n instanceof Person &&
+ * r instanceof Pirate &&
+ * r instanceof Person
+ * ); // => true
+ *
+ * console.log(r instanceof Ninja); // => false
+ */
+(function () {
+ function extend() {
+ var methods = {};
+ var mixins = new Array(arguments.length);
+ for (var i = 0; i < arguments.length; i++) {
+ mixins.push(arguments[i]);
+ }
+
+ /**
+ * The class constructor which calls the user `init` constructor.
+ * @ignore
+ */
+ function Class() {
+ // Call the user constructor
+ this.init.apply(this, arguments);
+ return this;
+ }
+
+ // Apply superClass
+ Class.prototype = Object.create(this.prototype);
+
+ // Apply all mixin methods to the class prototype
+ mixins.forEach(function (mixin) {
+ apply_methods(Class, methods, mixin.__methods__ || mixin);
+ });
+
+ // Verify constructor exists
+ if (!("init" in Class.prototype)) {
+ throw new TypeError(
+ "extend: Class is missing a constructor named `init`"
+ );
+ }
+
+ // Apply syntactic sugar for accessing methods on super classes
+ Object.defineProperty(Class.prototype, "_super", {
+ "value" : _super
+ });
+
+ // Create a hidden property on the class itself
+ // List of methods, used for applying classes as mixins
+ Object.defineProperty(Class, "__methods__", {
+ "value" : methods
+ });
+
+ // Make this class extendable
+ Class.extend = extend;
+
+ return Class;
+ }
+
+ /**
+ * Apply methods to the class prototype.
+ * @ignore
+ */
+ function apply_methods(Class, methods, descriptor) {
+ Object.keys(descriptor).forEach(function (method) {
+ methods[method] = descriptor[method];
+
+ if (typeof(descriptor[method]) !== "function") {
+ throw new TypeError(
+ "extend: Method `" + method + "` is not a function"
+ );
+ }
+
+ Object.defineProperty(Class.prototype, method, {
+ "configurable" : true,
+ "value" : descriptor[method]
+ });
+ });
+ }
+
+ /**
+ * Special method that acts as a proxy to the super class.
+ * @name _super
+ * @ignore
+ */
+ function _super(superClass, method, args) {
+ return superClass.prototype[method].apply(this, args);
+ }
+
+ /**
+ * The base class from which all jay-extend classes inherit.
+ * @ignore
+ */
+ var Jay = function () {
+ Object.apply(this, arguments);
+ };
+ Jay.prototype = Object.create(Object.prototype);
+ Jay.prototype.constructor = Jay;
+
+ Object.defineProperty(Jay, "extend", {
+ "value" : extend
+ });
+
+ /**
+ * The base class from which all melonJS objects inherit.
+ * See: {@link https://github.com/parasyte/jay-extend}
+ * @class
+ * @extends external:Object#
+ * @memberOf me
+ */
+ me.Object = Jay;
+})();
+/* eslint-enable no-self-compare */
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+/**
+ * The built in Error object.
+ * @external Error
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error|Error}
+ */
+
+/**
+ * melonJS base class for exception handling.
+ * @name Error
+ * @memberOf me
+ * @constructor
+ * @param {String} msg Error message.
+ */
+me.Error = me.Object.extend.bind(Error)({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ this.name = "me.Error";
+ this.message = msg;
+ }
+});
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+ /**
+ * The built in Math Object
+ * @external Math
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Math|Math}
+ */
+
+if (!Math.sign) {
+ /**
+ * The Math.sign() function returns the sign of a number, indicating whether the number is positive, negative or zero.
+ * @memberof! external:Math#
+ * @alias sign
+ * @param {number} x a number.
+ * @return {number} sign of the number
+ */
+ Math.sign = function(x) {
+ x = +x; // convert to a number
+ if (x === 0 || isNaN(x)) {
+ return x;
+ }
+ return x > 0 ? 1 : -1;
+ };
+}
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+ /**
+ * The built in Number Object
+ * @external Number
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Number|Number}
+ */
+
+/* eslint-disable no-extend-native */
+
+/**
+ * add a clamp fn to the Number object
+ * @memberof! external:Number#
+ * @alias clamp
+ * @param {number} low lower limit
+ * @param {number} high higher limit
+ * @return {number} clamped value
+ */
+Number.prototype.clamp = function (low, high) {
+ return this < low ? low : this > high ? high : +this;
+};
+
+/**
+ * return a random integer between min (included) and max (excluded)
+ * @memberof! external:Number#
+ * @alias random
+ * @param {number} [min=this] minimum value.
+ * @param {number} max maximum value.
+ * @return {number} random value
+ * @example
+ * // Print a random number; one of 5, 6, 7, 8, 9
+ * console.log( (5).random(10) );
+ * // Select a random array element
+ * var ar = [ "foo", "bar", "baz" ];
+ * console.log(ar[ (0).random(ar.length) ]);
+ */
+Number.prototype.random = function (min, max) {
+ if (!max) {
+ max = min;
+ min = this;
+ }
+ return (~~(Math.random() * (max - min)) + min);
+};
+
+/**
+ * return a random float between min, max (exclusive)
+ * @memberof! external:Number#
+ * @alias randomFloat
+ * @param {number} [min=this] minimum value.
+ * @param {number} max maximum value.
+ * @return {number} random value
+ * @example
+ * // Print a random number; one of 5, 6, 7, 8, 9
+ * console.log( (5).random(10) );
+ * // Select a random array element
+ * var ar = [ "foo", "bar", "baz" ];
+ * console.log(ar[ (0).random(ar.length) ]);
+ */
+Number.prototype.randomFloat = function (min, max) {
+ if (!max) {
+ max = min;
+ min = this;
+ }
+ return (Math.random() * (max - min)) + min;
+};
+
+/**
+ * return a weighted random between min, max (exclusive)
+ * favoring the lower numbers
+ * @memberof! external:Number#
+ * @alias weightedRandom
+ * @param {number} [min=this] minimum value.
+ * @param {number} max maximum value.
+ * @return {number} random value
+ * @example
+ * // Print a random number; one of 5, 6, 7, 8, 9
+ * console.log( (5).random(10) );
+ * // Select a random array element
+ * var ar = [ "foo", "bar", "baz" ];
+ * console.log(ar[ (0).random(ar.length) ]);
+ */
+Number.prototype.weightedRandom = function (min, max) {
+ if (!max) {
+ max = min;
+ min = this;
+ }
+ return (~~(Math.pow(Math.random(), 2) * (max - min)) + min);
+};
+
+/**
+ * round a value to the specified number of digit
+ * @memberof! external:Number#
+ * @alias round
+ * @param {number} [num=this] value to be rounded.
+ * @param {number} dec number of decimal digit to be rounded to.
+ * @return {number} rounded value
+ * @example
+ * // round a specific value to 2 digits
+ * Number.prototype.round (10.33333, 2); // return 10.33
+ * // round a float value to 4 digits
+ * num = 10.3333333
+ * num.round(4); // return 10.3333
+ */
+Number.prototype.round = function (num, dec) {
+ // if only one argument use the object value
+ num = (arguments.length < 2) ? this : num;
+ var powres = Math.pow(10, dec || num || 0);
+ return (~~(0.5 + num * powres) / powres);
+};
+
+/**
+ * a quick toHex function
+ * given number must be an int, with a value between 0 and 255
+ * @memberof! external:Number#
+ * @alias toHex
+ * @return {string} converted hexadecimal value
+ */
+Number.prototype.toHex = function () {
+ return "0123456789ABCDEF".charAt((this - (this % 16)) >> 4) + "0123456789ABCDEF".charAt(this % 16);
+};
+
+/**
+ * Converts an angle in degrees to an angle in radians
+ * @memberof! external:Number#
+ * @alias degToRad
+ * @param {number} [angle="angle"] angle in degrees
+ * @return {number} corresponding angle in radians
+ * @example
+ * // convert a specific angle
+ * Number.prototype.degToRad (60); // return 1.0471...
+ * // convert object value
+ * var num = 60
+ * num.degToRad(); // return 1.0471...
+ */
+Number.prototype.degToRad = function (angle) {
+ return (angle || this) / 180.0 * Math.PI;
+};
+
+/**
+ * Converts an angle in radians to an angle in degrees.
+ * @memberof! external:Number#
+ * @alias radToDeg
+ * @param {number} [angle="angle"] angle in radians
+ * @return {number} corresponding angle in degrees
+ * @example
+ * // convert a specific angle
+ * Number.prototype.radToDeg (1.0471975511965976); // return 59.9999...
+ * // convert object value
+ * num = 1.0471975511965976
+ * Math.ceil(num.radToDeg()); // return 60
+ */
+Number.prototype.radToDeg = function (angle) {
+ return (angle || this) * (180.0 / Math.PI);
+};
+/* eslint-enable no-extend-native */
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+/**
+ * The built in String Object
+ * @external String
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String|String}
+ */
+
+/* eslint-disable no-extend-native, yoda */
+
+if (!String.prototype.trim) {
+ if (!String.prototype.trim) {
+ (function() {
+ // Make sure we trim BOM and NBSP
+ var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
+ /**
+ * removes whitespace from both ends of a string. Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).
+ * @memberof! external:String#
+ * @alias trim
+ * @return {string} the string stripped of whitespace from both ends.
+ */
+ String.prototype.trim = function() {
+ return this.replace(rtrim, "");
+ };
+ })();
+ }
+}
+
+if (!String.prototype.trimLeft) {
+ /**
+ * returns the string stripped of whitespace from the left.
+ * @memberof! external:String#
+ * @alias trimLeft
+ * @return {string} trimmed string
+ */
+ String.prototype.trimLeft = function () {
+ return this.replace(/^\s+/, "");
+ };
+}
+
+if (!String.prototype.trimRight) {
+ /**
+ * returns the string stripped of whitespace from the right.
+ * @memberof! external:String#
+ * @alias trimRight
+ * @return {string} trimmed string
+ */
+ String.prototype.trimRight = function () {
+ return this.replace(/\s+$/, "");
+ };
+}
+
+/**
+ * add isNumeric fn to the string object
+ * @memberof! external:String#
+ * @alias isNumeric
+ * @return {boolean} true if string contains only digits
+ */
+String.prototype.isNumeric = function () {
+ return (!isNaN(this) && this.trim() !== "");
+};
+
+/**
+ * add a isBoolean fn to the string object
+ * @memberof! external:String#
+ * @alias isBoolean
+ * @return {boolean} true if the string is either true or false
+ */
+String.prototype.isBoolean = function () {
+ var trimmed = this.trim();
+ return ("true" === trimmed) || ("false" === trimmed);
+};
+
+if (!String.prototype.includes) {
+ /**
+ * determines whether one string may be found within another string.
+ * @memberof! external:String#
+ * @alias includes
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
+ * @param {string} searchString A string to be searched for within this string.
+ * @param {number} [position=0] The position in this string at which to begin searching for the given string.
+ * @return {boolean} true if contains the specified string
+ */
+ String.prototype.includes = function() {
+ return String.prototype.indexOf.apply(this, arguments) !== -1;
+ };
+}
+
+/**
+ * convert the string to hex value
+ * @memberof! external:String#
+ * @alias toHex
+ * @return {string}
+ */
+String.prototype.toHex = function () {
+ var res = "", c = 0;
+ while (c < this.length) {
+ res += this.charCodeAt(c++).toString(16);
+ }
+ return res;
+};
+/* eslint-enable no-extend-native */
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+/**
+ * The built in Array Object
+ * @external Array
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array|Array}
+ */
+
+/* eslint-disable no-extend-native */
+
+/**
+ * Remove the specified object from the Array
+ * @memberof! external:Array#
+ * @alias remove
+ * @param {Object} object to be removed
+ */
+Array.prototype.remove = function (obj) {
+ var i = Array.prototype.indexOf.call(this, obj);
+ if (i !== -1) {
+ Array.prototype.splice.call(this, i, 1);
+ }
+ return this;
+};
+
+if (!Array.prototype.forEach) {
+ /**
+ * provide a replacement for browsers that don't
+ * support Array.prototype.forEach (JS 1.6)
+ * @ignore
+ */
+ Array.prototype.forEach = function (callback, scope) {
+ for (var i = 0, j = this.length; j--; i++) {
+ callback.call(scope || this, this[i], i, this);
+ }
+ };
+}
+
+if (!Array.isArray) {
+ /**
+ * provide a replacement for browsers that don't
+ * natively support Array.isArray
+ * @ignore
+ */
+ Array.isArray = function (vArg) {
+ var isArray;
+ isArray = vArg instanceof Array;
+ return isArray;
+ };
+}
+
+/**
+ * return a random array element
+ * @memberof! external:Array#
+ * @alias random
+ * @param {array} entry array to pick a element
+ * @return {any} random member of array
+ */
+Array.prototype.random = function (entry) {
+ return entry[(0).random(entry.length)];
+};
+
+/**
+ * return a weighted random array element, favoring the earlier entries
+ * @memberof! external:Array#
+ * @alias weightedRandom
+ * @param {array} entry array to pick a element
+ * @return {any} random member of array
+ */
+Array.prototype.weightedRandom = function (entry) {
+ return entry[(0).weightedRandom(entry.length)];
+};
+
+/**
+ * A fake TypedArray object to be used for the TypedArray polyfills
+ * @ignore
+ */
+me.TypedArray = function (a) {
+ var i = 0;
+ if (Array.isArray(a)) {
+ this.concat(a.slice());
+ }
+ else if ((arguments.length === 1) && (typeof(a) === "number")) {
+ for (i = 0; i < a; i++) {
+ this.push(0);
+ }
+ }
+ else {
+ throw new me.Error(
+ "TypedArray polyfill: Unsupported constructor arguments",
+ arguments
+ );
+ }
+};
+me.TypedArray.prototype = Array.prototype;
+
+/**
+ * The set() method stores multiple values in the typed array, reading input values from a specified array.
+ * @ignore
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set|TypedArray.prototype.set}
+ */
+me.TypedArray.prototype.set = function (source, offset) {
+ offset = offset || 0;
+
+ if (source.length + offset > this.length) {
+ throw new me.Error(
+ "TypedArray pollyfill: Buffer overflow in set"
+ );
+ }
+
+ for (var i = 0; i < source.length; i++, offset++) {
+ this[offset] = source[i];
+ }
+};
+
+/**
+ * The built in Float32Array object.
+ * @external Float32Array
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Float32Array|Float32Array}
+ */
+window.Float32Array = window.Float32Array || me.TypedArray;
+
+/**
+ * The built in Uint8Array object.
+ * @external Uint8Array
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Uint8Array|Uint8Array}
+ */
+window.Uint8Array = window.Uint8Array || me.TypedArray;
+
+/**
+ * The built in Uint16Array object.
+ * @external Uint16Array
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Uint16Array|Uint16Array}
+ */
+window.Uint16Array = window.Uint16Array || me.TypedArray;
+
+/**
+ * The built in Uint32Array object.
+ * @external Uint32Array
+ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Uint32Array|Uint32Array}
+ */
+window.Uint32Array = window.Uint32Array || me.TypedArray;
+/* eslint-enable no-extend-native */
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+// define window.performance if undefined
+if (typeof window.performance === "undefined") {
+ window.performance = {};
+}
+
+if (typeof Date.now === "undefined") {
+ /**
+ * provide a replacement for browser not
+ * supporting Date.now (JS 1.5)
+ * @ignore
+ */
+ Date.now = function () {
+ return new Date().getTime();
+ };
+}
+
+if (!window.performance.now) {
+ var timeOffset = Date.now();
+
+ if (window.performance.timing &&
+ window.performance.timing.navigationStart) {
+ timeOffset = window.performance.timing.navigationStart;
+ }
+ /**
+ * provide a polyfill for window.performance now
+ * to provide consistent time information across browser
+ * (always return the elapsed time since the browser started)
+ * @ignore
+ */
+ window.performance.now = function () {
+ return Date.now() - timeOffset;
+ };
+}
+
+/*
+
+Copyright (C) 2011 by Andrea Giammarchi, @WebReflection
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* eslint-disable space-before-blocks, quotes, block-spacing, new-parens, comma-spacing, yoda, curly, no-self-compare, pace-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new, eqeqeq */
+
+(function (exports) {'use strict';
+ //shared pointer
+ var i;
+ //shortcuts
+ var defineProperty = Object.defineProperty, is = Object.is;
+
+
+ //Polyfill global objects
+ if (typeof WeakMap == 'undefined') {
+ exports.WeakMap = createCollection({
+ // WeakMap#delete(key:void*):boolean
+ 'delete': sharedDelete,
+ // WeakMap#clear():
+ clear: sharedClear,
+ // WeakMap#get(key:void*):void*
+ get: sharedGet,
+ // WeakMap#has(key:void*):boolean
+ has: mapHas,
+ // WeakMap#set(key:void*, value:void*):void
+ set: sharedSet
+ }, true);
+ }
+
+ if (typeof Map == 'undefined' || typeof ((new Map).values) !== 'function' || !(new Map).values().next) {
+ exports.Map = createCollection({
+ // WeakMap#delete(key:void*):boolean
+ 'delete': sharedDelete,
+ //:was Map#get(key:void*[, d3fault:void*]):void*
+ // Map#has(key:void*):boolean
+ has: mapHas,
+ // Map#get(key:void*):boolean
+ get: sharedGet,
+ // Map#set(key:void*, value:void*):void
+ set: sharedSet,
+ // Map#keys(void):Iterator
+ keys: sharedKeys,
+ // Map#values(void):Iterator
+ values: sharedValues,
+ // Map#entries(void):Iterator
+ entries: mapEntries,
+ // Map#forEach(callback:Function, context:void*):void ==> callback.call(context, key, value, mapObject) === not in specs`
+ forEach: sharedForEach,
+ // Map#clear():
+ clear: sharedClear
+ });
+ }
+
+ if (typeof Set == 'undefined' || typeof ((new Set).values) !== 'function' || !(new Set).values().next) {
+ exports.Set = createCollection({
+ // Set#has(value:void*):boolean
+ has: setHas,
+ // Set#add(value:void*):boolean
+ add: sharedAdd,
+ // Set#delete(key:void*):boolean
+ 'delete': sharedDelete,
+ // Set#clear():
+ clear: sharedClear,
+ // Set#keys(void):Iterator
+ keys: sharedValues, // specs actually say "the same function object as the initial value of the values property"
+ // Set#values(void):Iterator
+ values: sharedValues,
+ // Set#entries(void):Iterator
+ entries: setEntries,
+ // Set#forEach(callback:Function, context:void*):void ==> callback.call(context, value, index) === not in specs
+ forEach: sharedForEach
+ });
+ }
+
+ if (typeof WeakSet == 'undefined') {
+ exports.WeakSet = createCollection({
+ // WeakSet#delete(key:void*):boolean
+ 'delete': sharedDelete,
+ // WeakSet#add(value:void*):boolean
+ add: sharedAdd,
+ // WeakSet#clear():
+ clear: sharedClear,
+ // WeakSet#has(value:void*):boolean
+ has: setHas
+ }, true);
+ }
+
+
+ /**
+ * ES6 collection constructor
+ * @return {Function} a collection class
+ */
+ function createCollection(proto, objectOnly){
+ function Collection(a){
+ if (!this || this.constructor !== Collection) return new Collection(a);
+ this._keys = [];
+ this._values = [];
+ this._itp = []; // iteration pointers
+ this.objectOnly = objectOnly;
+
+ //parse initial iterable argument passed
+ if (a) init.call(this, a);
+ }
+
+ //define size for non object-only collections
+ if (!objectOnly) {
+ defineProperty(proto, 'size', {
+ get: sharedSize
+ });
+ }
+
+ //set prototype
+ proto.constructor = Collection;
+ Collection.prototype = proto;
+
+ return Collection;
+ }
+
+
+ /** parse initial iterable argument passed */
+ function init(a){
+ //init Set argument, like `[1,2,3,{}]`
+ if (this.add)
+ a.forEach(this.add, this);
+ //init Map argument like `[[1,2], [{}, 4]]`
+ else
+ a.forEach(function(a){this.set(a[0],a[1]);}, this);
+ }
+
+
+ /** delete */
+ function sharedDelete(key) {
+ if (this.has(key)) {
+ this._keys.splice(i, 1);
+ this._values.splice(i, 1);
+ // update iteration pointers
+ this._itp.forEach(function(p) { if (i < p[0]) p[0]--; });
+ }
+ // Aurora here does it while Canary doesn't
+ return -1 < i;
+ }
+
+ function sharedGet(key) {
+ return this.has(key) ? this._values[i] : undefined;
+ }
+
+ function has(list, key) {
+ if (this.objectOnly && key !== Object(key))
+ throw new TypeError("Invalid value used as weak collection key");
+ //NaN or 0 passed
+ if (key != key || key === 0) for (i = list.length; i-- && !is(list[i], key);){}
+ else i = list.indexOf(key);
+ return -1 < i;
+ }
+
+ function setHas(value) {
+ return has.call(this, this._values, value);
+ }
+
+ function mapHas(value) {
+ return has.call(this, this._keys, value);
+ }
+
+ /** @chainable */
+ function sharedSet(key, value) {
+ this.has(key) ?
+ this._values[i] = value
+ :
+ this._values[this._keys.push(key) - 1] = value
+ ;
+ return this;
+ }
+
+ /** @chainable */
+ function sharedAdd(value) {
+ if (!this.has(value)) this._values.push(value);
+ return this;
+ }
+
+ function sharedClear() {
+ (this._keys || 0).length =
+ this._values.length = 0;
+ }
+
+ /** keys, values, and iterate related methods */
+ function sharedKeys() {
+ return sharedIterator(this._itp, this._keys);
+ }
+
+ function sharedValues() {
+ return sharedIterator(this._itp, this._values);
+ }
+
+ function mapEntries() {
+ return sharedIterator(this._itp, this._keys, this._values);
+ }
+
+ function setEntries() {
+ return sharedIterator(this._itp, this._values, this._values);
+ }
+
+ function sharedIterator(itp, array, array2) {
+ var p = [0], done = false;
+ itp.push(p);
+ return {
+ next: function() {
+ var v, k = p[0];
+ if (!done && k < array.length) {
+ v = array2 ? [array[k], array2[k]]: array[k];
+ p[0]++;
+ } else {
+ done = true;
+ itp.splice(itp.indexOf(p), 1);
+ }
+ return { done: done, value: v };
+ }
+ };
+ }
+
+ function sharedSize() {
+ return this._values.length;
+ }
+
+ function sharedForEach(callback, context) {
+ var it = this.entries();
+ for (;;) {
+ var r = it.next();
+ if (r.done) break;
+ callback.call(context, r.value[1], r.value[0], this);
+ }
+ }
+
+})(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window );
+/* eslint-enable space-before-blocks, quotes, block-spacing, new-parens, comma-spacing, yoda, curly, no-self-compare, pace-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new, eqeqeq */
+
+/*!
+ * @overview es6-promise - a tiny implementation of Promises/A+.
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
+ * @license Licensed under MIT license
+ * See https://github.com/stefanpenner/es6-promise
+ * @version 3.2.1
+ */
+
+/* eslint-disable no-unused-vars, no-new-func, space-before-blocks, quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new*/
+
+(function() {
+ "use strict";
+ function lib$es6$promise$utils$$objectOrFunction(x) {
+ return typeof x === 'function' || (typeof x === 'object' && x !== null);
+ }
+
+ function lib$es6$promise$utils$$isFunction(x) {
+ return typeof x === 'function';
+ }
+
+ function lib$es6$promise$utils$$isMaybeThenable(x) {
+ return typeof x === 'object' && x !== null;
+ }
+
+ var lib$es6$promise$utils$$_isArray;
+ if (!Array.isArray) {
+ lib$es6$promise$utils$$_isArray = function (x) {
+ return Object.prototype.toString.call(x) === '[object Array]';
+ };
+ } else {
+ lib$es6$promise$utils$$_isArray = Array.isArray;
+ }
+
+ var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray;
+ var lib$es6$promise$asap$$len = 0;
+ var lib$es6$promise$asap$$vertxNext;
+ var lib$es6$promise$asap$$customSchedulerFn;
+
+ var lib$es6$promise$asap$$asap = function asap(callback, arg) {
+ lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback;
+ lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg;
+ lib$es6$promise$asap$$len += 2;
+ if (lib$es6$promise$asap$$len === 2) {
+ // If len is 2, that means that we need to schedule an async flush.
+ // If additional callbacks are queued before the queue is flushed, they
+ // will be processed by this flush that we are scheduling.
+ if (lib$es6$promise$asap$$customSchedulerFn) {
+ lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush);
+ } else {
+ lib$es6$promise$asap$$scheduleFlush();
+ }
+ }
+ }
+
+ function lib$es6$promise$asap$$setScheduler(scheduleFn) {
+ lib$es6$promise$asap$$customSchedulerFn = scheduleFn;
+ }
+
+ function lib$es6$promise$asap$$setAsap(asapFn) {
+ lib$es6$promise$asap$$asap = asapFn;
+ }
+
+ var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined;
+ var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {};
+ var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver;
+ var lib$es6$promise$asap$$isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
+
+ // test for web worker but not in IE10
+ var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
+ typeof importScripts !== 'undefined' &&
+ typeof MessageChannel !== 'undefined';
+
+ // node
+ function lib$es6$promise$asap$$useNextTick() {
+ // node version 0.10.x displays a deprecation warning when nextTick is used recursively
+ // see https://github.com/cujojs/when/issues/410 for details
+ return function() {
+ process.nextTick(lib$es6$promise$asap$$flush);
+ };
+ }
+
+ // vertx
+ function lib$es6$promise$asap$$useVertxTimer() {
+ return function() {
+ lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush);
+ };
+ }
+
+ function lib$es6$promise$asap$$useMutationObserver() {
+ var iterations = 0;
+ var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush);
+ var node = document.createTextNode('');
+ observer.observe(node, { characterData: true });
+
+ return function() {
+ node.data = (iterations = ++iterations % 2);
+ };
+ }
+
+ // web worker
+ function lib$es6$promise$asap$$useMessageChannel() {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = lib$es6$promise$asap$$flush;
+ return function () {
+ channel.port2.postMessage(0);
+ };
+ }
+
+ function lib$es6$promise$asap$$useSetTimeout() {
+ return function() {
+ setTimeout(lib$es6$promise$asap$$flush, 1);
+ };
+ }
+
+ var lib$es6$promise$asap$$queue = new Array(1000);
+ function lib$es6$promise$asap$$flush() {
+ for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) {
+ var callback = lib$es6$promise$asap$$queue[i];
+ var arg = lib$es6$promise$asap$$queue[i+1];
+
+ callback(arg);
+
+ lib$es6$promise$asap$$queue[i] = undefined;
+ lib$es6$promise$asap$$queue[i+1] = undefined;
+ }
+
+ lib$es6$promise$asap$$len = 0;
+ }
+
+ function lib$es6$promise$asap$$attemptVertx() {
+ try {
+ var r = require;
+ var vertx = r('vertx');
+ lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext;
+ return lib$es6$promise$asap$$useVertxTimer();
+ } catch(e) {
+ return lib$es6$promise$asap$$useSetTimeout();
+ }
+ }
+
+ var lib$es6$promise$asap$$scheduleFlush;
+ // Decide what async method to use to triggering processing of queued callbacks:
+ if (lib$es6$promise$asap$$isNode) {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick();
+ } else if (lib$es6$promise$asap$$BrowserMutationObserver) {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver();
+ } else if (lib$es6$promise$asap$$isWorker) {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel();
+ } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertx();
+ } else {
+ lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout();
+ }
+ function lib$es6$promise$then$$then(onFulfillment, onRejection) {
+ var parent = this;
+
+ var child = new this.constructor(lib$es6$promise$$internal$$noop);
+
+ if (child[lib$es6$promise$$internal$$PROMISE_ID] === undefined) {
+ lib$es6$promise$$internal$$makePromise(child);
+ }
+
+ var state = parent._state;
+
+ if (state) {
+ var callback = arguments[state - 1];
+ lib$es6$promise$asap$$asap(function(){
+ lib$es6$promise$$internal$$invokeCallback(state, child, callback, parent._result);
+ });
+ } else {
+ lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection);
+ }
+
+ return child;
+ }
+ var lib$es6$promise$then$$default = lib$es6$promise$then$$then;
+ function lib$es6$promise$promise$resolve$$resolve(object) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (object && typeof object === 'object' && object.constructor === Constructor) {
+ return object;
+ }
+
+ var promise = new Constructor(lib$es6$promise$$internal$$noop);
+ lib$es6$promise$$internal$$resolve(promise, object);
+ return promise;
+ }
+ var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve;
+ var lib$es6$promise$$internal$$PROMISE_ID = Math.random().toString(36).substring(16);
+
+ function lib$es6$promise$$internal$$noop() {}
+
+ var lib$es6$promise$$internal$$PENDING = void 0;
+ var lib$es6$promise$$internal$$FULFILLED = 1;
+ var lib$es6$promise$$internal$$REJECTED = 2;
+
+ var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject();
+
+ function lib$es6$promise$$internal$$selfFulfillment() {
+ return new TypeError("You cannot resolve a promise with itself");
+ }
+
+ function lib$es6$promise$$internal$$cannotReturnOwn() {
+ return new TypeError('A promises callback cannot return that same promise.');
+ }
+
+ function lib$es6$promise$$internal$$getThen(promise) {
+ try {
+ return promise.then;
+ } catch(error) {
+ lib$es6$promise$$internal$$GET_THEN_ERROR.error = error;
+ return lib$es6$promise$$internal$$GET_THEN_ERROR;
+ }
+ }
+
+ function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
+ try {
+ then.call(value, fulfillmentHandler, rejectionHandler);
+ } catch(e) {
+ return e;
+ }
+ }
+
+ function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) {
+ lib$es6$promise$asap$$asap(function(promise) {
+ var sealed = false;
+ var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) {
+ if (sealed) { return; }
+ sealed = true;
+ if (thenable !== value) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ } else {
+ lib$es6$promise$$internal$$fulfill(promise, value);
+ }
+ }, function(reason) {
+ if (sealed) { return; }
+ sealed = true;
+
+ lib$es6$promise$$internal$$reject(promise, reason);
+ }, 'Settle: ' + (promise._label || ' unknown promise'));
+
+ if (!sealed && error) {
+ sealed = true;
+ lib$es6$promise$$internal$$reject(promise, error);
+ }
+ }, promise);
+ }
+
+ function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) {
+ if (thenable._state === lib$es6$promise$$internal$$FULFILLED) {
+ lib$es6$promise$$internal$$fulfill(promise, thenable._result);
+ } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) {
+ lib$es6$promise$$internal$$reject(promise, thenable._result);
+ } else {
+ lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ }, function(reason) {
+ lib$es6$promise$$internal$$reject(promise, reason);
+ });
+ }
+ }
+
+ function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable, then) {
+ if (maybeThenable.constructor === promise.constructor &&
+ then === lib$es6$promise$then$$default &&
+ constructor.resolve === lib$es6$promise$promise$resolve$$default) {
+ lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable);
+ } else {
+ if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) {
+ lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error);
+ } else if (then === undefined) {
+ lib$es6$promise$$internal$$fulfill(promise, maybeThenable);
+ } else if (lib$es6$promise$utils$$isFunction(then)) {
+ lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then);
+ } else {
+ lib$es6$promise$$internal$$fulfill(promise, maybeThenable);
+ }
+ }
+ }
+
+ function lib$es6$promise$$internal$$resolve(promise, value) {
+ if (promise === value) {
+ lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFulfillment());
+ } else if (lib$es6$promise$utils$$objectOrFunction(value)) {
+ lib$es6$promise$$internal$$handleMaybeThenable(promise, value, lib$es6$promise$$internal$$getThen(value));
+ } else {
+ lib$es6$promise$$internal$$fulfill(promise, value);
+ }
+ }
+
+ function lib$es6$promise$$internal$$publishRejection(promise) {
+ if (promise._onerror) {
+ promise._onerror(promise._result);
+ }
+
+ lib$es6$promise$$internal$$publish(promise);
+ }
+
+ function lib$es6$promise$$internal$$fulfill(promise, value) {
+ if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; }
+
+ promise._result = value;
+ promise._state = lib$es6$promise$$internal$$FULFILLED;
+
+ if (promise._subscribers.length !== 0) {
+ lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise);
+ }
+ }
+
+ function lib$es6$promise$$internal$$reject(promise, reason) {
+ if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; }
+ promise._state = lib$es6$promise$$internal$$REJECTED;
+ promise._result = reason;
+
+ lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise);
+ }
+
+ function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
+ var subscribers = parent._subscribers;
+ var length = subscribers.length;
+
+ parent._onerror = null;
+
+ subscribers[length] = child;
+ subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment;
+ subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection;
+
+ if (length === 0 && parent._state) {
+ lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent);
+ }
+ }
+
+ function lib$es6$promise$$internal$$publish(promise) {
+ var subscribers = promise._subscribers;
+ var settled = promise._state;
+
+ if (subscribers.length === 0) { return; }
+
+ var child, callback, detail = promise._result;
+
+ for (var i = 0; i < subscribers.length; i += 3) {
+ child = subscribers[i];
+ callback = subscribers[i + settled];
+
+ if (child) {
+ lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail);
+ } else {
+ callback(detail);
+ }
+ }
+
+ promise._subscribers.length = 0;
+ }
+
+ function lib$es6$promise$$internal$$ErrorObject() {
+ this.error = null;
+ }
+
+ var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject();
+
+ function lib$es6$promise$$internal$$tryCatch(callback, detail) {
+ try {
+ return callback(detail);
+ } catch(e) {
+ lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e;
+ return lib$es6$promise$$internal$$TRY_CATCH_ERROR;
+ }
+ }
+
+ function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) {
+ var hasCallback = lib$es6$promise$utils$$isFunction(callback),
+ value, error, succeeded, failed;
+
+ if (hasCallback) {
+ value = lib$es6$promise$$internal$$tryCatch(callback, detail);
+
+ if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) {
+ failed = true;
+ error = value.error;
+ value = null;
+ } else {
+ succeeded = true;
+ }
+
+ if (promise === value) {
+ lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn());
+ return;
+ }
+
+ } else {
+ value = detail;
+ succeeded = true;
+ }
+
+ if (promise._state !== lib$es6$promise$$internal$$PENDING) {
+ // noop
+ } else if (hasCallback && succeeded) {
+ lib$es6$promise$$internal$$resolve(promise, value);
+ } else if (failed) {
+ lib$es6$promise$$internal$$reject(promise, error);
+ } else if (settled === lib$es6$promise$$internal$$FULFILLED) {
+ lib$es6$promise$$internal$$fulfill(promise, value);
+ } else if (settled === lib$es6$promise$$internal$$REJECTED) {
+ lib$es6$promise$$internal$$reject(promise, value);
+ }
+ }
+
+ function lib$es6$promise$$internal$$initializePromise(promise, resolver) {
+ try {
+ resolver(function resolvePromise(value){
+ lib$es6$promise$$internal$$resolve(promise, value);
+ }, function rejectPromise(reason) {
+ lib$es6$promise$$internal$$reject(promise, reason);
+ });
+ } catch(e) {
+ lib$es6$promise$$internal$$reject(promise, e);
+ }
+ }
+
+ var lib$es6$promise$$internal$$id = 0;
+ function lib$es6$promise$$internal$$nextId() {
+ return lib$es6$promise$$internal$$id++;
+ }
+
+ function lib$es6$promise$$internal$$makePromise(promise) {
+ promise[lib$es6$promise$$internal$$PROMISE_ID] = lib$es6$promise$$internal$$id++;
+ promise._state = undefined;
+ promise._result = undefined;
+ promise._subscribers = [];
+ }
+
+ function lib$es6$promise$promise$all$$all(entries) {
+ return new lib$es6$promise$enumerator$$default(this, entries).promise;
+ }
+ var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all;
+ function lib$es6$promise$promise$race$$race(entries) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (!lib$es6$promise$utils$$isArray(entries)) {
+ return new Constructor(function(resolve, reject) {
+ reject(new TypeError('You must pass an array to race.'));
+ });
+ } else {
+ return new Constructor(function(resolve, reject) {
+ var length = entries.length;
+ for (var i = 0; i < length; i++) {
+ Constructor.resolve(entries[i]).then(resolve, reject);
+ }
+ });
+ }
+ }
+ var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race;
+ function lib$es6$promise$promise$reject$$reject(reason) {
+ /*jshint validthis:true */
+ var Constructor = this;
+ var promise = new Constructor(lib$es6$promise$$internal$$noop);
+ lib$es6$promise$$internal$$reject(promise, reason);
+ return promise;
+ }
+ var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject;
+
+
+ function lib$es6$promise$promise$$needsResolver() {
+ throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
+ }
+
+ function lib$es6$promise$promise$$needsNew() {
+ throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
+ }
+
+ var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise;
+ /**
+ Promise objects represent the eventual result of an asynchronous operation. The
+ primary way of interacting with a promise is through its `then` method, which
+ registers callbacks to receive either a promise's eventual value or the reason
+ why the promise cannot be fulfilled.
+
+ Terminology
+ -----------
+
+ - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
+ - `thenable` is an object or function that defines a `then` method.
+ - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
+ - `exception` is a value that is thrown using the throw statement.
+ - `reason` is a value that indicates why a promise was rejected.
+ - `settled` the final resting state of a promise, fulfilled or rejected.
+
+ A promise can be in one of three states: pending, fulfilled, or rejected.
+
+ Promises that are fulfilled have a fulfillment value and are in the fulfilled
+ state. Promises that are rejected have a rejection reason and are in the
+ rejected state. A fulfillment value is never a thenable.
+
+ Promises can also be said to *resolve* a value. If this value is also a
+ promise, then the original promise's settled state will match the value's
+ settled state. So a promise that *resolves* a promise that rejects will
+ itself reject, and a promise that *resolves* a promise that fulfills will
+ itself fulfill.
+
+
+ Basic Usage:
+ ------------
+
+ ```js
+ var promise = new Promise(function(resolve, reject) {
+ // on success
+ resolve(value);
+
+ // on failure
+ reject(reason);
+ });
+
+ promise.then(function(value) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Advanced Usage:
+ ---------------
+
+ Promises shine when abstracting away asynchronous interactions such as
+ `XMLHttpRequest`s.
+
+ ```js
+ function getJSON(url) {
+ return new Promise(function(resolve, reject){
+ var xhr = new XMLHttpRequest();
+
+ xhr.open('GET', url);
+ xhr.onreadystatechange = handler;
+ xhr.responseType = 'json';
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.send();
+
+ function handler() {
+ if (this.readyState === this.DONE) {
+ if (this.status === 200) {
+ resolve(this.response);
+ } else {
+ reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
+ }
+ }
+ };
+ });
+ }
+
+ getJSON('/posts.json').then(function(json) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Unlike callbacks, promises are great composable primitives.
+
+ ```js
+ Promise.all([
+ getJSON('/posts'),
+ getJSON('/comments')
+ ]).then(function(values){
+ values[0] // => postsJSON
+ values[1] // => commentsJSON
+
+ return values;
+ });
+ ```
+
+ @class Promise
+ @param {function} resolver
+ Useful for tooling.
+ @constructor
+ */
+ function lib$es6$promise$promise$$Promise(resolver) {
+ this[lib$es6$promise$$internal$$PROMISE_ID] = lib$es6$promise$$internal$$nextId();
+ this._result = this._state = undefined;
+ this._subscribers = [];
+
+ if (lib$es6$promise$$internal$$noop !== resolver) {
+ typeof resolver !== 'function' && lib$es6$promise$promise$$needsResolver();
+ this instanceof lib$es6$promise$promise$$Promise ? lib$es6$promise$$internal$$initializePromise(this, resolver) : lib$es6$promise$promise$$needsNew();
+ }
+ }
+
+ lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default;
+ lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default;
+ lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default;
+ lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default;
+ lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler;
+ lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap;
+ lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap;
+
+ lib$es6$promise$promise$$Promise.prototype = {
+ constructor: lib$es6$promise$promise$$Promise,
+
+ /**
+ The primary way of interacting with a promise is through its `then` method,
+ which registers callbacks to receive either a promise's eventual value or the
+ reason why the promise cannot be fulfilled.
+
+ ```js
+ findUser().then(function(user){
+ // user is available
+ }, function(reason){
+ // user is unavailable, and you are given the reason why
+ });
+ ```
+
+ Chaining
+ --------
+
+ The return value of `then` is itself a promise. This second, 'downstream'
+ promise is resolved with the return value of the first promise's fulfillment
+ or rejection handler, or rejected if the handler throws an exception.
+
+ ```js
+ findUser().then(function (user) {
+ return user.name;
+ }, function (reason) {
+ return 'default name';
+ }).then(function (userName) {
+ // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
+ // will be `'default name'`
+ });
+
+ findUser().then(function (user) {
+ throw new Error('Found user, but still unhappy');
+ }, function (reason) {
+ throw new Error('`findUser` rejected and we're unhappy');
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
+ // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
+ });
+ ```
+ If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
+
+ ```js
+ findUser().then(function (user) {
+ throw new PedagogicalException('Upstream error');
+ }).then(function (value) {
+ // never reached
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // The `PedgagocialException` is propagated all the way down to here
+ });
+ ```
+
+ Assimilation
+ ------------
+
+ Sometimes the value you want to propagate to a downstream promise can only be
+ retrieved asynchronously. This can be achieved by returning a promise in the
+ fulfillment or rejection handler. The downstream promise will then be pending
+ until the returned promise is settled. This is called *assimilation*.
+
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // The user's comments are now available
+ });
+ ```
+
+ If the assimliated promise rejects, then the downstream promise will also reject.
+
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // If `findCommentsByAuthor` fulfills, we'll have the value here
+ }, function (reason) {
+ // If `findCommentsByAuthor` rejects, we'll have the reason here
+ });
+ ```
+
+ Simple Example
+ --------------
+
+ Synchronous Example
+
+ ```javascript
+ var result;
+
+ try {
+ result = findResult();
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+
+ Errback Example
+
+ ```js
+ findResult(function(result, err){
+ if (err) {
+ // failure
+ } else {
+ // success
+ }
+ });
+ ```
+
+ Promise Example;
+
+ ```javascript
+ findResult().then(function(result){
+ // success
+ }, function(reason){
+ // failure
+ });
+ ```
+
+ Advanced Example
+ --------------
+
+ Synchronous Example
+
+ ```javascript
+ var author, books;
+
+ try {
+ author = findAuthor();
+ books = findBooksByAuthor(author);
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+
+ Errback Example
+
+ ```js
+
+ function foundBooks(books) {
+
+ }
+
+ function failure(reason) {
+
+ }
+
+ findAuthor(function(author, err){
+ if (err) {
+ failure(err);
+ // failure
+ } else {
+ try {
+ findBoooksByAuthor(author, function(books, err) {
+ if (err) {
+ failure(err);
+ } else {
+ try {
+ foundBooks(books);
+ } catch(reason) {
+ failure(reason);
+ }
+ }
+ });
+ } catch(error) {
+ failure(err);
+ }
+ // success
+ }
+ });
+ ```
+
+ Promise Example;
+
+ ```javascript
+ findAuthor().
+ then(findBooksByAuthor).
+ then(function(books){
+ // found books
+ }).catch(function(reason){
+ // something went wrong
+ });
+ ```
+ @ignore
+ @method then
+ @param {Function} onFulfilled
+ @param {Function} onRejected
+ Useful for tooling.
+ @return {Promise}
+ */
+ then: lib$es6$promise$then$$default,
+
+ /**
+ `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
+ as the catch block of a try/catch statement.
+
+ ```js
+ function findAuthor(){
+ throw new Error('couldn't find that author');
+ }
+
+ // synchronous
+ try {
+ findAuthor();
+ } catch(reason) {
+ // something went wrong
+ }
+
+ // async with promises
+ findAuthor().catch(function(reason){
+ // something went wrong
+ });
+ ```
+ @ignore
+ @method catch
+ @param {Function} onRejection
+ Useful for tooling.
+ @return {Promise}
+ */
+ 'catch': function(onRejection) {
+ return this.then(null, onRejection);
+ }
+ };
+ var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator;
+ function lib$es6$promise$enumerator$$Enumerator(Constructor, input) {
+ this._instanceConstructor = Constructor;
+ this.promise = new Constructor(lib$es6$promise$$internal$$noop);
+
+ if (!this.promise[lib$es6$promise$$internal$$PROMISE_ID]) {
+ lib$es6$promise$$internal$$makePromise(this.promise);
+ }
+
+ if (Array.isArray(input)) {
+ this._input = input;
+ this.length = input.length;
+ this._remaining = input.length;
+
+ this._result = new Array(this.length);
+
+ if (this.length === 0) {
+ lib$es6$promise$$internal$$fulfill(this.promise, this._result);
+ } else {
+ this.length = this.length || 0;
+ this._enumerate();
+ if (this._remaining === 0) {
+ lib$es6$promise$$internal$$fulfill(this.promise, this._result);
+ }
+ }
+ } else {
+ lib$es6$promise$$internal$$reject(this.promise, lib$es6$promise$enumerator$$validationError());
+ }
+ }
+
+ function lib$es6$promise$enumerator$$validationError() {
+ return new Error('Array Methods must be provided an Array');
+ }
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() {
+ var length = this.length;
+ var input = this._input;
+
+ for (var i = 0; this._state === lib$es6$promise$$internal$$PENDING && i < length; i++) {
+ this._eachEntry(input[i], i);
+ }
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
+ var c = this._instanceConstructor;
+ var resolve = c.resolve;
+
+ if (resolve === lib$es6$promise$promise$resolve$$default) {
+ var then = lib$es6$promise$$internal$$getThen(entry);
+
+ if (then === lib$es6$promise$then$$default &&
+ entry._state !== lib$es6$promise$$internal$$PENDING) {
+ this._settledAt(entry._state, i, entry._result);
+ } else if (typeof then !== 'function') {
+ this._remaining--;
+ this._result[i] = entry;
+ } else if (c === lib$es6$promise$promise$$default) {
+ var promise = new c(lib$es6$promise$$internal$$noop);
+ lib$es6$promise$$internal$$handleMaybeThenable(promise, entry, then);
+ this._willSettleAt(promise, i);
+ } else {
+ this._willSettleAt(new c(function(resolve) { resolve(entry); }), i);
+ }
+ } else {
+ this._willSettleAt(resolve(entry), i);
+ }
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
+ var promise = this.promise;
+
+ if (promise._state === lib$es6$promise$$internal$$PENDING) {
+ this._remaining--;
+
+ if (state === lib$es6$promise$$internal$$REJECTED) {
+ lib$es6$promise$$internal$$reject(promise, value);
+ } else {
+ this._result[i] = value;
+ }
+ }
+
+ if (this._remaining === 0) {
+ lib$es6$promise$$internal$$fulfill(promise, this._result);
+ }
+ };
+
+ lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
+ var enumerator = this;
+
+ lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) {
+ enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value);
+ }, function(reason) {
+ enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason);
+ });
+ };
+ function lib$es6$promise$polyfill$$polyfill() {
+ var local;
+
+ if (typeof global !== 'undefined') {
+ local = global;
+ } else if (typeof self !== 'undefined') {
+ local = self;
+ } else {
+ try {
+ local = Function('return this')();
+ } catch (e) {
+ throw new Error('polyfill failed because global object is unavailable in this environment');
+ }
+ }
+
+ var P = local.Promise;
+
+ if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) {
+ return;
+ }
+
+ local.Promise = lib$es6$promise$promise$$default;
+ }
+ var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill;
+
+ var lib$es6$promise$umd$$ES6Promise = {
+ 'Promise': lib$es6$promise$promise$$default,
+ 'polyfill': lib$es6$promise$polyfill$$default
+ };
+
+ /* global define:true module:true window: true */
+ if (typeof define === 'function' && define['amd']) {
+ define(function() { return lib$es6$promise$umd$$ES6Promise; });
+ } else if (typeof module !== 'undefined' && module['exports']) {
+ module['exports'] = lib$es6$promise$umd$$ES6Promise;
+ } else if (typeof this !== 'undefined') {
+ this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise;
+ }
+
+ lib$es6$promise$polyfill$$default();
+}).call(this);
+/* eslint-enable no-unused-vars, no-new-func, space-before-blocks, quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new */
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+(function () {
+
+ /**
+ * me global references
+ * @ignore
+ */
+ me.mod = "melonJS";
+ me.version = "4.1.0";
+ /**
+ * global system settings and browser capabilities
+ * @namespace
+ */
+ me.sys = {
+
+ /*
+ * Global settings
+ */
+
+ /**
+ * Set game FPS limiting
+ * @see me.timer.tick
+ * @type {Number}
+ * @default 60
+ * @memberOf me.sys
+ */
+ fps : 60,
+
+ /**
+ * Rate at which the game updates;
+ * must be equal to or lower than the fps
+ * @see me.timer.tick
+ * @type {Number}
+ * @default 60
+ * @memberOf me.sys
+ */
+ updatesPerSecond : 60,
+
+ /**
+ * Enable/disable frame interpolation
+ * @see me.timer.tick
+ * @type {Boolean}
+ * @default false
+ * @memberOf me.sys
+ */
+ interpolation : false,
+
+ /**
+ * Global scaling factor
+ * @type {me.Vector2d}
+ * @default <0,0>
+ * @memberOf me.sys
+ */
+ scale : null, //initialized by me.video.init
+
+ /**
+ * Global gravity settings
+ * will override entities init value if defined
+ * @type {Number|undefined}
+ * @default undefined
+ * @memberOf me.sys
+ */
+ gravity : undefined,
+
+ /**
+ * Specify either to stop on audio loading error or not
+ * if true, melonJS will throw an exception and stop loading
+ * if false, melonJS will disable sounds and output a warning message
+ * in the console
+ * @type {Boolean}
+ * @default true
+ * @memberOf me.sys
+ */
+ stopOnAudioError : true,
+
+ /**
+ * Specify whether to pause the game when losing focus.
+ * @type {Boolean}
+ * @default true
+ * @memberOf me.sys
+ */
+ pauseOnBlur : true,
+
+ /**
+ * Specify whether to unpause the game when gaining focus.
+ * @type {Boolean}
+ * @default true
+ * @memberOf me.sys
+ */
+ resumeOnFocus : true,
+
+ /**
+ * Specify whether to stop the game when losing focus or not
+ * The engine restarts on focus if this is enabled.
+ * @type {boolean}
+ * @default false
+ * @memberOf me.sys
+ */
+ stopOnBlur : false,
+
+ /**
+ * Specify the rendering method for layers
+ * if false, visible part of the layers are rendered dynamically
+ * if true, the entire layers are first rendered into an offscreen
+ * canvas
+ * the "best" rendering method depends of your game
+ * (amount of layer, layer size, amount of tiles per layer, etc.)
+ * note : rendering method is also configurable per layer by adding this
+ * property to your layer (in Tiled)
+ * @type {Boolean}
+ * @default false
+ * @memberOf me.sys
+ */
+ preRender : false,
+
+ /*
+ * System methods
+ */
+
+ /**
+ * Compare two version strings
+ * @public
+ * @function
+ * @param {String} first First version string to compare
+ * @param {String} [second="4.1.0"] Second version string to compare
+ * @return {Number} comparison result
< 0 : first < second
+ * 0 : first == second
+ * > 0 : first > second
+ * @example
+ * if (me.sys.checkVersion("4.1.0") > 0) {
+ * console.error(
+ * "melonJS is too old. Expected: 4.1.0, Got: " + me.version
+ * );
+ * }
+ */
+ checkVersion : function (first, second) {
+ second = second || me.version;
+
+ var a = first.split(".");
+ var b = second.split(".");
+ var len = Math.min(a.length, b.length);
+ var result = 0;
+
+ for (var i = 0; i < len; i++) {
+ if ((result = +a[i] - +b[i])) {
+ break;
+ }
+ }
+
+ return result ? result : a.length - b.length;
+ }
+ };
+
+ function parseHash() {
+ var hash = {};
+
+ if (document.location.hash) {
+ document.location.hash.substr(1).split("&").filter(function (value) {
+ return (value !== "");
+ }).forEach(function (value) {
+ var kv = value.split("=");
+ var k = kv.shift();
+ var v = kv.join("=");
+ hash[k] = v || true;
+ });
+ }
+
+ return hash;
+ }
+
+ // a flag to know if melonJS
+ // is initialized
+ var me_initialized = false;
+
+ Object.defineProperty(me, "initialized", {
+ /**
+ * @ignore
+ */
+ get : function get() {
+ return me_initialized;
+ }
+ });
+
+
+ /**
+ * initial boot function
+ * @ignore
+ */
+ me.boot = function () {
+ // don't do anything if already initialized (should not happen anyway)
+ if (me_initialized) {
+ return;
+ }
+
+ // check the device capabilites
+ me.device._check();
+
+ // initialize me.save
+ me.save._init();
+
+ // parse optional url parameters/tags
+ me.game.HASH = parseHash();
+
+ // enable/disable the cache
+ me.loader.setNocache(
+ me.game.HASH.nocache || false
+ );
+
+ // init the FPS counter if needed
+ me.timer.init();
+
+ // init the App Manager
+ me.state.init();
+
+ // init the Entity Pool
+ me.pool.init();
+
+ // automatically enable keyboard events if on desktop
+ if (me.device.isMobile === false) {
+ me.input._enableKeyboardEvent();
+ }
+
+ // init the level Director
+ me.levelDirector.reset();
+
+ me_initialized = true;
+ };
+
+})();
+
+/**
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+(function () {
+
+ /**
+ * me.game represents your current game, it contains all the objects,
+ * tilemap layers, current viewport, collision map, etc...
+ * me.game is also responsible for updating (each frame) the object status
+ * and draw them
+ * @namespace me.game
+ * @memberOf me
+ */
+ me.game = (function () {
+ // hold public stuff in our singleton
+ var api = {};
+
+ /*
+ * PRIVATE STUFF
+ */
+
+ // flag to redraw the sprites
+ var initialized = false;
+
+ // to know when we have to refresh the display
+ var isDirty = true;
+
+ // always refresh the display when updatesPerSecond are lower than fps
+ var isAlwaysDirty = false;
+
+ // frame counter for frameSkipping
+ // reset the frame counter
+ var frameCounter = 0;
+ var frameRate = 1;
+
+ // time accumulation for multiple update calls
+ var accumulator = 0.0;
+ var accumulatorMax = 0.0;
+ var accumulatorUpdateDelta = 0;
+
+ // min update step size
+ var stepSize = 1000 / 60;
+ var updateDelta = 0;
+ var lastUpdateStart = null;
+ var updateAverageDelta = 0;
+
+ // reference to the renderer object
+ var renderer = null;
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * a reference to the game viewport.
+ * @public
+ * @type {me.Viewport}
+ * @name viewport
+ * @memberOf me.game
+ */
+ api.viewport = null;
+
+ /**
+ * a reference to the game world
+ * a world is a virtual environment containing all the game objects
+ * @public
+ * @type {me.Container}
+ * @name world
+ * @memberOf me.game
+ */
+ api.world = null;
+
+ /**
+ * when true, all objects will be added under the root world container
+ * when false, a `me.Container` object will be created for each
+ * corresponding `TMXObjectGroup`
+ * default value : true
+ * @public
+ * @type {boolean}
+ * @name mergeGroup
+ * @memberOf me.game
+ */
+ api.mergeGroup = true;
+
+ /**
+ * The property of should be used when sorting entities
+ * value : "x", "y", "z" (default: "z")
+ * @public
+ * @type {string}
+ * @name sortOn
+ * @memberOf me.game
+ */
+ api.sortOn = "z";
+
+ /**
+ * Fired when a level is fully loaded and
+ * and all entities instantiated.
+ * Additionnaly the level id will also be passed
+ * to the called function.
+ * @public
+ * @function
+ * @name onLevelLoaded
+ * @memberOf me.game
+ * @example
+ * // call myFunction () everytime a level is loaded
+ * me.game.onLevelLoaded = this.myFunction.bind(this);
+ */
+ api.onLevelLoaded = function () {};
+
+ /**
+ * Provide an object hash with all tag parameters specified in the url.
+ * @property {Boolean} [hitbox=false] draw the hitbox in the debug panel (if enabled)
+ * @property {Boolean} [velocity=false] draw the entities velocity in the debug panel (if enabled)
+ * @property {Boolean} [quadtree=false] draw the quadtree in the debug panel (if enabled)
+ * @property {Boolean} [webgl=false] force the renderer to WebGL
+ * @public
+ * @type {Object}
+ * @name HASH
+ * @memberOf me.game
+ * @example
+ * // http://www.example.com/index.html#debug&hitbox=true&mytag=value
+ * console.log(me.game.HASH["mytag"]); //> "value"
+ */
+ api.HASH = null;
+
+ /**
+ * Initialize the game manager
+ * @name init
+ * @memberOf me.game
+ * @private
+ * @ignore
+ * @function
+ * @param {Number} [width] width of the canvas
+ * @param {Number} [height] width of the canvas
+ * init function.
+ */
+ api.init = function (width, height) {
+ if (!initialized) {
+ // if no parameter specified use the system size
+ width = width || me.video.renderer.getWidth();
+ height = height || me.video.renderer.getHeight();
+
+ // create a defaut viewport of the same size
+ api.viewport = new me.Viewport(0, 0, width, height);
+
+ // the root object of our world is an entity container
+ api.world = new me.Container(0, 0, width, height);
+ api.world.name = "rootContainer";
+ api.world._root = true;
+
+ // initialize the collision system (the quadTree mostly)
+ me.collision.init();
+
+ renderer = me.video.renderer;
+
+ // publish init notification
+ me.event.publish(me.event.GAME_INIT);
+
+ // translate global pointer events
+ me.input._translatePointerEvents();
+
+ // make display dirty by default
+ isDirty = true;
+
+ // set as initialized
+ initialized = true;
+ }
+ };
+
+ /**
+ * reset the game Object manager
+ * destroy all current objects
+ * @name reset
+ * @memberOf me.game
+ * @public
+ * @function
+ */
+ api.reset = function () {
+
+ // clear the quadtree
+ me.collision.quadTree.clear();
+
+ // remove all objects
+ api.world.destroy();
+
+ // reset the viewport to zero ?
+ if (api.viewport) {
+ api.viewport.reset();
+ }
+
+ // reset the renderer
+ renderer.reset();
+
+ // publish reset notification
+ me.event.publish(me.event.GAME_RESET);
+
+ // Refresh internal variables for framerate limiting
+ api.updateFrameRate();
+ };
+
+ /**
+ * Update the renderer framerate using the system config variables.
+ * @name updateFrameRate
+ * @memberOf me.game
+ * @public
+ * @function
+ * @see me.sys.fps
+ * @see me.sys.updatesPerSecond
+ */
+ api.updateFrameRate = function () {
+ // reset the frame counter
+ frameCounter = 0;
+ frameRate = ~~(0.5 + 60 / me.sys.fps);
+
+ // set step size based on the updatesPerSecond
+ stepSize = (1000 / me.sys.updatesPerSecond);
+ accumulator = 0.0;
+ accumulatorMax = stepSize * 10;
+
+ // display should always re-draw when update speed doesn't match fps
+ // this means the user intends to write position prediction drawing logic
+ isAlwaysDirty = (me.sys.fps > me.sys.updatesPerSecond);
+ };
+
+ /**
+ * Returns the parent container of the specified Child in the game world
+ * @name getParentContainer
+ * @memberOf me.game
+ * @function
+ * @param {me.Renderable} child
+ * @return {me.Container}
+ */
+ api.getParentContainer = function (child) {
+ return child.ancestor;
+ };
+
+ /**
+ * force the redraw (not update) of all objects
+ * @name repaint
+ * @memberOf me.game
+ * @public
+ * @function
+ */
+
+ api.repaint = function () {
+ isDirty = true;
+ };
+
+
+ /**
+ * update all objects of the game manager
+ * @name update
+ * @memberOf me.game
+ * @private
+ * @ignore
+ * @function
+ * @param {Number} time current timestamp as provided by the RAF callback
+ */
+ api.update = function (time) {
+ // handle frame skipping if required
+ if ((++frameCounter % frameRate) === 0) {
+ // reset the frame counter
+ frameCounter = 0;
+
+ // update the timer
+ me.timer.update(time);
+
+ // update the gamepads
+ me.input._updateGamepads();
+
+ accumulator += me.timer.getDelta();
+ accumulator = Math.min(accumulator, accumulatorMax);
+
+ updateDelta = (me.sys.interpolation) ? me.timer.getDelta() : stepSize;
+ accumulatorUpdateDelta = (me.sys.interpolation) ? updateDelta : Math.max(updateDelta, updateAverageDelta);
+
+ while (accumulator >= accumulatorUpdateDelta || me.sys.interpolation) {
+ lastUpdateStart = window.performance.now();
+
+ // clear the quadtree
+ me.collision.quadTree.clear();
+
+ // insert the world container (children) into the quadtree
+ me.collision.quadTree.insertContainer(api.world);
+
+ // update all objects (and pass the elapsed time since last frame)
+ isDirty = api.world.update(updateDelta) || isDirty;
+
+ // update the camera/viewport
+ isDirty = api.viewport.update(updateDelta) || isDirty;
+
+ me.timer.lastUpdate = window.performance.now();
+ updateAverageDelta = me.timer.lastUpdate - lastUpdateStart;
+
+ accumulator -= accumulatorUpdateDelta;
+ if (me.sys.interpolation) {
+ accumulator = 0;
+ break;
+ }
+ }
+ }
+ };
+
+ /**
+ * draw all existing objects
+ * @name draw
+ * @memberOf me.game
+ * @private
+ * @ignore
+ * @function
+ * @param {me.Viewport} viewport viewport object
+ */
+ api.draw = function (viewport) {
+ if (isDirty || isAlwaysDirty) {
+ viewport = viewport || api.viewport;
+ // cache the viewport rendering position, so that other object
+ // can access it later (e,g. entityContainer when drawing floating objects)
+ var translateX = viewport.pos.x + viewport.offset.x;
+ var translateY = viewport.pos.y + viewport.offset.y;
+
+ // translate the world coordinates by default to screen coordinates
+ api.world.currentTransform.translate(-translateX, -translateY);
+
+ // prepare renderer to draw a new frame
+ renderer.clear();
+
+ // save the current state
+ me.video.renderer.save();
+
+ // apply viewport transform if needed
+ if (!viewport.currentTransform.isIdentity()) {
+ renderer.transform(viewport.currentTransform);
+ }
+
+ // update all objects,
+ // specifying the viewport as the rectangle area to redraw
+ api.world.draw(renderer, viewport);
+
+ // restore
+ renderer.restore();
+
+ // translate the world coordinates by default to screen coordinates
+ api.world.currentTransform.translate(translateX, translateY);
+
+ // draw the viewpor/camera effects
+ viewport.draw(renderer);
+
+ }
+
+ isDirty = false;
+
+ // flush/render our frame
+ renderer.flush();
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org/
+ *
+ */
+(function () {
+ /**
+ * Convert first character of a string to uppercase, if it's a letter.
+ * @ignore
+ * @function
+ * @name capitalize
+ * @param {String} str Input string.
+ * @return {String} String with first letter made uppercase.
+ */
+ var capitalize = function (str) {
+ return str.substring(0, 1).toUpperCase() + str.substring(1, str.length);
+ };
+
+ /**
+ * A collection of utilities to ease porting between different user agents.
+ * @namespace me.agent
+ * @memberOf me
+ */
+ me.agent = (function () {
+ var api = {};
+
+ /**
+ * Known agent vendors
+ * @ignore
+ */
+ var vendors = [ "ms", "MS", "moz", "webkit", "o" ];
+
+ /**
+ * Get a vendor-prefixed property
+ * @public
+ * @name prefixed
+ * @function
+ * @param {String} name Property name
+ * @param {Object} [obj=window] Object or element reference to access
+ * @return {Mixed} Value of property
+ * @memberOf me.agent
+ */
+ api.prefixed = function (name, obj) {
+ obj = obj || window;
+ if (name in obj) {
+ return obj[name];
+ }
+
+ var uc_name = capitalize(name);
+
+ var result;
+ vendors.some(function (vendor) {
+ var name = vendor + uc_name;
+ return (result = (name in obj) ? obj[name] : undefined);
+ });
+ return result;
+ };
+
+ /**
+ * Set a vendor-prefixed property
+ * @public
+ * @name setPrefixed
+ * @function
+ * @param {String} name Property name
+ * @param {Mixed} value Property value
+ * @param {Object} [obj=window] Object or element reference to access
+ * @memberOf me.agent
+ */
+ api.setPrefixed = function (name, value, obj) {
+ obj = obj || window;
+ if (name in obj) {
+ obj[name] = value;
+ return;
+ }
+
+ var uc_name = capitalize(name);
+
+ vendors.some(function (vendor) {
+ var name = vendor + uc_name;
+ if (name in obj) {
+ obj[name] = value;
+ return true;
+ }
+ return false;
+ });
+ };
+
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * A singleton object representing the device capabilities and specific events
+ * @namespace me.device
+ * @memberOf me
+ */
+ me.device = (function () {
+ // defines object for holding public information/functionality.
+ var api = {};
+ // private properties
+ var accelInitialized = false;
+ var deviceOrientationInitialized = false;
+ var devicePixelRatio = null;
+
+ // swipe utility fn & flag
+ var swipeEnabled = true;
+ var disableSwipeFn = function (e) {
+ e.preventDefault();
+ window.scroll(0, 0);
+ return false;
+ };
+
+
+ /**
+ * check the device capapbilities
+ * @ignore
+ */
+ api._check = function () {
+
+ // detect device type/platform
+ me.device._detectDevice();
+
+ // Mobile browser hacks
+ if (me.device.isMobile && !me.device.cocoon) {
+ // Prevent the webview from moving on a swipe
+ api.enableSwipe(false);
+ }
+
+ // future proofing (MS) feature detection
+ me.device.pointerEvent = me.agent.prefixed("PointerEvent", window);
+ me.device.maxTouchPoints = me.agent.prefixed("maxTouchPoints", navigator) || 0;
+ window.gesture = me.agent.prefixed("gesture");
+
+ // detect touch capabilities
+ me.device.touch = ("createTouch" in document) || ("ontouchstart" in window) ||
+ (me.device.cocoon) || (me.device.pointerEvent && (me.device.maxTouchPoints > 0));
+
+ // accelerometer detection
+ me.device.hasAccelerometer = (
+ (typeof (window.DeviceMotionEvent) !== "undefined") || (
+ (typeof (window.Windows) !== "undefined") &&
+ (typeof (Windows.Devices.Sensors.Accelerometer) === "function")
+ )
+ );
+
+ // pointerlock detection
+ this.hasPointerLockSupport = me.agent.prefixed("pointerLockElement", document);
+
+ if (this.hasPointerLockSupport) {
+ document.exitPointerLock = me.agent.prefixed("exitPointerLock", document);
+ }
+
+ // device motion detection
+ if (window.DeviceOrientationEvent) {
+ me.device.hasDeviceOrientation = true;
+ }
+
+ // fullscreen api detection & polyfill when possible
+ this.hasFullscreenSupport = me.agent.prefixed("fullscreenEnabled", document) ||
+ document.mozFullScreenEnabled;
+
+ document.exitFullscreen = me.agent.prefixed("cancelFullScreen", document) ||
+ me.agent.prefixed("exitFullscreen", document);
+
+ // vibration API poyfill
+ navigator.vibrate = me.agent.prefixed("vibrate", navigator);
+
+ try {
+ api.localStorage = !!window.localStorage;
+ } catch (e) {
+ // the above generates an exception when cookies are blocked
+ api.localStorage = false;
+ }
+
+ // set pause/stop action on losing focus
+ window.addEventListener("blur", function () {
+ if (me.sys.stopOnBlur) {
+ me.state.stop(true);
+ }
+ if (me.sys.pauseOnBlur) {
+ me.state.pause(true);
+ }
+ }, false);
+ // set restart/resume action on gaining focus
+ window.addEventListener("focus", function () {
+ if (me.sys.stopOnBlur) {
+ me.state.restart(true);
+ }
+ if (me.sys.resumeOnFocus) {
+ me.state.resume(true);
+ }
+ }, false);
+
+
+ // Set the name of the hidden property and the change event for visibility
+ var hidden, visibilityChange;
+ if (typeof document.hidden !== "undefined") {
+ // Opera 12.10 and Firefox 18 and later support
+ hidden = "hidden";
+ visibilityChange = "visibilitychange";
+ } else if (typeof document.mozHidden !== "undefined") {
+ hidden = "mozHidden";
+ visibilityChange = "mozvisibilitychange";
+ } else if (typeof document.msHidden !== "undefined") {
+ hidden = "msHidden";
+ visibilityChange = "msvisibilitychange";
+ } else if (typeof document.webkitHidden !== "undefined") {
+ hidden = "webkitHidden";
+ visibilityChange = "webkitvisibilitychange";
+ }
+
+ // register on the event if supported
+ if (typeof (visibilityChange) === "string") {
+ // add the corresponding event listener
+ document.addEventListener(visibilityChange,
+ function () {
+ if (document[hidden]) {
+ if (me.sys.stopOnBlur) {
+ me.state.stop(true);
+ }
+ if (me.sys.pauseOnBlur) {
+ me.state.pause(true);
+ }
+ } else {
+ if (me.sys.stopOnBlur) {
+ me.state.restart(true);
+ }
+ if (me.sys.resumeOnFocus) {
+ me.state.resume(true);
+ }
+ }
+ }, false
+ );
+ }
+ };
+
+ /**
+ * detect the device type
+ * @ignore
+ */
+ api._detectDevice = function () {
+ // iOS Device ?
+ me.device.iOS = /iPhone|iPad|iPod/i.test(me.device.ua);
+ // Android Device ?
+ me.device.android = /Android/i.test(me.device.ua);
+ me.device.android2 = /Android 2/i.test(me.device.ua);
+ // Chrome OS ?
+ me.device.chromeOS = /CrOS/.test(me.device.ua);
+ // Windows Device ?
+ me.device.wp = /Windows Phone/i.test(me.device.ua);
+ // Kindle device ?
+ me.device.BlackBerry = /BlackBerry/i.test(me.device.ua);
+ // Kindle device ?
+ me.device.Kindle = /Kindle|Silk.*Mobile Safari/i.test(me.device.ua);
+
+ // Mobile platform
+ me.device.isMobile = /Mobi/i.test(me.device.ua) ||
+ me.device.iOS ||
+ me.device.android ||
+ me.device.wp ||
+ me.device.BlackBerry ||
+ me.device.Kindle || false;
+ // ejecta
+ me.device.ejecta = (typeof window.ejecta !== "undefined");
+
+ // cocoon/cocoonJS
+ me.device.cocoon = navigator.isCocoonJS || // former cocoonJS
+ (typeof window.Cocoon !== "undefined"); // new cocoon
+
+ };
+
+ /*
+ * PUBLIC Properties & Functions
+ */
+
+ // Browser capabilities
+
+ /**
+ * the `ua` read-only property returns the user agent string for the current browser.
+ * @type String
+ * @readonly
+ * @name ua
+ * @memberOf me.device
+ */
+ api.ua = navigator.userAgent;
+
+ /**
+ * Browser Local Storage capabilities
+ * (this flag will be set to false if cookies are blocked)
+ * @type Boolean
+ * @readonly
+ * @name localStorage
+ * @memberOf me.device
+ */
+ api.localStorage = false;
+
+ /**
+ * Browser accelerometer capabilities
+ * @type Boolean
+ * @readonly
+ * @name hasAccelerometer
+ * @memberOf me.device
+ */
+ api.hasAccelerometer = false;
+
+ /**
+ * Browser device orientation
+ * @type Boolean
+ * @readonly
+ * @name hasDeviceOrientation
+ * @memberOf me.device
+ */
+ api.hasDeviceOrientation = false;
+
+ /**
+ * Browser full screen support
+ * @type Boolean
+ * @readonly
+ * @name hasFullscreenSupport
+ * @memberOf me.device
+ */
+ api.hasFullscreenSupport = false;
+
+ /**
+ * Browser pointerlock api support
+ * @type Boolean
+ * @readonly
+ * @name hasPointerLockSupport
+ * @memberOf me.device
+ */
+ api.hasPointerLockSupport = false;
+
+ /**
+ * Browser Base64 decoding capability
+ * @type Boolean
+ * @readonly
+ * @name nativeBase64
+ * @memberOf me.device
+ */
+ api.nativeBase64 = (typeof(window.atob) === "function");
+
+ /**
+ * Return the maximum number of touch contacts of current device.
+ * @type Number
+ * @readonly
+ * @name maxTouchPoints
+ * @memberOf me.device
+ */
+ api.maxTouchPoints = 0;
+
+ /**
+ * Touch capabilities
+ * @type Boolean
+ * @readonly
+ * @name touch
+ * @memberOf me.device
+ */
+ api.touch = false;
+
+ /**
+ * equals to true if a mobile device
+ * (Android | iPhone | iPad | iPod | BlackBerry | Windows Phone | Kindle)
+ * @type Boolean
+ * @readonly
+ * @name isMobile
+ * @memberOf me.device
+ */
+ api.isMobile = false;
+
+ /**
+ * equals to true if the device is an iOS platform.
+ * @type Boolean
+ * @readonly
+ * @name iOS
+ * @memberOf me.device
+ */
+ api.iOS = false;
+
+ /**
+ * equals to true if the device is an Android platform.
+ * @type Boolean
+ * @readonly
+ * @name android
+ * @memberOf me.device
+ */
+ api.android = false;
+
+ /**
+ * equals to true if the device is an Android 2.x platform.
+ * @type Boolean
+ * @readonly
+ * @name android2
+ * @memberOf me.device
+ */
+ api.android2 = false;
+
+ /**
+ * equals to true if the game is running under Ejecta.
+ * @type Boolean
+ * @readonly
+ * @see http://impactjs.com/ejecta
+ * @name ejecta
+ * @memberOf me.device
+ */
+ api.ejecta = false;
+
+ /**
+ * equals to true if the game is running under cocoon/cocoonJS.
+ * @type Boolean
+ * @readonly
+ * @see https://cocoon.io
+ * @name cocoon
+ * @memberOf me.device
+ */
+ api.cocoon = false;
+
+ /**
+ * equals to true if the device is running on ChromeOS.
+ * @type Boolean
+ * @readonly
+ * @name chromeOS
+ * @memberOf me.device
+ */
+ api.chromeOS = false;
+
+
+ /**
+ * equals to true if the device is a Windows Phone platform.
+ * @type Boolean
+ * @readonly
+ * @name wp
+ * @memberOf me.device
+ */
+ api.wp = false;
+
+ /**
+ * equals to true if the device is a BlackBerry platform.
+ * @type Boolean
+ * @readonly
+ * @name BlackBerry
+ * @memberOf me.device
+ */
+ api.BlackBerry = false;
+
+ /**
+ * equals to true if the device is a Kindle platform.
+ * @type Boolean
+ * @readonly
+ * @name Kindle
+ * @memberOf me.device
+ */
+ api.Kindle = false;
+
+ /**
+ * The device current orientation status.
+ * 0 : default orientation
+ * 90 : 90 degrees clockwise from default
+ * -90 : 90 degrees anti-clockwise from default
+ * 180 : 180 degrees from default
+ * @type Number
+ * @readonly
+ * @name orientation
+ * @memberOf me.device
+ */
+ api.orientation = 0;
+
+ /**
+ * contains the g-force acceleration along the x-axis.
+ * @public
+ * @type Number
+ * @readonly
+ * @name accelerationX
+ * @memberOf me.device
+ */
+ api.accelerationX = 0;
+
+ /**
+ * contains the g-force acceleration along the y-axis.
+ * @public
+ * @type Number
+ * @readonly
+ * @name accelerationY
+ * @memberOf me.device
+ */
+ api.accelerationY = 0;
+
+ /**
+ * contains the g-force acceleration along the z-axis.
+ * @public
+ * @type Number
+ * @readonly
+ * @name accelerationZ
+ * @memberOf me.device
+ */
+ api.accelerationZ = 0;
+
+ /**
+ * Device orientation Gamma property. Gives angle on tilting a portrait held phone left or right
+ * @public
+ * @type Number
+ * @readonly
+ * @name gamma
+ * @memberOf me.device
+ */
+ api.gamma = 0;
+
+ /**
+ * Device orientation Beta property. Gives angle on tilting a portrait held phone forward or backward
+ * @public
+ * @type Number
+ * @readonly
+ * @name beta
+ * @memberOf me.device
+ */
+ api.beta = 0;
+
+ /**
+ * Device orientation Alpha property. Gives angle based on the rotation of the phone around its z axis.
+ * The z-axis is perpendicular to the phone, facing out from the center of the screen.
+ * @public
+ * @type Number
+ * @readonly
+ * @name alpha
+ * @memberOf me.device
+ */
+ api.alpha = 0;
+
+ /**
+ * a string representing the preferred language of the user, usually the language of the browser UI.
+ * (will default to "en" if the information is not available)
+ * @public
+ * @type String
+ * @readonly
+ * @see http://www.w3schools.com/tags/ref_language_codes.asp
+ * @name language
+ * @memberOf me.device
+ */
+ api.language = navigator.language || navigator.browserLanguage || navigator.userLanguage || "en";
+
+ /**
+ * enable/disable swipe on WebView.
+ * @name enableSwipe
+ * @memberOf me.device
+ * @function
+ * @param {boolean} [enable=true] enable or disable swipe.
+ */
+ api.enableSwipe = function (enable) {
+ if (enable !== false) {
+ if (swipeEnabled === false) {
+ window.document.removeEventListener("touchmove", disableSwipeFn, false);
+ swipeEnabled = true;
+ }
+ } else if (swipeEnabled === true) {
+ window.document.addEventListener("touchmove", disableSwipeFn, false);
+ swipeEnabled = false;
+ }
+ };
+
+ /**
+ * Triggers a fullscreen request. Requires fullscreen support from the browser/device.
+ * @name requestFullscreen
+ * @memberOf me.device
+ * @function
+ * @param {Object} [element=default canvas object] the element to be set in full-screen mode.
+ * @example
+ * // add a keyboard shortcut to toggle Fullscreen mode on/off
+ * me.input.bindKey(me.input.KEY.F, "toggleFullscreen");
+ * me.event.subscribe(me.event.KEYDOWN, function (action, keyCode, edge) {
+ * // toggle fullscreen on/off
+ * if (action === "toggleFullscreen") {
+ * if (!me.device.isFullscreen) {
+ * me.device.requestFullscreen();
+ * } else {
+ * me.device.exitFullscreen();
+ * }
+ * }
+ * });
+ */
+ api.requestFullscreen = function (element) {
+ if (this.hasFullscreenSupport) {
+ element = element || me.video.getWrapper();
+ element.requestFullscreen = me.agent.prefixed("requestFullscreen", element) ||
+ element.mozRequestFullScreen;
+
+ element.requestFullscreen();
+ }
+ };
+
+ /**
+ * Exit fullscreen mode. Requires fullscreen support from the browser/device.
+ * @name exitFullscreen
+ * @memberOf me.device
+ * @function
+ */
+ api.exitFullscreen = function () {
+ if (this.hasFullscreenSupport) {
+ document.exitFullscreen();
+ }
+ };
+
+ /**
+ * return the device pixel ratio
+ * @name getPixelRatio
+ * @memberOf me.device
+ * @function
+ */
+ api.getPixelRatio = function () {
+
+ if (devicePixelRatio === null) {
+ var _context;
+ if (typeof me.video.renderer !== "undefined") {
+ _context = me.video.renderer.getScreenContext();
+ } else {
+ _context = me.Renderer.prototype.getContext2d(document.createElement("canvas"));
+ }
+ var _devicePixelRatio = window.devicePixelRatio || 1,
+ _backingStoreRatio = me.agent.prefixed("backingStorePixelRatio", _context) || 1;
+ devicePixelRatio = _devicePixelRatio / _backingStoreRatio;
+ }
+ return devicePixelRatio;
+ };
+
+ /**
+ * return the device storage
+ * @name getStorage
+ * @memberOf me.device
+ * @function
+ * @param {String} [type="local"]
+ * @return me.save object
+ */
+ api.getStorage = function (type) {
+
+ type = type || "local";
+
+ switch (type) {
+ case "local" :
+ return me.save;
+
+ default :
+ throw new me.Error("storage type " + type + " not supported");
+ }
+ };
+
+ /**
+ * event management (Accelerometer)
+ * http://www.mobilexweb.com/samples/ball.html
+ * http://www.mobilexweb.com/blog/safari-ios-accelerometer-websockets-html5
+ * @ignore
+ */
+ function onDeviceMotion(e) {
+ if (e.reading) {
+ // For Windows 8 devices
+ api.accelerationX = e.reading.accelerationX;
+ api.accelerationY = e.reading.accelerationY;
+ api.accelerationZ = e.reading.accelerationZ;
+ }
+ else {
+ // Accelerometer information
+ api.accelerationX = e.accelerationIncludingGravity.x;
+ api.accelerationY = e.accelerationIncludingGravity.y;
+ api.accelerationZ = e.accelerationIncludingGravity.z;
+ }
+ }
+
+ function onDeviceRotate(e) {
+ api.gamma = e.gamma;
+ api.beta = e.beta;
+ api.alpha = e.alpha;
+ }
+
+ /**
+ * Enters pointer lock, requesting it from the user first. Works on supported devices & browsers
+ * Must be called in a click event or an event that requires user interaction.
+ * If you need to run handle events for errors or change of the pointer lock, see below.
+ * @name turnOnPointerLock
+ * @memberOf me.device
+ * @function
+ * @example
+ * document.addEventListener("pointerlockchange", pointerlockchange, false);
+ * document.addEventListener("mozpointerlockchange", pointerlockchange, false);
+ * document.addEventListener("webkitpointerlockchange", pointerlockchange, false);
+ *
+ * document.addEventListener("pointerlockerror", pointerlockerror, false);
+ * document.addEventListener("mozpointerlockerror", pointerlockerror, false);
+ * document.addEventListener("webkitpointerlockerror", pointerlockerror, false);
+ */
+ api.turnOnPointerLock = function () {
+ if (this.hasPointerLockSupport) {
+ var element = me.video.getWrapper();
+ if (me.device.ua.match(/Firefox/i)) {
+ var fullscreenchange = function () {
+ if ((me.agent.prefixed("fullscreenElement", document) ||
+ document.mozFullScreenElement) === element) {
+
+ document.removeEventListener("fullscreenchange", fullscreenchange);
+ document.removeEventListener("mozfullscreenchange", fullscreenchange);
+ element.requestPointerLock = me.agent.prefixed("requestPointerLock", element);
+ element.requestPointerLock();
+ }
+ };
+
+ document.addEventListener("fullscreenchange", fullscreenchange, false);
+ document.addEventListener("mozfullscreenchange", fullscreenchange, false);
+
+ me.device.requestFullscreen();
+
+ }
+ else {
+ element.requestPointerLock();
+ }
+ }
+ };
+
+ /**
+ * Exits pointer lock. Works on supported devices & browsers
+ * @name turnOffPointerLock
+ * @memberOf me.device
+ * @function
+ */
+ api.turnOffPointerLock = function () {
+ if (this.hasPointerLockSupport) {
+ document.exitPointerLock();
+ }
+ };
+
+ /**
+ * watch Accelerator event
+ * @name watchAccelerometer
+ * @memberOf me.device
+ * @public
+ * @function
+ * @return {Boolean} false if not supported by the device
+ */
+ api.watchAccelerometer = function () {
+ if (me.device.hasAccelerometer) {
+ if (!accelInitialized) {
+ if (typeof Windows === "undefined") {
+ // add a listener for the devicemotion event
+ window.addEventListener("devicemotion", onDeviceMotion, false);
+ }
+ else {
+ // On Windows 8 Device
+ var accelerometer = Windows.Devices.Sensors.Accelerometer.getDefault();
+ if (accelerometer) {
+ // Capture event at regular intervals
+ var minInterval = accelerometer.minimumReportInterval;
+ var Interval = minInterval >= 16 ? minInterval : 25;
+ accelerometer.reportInterval = Interval;
+
+ accelerometer.addEventListener("readingchanged", onDeviceMotion, false);
+ }
+ }
+ accelInitialized = true;
+ }
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * unwatch Accelerometor event
+ * @name unwatchAccelerometer
+ * @memberOf me.device
+ * @public
+ * @function
+ */
+ api.unwatchAccelerometer = function () {
+ if (accelInitialized) {
+ if (typeof Windows === "undefined") {
+ // add a listener for the mouse
+ window.removeEventListener("devicemotion", onDeviceMotion, false);
+ } else {
+ // On Windows 8 Devices
+ var accelerometer = Windows.Device.Sensors.Accelerometer.getDefault();
+
+ accelerometer.removeEventListener("readingchanged", onDeviceMotion, false);
+ }
+ accelInitialized = false;
+ }
+ };
+
+ /**
+ * watch the device orientation event
+ * @name watchDeviceOrientation
+ * @memberOf me.device
+ * @public
+ * @function
+ * @return {Boolean} false if not supported by the device
+ */
+ api.watchDeviceOrientation = function () {
+ if (me.device.hasDeviceOrientation && !deviceOrientationInitialized) {
+ window.addEventListener("deviceorientation", onDeviceRotate, false);
+ deviceOrientationInitialized = true;
+ }
+ return false;
+ };
+
+ /**
+ * unwatch Device orientation event
+ * @name unwatchDeviceOrientation
+ * @memberOf me.device
+ * @public
+ * @function
+ */
+ api.unwatchDeviceOrientation = function () {
+ if (deviceOrientationInitialized) {
+ window.removeEventListener("deviceorientation", onDeviceRotate, false);
+ deviceOrientationInitialized = false;
+ }
+ };
+
+ /**
+ * the vibrate method pulses the vibration hardware on the device,
+ * If the device doesn't support vibration, this method has no effect.
+ * If a vibration pattern is already in progress when this method is called,
+ * the previous pattern is halted and the new one begins instead.
+ * @name vibrate
+ * @memberOf me.device
+ * @public
+ * @function
+ * @param {Number|Number[]} pattern pattern of vibration and pause intervals
+ * @example
+ * // vibrate for 1000 ms
+ * navigator.vibrate(1000);
+ * // or alternatively
+ * navigator.vibrate([1000]);
+ * // vibrate for 50 ms, be still for 100 ms, and then vibrate for 150 ms:
+ * navigator.vibrate([50, 100, 150]);
+ * // cancel any existing vibrations
+ * navigator.vibrate(0);
+ */
+ api.vibrate = function (pattern) {
+ if (navigator.vibrate) {
+ navigator.vibrate(pattern);
+ }
+ };
+
+
+ return api;
+ })();
+
+ /**
+ * Returns true if the browser/device is in full screen mode.
+ * @name isFullscreen
+ * @memberOf me.device
+ * @public
+ * @type Boolean
+ * @readonly
+ * @return {boolean}
+ */
+ Object.defineProperty(me.device, "isFullscreen", {
+ /**
+ * @ignore
+ */
+ get: function () {
+ if (me.device.hasFullscreenSupport) {
+ var el = me.agent.prefixed("fullscreenElement", document) ||
+ document.mozFullScreenElement;
+ return (el === me.video.getWrapper());
+ } else {
+ return false;
+ }
+ }
+ });
+
+ /**
+ * Returns true if the browser/device has audio capabilities.
+ * @name sound
+ * @memberOf me.device
+ * @public
+ * @type Boolean
+ * @readonly
+ * @return {boolean}
+ */
+ Object.defineProperty(me.device, "sound", {
+ /**
+ * @ignore
+ */
+ get: function () {
+ return !Howler.noAudio;
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a Timer object to manage time function (FPS, Game Tick, Time...)
+ * There is no constructor function for me.timer
+ * @namespace me.timer
+ * @memberOf me
+ */
+ me.timer = (function () {
+ // hold public stuff in our api
+ var api = {};
+
+ /*
+ * PRIVATE STUFF
+ */
+
+ //hold element to display fps
+ var framecount = 0;
+ var framedelta = 0;
+
+ /* fps count stuff */
+ var last = 0;
+ var now = 0;
+ var delta = 0;
+ var step = Math.ceil(1000 / me.sys.fps); // ROUND IT ?
+ // define some step with some margin
+ var minstep = (1000 / me.sys.fps) * 1.25; // IS IT NECESSARY?\
+
+ // list of defined timer function
+ var timers = [];
+ var timerId = 0;
+
+ /**
+ * @ignore
+ */
+ var clearTimer = function (timerId) {
+ for (var i = 0, len = timers.length; i < len; i++) {
+ if (timers[i].timerId === timerId) {
+ timers.splice(i, 1);
+ break;
+ }
+ }
+ };
+
+ /**
+ * update timers
+ * @ignore
+ */
+ var updateTimers = function (dt) {
+ for (var i = 0, len = timers.length; i < len; i++) {
+ var _timer = timers[i];
+ if (!(_timer.pauseable && me.state.isPaused())) {
+ _timer.elapsed += dt;
+ }
+ if (_timer.elapsed >= _timer.delay) {
+ _timer.fn.apply(this);
+ if (_timer.repeat === true) {
+ _timer.elapsed -= _timer.delay;
+ } else {
+ me.timer.clearTimeout(_timer.timerId);
+ }
+ }
+ }
+ };
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * Last game tick value.
+ * Use this value to scale velocities during frame drops due to slow
+ * hardware or when setting an FPS limit. (See {@link me.sys.fps})
+ * This feature is disabled by default. Enable me.sys.interpolation to
+ * use it.
+ * @public
+ * @see me.sys.interpolation
+ * @type Number
+ * @name tick
+ * @memberOf me.timer
+ */
+ api.tick = 1.0;
+
+ /**
+ * Last measured fps rate.
+ * This feature is disabled by default. Load and enable the DebugPanel
+ * plugin to use it.
+ * @public
+ * @type Number
+ * @name fps
+ * @memberOf me.timer
+ */
+ api.fps = 0;
+
+ /**
+ * Last update time.
+ * Use this value to implement frame prediction in drawing events,
+ * for creating smooth motion while running game update logic at
+ * a lower fps.
+ * @public
+ * @type Date
+ * @name lastUpdate
+ * @memberOf me.timer
+ */
+ api.lastUpdate = window.performance.now();
+
+ /**
+ * init the timer
+ * @ignore
+ */
+ api.init = function () {
+ // reset variables to initial state
+ api.reset();
+ now = last = 0;
+ };
+
+ /**
+ * reset time (e.g. usefull in case of pause)
+ * @name reset
+ * @memberOf me.timer
+ * @ignore
+ * @function
+ */
+ api.reset = function () {
+ // set to "now"
+ last = now = window.performance.now();
+ delta = 0;
+ // reset delta counting variables
+ framedelta = 0;
+ framecount = 0;
+ };
+
+ /**
+ * Calls a function once after a specified delay.
+ * @name setTimeout
+ * @memberOf me.timer
+ * @param {Function} fn the function you want to execute after delay milliseconds.
+ * @param {Number} delay the number of milliseconds (thousandths of a second) that the function call should be delayed by.
+ * @param {Boolean} [pauseable=true] respects the pause state of the engine.
+ * @return {Number} The numerical ID of the timeout, which can be used later with me.timer.clearTimeout().
+ * @function
+ */
+ api.setTimeout = function (fn, delay, pauseable) {
+ timers.push({
+ fn : fn,
+ delay : delay,
+ elapsed : 0,
+ repeat : false,
+ timerId : ++timerId,
+ pauseable : pauseable === true || true
+ });
+ return timerId;
+ };
+
+ /**
+ * Calls a function at specified interval.
+ * @name setInterval
+ * @memberOf me.timer
+ * @param {Function} fn the function to execute
+ * @param {Number} delay the number of milliseconds (thousandths of a second) on how often to execute the function
+ * @param {Boolean} [pauseable=true] respects the pause state of the engine.
+ * @return {Number} The numerical ID of the timeout, which can be used later with me.timer.clearInterval().
+ * @function
+ */
+ api.setInterval = function (fn, delay, pauseable) {
+ timers.push({
+ fn : fn,
+ delay : delay,
+ elapsed : 0,
+ repeat : true,
+ timerId : ++timerId,
+ pauseable : pauseable === true || true
+ });
+ return timerId;
+ };
+
+ /**
+ * Clears the delay set by me.timer.setTimeout().
+ * @name clearTimeout
+ * @memberOf me.timer
+ * @function
+ * @param {Number} timeoutID ID of the timeout to be cleared
+ */
+ api.clearTimeout = function (timeoutID) {
+ clearTimer.defer(this, timeoutID);
+ };
+
+ /**
+ * Clears the Interval set by me.timer.setInterval().
+ * @name clearInterval
+ * @memberOf me.timer
+ * @function
+ * @param {Number} intervalID ID of the interval to be cleared
+ */
+ api.clearInterval = function (intervalID) {
+ clearTimer.defer(this, intervalID);
+ };
+
+ /**
+ * Return the current timestamp in milliseconds
+ * since the game has started or since linux epoch (based on browser support for High Resolution Timer)
+ * @name getTime
+ * @memberOf me.timer
+ * @return {Number}
+ * @function
+ */
+ api.getTime = function () {
+ return now;
+ };
+
+ /**
+ * Return elapsed time in milliseconds since the last update
+ * @name getDelta
+ * @memberOf me.timer
+ * @return {Number}
+ * @function
+ */
+ api.getDelta = function () {
+ return delta;
+ };
+
+ /**
+ * compute the actual frame time and fps rate
+ * @name computeFPS
+ * @ignore
+ * @memberOf me.timer
+ * @function
+ */
+ api.countFPS = function () {
+ framecount++;
+ framedelta += delta;
+ if (framecount % 10 === 0) {
+ this.fps = (~~((1000 * framecount) / framedelta)).clamp(0, me.sys.fps);
+ framedelta = 0;
+ framecount = 0;
+ }
+ };
+
+ /**
+ * update game tick
+ * should be called once a frame
+ * @param {Number} time current timestamp as provided by the RAF callback
+ * @return {Number} time elapsed since the last update
+ * @ignore
+ */
+ api.update = function (time) {
+ last = now;
+ now = time;
+ delta = (now - last);
+
+ // get the game tick
+ api.tick = (delta > minstep && me.sys.interpolation) ? delta / step : 1;
+
+ // update defined timers
+ updateTimers(delta);
+
+ return delta;
+ };
+
+ // return our apiect
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * A pool of Object entity
+ * This object is used for object pooling - a technique that might speed up your game
+ * if used properly.
+ * If some of your classes will be instantiated and removed a lot at a time, it is a
+ * good idea to add the class to this entity pool. A separate pool for that class
+ * will be created, which will reuse objects of the class. That way they won't be instantiated
+ * each time you need a new one (slowing your game), but stored into that pool and taking one
+ * already instantiated when you need it.
+ * This object is also used by the engine to instantiate objects defined in the map,
+ * which means, that on level loading the engine will try to instantiate every object
+ * found in the map, based on the user defined name in each Object Properties
+ *
+ * @namespace me.pool
+ * @memberOf me
+ */
+ me.pool = (function () {
+ // hold public stuff in our singleton
+ var api = {};
+
+ var entityClass = {};
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * Constructor
+ * @ignore
+ */
+ api.init = function () {
+ // add default entity object
+ api.register("me.Entity", me.Entity);
+ api.register("me.CollectableEntity", me.CollectableEntity);
+ api.register("me.LevelEntity", me.LevelEntity);
+ api.register("me.Tween", me.Tween, true);
+ api.register("me.Color", me.Color, true);
+ api.register("me.Particle", me.Particle, true);
+ api.register("me.Sprite", me.Sprite);
+ api.register("me.Vector2d", me.Vector2d, true);
+ api.register("me.Glyph", me.Glyph, true);
+ api.register("me.Matrix2d", me.Matrix2d, true);
+ };
+
+ /**
+ * register an object to the pool.
+ * Pooling must be set to true if more than one such objects will be created.
+ * (note) If pooling is enabled, you shouldn't instantiate objects with `new`.
+ * See examples in {@link me.pool#pull}
+ * @name register
+ * @memberOf me.pool
+ * @public
+ * @function
+ * @param {String} className as defined in the Name field of the Object Properties (in Tiled)
+ * @param {Object} class corresponding Class to be instantiated
+ * @param {Boolean} [objectPooling=false] enables object pooling for the specified class
+ * - speeds up the game by reusing existing objects
+ * @example
+ * // add our users defined entities in the entity pool
+ * me.pool.register("playerspawnpoint", PlayerEntity);
+ * me.pool.register("cherryentity", CherryEntity, true);
+ * me.pool.register("heartentity", HeartEntity, true);
+ * me.pool.register("starentity", StarEntity, true);
+ */
+ api.register = function (className, classObj, pooling) {
+ if (typeof (classObj) !== "undefined") {
+ entityClass[className] = {
+ "class" : classObj,
+ "pool" : (pooling ? [] : undefined)
+ };
+ } else {
+ throw new me.Error("Cannot register object '" + className + "', invalid class");
+ }
+ };
+
+ /**
+ * Pull a new instance of the requested object (if added into the object pool)
+ * @name pull
+ * @memberOf me.pool
+ * @public
+ * @function
+ * @param {String} className as used in {@link me.pool.register}
+ * @param {} [arguments...] arguments to be passed when instantiating/reinitializing the object
+ * @return {Object} the instance of the requested object
+ * @example
+ * me.pool.register("player", PlayerEntity);
+ * var player = me.pool.pull("player");
+ * @example
+ * me.pool.register("bullet", BulletEntity, true);
+ * me.pool.register("enemy", EnemyEntity, true);
+ * // ...
+ * // when we need to manually create a new bullet:
+ * var bullet = me.pool.pull("bullet", x, y, direction);
+ * // ...
+ * // params aren't a fixed number
+ * // when we need new enemy we can add more params, that the object construct requires:
+ * var enemy = me.pool.pull("enemy", x, y, direction, speed, power, life);
+ * // ...
+ * // when we want to destroy existing object, the remove
+ * // function will ensure the object can then be reallocated later
+ * me.game.world.removeChild(enemy);
+ * me.game.world.removeChild(bullet);
+ */
+ api.pull = function (name) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < arguments.length; i++) {
+ args[i] = arguments[i];
+ }
+ var entity = entityClass[name];
+ if (entity) {
+ var proto = entity["class"],
+ pool = entity.pool,
+ obj;
+
+ if (pool && ((obj = pool.pop()))) {
+ args.shift();
+ // call the object onResetEvent function if defined
+ if (typeof(obj.onResetEvent) === "function") {
+ obj.onResetEvent.apply(obj, args);
+ }
+ else {
+ obj.init.apply(obj, args);
+ }
+ }
+ else {
+ args[0] = proto;
+ obj = new (proto.bind.apply(proto, args))();
+ if (pool) {
+ obj.className = name;
+ }
+ }
+ return obj;
+ }
+
+ throw new me.Error("Cannot instantiate entity of type '" + name + "'");
+ };
+
+ /**
+ * purge the entity pool from any inactive object
+ * Object pooling must be enabled for this function to work
+ * note: this will trigger the garbage collector
+ * @name purge
+ * @memberOf me.pool
+ * @public
+ * @function
+ */
+ api.purge = function () {
+ for (var className in entityClass) {
+ if (entityClass[className]) {
+ entityClass[className].pool = [];
+ }
+ }
+ };
+
+ /**
+ * Push back an object instance into the entity pool
+ * Object pooling for the object class must be enabled,
+ * and object must have been instantiated using {@link me.pool#pull},
+ * otherwise this function won't work
+ * @name push
+ * @memberOf me.pool
+ * @public
+ * @function
+ * @param {Object} instance to be recycled
+ */
+ api.push = function (obj) {
+ var name = obj.className;
+ if (typeof(name) === "undefined" || !entityClass[name]) {
+ // object is not registered, don't do anything
+ return;
+ }
+ // store back the object instance for later recycling
+ entityClass[name].pool.push(obj);
+ };
+
+ /**
+ * Check if an object with the provided name is registered
+ * @name exists
+ * @memberOf me.pool
+ * @public
+ * @function
+ * @param {String} name of the registered object
+ * @return {Boolean} true if the classname is registered
+ */
+ api.exists = function (name) {
+ return name in entityClass;
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a generic 2D Vector Object
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Number} [x=0] x value of the vector
+ * @param {Number} [y=0] y value of the vector
+ */
+ me.Vector2d = me.Object.extend(
+ /** @scope me.Vector2d.prototype */
+ {
+ /** @ignore */
+ init : function (x, y) {
+ return this.set(x || 0, y || 0);
+ },
+
+ /**
+ * @ignore */
+ _set : function (x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ },
+
+ /**
+ * set the Vector x and y properties to the given values
+ * @name set
+ * @memberOf me.Vector2d
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ set : function (x, y) {
+ if (x !== +x || y !== +y) {
+ throw new me.Vector2d.Error(
+ "invalid x,y parameters (not a number)"
+ );
+ }
+
+ /**
+ * x value of the vector
+ * @public
+ * @type Number
+ * @name x
+ * @memberOf me.Vector2d
+ */
+ //this.x = x;
+
+ /**
+ * y value of the vector
+ * @public
+ * @type Number
+ * @name y
+ * @memberOf me.Vector2d
+ */
+ //this.y = y;
+
+ return this._set(x, y);
+ },
+
+ /**
+ * set the Vector x and y properties to 0
+ * @name setZero
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ setZero : function () {
+ return this.set(0, 0);
+ },
+
+ /**
+ * set the Vector x and y properties using the passed vector
+ * @name setV
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ setV : function (v) {
+ return this._set(v.x, v.y);
+ },
+
+ /**
+ * Add the passed vector to this vector
+ * @name add
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ add : function (v) {
+ return this._set(this.x + v.x, this.y + v.y);
+ },
+
+ /**
+ * Substract the passed vector to this vector
+ * @name sub
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ sub : function (v) {
+ return this._set(this.x - v.x, this.y - v.y);
+ },
+
+ /**
+ * Multiply this vector values by the given scalar
+ * @name scale
+ * @memberOf me.Vector2d
+ * @function
+ * @param {Number} x
+ * @param {Number} [y=x]
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ scale : function (x, y) {
+ return this._set(this.x * x, this.y * (typeof (y) !== "undefined" ? y : x));
+ },
+
+ /**
+ * Convert this vector into isometric coordinate space
+ * @name toIso
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ toIso : function () {
+ return this._set(this.x - this.y, (this.x + this.y) * 0.5);
+ },
+
+ /**
+ * Convert this vector into 2d coordinate space
+ * @name to2d
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ to2d : function () {
+ return this._set(this.y + this.x / 2, this.y - this.x / 2);
+ },
+
+ /**
+ * Multiply this vector values by the passed vector
+ * @name scaleV
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ scaleV : function (v) {
+ return this._set(this.x * v.x, this.y * v.y);
+ },
+
+ /**
+ * Divide this vector values by the passed value
+ * @name div
+ * @memberOf me.Vector2d
+ * @function
+ * @param {Number} value
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ div : function (n) {
+ return this._set(this.x / n, this.y / n);
+ },
+
+ /**
+ * Update this vector values to absolute values
+ * @name abs
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ abs : function () {
+ return this._set((this.x < 0) ? -this.x : this.x, (this.y < 0) ? -this.y : this.y);
+ },
+
+ /**
+ * Clamp the vector value within the specified value range
+ * @name clamp
+ * @memberOf me.Vector2d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.Vector2d} new me.Vector2d
+ */
+ clamp : function (low, high) {
+ return new me.Vector2d(this.x.clamp(low, high), this.y.clamp(low, high));
+ },
+
+ /**
+ * Clamp this vector value within the specified value range
+ * @name clampSelf
+ * @memberOf me.Vector2d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ clampSelf : function (low, high) {
+ return this._set(this.x.clamp(low, high), this.y.clamp(low, high));
+ },
+
+ /**
+ * Update this vector with the minimum value between this and the passed vector
+ * @name minV
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ minV : function (v) {
+ return this._set((this.x < v.x) ? this.x : v.x, (this.y < v.y) ? this.y : v.y);
+ },
+
+ /**
+ * Update this vector with the maximum value between this and the passed vector
+ * @name maxV
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ maxV : function (v) {
+ return this._set((this.x > v.x) ? this.x : v.x, (this.y > v.y) ? this.y : v.y);
+ },
+
+ /**
+ * Floor the vector values
+ * @name floor
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} new me.Vector2d
+ */
+ floor : function () {
+ return new me.Vector2d(Math.floor(this.x), Math.floor(this.y));
+ },
+
+ /**
+ * Floor this vector values
+ * @name floorSelf
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ floorSelf : function () {
+ return this._set(Math.floor(this.x), Math.floor(this.y));
+ },
+
+ /**
+ * Ceil the vector values
+ * @name ceil
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} new me.Vector2d
+ */
+ ceil : function () {
+ return new me.Vector2d(Math.ceil(this.x), Math.ceil(this.y));
+ },
+
+ /**
+ * Ceil this vector values
+ * @name ceilSelf
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ ceilSelf : function () {
+ return this._set(Math.ceil(this.x), Math.ceil(this.y));
+ },
+
+ /**
+ * Negate the vector values
+ * @name negate
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} new me.Vector2d
+ */
+ negate : function () {
+ return new me.Vector2d(-this.x, -this.y);
+ },
+
+ /**
+ * Negate this vector values
+ * @name negateSelf
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ negateSelf : function () {
+ return this._set(-this.x, -this.y);
+ },
+
+ /**
+ * Copy the x,y values of the passed vector to this one
+ * @name copy
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ copy : function (v) {
+ return this._set(v.x, v.y);
+ },
+
+ /**
+ * return true if the two vectors are the same
+ * @name equals
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {Boolean}
+ */
+ equals : function (v) {
+ return ((this.x === v.x) && (this.y === v.y));
+ },
+
+ /**
+ * normalize this vector (scale the vector so that its magnitude is 1)
+ * @name normalize
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ normalize : function () {
+ var d = this.length();
+ if (d > 0) {
+ return this._set(this.x / d, this.y / d);
+ }
+ return this;
+ },
+
+ /**
+ * change this vector to be perpendicular to what it was before.
+ * (Effectively rotates it 90 degrees in a clockwise direction)
+ * @name perp
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ perp : function () {
+ return this._set(this.y, -this.x);
+ },
+
+ /**
+ * Rotate this vector (counter-clockwise) by the specified angle (in radians).
+ * @name rotate
+ * @memberOf me.Vector2d
+ * @function
+ * @param {number} angle The angle to rotate (in radians)
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ rotate : function (angle) {
+ var x = this.x;
+ var y = this.y;
+ return this._set(x * Math.cos(angle) - y * Math.sin(angle), x * Math.sin(angle) + y * Math.cos(angle));
+ },
+
+ /**
+ * return the dot product of this vector and the passed one
+ * @name dotProduct
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {Number} The dot product.
+ */
+ dotProduct : function (v) {
+ return this.x * v.x + this.y * v.y;
+ },
+
+ /**
+ * return the square length of this vector
+ * @name length2
+ * @memberOf me.Vector2d
+ * @function
+ * @return {Number} The length^2 of this vector.
+ */
+ length2 : function () {
+ return this.dotProduct(this);
+ },
+
+ /**
+ * return the length (magnitude) of this vector
+ * @name length
+ * @memberOf me.Vector2d
+ * @function
+ * @return {Number} the length of this vector
+ */
+ length : function () {
+ return Math.sqrt(this.length2());
+ },
+
+ /**
+ * return the distance between this vector and the passed one
+ * @name distance
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {Number}
+ */
+ distance : function (v) {
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return Math.sqrt(dx * dx + dy * dy);
+ },
+
+ /**
+ * return the angle between this vector and the passed one
+ * @name angle
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v
+ * @return {Number} angle in radians
+ */
+ angle : function (v) {
+ return Math.acos((this.dotProduct(v) / (this.length() * v.length())).clamp(-1, 1));
+ },
+
+ /**
+ * project this vector on to another vector.
+ * @name project
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v The vector to project onto.
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ project : function (v) {
+ return this.scale(this.dotProduct(v) / v.length2());
+ },
+
+ /**
+ * Project this vector onto a vector of unit length.
+ * This is slightly more efficient than `project` when dealing with unit vectors.
+ * @name projectN
+ * @memberOf me.Vector2d
+ * @function
+ * @param {me.Vector2d} v The unit vector to project onto.
+ * @return {me.Vector2d} Reference to this object for method chaining
+ */
+ projectN : function (v) {
+ return this.scale(this.dotProduct(v));
+ },
+
+ /**
+ * return a clone copy of this vector
+ * @name clone
+ * @memberOf me.Vector2d
+ * @function
+ * @return {me.Vector2d} new me.Vector2d
+ */
+ clone : function () {
+ return new me.Vector2d(this.x, this.y);
+ },
+
+ /**
+ * convert the object to a string representation
+ * @name toString
+ * @memberOf me.Vector2d
+ * @function
+ * @return {String}
+ */
+ toString : function () {
+ return "x:" + this.x + ",y:" + this.y;
+ }
+ });
+
+ /**
+ * Base class for Vector2d exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.Vector2d
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.Vector2d.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.Vector2d.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a generic 3D Vector Object
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Number} [x=0] x value of the vector
+ * @param {Number} [y=0] y value of the vector
+ * @param {Number} [z=0] z value of the vector
+ */
+ me.Vector3d = me.Object.extend(
+ /** @scope me.Vector3d.prototype */
+ {
+ /** @ignore */
+ init : function (x, y, z) {
+ return this.set(x || 0, y || 0, z || 0);
+ },
+
+ /**
+ * @ignore */
+ _set : function (x, y, z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ },
+
+ /**
+ * set the Vector x and y properties to the given values
+ * @name set
+ * @memberOf me.Vector3d
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} z
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ set : function (x, y, z) {
+ if (x !== +x || y !== +y || z !== +z) {
+ throw new me.Vector3d.Error(
+ "invalid x, y, z parameters (not a number)"
+ );
+ }
+
+ /**
+ * x value of the vector
+ * @public
+ * @type Number
+ * @name x
+ * @memberOf me.Vector3d
+ */
+ //this.x = x;
+
+ /**
+ * y value of the vector
+ * @public
+ * @type Number
+ * @name y
+ * @memberOf me.Vector3d
+ */
+ //this.y = y;
+
+ /**
+ * z value of the vector
+ * @public
+ * @type Number
+ * @name z
+ * @memberOf me.Vector3d
+ */
+ //this.z = z;
+
+ return this._set(x, y, z);
+ },
+
+ /**
+ * set the Vector x and y properties to 0
+ * @name setZero
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ setZero : function () {
+ return this.set(0, 0, 0);
+ },
+
+ /**
+ * set the Vector x and y properties using the passed vector
+ * @name setV
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ setV : function (v) {
+ return this._set(v.x, v.y, typeof (v.z) !== "undefined" ? v.z : this.z);
+ },
+
+ /**
+ * Add the passed vector to this vector
+ * @name add
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ add : function (v) {
+ return this._set(this.x + v.x, this.y + v.y, this.z + (v.z || 0));
+ },
+
+ /**
+ * Substract the passed vector to this vector
+ * @name sub
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ sub : function (v) {
+ return this._set(this.x - v.x, this.y - v.y, this.z - (v.z || 0));
+ },
+
+ /**
+ * Multiply this vector values by the given scalar
+ * @name scale
+ * @memberOf me.Vector3d
+ * @function
+ * @param {Number} x
+ * @param {Number} [y=x]
+ * @param {Number} [z=x]
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ scale : function (x, y, z) {
+ y = (typeof (y) !== "undefined" ? y : x);
+ z = (typeof (z) !== "undefined" ? z : x);
+ return this._set(this.x * x, this.y * y, this.z * z);
+ },
+
+ /**
+ * Multiply this vector values by the passed vector
+ * @name scaleV
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ scaleV : function (v) {
+ return this._set(this.x * v.x, this.y * v.y, this.z * (v.z || 1));
+ },
+
+ /**
+ * Convert this vector into isometric coordinate space
+ * @name toIso
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ toIso : function () {
+ return this._set(this.x - this.y, (this.x + this.y) * 0.5, this.z);
+ },
+
+ /**
+ * Convert this vector into 2d coordinate space
+ * @name to2d
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ to2d : function () {
+ return this._set(this.y + this.x / 2, this.y - this.x / 2, this.z);
+ },
+
+ /**
+ * Divide this vector values by the passed value
+ * @name div
+ * @memberOf me.Vector3d
+ * @function
+ * @param {Number} value
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ div : function (n) {
+ return this._set(this.x / n, this.y / n, this.z / n);
+ },
+
+ /**
+ * Update this vector values to absolute values
+ * @name abs
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ abs : function () {
+ return this._set((this.x < 0) ? -this.x : this.x, (this.y < 0) ? -this.y : this.y, (this.z < 0) ? -this.Z : this.z);
+ },
+
+ /**
+ * Clamp the vector value within the specified value range
+ * @name clamp
+ * @memberOf me.Vector3d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.Vector3d} new me.Vector3d
+ */
+ clamp : function (low, high) {
+ return new me.Vector3d(this.x.clamp(low, high), this.y.clamp(low, high), this.z.clamp(low, high));
+ },
+
+ /**
+ * Clamp this vector value within the specified value range
+ * @name clampSelf
+ * @memberOf me.Vector3d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ clampSelf : function (low, high) {
+ return this._set(this.x.clamp(low, high), this.y.clamp(low, high), this.z.clamp(low, high));
+ },
+
+ /**
+ * Update this vector with the minimum value between this and the passed vector
+ * @name minV
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ minV : function (v) {
+ var _vz = v.z || 0;
+ return this._set((this.x < v.x) ? this.x : v.x, (this.y < v.y) ? this.y : v.y, (this.z < _vz) ? this.z : _vz);
+ },
+
+ /**
+ * Update this vector with the maximum value between this and the passed vector
+ * @name maxV
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ maxV : function (v) {
+ var _vz = v.z || 0;
+ return this._set((this.x > v.x) ? this.x : v.x, (this.y > v.y) ? this.y : v.y, (this.z > _vz) ? this.z : _vz);
+ },
+
+ /**
+ * Floor the vector values
+ * @name floor
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} new me.Vector3d
+ */
+ floor : function () {
+ return new me.Vector3d(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
+ },
+
+ /**
+ * Floor this vector values
+ * @name floorSelf
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ floorSelf : function () {
+ return this._set(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
+ },
+
+ /**
+ * Ceil the vector values
+ * @name ceil
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} new me.Vector3d
+ */
+ ceil : function () {
+ return new me.Vector3d(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z));
+ },
+
+ /**
+ * Ceil this vector values
+ * @name ceilSelf
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ ceilSelf : function () {
+ return this._set(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z));
+ },
+
+ /**
+ * Negate the vector values
+ * @name negate
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} new me.Vector3d
+ */
+ negate : function () {
+ return new me.Vector3d(-this.x, -this.y, -this.z);
+ },
+
+ /**
+ * Negate this vector values
+ * @name negateSelf
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ negateSelf : function () {
+ return this._set(-this.x, -this.y, -this.z);
+ },
+
+ /**
+ * Copy the x,y values of the passed vector to this one
+ * @name copy
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ copy : function (v) {
+ return this._set(v.x, v.y, typeof (v.z) !== "undefined" ? v.z : this.z);
+ },
+
+ /**
+ * return true if the two vectors are the same
+ * @name equals
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {Boolean}
+ */
+ equals : function (v) {
+ return ((this.x === v.x) && (this.y === v.y) && (this.z === (v.z || this.z)));
+ },
+
+ /**
+ * normalize this vector (scale the vector so that its magnitude is 1)
+ * @name normalize
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ normalize : function () {
+ var d = this.length();
+ if (d > 0) {
+ return this._set(this.x / d, this.y / d, this.z / d);
+ }
+ return this;
+ },
+
+ /**
+ * change this vector to be perpendicular to what it was before.
+ * (Effectively rotates it 90 degrees in a clockwise direction around the z axis)
+ * @name perp
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ perp : function () {
+ return this._set(this.y, -this.x, this.z);
+ },
+
+ /**
+ * Rotate this vector (counter-clockwise) by the specified angle (in radians) around the z axis
+ * @name rotate
+ * @memberOf me.Vector3d
+ * @function
+ * @param {number} angle The angle to rotate (in radians)
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ rotate : function (angle) {
+ var x = this.x;
+ var y = this.y;
+ return this._set(x * Math.cos(angle) - y * Math.sin(angle), x * Math.sin(angle) + y * Math.cos(angle), this.z);
+ },
+
+ /**
+ * return the dot product of this vector and the passed one
+ * @name dotProduct
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {Number} The dot product.
+ */
+ dotProduct : function (v) {
+ return this.x * v.x + this.y * v.y + this.z * (v.z || 1);
+ },
+
+ /**
+ * return the square length of this vector
+ * @name length2
+ * @memberOf me.Vector3d
+ * @function
+ * @return {Number} The length^2 of this vector.
+ */
+ length2 : function () {
+ return this.dotProduct(this);
+ },
+
+ /**
+ * return the length (magnitude) of this vector
+ * @name length
+ * @memberOf me.Vector3d
+ * @function
+ * @return {Number} the length of this vector
+ */
+ length : function () {
+ return Math.sqrt(this.length2());
+ },
+
+ /**
+ * return the distance between this vector and the passed one
+ * @name distance
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {Number}
+ */
+ distance : function (v) {
+ var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - (v.z || 0);
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
+ },
+
+ /**
+ * return the angle between this vector and the passed one
+ * @name angle
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v
+ * @return {Number} angle in radians
+ */
+ angle : function (v) {
+ return Math.acos((this.dotProduct(v) / (this.length() * v.length())).clamp(-1, 1));
+ },
+
+ /**
+ * project this vector on to another vector.
+ * @name project
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v The vector to project onto.
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ project : function (v) {
+ return this.scale(this.dotProduct(v) / v.length2());
+ },
+
+ /**
+ * Project this vector onto a vector of unit length.
+ * This is slightly more efficient than `project` when dealing with unit vectors.
+ * @name projectN
+ * @memberOf me.Vector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d} v The unit vector to project onto.
+ * @return {me.Vector3d} Reference to this object for method chaining
+ */
+ projectN : function (v) {
+ return this.scale(this.dotProduct(v));
+ },
+
+ /**
+ * return a clone copy of this vector
+ * @name clone
+ * @memberOf me.Vector3d
+ * @function
+ * @return {me.Vector3d} new me.Vector3d
+ */
+ clone : function () {
+ return new me.Vector3d(this.x, this.y, this.z);
+ },
+
+ /**
+ * convert the object to a string representation
+ * @name toString
+ * @memberOf me.Vector3d
+ * @function
+ * @return {String}
+ */
+ toString : function () {
+ return "x:" + this.x + ",y:" + this.y + ",z:" + this.z;
+ }
+ });
+
+ /**
+ * Base class for Vector3d exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.Vector3d
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.Vector3d.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.Vector3d.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * A Vector2d object that provide notification by executing the given callback when the vector is changed.
+ * @class
+ * @extends me.Vector2d
+ * @constructor
+ * @param {Number} [x=0] x value of the vector
+ * @param {Number} [y=0] y value of the vector
+ * @param {Object} settings additional required parameters
+ * @param {Function} settings.onUpdate the callback to be executed when the vector is changed
+ */
+ me.ObservableVector2d = me.Vector2d.extend({
+ /** @scope me.ObservableVector2d.prototype */
+
+ /** @ignore */
+ init : function (x, y, settings) {
+ /**
+ * x value of the vector
+ * @public
+ * @type Number
+ * @name x
+ * @memberOf me.ObservableVector2d
+ */
+ Object.defineProperty(this, "x", {
+ /**
+ * @ignore
+ */
+ get : function () {
+ return this._x;
+ },
+ /**
+ * @ignore
+ */
+ set : function (value) {
+ this.onUpdate(value, this._y, this._x, this._y);
+ this._x = value;
+ }
+ });
+
+ /**
+ * y value of the vector
+ * @public
+ * @type Number
+ * @name y
+ * @memberOf me.ObservableVector2d
+ */
+ Object.defineProperty(this, "y", {
+ /**
+ * @ignore
+ */
+ get : function () {
+ return this._y;
+ },
+ /**
+ * @ignore
+ */
+ set : function (value) {
+ this.onUpdate(this._x, value, this._x, this._y);
+ this._y = value;
+ }
+ });
+
+ if (typeof(settings) === "undefined") {
+ throw new me.ObservableVector2d.Error(
+ "undefined `onUpdate` callback"
+ );
+ }
+ this.setCallback(settings.onUpdate);
+ this._x = x || 0;
+ this._y = y || 0;
+ },
+
+ /** @ignore */
+ _set : function (x, y) {
+ this.onUpdate(x, y, this._x, this._y);
+ this._x = x;
+ this._y = y;
+ return this;
+ },
+
+ /**
+ * set the vector value without triggering the callback
+ * @name setMuted
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {Number} x x value of the vector
+ * @param {Number} y y value of the vector
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ setMuted : function (x, y) {
+ this._x = x;
+ this._y = y;
+ return this;
+ },
+
+ /**
+ * set the callback to be executed when the vector is changed
+ * @name setCallback
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {function} onUpdate callback
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ setCallback : function (fn) {
+ if (typeof(fn) !== "function") {
+ throw new me.ObservableVector2d.Error(
+ "invalid `onUpdate` callback"
+ );
+ }
+ this.onUpdate = fn;
+ return this;
+ },
+
+ /**
+ * Add the passed vector to this vector
+ * @name add
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ add : function (v) {
+ return this._set(this._x + v.x, this._y + v.y);
+ },
+
+ /**
+ * Substract the passed vector to this vector
+ * @name sub
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ sub : function (v) {
+ return this._set(this._x - v.x, this._y - v.y);
+ },
+
+ /**
+ * Multiply this vector values by the given scalar
+ * @name scale
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {Number} x
+ * @param {Number} [y=x]
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ scale : function (x, y) {
+ return this._set(this._x * x, this._y * (typeof (y) !== "undefined" ? y : x));
+ },
+
+ /**
+ * Multiply this vector values by the passed vector
+ * @name scaleV
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ scaleV : function (v) {
+ return this._set(this._x * v.x, this._y * v.y);
+ },
+
+ /**
+ * Divide this vector values by the passed value
+ * @name div
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {Number} value
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ div : function (n) {
+ return this._set(this._x / n, this._y / n);
+ },
+
+ /**
+ * Update this vector values to absolute values
+ * @name abs
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ abs : function () {
+ return this._set((this._x < 0) ? -this._x : this._x, (this._y < 0) ? -this._y : this._y);
+ },
+
+ /**
+ * Clamp the vector value within the specified value range
+ * @name clamp
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.ObservableVector2d} new me.ObservableVector2d
+ */
+ clamp : function (low, high) {
+ return new me.ObservableVector2d(this.x.clamp(low, high), this.y.clamp(low, high), {onUpdate: this.onUpdate});
+ },
+
+ /**
+ * Clamp this vector value within the specified value range
+ * @name clampSelf
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ clampSelf : function (low, high) {
+ return this._set(this._x.clamp(low, high), this._y.clamp(low, high));
+ },
+
+ /**
+ * Update this vector with the minimum value between this and the passed vector
+ * @name minV
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ minV : function (v) {
+ return this._set((this._x < v.x) ? this._x : v.x, (this._y < v.y) ? this._y : v.y);
+ },
+
+ /**
+ * Update this vector with the maximum value between this and the passed vector
+ * @name maxV
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ maxV : function (v) {
+ return this._set((this._x > v.x) ? this._x : v.x, (this._y > v.y) ? this._y : v.y);
+ },
+
+ /**
+ * Floor the vector values
+ * @name floor
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} new me.ObservableVector2d
+ */
+ floor : function () {
+ return new me.ObservableVector2d(Math.floor(this._x), Math.floor(this._y), {onUpdate: this.onUpdate});
+ },
+
+ /**
+ * Floor this vector values
+ * @name floorSelf
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ floorSelf : function () {
+ return this._set(Math.floor(this._x), Math.floor(this._y));
+ },
+
+ /**
+ * Ceil the vector values
+ * @name ceil
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} new me.ObservableVector2d
+ */
+ ceil : function () {
+ return new me.ObservableVector2d(Math.ceil(this._x), Math.ceil(this._y), {onUpdate: this.onUpdate});
+ },
+
+ /**
+ * Ceil this vector values
+ * @name ceilSelf
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ ceilSelf : function () {
+ return this._set(Math.ceil(this._x), Math.ceil(this._y));
+ },
+
+ /**
+ * Negate the vector values
+ * @name negate
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} new me.ObservableVector2d
+ */
+ negate : function () {
+ return new me.ObservableVector2d(-this._x, -this._y, {onUpdate: this.onUpdate});
+ },
+
+ /**
+ * Negate this vector values
+ * @name negateSelf
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ negateSelf : function () {
+ return this._set(-this._x, -this._y);
+ },
+
+ /**
+ * Copy the x,y values of the passed vector to this one
+ * @name copy
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ copy : function (v) {
+ return this._set(v.x, v.y);
+ },
+
+ /**
+ * return true if the two vectors are the same
+ * @name equals
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {Boolean}
+ */
+ equals : function (v) {
+ return ((this._x === v.x) && (this._y === v.y));
+ },
+
+ /**
+ * normalize this vector (scale the vector so that its magnitude is 1)
+ * @name normalize
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ normalize : function () {
+ var d = this.length();
+ if (d > 0) {
+ return this._set(this._x / d, this._y / d);
+ }
+ return this;
+ },
+
+ /**
+ * change this vector to be perpendicular to what it was before.
+ * (Effectively rotates it 90 degrees in a clockwise direction)
+ * @name perp
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ perp : function () {
+ return this._set(this._y, -this._x);
+ },
+
+ /**
+ * Rotate this vector (counter-clockwise) by the specified angle (in radians).
+ * @name rotate
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {number} angle The angle to rotate (in radians)
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ rotate : function (angle) {
+ var x = this._x;
+ var y = this._y;
+ return this._set(x * Math.cos(angle) - y * Math.sin(angle), x * Math.sin(angle) + y * Math.cos(angle));
+ },
+
+ /**
+ * return the dot product of this vector and the passed one
+ * @name dotProduct
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {Number} The dot product.
+ */
+ dotProduct : function (v) {
+ return this._x * v.x + this._y * v.y;
+ },
+
+ /**
+ * return the distance between this vector and the passed one
+ * @name distance
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @param {me.ObservableVector2d} v
+ * @return {Number}
+ */
+ distance : function (v) {
+ return Math.sqrt((this._x - v.x) * (this._x - v.x) + (this._y - v.y) * (this._y - v.y));
+ },
+
+ /**
+ * return a clone copy of this vector
+ * @name clone
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.ObservableVector2d} new me.ObservableVector2d
+ */
+ clone : function () {
+ // shall we return a cloned me.ObservableVector2d here ?
+ return new me.ObservableVector2d(this._x, this._y, {onUpdate: this.onUpdate});
+ },
+
+ /**
+ * return a `me.Vector2d` copy of this `me.ObservableVector2d` object
+ * @name toVector2d
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {me.Vector2d} new me.Vector2d
+ */
+ toVector2d : function () {
+ return new me.Vector2d(this._x, this._y);
+ },
+
+ /**
+ * convert the object to a string representation
+ * @name toString
+ * @memberOf me.ObservableVector2d
+ * @function
+ * @return {String}
+ */
+ toString : function () {
+ return "x:" + this._x + ",y:" + this._y;
+ }
+ });
+
+ /**
+ * Base class for Vector2d exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.ObservableVector2d
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.ObservableVector2d.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.ObservableVector2d.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * A Vector3d object that provide notification by executing the given callback when the vector is changed.
+ * @class
+ * @extends me.Vector3d
+ * @constructor
+ * @param {Number} [x=0] x value of the vector
+ * @param {Number} [y=0] y value of the vector
+ * @param {Number} [z=0] z value of the vector
+ * @param {Object} settings additional required parameters
+ * @param {Function} settings.onUpdate the callback to be executed when the vector is changed
+ */
+ me.ObservableVector3d = me.Vector3d.extend({
+ /** @scope me.ObservableVector3d.prototype */
+
+ /**
+ * @ignore
+ */
+ init : function (x, y, z, settings) {
+ /**
+ * x value of the vector
+ * @public
+ * @type Number
+ * @name x
+ * @memberOf me.ObservableVector3d
+ */
+ Object.defineProperty(this, "x", {
+ /**
+ * @ignore
+ */
+ get : function () {
+ return this._x;
+ },
+ /**
+ * @ignore
+ */
+ set : function (value) {
+ this.onUpdate(value, this._y, this._z, this._x, this._y, this._z);
+ this._x = value;
+ }
+ });
+
+ /**
+ * y value of the vector
+ * @public
+ * @type Number
+ * @name y
+ * @memberOf me.ObservableVector3d
+ */
+ Object.defineProperty(this, "y", {
+ /**
+ * @ignore
+ */
+ get : function () {
+ return this._y;
+ },
+ /**
+ * @ignore
+ */
+ set : function (value) {
+ this.onUpdate(this._x, value, this._z, this._x, this._y, this._z);
+ this._y = value;
+ }
+ });
+
+ /**
+ * z value of the vector
+ * @public
+ * @type Number
+ * @name z
+ * @memberOf me.ObservableVector3d
+ */
+ Object.defineProperty(this, "z", {
+ /**
+ * @ignore
+ */
+ get : function () {
+ return this._z;
+ },
+ /**
+ * @ignore
+ */
+ set : function (value) {
+ this.onUpdate(this._x, this._y, value, this._x, this._y, this._z);
+ this._z = value;
+ }
+ });
+
+ if (typeof(settings) === "undefined") {
+ throw new me.ObservableVector3d.Error(
+ "undefined `onUpdate` callback"
+ );
+ }
+ this.setCallback(settings.onUpdate);
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ },
+
+ /**
+ * @ignore */
+ _set : function (x, y, z) {
+ this.onUpdate(x, y, z, this._x, this._y, this._z);
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ return this;
+ },
+
+ /**
+ * set the vector value without triggering the callback
+ * @name setMuted
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {Number} x x value of the vector
+ * @param {Number} y y value of the vector
+ * @param {Number} z z value of the vector
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ setMuted : function (x, y, z) {
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ return this;
+ },
+
+ /**
+ * set the callback to be executed when the vector is changed
+ * @name setCallback
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {function} onUpdate callback
+ * @return {me.ObservableVector2d} Reference to this object for method chaining
+ */
+ setCallback : function (fn) {
+ if (typeof(fn) !== "function") {
+ throw new me.ObservableVector2d.Error(
+ "invalid `onUpdate` callback"
+ );
+ }
+ this.onUpdate = fn;
+ return this;
+ },
+
+ /**
+ * Add the passed vector to this vector
+ * @name add
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ add : function (v) {
+ return this._set(this._x + v.x, this._y + v.y, this._z + (v.z || 0));
+ },
+
+ /**
+ * Substract the passed vector to this vector
+ * @name sub
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ sub : function (v) {
+ return this._set(this._x - v.x, this._y - v.y, this._z - (v.z || 0));
+ },
+
+ /**
+ * Multiply this vector values by the given scalar
+ * @name scale
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {Number} x
+ * @param {Number} [y=x]
+ * @param {Number} [z=x]
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ scale : function (x, y, z) {
+ y = (typeof (y) !== "undefined" ? y : x);
+ z = (typeof (z) !== "undefined" ? z : x);
+ return this._set(this._x * x, this._y * y, this._z * z);
+ },
+
+ /**
+ * Multiply this vector values by the passed vector
+ * @name scaleV
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ scaleV : function (v) {
+ return this._set(this._x * v.x, this._y * v.y, this._z * (v.z || 1));
+ },
+
+ /**
+ * Divide this vector values by the passed value
+ * @name div
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {Number} value
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ div : function (n) {
+ return this._set(this._x / n, this._y / n, this._z / n);
+ },
+
+ /**
+ * Update this vector values to absolute values
+ * @name abs
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ abs : function () {
+ return this._set(
+ (this._x < 0) ? -this._x : this._x,
+ (this._y < 0) ? -this._y : this._y,
+ (this._Z < 0) ? -this._z : this._z
+ );
+ },
+
+ /**
+ * Clamp the vector value within the specified value range
+ * @name clamp
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.ObservableVector3d} new me.ObservableVector3d
+ */
+ clamp : function (low, high) {
+ return new me.ObservableVector3d(
+ this._x.clamp(low, high),
+ this._y.clamp(low, high),
+ this._z.clamp(low, high),
+ {onUpdate: this.onUpdate}
+ );
+ },
+
+ /**
+ * Clamp this vector value within the specified value range
+ * @name clampSelf
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {Number} low
+ * @param {Number} high
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ clampSelf : function (low, high) {
+ return this._set(
+ this._x.clamp(low, high),
+ this._y.clamp(low, high),
+ this._z.clamp(low, high)
+ );
+ },
+
+ /**
+ * Update this vector with the minimum value between this and the passed vector
+ * @name minV
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ minV : function (v) {
+ var _vz = v.z || 0;
+ return this._set(
+ (this._x < v.x) ? this._x : v.x,
+ (this._y < v.y) ? this._y : v.y,
+ (this._z < _vz) ? this._z : _vz
+ );
+ },
+
+ /**
+ * Update this vector with the maximum value between this and the passed vector
+ * @name maxV
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ maxV : function (v) {
+ var _vz = v.z || 0;
+ return this._set(
+ (this._x > v.x) ? this._x : v.x,
+ (this._y > v.y) ? this._y : v.y,
+ (this._z > _vz) ? this._z : _vz
+ );
+ },
+
+ /**
+ * Floor the vector values
+ * @name floor
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} new me.ObservableVector3d
+ */
+ floor : function () {
+ return new me.ObservableVector3d(
+ Math.floor(this._x),
+ Math.floor(this._y),
+ Math.floor(this._z),
+ {onUpdate: this.onUpdate}
+ );
+ },
+
+ /**
+ * Floor this vector values
+ * @name floorSelf
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ floorSelf : function () {
+ return this._set(Math.floor(this._x), Math.floor(this._y), Math.floor(this._z));
+ },
+
+ /**
+ * Ceil the vector values
+ * @name ceil
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} new me.ObservableVector3d
+ */
+ ceil : function () {
+ return new me.ObservableVector3d(
+ Math.ceil(this._x),
+ Math.ceil(this._y),
+ Math.ceil(this._z),
+ {onUpdate: this.onUpdate}
+ );
+ },
+
+ /**
+ * Ceil this vector values
+ * @name ceilSelf
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ ceilSelf : function () {
+ return this._set(Math.ceil(this._x), Math.ceil(this._y), Math.ceil(this._z));
+ },
+
+ /**
+ * Negate the vector values
+ * @name negate
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} new me.ObservableVector3d
+ */
+ negate : function () {
+ return new me.ObservableVector3d(
+ -this._x,
+ -this._y,
+ -this._z,
+ {onUpdate: this.onUpdate}
+ );
+ },
+
+ /**
+ * Negate this vector values
+ * @name negateSelf
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ negateSelf : function () {
+ return this._set(-this._x, -this._y, -this._z);
+ },
+
+ /**
+ * Copy the x,y,z values of the passed vector to this one
+ * @name copy
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ copy : function (v) {
+ return this._set(v.x, v.y, typeof (v.z) !== "undefined" ? v.z : this._z);
+ },
+
+ /**
+ * return true if the two vectors are the same
+ * @name equals
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {Boolean}
+ */
+ equals : function (v) {
+ return ((this._x === v.x) && (this._y === v.y) && (this._z === (v.z || this._z)));
+ },
+
+ /**
+ * normalize this vector (scale the vector so that its magnitude is 1)
+ * @name normalize
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ normalize : function () {
+ var d = this.length();
+ if (d > 0) {
+ return this._set(this._x / d, this._y / d, this._z / d);
+ }
+ return this;
+ },
+
+ /**
+ * change this vector to be perpendicular to what it was before.
+ * (Effectively rotates it 90 degrees in a clockwise direction)
+ * @name perp
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ perp : function () {
+ return this._set(this._y, -this._x, this._z);
+ },
+
+ /**
+ * Rotate this vector (counter-clockwise) by the specified angle (in radians).
+ * @name rotate
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {number} angle The angle to rotate (in radians)
+ * @return {me.ObservableVector3d} Reference to this object for method chaining
+ */
+ rotate : function (angle) {
+ var x = this._x;
+ var y = this._y;
+ return this._set(
+ x * Math.cos(angle) - y * Math.sin(angle),
+ x * Math.sin(angle) + y * Math.cos(angle),
+ this._z
+ );
+ },
+
+ /**
+ * return the dot product of this vector and the passed one
+ * @name dotProduct
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {Number} The dot product.
+ */
+ dotProduct : function (v) {
+ return this._x * v.x + this._y * v.y + this._z * (v.z || 1);
+ },
+
+ /**
+ * return the distance between this vector and the passed one
+ * @name distance
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @param {me.Vector2d|me.Vector3d|me.ObservableVector2d|me.ObservableVector3d} v
+ * @return {Number}
+ */
+ distance : function (v) {
+ var dx = this._x - v.x, dy = this._y - v.y, dz = this._z - (v.z || 0);
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
+ },
+
+ /**
+ * return a clone copy of this vector
+ * @name clone
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.ObservableVector3d} new me.ObservableVector3d
+ */
+ clone : function () {
+ // shall we return a cloned me.ObservableVector3d here ?
+ return new me.ObservableVector3d(
+ this._x,
+ this._y,
+ this._z,
+ {onUpdate: this.onUpdate}
+ );
+ },
+
+ /**
+ * return a `me.Vector3d` copy of this `me.ObservableVector3d` object
+ * @name toVector3d
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {me.Vector3d} new me.Vector3d
+ */
+ toVector3d : function () {
+ return new me.Vector3d(this._x, this._y, this._z);
+ },
+
+ /**
+ * convert the object to a string representation
+ * @name toString
+ * @memberOf me.ObservableVector3d
+ * @function
+ * @return {String}
+ */
+ toString : function () {
+ return "x:" + this._x + ",y:" + this._y + ",z:" + this._z;
+ }
+ });
+
+ /**
+ * Base class for Vector3d exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.ObservableVector3d
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.ObservableVector3d.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.ObservableVector3d.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a Matrix2d Object.
+ * the identity matrix and parameters position :
+ *
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {me.Matrix2d} [mat2d] An instance of me.Matrix2d to copy from
+ * @param {Number[]} [arguments...] Matrix elements. See {@link me.Matrix2d.set}
+ */
+ me.Matrix2d = me.Object.extend(
+ /** @scope me.Matrix2d.prototype */ {
+
+ /** @ignore */
+ init : function () {
+ if (typeof(this.val) === "undefined") {
+ this.val = new Float32Array(9);
+ }
+ if (arguments.length && arguments[0] instanceof me.Matrix2d) {
+ this.copy(arguments[0]);
+ }
+ else if (arguments.length >= 6) {
+ this.setTransform.apply(this, arguments);
+ }
+ else {
+ this.onResetEvent();
+ }
+ },
+
+ /** @ignore */
+ onResetEvent : function() {
+ this.identity();
+ },
+
+ /**
+ * reset the transformation matrix to the identity matrix (no transformation).
+ * the identity matrix and parameters position :
+ *
+ * @name identity
+ * @memberOf me.Matrix2d
+ * @function
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ identity : function () {
+ this.setTransform(
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+ );
+ return this;
+ },
+
+ /**
+ * set the matrix to the specified value
+ * @name setTransform
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {Number} a
+ * @param {Number} b
+ * @param {Number} c
+ * @param {Number} d
+ * @param {Number} e
+ * @param {Number} f
+ * @param {Number} [g=0]
+ * @param {Number} [h=0]
+ * @param {Number} [i=1]
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ setTransform : function () {
+ var a = this.val;
+
+ if (arguments.length === 9) {
+ a[0] = arguments[0]; // a - m00
+ a[1] = arguments[1]; // b - m10
+ a[2] = arguments[2]; // c - m20
+ a[3] = arguments[3]; // d - m01
+ a[4] = arguments[4]; // e - m11
+ a[5] = arguments[5]; // f - m21
+ a[6] = arguments[6]; // g - m02
+ a[7] = arguments[7]; // h - m12
+ a[8] = arguments[8]; // i - m22
+ } else if (arguments.length === 6) {
+ a[0] = arguments[0]; // a
+ a[1] = arguments[2]; // c
+ a[2] = arguments[4]; // e
+ a[3] = arguments[1]; // b
+ a[4] = arguments[3]; // d
+ a[5] = arguments[5]; // f
+ a[6] = 0; // g
+ a[7] = 0; // h
+ a[8] = 1; // i
+ }
+
+ return this;
+ },
+
+ /**
+ * Copies over the values from another me.Matrix2d.
+ * @name copy
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {me.Matrix2d} m the matrix object to copy from
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ copy : function (b) {
+ this.val.set(b.val);
+ return this;
+ },
+
+ /**
+ * multiply both matrix
+ * @name multiply
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {me.Matrix2d} b Other matrix
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ multiply : function (b) {
+ b = b.val;
+ var a = this.val,
+ a0 = a[0],
+ a1 = a[1],
+ a3 = a[3],
+ a4 = a[4],
+ b0 = b[0],
+ b1 = b[1],
+ b3 = b[3],
+ b4 = b[4],
+ b6 = b[6],
+ b7 = b[7];
+
+ a[0] = a0 * b0 + a3 * b1;
+ a[1] = a1 * b0 + a4 * b1;
+ a[3] = a0 * b3 + a3 * b4;
+ a[4] = a1 * b3 + a4 * b4;
+ a[6] += a0 * b6 + a3 * b7;
+ a[7] += a1 * b6 + a4 * b7;
+
+ return this;
+ },
+
+ /**
+ * Transpose the value of this matrix.
+ * @name transpose
+ * @memberOf me.Matrix2d
+ * @function
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ transpose : function () {
+ var tmp, a = this.val;
+
+ tmp = a[1];
+ a[1] = a[3];
+ a[3] = tmp;
+ tmp = a[2];
+ a[2] = a[6];
+ a[6] = tmp;
+ tmp = a[5];
+ a[5] = a[7];
+ a[7] = tmp;
+
+ return this;
+ },
+
+ /**
+ * invert this matrix, causing it to apply the opposite transformation.
+ * @name invert
+ * @memberOf me.Matrix2d
+ * @function
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ invert : function () {
+ var val = this.val;
+
+ var a = val[ 0 ], b = val[ 1 ], c = val[ 2 ],
+ d = val[ 3 ], e = val[ 4 ], f = val[ 5 ],
+ g = val[ 6 ], h = val[ 7 ], i = val[ 8 ];
+
+ var ta = i * e - f * h,
+ td = f * g - i * d,
+ tg = h * d - e * g;
+
+ var n = a * ta + b * td + c * tg;
+
+ val[ 0 ] = ta / n;
+ val[ 1 ] = ( c * h - i * b ) / n;
+ val[ 2 ] = ( f * b - c * e ) / n;
+
+ val[ 3 ] = td / n;
+ val[ 4 ] = ( i * a - c * g ) / n;
+ val[ 5 ] = ( c * d - f * a ) / n;
+
+ val[ 6 ] = tg / n;
+ val[ 7 ] = ( b * g - h * a ) / n;
+ val[ 8 ] = ( e * a - b * d ) / n;
+
+ return this;
+ },
+
+ /**
+ * Transforms the given vector according to this matrix.
+ * @name multiplyVector
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {me.Vector2d} vector the vector object to be transformed
+ * @return {me.Vector2d} result vector object. Useful for chaining method calls.
+ **/
+ multiplyVector : function (v) {
+ var a = this.val,
+ x = v.x,
+ y = v.y;
+
+ v.x = x * a[0] + y * a[3] + a[6];
+ v.y = x * a[1] + y * a[4] + a[7];
+
+ return v;
+ },
+
+ /**
+ * scale the matrix
+ * @name scale
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {Number} x a number representing the abscissa of the scaling vector.
+ * @param {Number} [y=x] a number representing the ordinate of the scaling vector.
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ scale : function (x, y) {
+ var a = this.val,
+ _x = x,
+ _y = typeof(y) === "undefined" ? _x : y;
+
+ a[0] *= _x;
+ a[1] *= _x;
+ a[3] *= _y;
+ a[4] *= _y;
+
+ return this;
+ },
+
+ /**
+ * adds a 2D scaling transformation.
+ * @name scaleV
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {me.Vector2d} vector scaling vector
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ scaleV : function (v) {
+ return this.scale(v.x, v.y);
+ },
+
+ /**
+ * specifies a 2D scale operation using the [sx, 1] scaling vector
+ * @name scaleX
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {Number} x x scaling vector
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ scaleX : function (x) {
+ return this.scale(x, 1);
+ },
+
+ /**
+ * specifies a 2D scale operation using the [1,sy] scaling vector
+ * @name scaleY
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {Number} y y scaling vector
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ scaleY : function (y) {
+ return this.scale(1, y);
+ },
+
+ /**
+ * rotate the matrix (counter-clockwise) by the specified angle (in radians).
+ * @name rotate
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {Number} angle Rotation angle in radians.
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ rotate : function (angle) {
+ if (angle !== 0) {
+ var a = this.val,
+ a0 = a[0],
+ a1 = a[1],
+ a3 = a[3],
+ a4 = a[4],
+ s = Math.sin(angle),
+ c = Math.cos(angle);
+
+ a[0] = a0 * c + a3 * s;
+ a[1] = a1 * c + a4 * s;
+ a[3] = a0 * -s + a3 * c;
+ a[4] = a1 * -s + a4 * c;
+ }
+ return this;
+ },
+
+ /**
+ * translate the matrix position on the horizontal and vertical axis
+ * @name translate
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {Number} x the x coordindates to translate the matrix by
+ * @param {Number} y the y coordindates to translate the matrix by
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ translate : function (x, y) {
+ var a = this.val;
+
+ a[6] += a[0] * x + a[3] * y;
+ a[7] += a[1] * x + a[4] * y;
+
+ return this;
+ },
+
+ /**
+ * translate the matrix by a vector on the horizontal and vertical axis
+ * @name translateV
+ * @memberOf me.Matrix2d
+ * @function
+ * @param {me.Vector2d} v the vector to translate the matrix by
+ * @return {me.Matrix2d} Reference to this object for method chaining
+ */
+ translateV : function (v) {
+ return this.translate(v.x, v.y);
+ },
+
+ /**
+ * returns true if the matrix is an identity matrix.
+ * @name isIdentity
+ * @memberOf me.Matrix2d
+ * @function
+ * @return {Boolean}
+ **/
+ isIdentity : function () {
+ var a = this.val;
+
+ return (
+ a[0] === 1 &&
+ a[1] === 0 &&
+ a[2] === 0 &&
+ a[3] === 0 &&
+ a[4] === 1 &&
+ a[5] === 0 &&
+ a[6] === 0 &&
+ a[7] === 0 &&
+ a[8] === 1
+ );
+ },
+
+ /**
+ * Clone the Matrix
+ * @name clone
+ * @memberOf me.Matrix2d
+ * @function
+ * @return {me.Matrix2d}
+ */
+ clone : function () {
+ return me.pool.pull("me.Matrix2d").copy(this);
+ },
+
+ /**
+ * convert the object to a string representation
+ * @name toString
+ * @memberOf me.Matrix2d
+ * @function
+ * @return {String}
+ */
+ toString : function () {
+ var a = this.val;
+
+ return "me.Matrix2d(" +
+ a[0] + ", " + a[1] + ", " + a[2] + ", " +
+ a[3] + ", " + a[4] + ", " + a[5] + ", " +
+ a[6] + ", " + a[7] + ", " + a[8] +
+ ")";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * an ellipse Object
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Number} x the center x coordinate of the ellipse
+ * @param {Number} y the center y coordinate of the ellipse
+ * @param {Number} w width (diameter) of the ellipse
+ * @param {Number} h height (diameter) of the ellipse
+ */
+ me.Ellipse = me.Object.extend(
+ {
+ /** @scope me.Ellipse.prototype */
+ /** @ignore */
+ init : function (x, y, w, h) {
+ /**
+ * the center coordinates of the ellipse
+ * @public
+ * @type {me.Vector2d}
+ * @name pos
+ * @memberOf me.Ellipse
+ */
+ this.pos = new me.Vector2d();
+
+ /**
+ * The bounding rectangle for this shape
+ * @private
+ * @type {me.Rect}
+ * @name _bounds
+ * @memberOf me.Ellipse
+ */
+ this._bounds = undefined;
+
+ /**
+ * Maximum radius of the ellipse
+ * @public
+ * @type {Number}
+ * @name radius
+ * @memberOf me.Ellipse
+ */
+ this.radius = NaN;
+
+ /**
+ * Pre-scaled radius vector for ellipse
+ * @public
+ * @type {me.Vector2d}
+ * @name radiusV
+ * @memberOf me.Ellipse
+ */
+ this.radiusV = new me.Vector2d();
+
+ /**
+ * Radius squared, for pythagorean theorom
+ * @public
+ * @type {me.Vector2d}
+ * @name radiusSq
+ * @memberOf me.Ellipse
+ */
+ this.radiusSq = new me.Vector2d();
+
+ /**
+ * x/y scaling ratio for ellipse
+ * @public
+ * @type {me.Vector2d}
+ * @name ratio
+ * @memberOf me.Ellipse
+ */
+ this.ratio = new me.Vector2d();
+
+ // the shape type
+ this.shapeType = "Ellipse";
+ this.setShape(x, y, w, h);
+ },
+
+ /**
+ * set new value to the Ellipse shape
+ * @name setShape
+ * @memberOf me.Ellipse
+ * @function
+ * @param {Number} x position of the ellipse
+ * @param {Number} y position of the ellipse
+ * @param {Number} w width (diameter) of the ellipse
+ * @param {Number} h height (diameter) of the ellipse
+ */
+ setShape : function (x, y, w, h) {
+ var hW = w / 2;
+ var hH = h / 2;
+ this.pos.set(x, y);
+ this.radius = Math.max(hW, hH);
+ this.ratio.set(hW / this.radius, hH / this.radius);
+ this.radiusV.set(this.radius, this.radius).scaleV(this.ratio);
+ var r = this.radius * this.radius;
+ this.radiusSq.set(r, r).scaleV(this.ratio);
+ this.updateBounds();
+ return this;
+ },
+
+ /**
+ * Rotate this Ellipse (counter-clockwise) by the specified angle (in radians).
+ * @name rotate
+ * @memberOf me.Ellipse
+ * @function
+ * @param {Number} angle The angle to rotate (in radians)
+ * @return {me.Ellipse} Reference to this object for method chaining
+ */
+ rotate : function (/*angle*/) {
+ // TODO
+ return this;
+ },
+
+ /**
+ * Scale this Ellipse by the specified scalar.
+ * @name scale
+ * @memberOf me.Ellipse
+ * @function
+ * @param {Number} x
+ * @param {Number} [y=x]
+ * @return {me.Ellipse} Reference to this object for method chaining
+ */
+ scale : function (x, y) {
+ y = typeof (y) !== "undefined" ? y : x;
+ return this.setShape(
+ this.pos.x,
+ this.pos.y,
+ this.radiusV.x * 2 * x,
+ this.radiusV.y * 2 * y
+ );
+ },
+
+ /**
+ * Scale this Ellipse by the specified vector.
+ * @name scale
+ * @memberOf me.Ellipse
+ * @function
+ * @param {me.Vector2d} v
+ * @return {me.Ellipse} Reference to this object for method chaining
+ */
+ scaleV : function (v) {
+ return this.scale(v.x, v.y);
+ },
+
+ /**
+ * apply the given transformation matrix to this ellipse
+ * @name transform
+ * @memberOf me.Ellipse
+ * @function
+ * @param {me.Matrix2d} matrix the transformation matrix
+ * @return {me.Polygon} Reference to this object for method chaining
+ */
+ transform : function (/* m */) {
+ // TODO
+ return this;
+ },
+
+ /**
+ * translate the circle/ellipse by the specified offset
+ * @name translate
+ * @memberOf me.Ellipse
+ * @function
+ * @param {Number} x x offset
+ * @param {Number} y y offset
+ * @return {me.Ellipse} this ellipse
+ */
+ translate : function (x, y) {
+ this.pos.x += x;
+ this.pos.y += y;
+ this._bounds.translate(x, y);
+ return this;
+ },
+
+ /**
+ * translate the circle/ellipse by the specified vector
+ * @name translateV
+ * @memberOf me.Ellipse
+ * @function
+ * @param {me.Vector2d} v vector offset
+ * @return {me.Rect} this ellipse
+ */
+ translateV : function (v) {
+ this.pos.add(v);
+ this._bounds.translateV(v);
+ return this;
+ },
+
+ /**
+ * check if this circle/ellipse contains the specified point
+ * @name containsPointV
+ * @memberOf me.Ellipse
+ * @function
+ * @param {me.Vector2d} point
+ * @return {boolean} true if contains
+ */
+ containsPointV: function (v) {
+ return this.containsPoint(v.x, v.y);
+ },
+
+ /**
+ * check if this circle/ellipse contains the specified point
+ * @name containsPoint
+ * @memberOf me.Ellipse
+ * @function
+ * @param {Number} x x coordinate
+ * @param {Number} y y coordinate
+ * @return {boolean} true if contains
+ */
+ containsPoint: function (x, y) {
+ // Make position relative to object center point.
+ x -= this.pos.x;
+ y -= this.pos.y;
+ // Pythagorean theorem.
+ return (
+ ((x * x) / this.radiusSq.x) +
+ ((y * y) / this.radiusSq.y)
+ ) <= 1.0;
+ },
+
+ /**
+ * returns the bounding box for this shape, the smallest Rectangle object completely containing this shape.
+ * @name getBounds
+ * @memberOf me.Ellipse
+ * @function
+ * @return {me.Rect} this shape bounding box Rectangle object
+ */
+ getBounds : function () {
+ return this._bounds;
+ },
+
+ /**
+ * update the bounding box for this shape.
+ * @name updateBounds
+ * @memberOf me.Ellipse
+ * @function
+ * @return {me.Rect} this shape bounding box Rectangle object
+ */
+ updateBounds : function () {
+ var rx = this.radiusV.x,
+ ry = this.radiusV.y,
+ x = this.pos.x - rx,
+ y = this.pos.y - ry,
+ w = rx * 2,
+ h = ry * 2;
+
+ if (!this._bounds) {
+ this._bounds = new me.Rect(x, y, w, h);
+ } else {
+ this._bounds.setShape(x, y, w, h);
+ }
+ return this._bounds;
+ },
+
+ /**
+ * clone this Ellipse
+ * @name clone
+ * @memberOf me.Ellipse
+ * @function
+ * @return {me.Ellipse} new Ellipse
+ */
+ clone : function () {
+ return new me.Ellipse(
+ this.pos.x,
+ this.pos.y,
+ this.radiusV.x * 2,
+ this.radiusV.y * 2
+ );
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a polygon Object.
+ * Please do note that melonJS implements a simple Axis-Aligned Boxes collision algorithm, which requires all polygons used for collision to be convex with all vertices defined with clockwise winding.
+ * A polygon is convex when all line segments connecting two points in the interior do not cross any edge of the polygon
+ * (which means that all angles are less than 180 degrees), as described here below :
+ *
+ * | (0) | + * (-1) [S]--------------[E] (1) + * | (0) | + *+ * + * @ignore + * @param {Vector} line The line segment. + * @param {Vector} point The point. + * @return {number} LEFT_VORNOI_REGION (-1) if it is the left region, + * MIDDLE_VORNOI_REGION (0) if it is the middle region, + * RIGHT_VORNOI_REGION (1) if it is the right region. + */ + function vornoiRegion(line, point) { + var len2 = line.length2(); + var dp = point.dotProduct(line); + if (dp < 0) { + // If the point is beyond the start of the line, it is in the + // left vornoi region. + return LEFT_VORNOI_REGION; + } else if (dp > len2) { + // If the point is beyond the end of the line, it is in the + // right vornoi region. + return RIGHT_VORNOI_REGION; + } else { + // Otherwise, it's in the middle one. + return MIDDLE_VORNOI_REGION; + } + } + + /** + * A singleton for managing collision detection (and projection-based collision response) of 2D shapes.
+ * screen is filled with the specified color and slowly goes back to normal + * @name fadeOut + * @memberOf me.Viewport + * @function + * @param {me.Color|String} color a CSS color value + * @param {Number} [duration=1000] expressed in milliseconds + * @param {Function} [onComplete] callback once effect is over + */ + fadeOut : function (color, duration, onComplete) { + this._fadeOut.color = me.pool.pull("me.Color").copy(color); + this._fadeOut.tween = me.pool.pull("me.Tween", this._fadeOut.color) + .to({ alpha: 0.0 }, duration || 1000) + .onComplete(onComplete || null); + this._fadeOut.tween.isPersistent = true; + this._fadeOut.tween.start(); + }, + + /** + * fadeIn effect
+ * fade to the specified color
+ * @name fadeIn
+ * @memberOf me.Viewport
+ * @function
+ * @param {me.Color|String} color a CSS color value
+ * @param {Number} [duration=1000] expressed in milliseconds
+ * @param {Function} [onComplete] callback once effect is over
+ */
+ fadeIn : function (color, duration, onComplete) {
+ this._fadeIn.color = me.pool.pull("me.Color").copy(color);
+ var _alpha = this._fadeIn.color.alpha;
+ this._fadeIn.color.alpha = 0.0;
+ this._fadeIn.tween = me.pool.pull("me.Tween", this._fadeIn.color)
+ .to({ alpha: _alpha }, duration || 1000)
+ .onComplete(onComplete || null);
+ this._fadeIn.tween.isPersistent = true;
+ this._fadeIn.tween.start();
+ },
+
+ /**
+ * return the viewport width
+ * @name getWidth
+ * @memberOf me.Viewport
+ * @function
+ * @return {Number}
+ */
+ getWidth : function () {
+ return this.width;
+ },
+
+ /**
+ * return the viewport height
+ * @name getHeight
+ * @memberOf me.Viewport
+ * @function
+ * @return {Number}
+ */
+ getHeight : function () {
+ return this.height;
+ },
+
+ /**
+ * set the viewport position around the specified object
+ * @name focusOn
+ * @memberOf me.Viewport
+ * @function
+ * @param {me.Renderable}
+ */
+ focusOn : function (target) {
+ var bounds = target.getBounds();
+ this.moveTo(
+ target.pos.x + bounds.pos.x + (bounds.width / 2),
+ target.pos.y + bounds.pos.y + (bounds.height / 2)
+ );
+ },
+
+ /**
+ * check if the specified rectangle is in the viewport
+ * @name isVisible
+ * @memberOf me.Viewport
+ * @function
+ * @param {me.Rect} rect
+ * @return {Boolean}
+ */
+ isVisible : function (rect) {
+ return rect.overlaps(this);
+ },
+
+ /**
+ * convert the given "local" (screen) coordinates into world coordinates
+ * @name localToWorld
+ * @memberOf me.Viewport
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} [v] an optional vector object where to set the
+ * converted value
+ * @return {me.Vector2d}
+ */
+ localToWorld : function (x, y, v) {
+ v = v || new me.Vector2d();
+ return (v.set(x, y)).add(this.pos).sub(me.game.world.pos);
+ },
+
+ /**
+ * convert the given world coordinates into "local" (screen) coordinates
+ * @name worldToLocal
+ * @memberOf me.Viewport
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} [v] an optional vector object where to set the
+ * converted value
+ * @return {me.Vector2d}
+ */
+ worldToLocal : function (x, y, v) {
+ v = v || new me.Vector2d();
+ return (v.set(x, y)).sub(this.pos).add(me.game.world.pos);
+ },
+
+ /**
+ * render the camera effects
+ * @ignore
+ */
+ draw : function (renderer) {
+ // fading effect
+ if (this._fadeIn.tween) {
+ renderer.clearColor(this._fadeIn.color);
+ // remove the tween if over
+ if (this._fadeIn.color.alpha === 1.0) {
+ this._fadeIn.tween = null;
+ me.pool.push(this._fadeIn.color);
+ this._fadeIn.color = null;
+ }
+ }
+
+ // flashing effect
+ if (this._fadeOut.tween) {
+ renderer.clearColor(this._fadeOut.color);
+ // remove the tween if over
+ if (this._fadeOut.color.alpha === 0.0) {
+ this._fadeOut.tween = null;
+ me.pool.push(this._fadeOut.color);
+ this._fadeOut.color = null;
+ }
+ }
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+
+ /**
+ * GUI Object
+ * A very basic object to manage GUI elements
+ * The object simply register on the "pointerdown"
+ * or "touchstart" event and call the onClick function"
+ * @class
+ * @extends me.Sprite
+ * @memberOf me
+ * @constructor
+ * @param {Number} x the x coordinate of the GUI Object
+ * @param {Number} y the y coordinate of the GUI Object
+ * @param {Object} settings See {@link me.Entity}
+ * @example
+ *
+ * // create a basic GUI Object
+ * var myButton = me.GUI_Object.extend(
+ * {
+ * init:function (x, y)
+ * {
+ * var settings = {}
+ * settings.image = "button";
+ * settings.framewidth = 100;
+ * settings.frameheight = 50;
+ * // super constructor
+ * me.GUI_Object.prototype.init.apply(this, [x, y, settings]);
+ * // define the object z order
+ * this.pos.z = 4;
+ * },
+ *
+ * // output something in the console
+ * // when the object is clicked
+ * onClick:function (event)
+ * {
+ * console.log("clicked!");
+ * // don't propagate the event
+ * return false;
+ * }
+ * });
+ *
+ * // add the object at pos (10,10)
+ * me.game.world.addChild(new myButton(10,10));
+ *
+ */
+ me.GUI_Object = me.Sprite.extend({
+ /** @scope me.GUI_Object.prototype */
+
+ /**
+ * @ignore
+ */
+ init : function (x, y, settings) {
+ /**
+ * object can be clicked or not
+ * @public
+ * @type boolean
+ * @default true
+ * @name me.GUI_Object#isClickable
+ */
+ this.isClickable = true;
+
+ /**
+ * Tap and hold threshold timeout in ms
+ * @type {number}
+ * @default 250
+ * @name me.GUI_Object#holdThreshold
+ */
+ this.holdThreshold = 250;
+
+ /**
+ * object can be tap and hold
+ * @public
+ * @type boolean
+ * @default false
+ * @name me.GUI_Object#isHoldable
+ */
+ this.isHoldable = false;
+
+ /**
+ * true if the pointer is over the object
+ * @public
+ * @type boolean
+ * @default false
+ * @name me.GUI_Object#hover
+ */
+ this.hover = false;
+
+ // object has been updated (clicked,etc..)
+ this.holdTimeout = null;
+ this.updated = false;
+ this.released = true;
+
+ // call the parent constructor
+ me.Sprite.prototype.init.apply(this, [ x, y, settings ]);
+
+ // GUI items use screen coordinates
+ this.floating = true;
+ },
+
+ /**
+ * return true if the object has been clicked
+ * @ignore
+ */
+ update : function () {
+ if (this.updated) {
+ // clear the flag
+ if (!this.released) {
+ this.updated = false;
+ }
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * function callback for the pointerdown event
+ * @ignore
+ */
+ clicked : function (event) {
+ // Check if left mouse button is pressed OR if device has touch
+ if ((event.which === 1 || me.device.touch) && this.isClickable) {
+ this.updated = true;
+ this.released = false;
+ if (this.isHoldable) {
+ if (this.holdTimeout !== null) {
+ me.timer.clearTimeout(this.holdTimeout);
+ }
+ this.holdTimeout = me.timer.setTimeout(this.hold.bind(this), this.holdThreshold, false);
+ this.released = false;
+ }
+ return this.onClick(event);
+ }
+ },
+
+ /**
+ * function called when the object is pressed
+ * to be extended
+ * return false if we need to stop propagating the event
+ * @name onClick
+ * @memberOf me.GUI_Object
+ * @public
+ * @function
+ * @param {Event} event the event object
+ */
+ onClick : function (/* event */) {
+ return false;
+ },
+
+ /**
+ * function callback for the pointerEnter event
+ * @ignore
+ */
+ enter : function (event) {
+ this.hover = true;
+ return this.onOver(event);
+ },
+
+ /**
+ * function called when the pointer is over the object
+ * @name onOver
+ * @memberOf me.GUI_Object
+ * @public
+ * @function
+ * @param {Event} event the event object
+ */
+ onOver : function (/* event */) {},
+
+ /**
+ * function callback for the pointerLeave event
+ * @ignore
+ */
+ leave : function (event) {
+ this.hover = false;
+ this.release.call(this, event);
+ return this.onOut(event);
+ },
+
+ /**
+ * function called when the pointer is leaving the object area
+ * @name onOut
+ * @memberOf me.GUI_Object
+ * @public
+ * @function
+ * @param {Event} event the event object
+ */
+ onOut : function (/* event */) {},
+
+ /**
+ * function callback for the pointerup event
+ * @ignore
+ */
+ release : function (event) {
+ if (this.released === false) {
+ this.released = true;
+ me.timer.clearTimeout(this.holdTimeout);
+ return this.onRelease(event);
+ }
+ },
+
+ /**
+ * function called when the object is pressed and released
+ * to be extended
+ * return false if we need to stop propagating the event
+ * @name onRelease
+ * @memberOf me.GUI_Object
+ * @public
+ * @function
+ * @param {Event} event the event object
+ */
+ onRelease : function () {
+ return false;
+ },
+
+ /**
+ * function callback for the tap and hold timer event
+ * @ignore
+ */
+ hold : function () {
+ me.timer.clearTimeout(this.holdTimeout);
+ if (!this.released) {
+ this.onHold();
+ }
+ },
+
+ /**
+ * function called when the object is pressed and held
+ * to be extended
+ * @name onHold
+ * @memberOf me.GUI_Object
+ * @public
+ * @function
+ */
+ onHold : function () {},
+
+ /**
+ * function called when added to the game world or a container
+ * @ignore
+ */
+ onActivateEvent : function () {
+ // register pointer events
+ me.input.registerPointerEvent("pointerdown", this, this.clicked.bind(this));
+ me.input.registerPointerEvent("pointerup", this, this.release.bind(this));
+ me.input.registerPointerEvent("pointercancel", this, this.release.bind(this));
+ me.input.registerPointerEvent("pointerenter", this, this.enter.bind(this));
+ me.input.registerPointerEvent("pointerleave", this, this.leave.bind(this));
+ },
+
+ /**
+ * function called when removed from the game world or a container
+ * @ignore
+ */
+ onDeactivateEvent : function () {
+ // release pointer events
+ me.input.releasePointerEvent("pointerdown", this);
+ me.input.releasePointerEvent("pointerup", this);
+ me.input.releasePointerEvent("pointercancel", this);
+ me.input.releasePointerEvent("pointerenter", this);
+ me.input.releasePointerEvent("pointerleave", this);
+ me.timer.clearTimeout(this.holdTimeout);
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * Private function to re-use for object removal in a defer
+ * @ignore
+ */
+ var deferredRemove = function (child, keepalive) {
+ this.removeChildNow(child, keepalive);
+ };
+
+ var globalFloatingCounter = 0;
+
+ /**
+ * me.Container represents a collection of child objects
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {Number} [x=0] position of the container
+ * @param {Number} [y=0] position of the container
+ * @param {Number} [w=me.game.viewport.width] width of the container
+ * @param {number} [h=me.game.viewport.height] height of the container
+ */
+ me.Container = me.Renderable.extend(
+ /** @scope me.Container.prototype */
+ {
+ /**
+ * constructor
+ * @ignore
+ */
+ init : function (x, y, width, height) {
+ /**
+ * keep track of pending sort
+ * @ignore
+ */
+ this.pendingSort = null;
+
+ // TODO; container do not have a physic body
+ // ADD child container child one by one to the quadtree?
+
+ /**
+ * whether the container is the root of the scene
+ * @private
+ * @ignore
+ */
+ this._root = false;
+
+ // call the _super constructor
+ me.Renderable.prototype.init.apply(this,
+ [x || 0, y || 0,
+ width || Infinity,
+ height || Infinity]
+ );
+
+ /**
+ * The array of children of this container.
+ * @ignore
+ */
+ this.children = [];
+
+ /**
+ * The property of the child object that should be used to sort on
+ * value : "x", "y", "z"
+ * @public
+ * @type String
+ * @default me.game.sortOn
+ * @name sortOn
+ * @memberOf me.Container
+ */
+ this.sortOn = me.game.sortOn;
+
+ /**
+ * Specify if the children list should be automatically sorted when adding a new child
+ * @public
+ * @type Boolean
+ * @default true
+ * @name autoSort
+ * @memberOf me.Container
+ */
+ this.autoSort = true;
+
+ /**
+ * Specify if the children z index should automatically be managed by the parent container
+ * @public
+ * @type Boolean
+ * @default true
+ * @name autoDepth
+ * @memberOf me.Container
+ */
+ this.autoDepth = true;
+
+ /**
+ * Used by the debug panel plugin
+ * @ignore
+ */
+ this.drawCount = 0;
+
+ /**
+ * The bounds that contains all its children
+ * @public
+ * @type me.Rect
+ * @name childBounds
+ * @memberOf me.Container
+ */
+ this.childBounds = this.getBounds().clone();
+
+ // container self apply any defined transformation
+ this.autoTransform = false;
+ },
+
+
+ /**
+ * Add a child to the container
+ * if auto-sort is disable, the object will be appended at the bottom of the list
+ * @name addChild
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ * @param {number} [z] forces the z index of the child to the specified value
+ * @return {me.Renderable} the added child
+ */
+ addChild : function (child, z) {
+ if (child.ancestor instanceof me.Container) {
+ child.ancestor.removeChildNow(child);
+ }
+ else {
+ // only allocate a GUID if the object has no previous ancestor
+ // (e.g. move one child from one container to another)
+ if (child.isRenderable) {
+ // allocated a GUID value (use child.id as based index if defined)
+ child.GUID = me.utils.createGUID(child.id);
+ }
+ }
+
+ child.ancestor = this;
+ this.children.push(child);
+
+ // set the child z value if required
+ if (typeof(child.pos) !== "undefined") {
+ if (typeof(z) === "number") {
+ child.pos.z = z;
+ } else if (this.autoDepth === true) {
+ child.pos.z = this.children.length;
+ }
+ }
+
+ if (this.autoSort === true) {
+ this.sort();
+ }
+
+ if (typeof child.onActivateEvent === "function" && this.isAttachedToRoot()) {
+ child.onActivateEvent();
+ }
+
+ return child;
+ },
+
+ /**
+ * Add a child to the container at the specified index
+ * (the list won't be sorted after insertion)
+ * @name addChildAt
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ * @param {Number} index
+ * @return {me.Renderable} the added child
+ */
+ addChildAt : function (child, index) {
+ if (index >= 0 && index < this.children.length) {
+ if (child.ancestor instanceof me.Container) {
+ child.ancestor.removeChildNow(child);
+ }
+ else {
+ // only allocate a GUID if the object has no previous ancestor
+ // (e.g. move one child from one container to another)
+ if (child.isRenderable) {
+ // allocated a GUID value
+ child.GUID = me.utils.createGUID();
+ }
+ }
+ child.ancestor = this;
+
+ this.children.splice(index, 0, child);
+
+ if (typeof child.onActivateEvent === "function" && this.isAttachedToRoot()) {
+ child.onActivateEvent();
+ }
+
+ return child;
+ }
+ else {
+ throw new me.Container.Error("Index (" + index + ") Out Of Bounds for addChildAt()");
+ }
+ },
+
+ /**
+ * The forEach() method executes a provided function once per child element.
+ * callback is invoked with three arguments:
+ * - the element value
+ * - the element index
+ * - the array being traversed
+ * @name forEach
+ * @memberOf me.Container
+ * @function
+ * @param {Function} callback
+ * @param {Object} [thisArg] value to use as this(i.e reference Object) when executing callback.
+ * @example
+ * // iterate through all children of the root container
+ * me.game.world.forEach(function (child) {
+ * // do something with the child
+ * });
+ */
+ forEach : function (callback, thisArg) {
+ var context = this, i = 0;
+
+ var len = this.children.length;
+
+ if (typeof callback !== "function") {
+ throw new me.Container.Error(callback + " is not a function");
+ }
+
+ if (arguments.length > 1) {
+ context = thisArg;
+ }
+
+ while (i < len) {
+ callback.call(context, this.children[i], i, this.children);
+ i++;
+ }
+ },
+
+ /**
+ * Swaps the position (z-index) of 2 children
+ * @name swapChildren
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ * @param {me.Renderable} child2
+ */
+ swapChildren : function (child, child2) {
+ var index = this.getChildIndex(child);
+ var index2 = this.getChildIndex(child2);
+
+ if ((index !== -1) && (index2 !== -1)) {
+ // swap z index
+ var _z = child.pos.z;
+ child.pos.z = child2.pos.z;
+ child2.pos.z = _z;
+ // swap the positions..
+ this.children[index] = child2;
+ this.children[index2] = child;
+ }
+ else {
+ throw new me.Container.Error(child + " Both the supplied childs must be a child of the caller " + this);
+ }
+ },
+
+ /**
+ * Returns the Child at the specified index
+ * @name getChildAt
+ * @memberOf me.Container
+ * @function
+ * @param {Number} index
+ */
+ getChildAt : function (index) {
+ if (index >= 0 && index < this.children.length) {
+ return this.children[index];
+ }
+ else {
+ throw new me.Container.Error("Index (" + index + ") Out Of Bounds for getChildAt()");
+ }
+ },
+
+ /**
+ * Returns the index of the Child
+ * @name getChildAt
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ */
+ getChildIndex : function (child) {
+ return this.children.indexOf(child);
+ },
+
+ /**
+ * Returns true if contains the specified Child
+ * @name hasChild
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ * @return {Boolean}
+ */
+ hasChild : function (child) {
+ return this === child.ancestor;
+ },
+
+ /**
+ * return the child corresponding to the given property and value.
+ * note : avoid calling this function every frame since
+ * it parses the whole object tree each time
+ * @name getChildByProp
+ * @memberOf me.Container
+ * @public
+ * @function
+ * @param {String} prop Property name
+ * @param {String|RegExp|Number|Boolean} value Value of the property
+ * @return {me.Renderable[]} Array of childs
+ * @example
+ * // get the first child object called "mainPlayer" in a specific container :
+ * var ent = myContainer.getChildByProp("name", "mainPlayer");
+ *
+ * // or query the whole world :
+ * var ent = me.game.world.getChildByProp("name", "mainPlayer");
+ *
+ * // partial property matches are also allowed by using a RegExp.
+ * // the following matches "redCOIN", "bluecoin", "bagOfCoins", etc :
+ * var allCoins = me.game.world.getChildByProp("name", /coin/i);
+ *
+ * // searching for numbers or other data types :
+ * var zIndex10 = me.game.world.getChildByProp("z", 10);
+ * var inViewport = me.game.world.getChildByProp("inViewport", true);
+ */
+ getChildByProp : function (prop, value) {
+ var objList = [];
+
+ function compare(obj, prop) {
+ var v = obj[prop];
+ if (value instanceof RegExp && typeof(v) === "string") {
+ if (value.test(v)) {
+ objList.push(obj);
+ }
+ }
+ else if (v === value) {
+ objList.push(obj);
+ }
+ }
+
+ for (var i = this.children.length - 1; i >= 0; i--) {
+ var obj = this.children[i];
+ compare(obj, prop);
+ if (obj instanceof me.Container) {
+ objList = objList.concat(obj.getChildByProp(prop, value));
+ }
+ }
+ return objList;
+ },
+
+ /**
+ * returns the list of childs with the specified class type
+ * @name getChildByType
+ * @memberOf me.Container
+ * @public
+ * @function
+ * @param {Object} class type
+ * @return {me.Renderable[]} Array of children
+ */
+ getChildByType : function (_class) {
+ var objList = [];
+
+ for (var i = this.children.length - 1; i >= 0; i--) {
+ var obj = this.children[i];
+ if (obj instanceof _class) {
+ objList.push(obj);
+ }
+ if (obj instanceof me.Container) {
+ objList = objList.concat(obj.getChildByType(_class));
+ }
+ }
+ return objList;
+ },
+
+ /**
+ * returns the list of childs with the specified name
+ * as defined in Tiled (Name field of the Object Properties)
+ * note : avoid calling this function every frame since
+ * it parses the whole object list each time
+ * @name getChildByName
+ * @memberOf me.Container
+ * @public
+ * @function
+ * @param {String|RegExp|Number|Boolean} name entity name
+ * @return {me.Renderable[]} Array of children
+ */
+ getChildByName : function (name) {
+ return this.getChildByProp("name", name);
+ },
+
+ /**
+ * return the child corresponding to the specified GUID
+ * note : avoid calling this function every frame since
+ * it parses the whole object list each time
+ * @name getChildByGUID
+ * @memberOf me.Container
+ * @public
+ * @function
+ * @param {String|RegExp|Number|Boolean} GUID entity GUID
+ * @return {me.Renderable} corresponding child or null
+ */
+ getChildByGUID : function (guid) {
+ var obj = this.getChildByProp("GUID", guid);
+ return (obj.length > 0) ? obj[0] : null;
+ },
+
+ /**
+ * resizes the child bounds rectangle, based on children bounds.
+ * @name updateChildBounds
+ * @memberOf me.Container
+ * @function
+ * @return {me.Rect} updated child bounds
+ */
+ updateChildBounds : function () {
+ this.childBounds.pos.set(Infinity, Infinity);
+ this.childBounds.resize(-Infinity, -Infinity);
+ var childBounds;
+ for (var i = this.children.length, child; i--, (child = this.children[i]);) {
+ if (child.isRenderable) {
+ if (child instanceof me.Container) {
+ childBounds = child.childBounds;
+ }
+ else {
+ childBounds = child.getBounds();
+ }
+ // TODO : returns an "empty" rect instead of null (e.g. EntityObject)
+ // TODO : getBounds should always return something anyway
+ if (childBounds !== null) {
+ this.childBounds.union(childBounds);
+ }
+ }
+ }
+ return this.childBounds;
+ },
+
+ /**
+ * Checks if this container is root or if ti's attached to the root container.
+ * @private
+ * @name isAttachedToRoot
+ * @memberOf me.Container
+ * @function
+ * @returns Boolean
+ */
+ isAttachedToRoot : function () {
+ if (this._root) {
+ return true;
+ } else {
+ var ancestor = this.ancestor;
+ while (ancestor) {
+ if (ancestor._root === true) {
+ return true;
+ }
+ ancestor = ancestor.ancestor;
+ }
+ return false;
+ }
+ },
+
+ /**
+ * update the renderable's bounding rect (private)
+ * @private
+ * @name updateBoundsPos
+ * @memberOf me.Container
+ * @function
+ */
+ updateBoundsPos : function (newX, newY) {
+ me.Renderable.prototype.updateBoundsPos.apply(this, [ newX, newY ]);
+
+ // Update container's absolute position
+ this._absPos.set(newX, newY);
+ if (this.ancestor) {
+ this._absPos.add(this.ancestor._absPos);
+ }
+
+ // Notify children that the parent's position has changed
+ for (var i = this.children.length, child; i--, (child = this.children[i]);) {
+ if (child.isRenderable) {
+ child.updateBoundsPos(child.pos.x, child.pos.y);
+ }
+ }
+
+ return this.getBounds();
+ },
+
+ /**
+ * @ignore
+ */
+ onActivateEvent : function () {
+ for (var i = this.children.length, child; i--, (child = this.children[i]);) {
+ if (typeof child.onActivateEvent === "function") {
+ child.onActivateEvent();
+ }
+ }
+ },
+
+ /**
+ * Invokes the removeChildNow in a defer, to ensure the child is removed safely after the update & draw stack has completed
+ * @name removeChild
+ * @memberOf me.Container
+ * @public
+ * @function
+ * @param {me.Renderable} child
+ * @param {Boolean} [keepalive=False] True to prevent calling child.destroy()
+ */
+ removeChild : function (child, keepalive) {
+ if (this.hasChild(child)) {
+ deferredRemove.defer(this, child, keepalive);
+ }
+ else {
+ throw new me.Container.Error("Child is not mine.");
+ }
+ },
+
+
+ /**
+ * Removes (and optionally destroys) a child from the container.
+ * (removal is immediate and unconditional)
+ * Never use keepalive=true with objects from {@link me.pool}. Doing so will create a memory leak.
+ * @name removeChildNow
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ * @param {Boolean} [keepalive=False] True to prevent calling child.destroy()
+ */
+ removeChildNow : function (child, keepalive) {
+ if (this.hasChild(child) && (this.getChildIndex(child) >= 0)) {
+ if (typeof child.onDeactivateEvent === "function") {
+ child.onDeactivateEvent();
+ }
+
+ if (!keepalive) {
+ if (typeof (child.destroy) === "function") {
+ child.destroy();
+ }
+
+ me.pool.push(child);
+ }
+
+ // Don't cache the child index; another element might have been removed
+ // by the child's `onDeactivateEvent` or `destroy` methods
+ var childIndex = this.getChildIndex(child);
+ if (childIndex >= 0) {
+ this.children.splice(childIndex, 1);
+ child.ancestor = undefined;
+ }
+ }
+ },
+
+ /**
+ * Automatically set the specified property of all childs to the given value
+ * @name setChildsProperty
+ * @memberOf me.Container
+ * @function
+ * @param {String} property property name
+ * @param {Object} value property value
+ * @param {Boolean} [recursive=false] recursively apply the value to child containers if true
+ */
+ setChildsProperty : function (prop, val, recursive) {
+ for (var i = this.children.length; i >= 0; i--) {
+ var obj = this.children[i];
+ if ((recursive === true) && (obj instanceof me.Container)) {
+ obj.setChildsProperty(prop, val, recursive);
+ }
+ obj[prop] = val;
+ }
+ },
+
+ /**
+ * Move the child in the group one step forward (z depth).
+ * @name moveUp
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ */
+ moveUp : function (child) {
+ var childIndex = this.getChildIndex(child);
+ if (childIndex - 1 >= 0) {
+ // note : we use an inverted loop
+ this.swapChildren(child, this.getChildAt(childIndex - 1));
+ }
+ },
+
+ /**
+ * Move the child in the group one step backward (z depth).
+ * @name moveDown
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ */
+ moveDown : function (child) {
+ var childIndex = this.getChildIndex(child);
+ if (childIndex >= 0 && (childIndex + 1) < this.children.length) {
+ // note : we use an inverted loop
+ this.swapChildren(child, this.getChildAt(childIndex + 1));
+ }
+ },
+
+ /**
+ * Move the specified child to the top(z depth).
+ * @name moveToTop
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ */
+ moveToTop : function (child) {
+ var childIndex = this.getChildIndex(child);
+ if (childIndex > 0) {
+ // note : we use an inverted loop
+ this.children.splice(0, 0, this.children.splice(childIndex, 1)[0]);
+ // increment our child z value based on the previous child depth
+ child.pos.z = this.children[1].pos.z + 1;
+ }
+ },
+
+ /**
+ * Move the specified child the bottom (z depth).
+ * @name moveToBottom
+ * @memberOf me.Container
+ * @function
+ * @param {me.Renderable} child
+ */
+ moveToBottom : function (child) {
+ var childIndex = this.getChildIndex(child);
+ if (childIndex >= 0 && childIndex < (this.children.length - 1)) {
+ // note : we use an inverted loop
+ this.children.splice((this.children.length - 1), 0, this.children.splice(childIndex, 1)[0]);
+ // increment our child z value based on the next child depth
+ child.pos.z = this.children[(this.children.length - 2)].pos.z - 1;
+ }
+ },
+
+ /**
+ * Manually trigger the sort of all the childs in the container
+ * There is no constructor function for me.state.
+ * @namespace me.state
+ * @memberOf me
+ */
+
+ me.state = (function () {
+ // hold public stuff in our singleton
+ var api = {};
+
+ /*-------------------------------------------
+ PRIVATE STUFF
+ --------------------------------------------*/
+
+ // current state
+ var _state = -1;
+
+ // requestAnimeFrame Id
+ var _animFrameId = -1;
+
+ // whether the game state is "paused"
+ var _isPaused = false;
+
+ // list of screenObject
+ var _screenObject = {};
+
+ // fading transition parameters between screen
+ var _fade = {
+ color : "",
+ duration : 0
+ };
+
+ // callback when state switch is done
+ /** @ignore */
+ var _onSwitchComplete = null;
+
+ // just to keep track of possible extra arguments
+ var _extraArgs = null;
+
+ // store the elapsed time during pause/stop period
+ var _pauseTime = 0;
+
+ /**
+ * @ignore
+ */
+ function _startRunLoop() {
+ // ensure nothing is running first and in valid state
+ if ((_animFrameId === -1) && (_state !== -1)) {
+ // reset the timer
+ me.timer.reset();
+
+ // start the main loop
+ _animFrameId = window.requestAnimationFrame(_renderFrame);
+ }
+ }
+
+ /**
+ * Resume the game loop after a pause.
+ * @ignore
+ */
+ function _resumeRunLoop() {
+ // ensure game is actually paused and in valid state
+ if (_isPaused && (_state !== -1)) {
+ // reset the timer
+ me.timer.reset();
+
+ _isPaused = false;
+ }
+ }
+
+ /**
+ * Pause the loop for most screen objects.
+ * @ignore
+ */
+ function _pauseRunLoop() {
+ // Set the paused boolean to stop updates on (most) entities
+ _isPaused = true;
+ }
+
+ /**
+ * this is only called when using requestAnimFrame stuff
+ * @param {Number} time current timestamp in milliseconds
+ * @ignore
+ */
+ function _renderFrame(time) {
+ // update all game objects
+ me.game.update(time);
+ // render all game objects
+ me.game.draw();
+ // schedule the next frame update
+ if (_animFrameId !== -1) {
+ _animFrameId = window.requestAnimationFrame(_renderFrame);
+ }
+ }
+
+ /**
+ * stop the SO main loop
+ * @ignore
+ */
+ function _stopRunLoop() {
+ // cancel any previous animationRequestFrame
+ window.cancelAnimationFrame(_animFrameId);
+ _animFrameId = -1;
+ }
+
+ /**
+ * start the SO main loop
+ * @ignore
+ */
+ function _switchState(state) {
+ // clear previous interval if any
+ _stopRunLoop();
+
+ // call the screen object destroy method
+ if (_screenObject[_state]) {
+ // just notify the object
+ _screenObject[_state].screen.destroy();
+ }
+
+ if (_screenObject[state]) {
+ // set the global variable
+ _state = state;
+
+ // call the reset function with _extraArgs as arguments
+ _screenObject[_state].screen.reset.apply(_screenObject[_state].screen, _extraArgs);
+
+ // and start the main loop of the
+ // new requested state
+ _startRunLoop();
+
+ // execute callback if defined
+ if (_onSwitchComplete) {
+ _onSwitchComplete();
+ }
+
+ // force repaint
+ me.game.repaint();
+ }
+ }
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * default state ID for Loading Screen
+ * @constant
+ * @name LOADING
+ * @memberOf me.state
+ */
+ api.LOADING = 0;
+
+ /**
+ * default state ID for Menu Screen
+ * @constant
+ * @name MENU
+ * @memberOf me.state
+ */
+
+ api.MENU = 1;
+ /**
+ * default state ID for "Ready" Screen
+ * @constant
+ * @name READY
+ * @memberOf me.state
+ */
+
+ api.READY = 2;
+ /**
+ * default state ID for Play Screen
+ * @constant
+ * @name PLAY
+ * @memberOf me.state
+ */
+
+ api.PLAY = 3;
+ /**
+ * default state ID for Game Over Screen
+ * @constant
+ * @name GAMEOVER
+ * @memberOf me.state
+ */
+
+ api.GAMEOVER = 4;
+ /**
+ * default state ID for Game End Screen
+ * @constant
+ * @name GAME_END
+ * @memberOf me.state
+ */
+
+ api.GAME_END = 5;
+ /**
+ * default state ID for High Score Screen
+ * @constant
+ * @name SCORE
+ * @memberOf me.state
+ */
+
+ api.SCORE = 6;
+ /**
+ * default state ID for Credits Screen
+ * @constant
+ * @name CREDITS
+ * @memberOf me.state
+ */
+
+ api.CREDITS = 7;
+ /**
+ * default state ID for Settings Screen
+ * @constant
+ * @name SETTINGS
+ * @memberOf me.state
+ */
+ api.SETTINGS = 8;
+
+ /**
+ * default state ID for user defined constants
+ * Super simple, fast and easy to use tweening engine which incorporates optimised Robert Penner's equation
+ * https://github.com/sole/Tween.js
+ * author sole / http://soledadpenades.com
+ * me.Tween.Easing.Linear.None
+ * me.Tween.Interpolation.Linear
+ * @constant
+ * @name USER
+ * @memberOf me.state
+ * @example
+ * var STATE_INFO = me.state.USER + 0;
+ * var STATE_WARN = me.state.USER + 1;
+ * var STATE_ERROR = me.state.USER + 2;
+ * var STATE_CUTSCENE = me.state.USER + 3;
+ */
+ api.USER = 100;
+
+ /**
+ * onPause callback
+ * @function
+ * @name onPause
+ * @memberOf me.state
+ */
+ api.onPause = null;
+
+ /**
+ * onResume callback
+ * @function
+ * @name onResume
+ * @memberOf me.state
+ */
+ api.onResume = null;
+
+ /**
+ * onStop callback
+ * @function
+ * @name onStop
+ * @memberOf me.state
+ */
+ api.onStop = null;
+
+ /**
+ * onRestart callback
+ * @function
+ * @name onRestart
+ * @memberOf me.state
+ */
+ api.onRestart = null;
+
+ /**
+ * @ignore
+ */
+ api.init = function () {
+ // set the embedded loading screen
+ api.set(api.LOADING, new me.DefaultLoadingScreen());
+ };
+
+ /**
+ * Stop the current screen object.
+ * @name stop
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Boolean} pauseTrack pause current track on screen stop.
+ */
+ api.stop = function (music) {
+ // only stop when we are not loading stuff
+ if ((_state !== api.LOADING) && api.isRunning()) {
+ // stop the main loop
+ _stopRunLoop();
+ // current music stop
+ if (music === true) {
+ me.audio.pauseTrack();
+ }
+
+ // store time when stopped
+ _pauseTime = window.performance.now();
+
+ // publish the stop notification
+ me.event.publish(me.event.STATE_STOP);
+ // any callback defined ?
+ if (typeof(api.onStop) === "function") {
+ api.onStop();
+ }
+ }
+ };
+
+ /**
+ * pause the current screen object
+ * @name pause
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Boolean} pauseTrack pause current track on screen pause
+ */
+ api.pause = function (music) {
+ // only pause when we are not loading stuff
+ if ((_state !== api.LOADING) && !api.isPaused()) {
+ // stop the main loop
+ _pauseRunLoop();
+ // current music stop
+ if (music === true) {
+ me.audio.pauseTrack();
+ }
+
+ // store time when paused
+ _pauseTime = window.performance.now();
+
+ // publish the pause event
+ me.event.publish(me.event.STATE_PAUSE);
+ // any callback defined ?
+ if (typeof(api.onPause) === "function") {
+ api.onPause();
+ }
+ }
+ };
+
+ /**
+ * Restart the screen object from a full stop.
+ * @name restart
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Boolean} resumeTrack resume current track on screen resume
+ */
+ api.restart = function (music) {
+ if (!api.isRunning()) {
+ // restart the main loop
+ _startRunLoop();
+ // current music stop
+ if (music === true) {
+ me.audio.resumeTrack();
+ }
+
+ // calculate the elpased time
+ _pauseTime = window.performance.now() - _pauseTime;
+
+ // force repaint
+ me.game.repaint();
+
+ // publish the restart notification
+ me.event.publish(me.event.STATE_RESTART, [ _pauseTime ]);
+ // any callback defined ?
+ if (typeof(api.onRestart) === "function") {
+ api.onRestart();
+ }
+ }
+ };
+
+ /**
+ * resume the screen object
+ * @name resume
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Boolean} resumeTrack resume current track on screen resume
+ */
+ api.resume = function (music) {
+ if (api.isPaused()) {
+ // resume the main loop
+ _resumeRunLoop();
+ // current music stop
+ if (music === true) {
+ me.audio.resumeTrack();
+ }
+
+ // calculate the elpased time
+ _pauseTime = window.performance.now() - _pauseTime;
+
+ // publish the resume event
+ me.event.publish(me.event.STATE_RESUME, [ _pauseTime ]);
+ // any callback defined ?
+ if (typeof(api.onResume) === "function") {
+ api.onResume();
+ }
+ }
+ };
+
+ /**
+ * return the running state of the state manager
+ * @name isRunning
+ * @memberOf me.state
+ * @public
+ * @function
+ * @return {Boolean} true if a "process is running"
+ */
+ api.isRunning = function () {
+ return _animFrameId !== -1;
+ };
+
+ /**
+ * Return the pause state of the state manager
+ * @name isPaused
+ * @memberOf me.state
+ * @public
+ * @function
+ * @return {Boolean} true if the game is paused
+ */
+ api.isPaused = function () {
+ return _isPaused;
+ };
+
+ /**
+ * associate the specified state with a screen object
+ * @name set
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Number} state State ID (see constants)
+ * @param {me.ScreenObject} so Instantiated ScreenObject to associate
+ * with state ID
+ * @example
+ * var MenuButton = me.GUI_Object.extend({
+ * "onClick" : function () {
+ * // Change to the PLAY state when the button is clicked
+ * me.state.change(me.state.PLAY);
+ * return true;
+ * }
+ * });
+ *
+ * var MenuScreen = me.ScreenObject.extend({
+ * onResetEvent: function() {
+ * // Load background image
+ * me.game.world.addChild(
+ * new me.ImageLayer(0, 0, {
+ * image : "bg",
+ * z: 0 // z-index
+ * }
+ * );
+ *
+ * // Add a button
+ * me.game.world.addChild(
+ * new MenuButton(350, 200, { "image" : "start" }),
+ * 1 // z-index
+ * );
+ *
+ * // Play music
+ * me.audio.playTrack("menu");
+ * },
+ *
+ * "onDestroyEvent" : function () {
+ * // Stop music
+ * me.audio.stopTrack();
+ * }
+ * });
+ *
+ * me.state.set(me.state.MENU, new MenuScreen());
+ */
+ api.set = function (state, so) {
+ _screenObject[state] = {};
+ _screenObject[state].screen = so;
+ _screenObject[state].transition = true;
+ };
+
+ /**
+ * return a reference to the current screen object
+ * useful to call a object specific method
+ * @name current
+ * @memberOf me.state
+ * @public
+ * @function
+ * @return {me.ScreenObject}
+ */
+ api.current = function () {
+ return _screenObject[_state].screen;
+ };
+
+ /**
+ * specify a global transition effect
+ * @name transition
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {String} effect (only "fade" is supported for now)
+ * @param {me.Color|String} color a CSS color value
+ * @param {Number} [duration=1000] expressed in milliseconds
+ */
+ api.transition = function (effect, color, duration) {
+ if (effect === "fade") {
+ _fade.color = color;
+ _fade.duration = duration;
+ }
+ };
+
+ /**
+ * enable/disable transition for a specific state (by default enabled for all)
+ * @name setTransition
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Number} state State ID (see constants)
+ * @param {Boolean} enable
+ */
+ api.setTransition = function (state, enable) {
+ _screenObject[state].transition = enable;
+ };
+
+ /**
+ * change the game/app state
+ * @name change
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Number} state State ID (see constants)
+ * @param {} [arguments...] extra arguments to be passed to the reset functions
+ * @example
+ * // The onResetEvent method on the play screen will receive two args:
+ * // "level_1" and the number 3
+ * me.state.change(me.state.PLAY, "level_1", 3);
+ */
+ api.change = function (state) {
+ // Protect against undefined ScreenObject
+ if (typeof(_screenObject[state]) === "undefined") {
+ throw new me.Error("Undefined ScreenObject for state '" + state + "'");
+ }
+
+ if (api.isCurrent(state)) {
+ // do nothing if already the current state
+ return;
+ }
+
+ _extraArgs = null;
+ if (arguments.length > 1) {
+ // store extra arguments if any
+ _extraArgs = Array.prototype.slice.call(arguments, 1);
+ }
+ // if fading effect
+ if (_fade.duration && _screenObject[state].transition) {
+ /** @ignore */
+ _onSwitchComplete = function () {
+ me.game.viewport.fadeOut(_fade.color, _fade.duration);
+ };
+ me.game.viewport.fadeIn(
+ _fade.color,
+ _fade.duration,
+ function () {
+ _switchState.defer(this, state);
+ }
+ );
+
+ }
+ // else just switch without any effects
+ else {
+ // wait for the last frame to be
+ // "finished" before switching
+ _switchState.defer(this, state);
+ }
+ };
+
+ /**
+ * return true if the specified state is the current one
+ * @name isCurrent
+ * @memberOf me.state
+ * @public
+ * @function
+ * @param {Number} state State ID (see constants)
+ */
+ api.isCurrent = function (state) {
+ return _state === state;
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+(function () {
+ // a basic progress bar object
+ var ProgressBar = me.Renderable.extend({
+ /**
+ * @ignore
+ */
+ init: function (v, w, h) {
+ me.Renderable.prototype.init.apply(this, [v.x, v.y, w, h]);
+ // flag to know if we need to refresh the display
+ this.invalidate = false;
+
+ // default progress bar height
+ this.barHeight = 4;
+
+ // current progress
+ this.progress = 0;
+ },
+
+ /**
+ * make sure the screen is refreshed every frame
+ * @ignore
+ */
+ onProgressUpdate : function (progress) {
+ this.progress = ~~(progress * this.width);
+ this.invalidate = true;
+ },
+
+ /**
+ * @ignore
+ */
+ update : function () {
+ if (this.invalidate === true) {
+ // clear the flag
+ this.invalidate = false;
+ // and return true
+ return true;
+ }
+ // else return false
+ return false;
+ },
+
+ /**
+ * draw function
+ * @ignore
+ */
+ draw : function (renderer) {
+ // draw the progress bar
+ renderer.setColor("black");
+ renderer.fillRect(0, (this.height / 2) - (this.barHeight / 2), this.width, this.barHeight);
+
+ renderer.setColor("#55aa00");
+ renderer.fillRect(2, (this.height / 2) - (this.barHeight / 2), this.progress, this.barHeight);
+
+ renderer.setColor("white");
+ }
+ });
+
+ // the melonJS Logo
+ var IconLogo = me.Renderable.extend({
+ /**
+ * @ignore
+ */
+ init : function (x, y) {
+ me.Renderable.prototype.init.apply(this, [x, y, 100, 85]);
+
+ this.iconCanvas = me.video.createCanvas(
+ me.utils.nextPowerOfTwo(this.width),
+ me.utils.nextPowerOfTwo(this.height),
+ false);
+
+
+ var context = me.video.renderer.getContext2d(this.iconCanvas);
+
+ context.beginPath();
+ context.moveTo(0.7, 48.9);
+ context.bezierCurveTo(10.8, 68.9, 38.4, 75.8, 62.2, 64.5);
+ context.bezierCurveTo(86.1, 53.1, 97.2, 27.7, 87.0, 7.7);
+ context.lineTo(87.0, 7.7);
+ context.bezierCurveTo(89.9, 15.4, 73.9, 30.2, 50.5, 41.4);
+ context.bezierCurveTo(27.1, 52.5, 5.2, 55.8, 0.7, 48.9);
+ context.lineTo(0.7, 48.9);
+ context.lineTo(0.7, 48.9);
+ context.closePath();
+ context.fillStyle = "rgb(255, 255, 255)";
+ context.fill();
+
+ context.beginPath();
+ context.moveTo(84.0, 7.0);
+ context.bezierCurveTo(87.6, 14.7, 72.5, 30.2, 50.2, 41.6);
+ context.bezierCurveTo(27.9, 53.0, 6.9, 55.9, 3.2, 48.2);
+ context.bezierCurveTo(-0.5, 40.4, 14.6, 24.9, 36.9, 13.5);
+ context.bezierCurveTo(59.2, 2.2, 80.3, -0.8, 84.0, 7.0);
+ context.lineTo(84.0, 7.0);
+ context.closePath();
+ context.lineWidth = 5.3;
+ context.strokeStyle = "rgb(255, 255, 255)";
+ context.lineJoin = "miter";
+ context.miterLimit = 4.0;
+ context.stroke();
+ },
+ /**
+ * @ignore
+ */
+ draw : function (renderer) {
+ renderer.drawImage(this.iconCanvas, this.pos.x, this.pos.y);
+ }
+ });
+
+ // the melonJS Text Logo
+ var TextLogo = me.Renderable.extend({
+ /**
+ * @ignore
+ */
+ init : function (w, h) {
+ me.Renderable.prototype.init.apply(this, [0, 0, w, h]);
+
+ // offscreen cache canvas
+ this.fontCanvas = me.video.createCanvas(128, 32);
+ this.drawFont(me.video.renderer.getContext2d(this.fontCanvas));
+ },
+
+ drawFont : function (context) {
+ var logo1 = new me.Font("century gothic", 32, "white", "middle");
+ var logo2 = new me.Font("century gothic", 32, "#55aa00", "middle");
+ var logo1_width = 0;
+
+ // configure the font
+ logo2.bold();
+ logo1.textBaseline = logo2.textBaseline = "top";
+
+ // measure the logo size (using standard 2d context)
+ context.font = logo1.font;
+ context.fillStyle = logo1.fillStyle.toRGBA();
+ context.textAlign = logo1.textAlign;
+ context.textBaseline = logo1.textBaseline;
+ logo1_width = context.measureText("melon").width;
+
+ // calculate the final rendering position
+ this.pos.x = Math.round((this.width - logo1_width - context.measureText("JS").width) / 2);
+ this.pos.y = this.height / 2 + 16;
+
+ // use the private _drawFont method to directly draw on the canvas context
+ logo1._drawFont(context, "melon", 0, 0);
+ logo2._drawFont(context, "JS", logo1_width, 0);
+ },
+
+ /**
+ * @ignore
+ */
+ draw : function (renderer) {
+ renderer.drawImage(this.fontCanvas, this.pos.x, this.pos.y);
+ }
+
+ });
+
+ /**
+ * a default loading screen
+ * @memberOf me
+ * @ignore
+ * @constructor
+ */
+ me.DefaultLoadingScreen = me.ScreenObject.extend({
+ /**
+ * call when the loader is resetted
+ * @ignore
+ */
+ onResetEvent : function () {
+ // background color
+ me.game.world.addChild(new me.ColorLayer("background", "#202020", 0), 0);
+
+ // progress bar
+ var progressBar = new ProgressBar(
+ new me.Vector2d(),
+ me.video.renderer.getWidth(),
+ me.video.renderer.getHeight()
+ );
+
+ this.loaderHdlr = me.event.subscribe(
+ me.event.LOADER_PROGRESS,
+ progressBar.onProgressUpdate.bind(progressBar)
+ );
+
+ this.resizeHdlr = me.event.subscribe(
+ me.event.VIEWPORT_ONRESIZE,
+ progressBar.resize.bind(progressBar)
+ );
+
+ me.game.world.addChild(progressBar, 1);
+ // melonJS text & logo
+ var icon = new IconLogo(
+ (me.video.renderer.getWidth() - 100) / 2,
+ (me.video.renderer.getHeight() / 2) - (progressBar.barHeight / 2) - 90
+ );
+ me.game.world.addChild(icon, 1);
+ me.game.world.addChild(new TextLogo(me.video.renderer.getWidth(), me.video.renderer.getHeight()), 1);
+ },
+
+ /**
+ * destroy object at end of loading
+ * @ignore
+ */
+ onDestroyEvent : function () {
+ // cancel the callback
+ me.event.unsubscribe(this.loaderHdlr);
+ me.event.unsubscribe(this.resizeHdlr);
+ this.loaderHdlr = this.resizeHdlr = null;
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a small class to manage loading of stuff and manage resources
+ * There is no constructor function for me.input.
+ * @namespace me.loader
+ * @memberOf me
+ */
+ me.loader = (function () {
+ // hold public stuff in our singleton
+ var api = {};
+
+ // contains all the images loaded
+ var imgList = {};
+ // contains all the TMX loaded
+ var tmxList = {};
+ // contains all the binary files loaded
+ var binList = {};
+ // contains all the JSON files
+ var jsonList = {};
+ // baseURL
+ var baseURL = {};
+
+ // flag to check loading status
+ var resourceCount = 0;
+ var loadCount = 0;
+ var timerId = 0;
+
+ /**
+ * check the loading status
+ * @ignore
+ */
+ function checkLoadStatus(onload) {
+ if (loadCount === resourceCount) {
+ // wait 1/2s and execute callback (cheap workaround to ensure everything is loaded)
+ if (onload || api.onload) {
+ // make sure we clear the timer
+ clearTimeout(timerId);
+ // trigger the onload callback
+ // we call either the supplied callback (which takes precedence) or the global one
+ var callback = onload || api.onload;
+ setTimeout(function () {
+ callback();
+ me.event.publish(me.event.LOADER_COMPLETE);
+ }, 300);
+ }
+ else {
+ console.error("no load callback defined");
+ }
+ }
+ else {
+ timerId = setTimeout(function() {
+ checkLoadStatus(onload);
+ }, 100);
+ }
+ }
+
+ /**
+ * load Images
+ * @example
+ * preloadImages([
+ * { name : 'image1', src : 'images/image1.png'},
+ * { name : 'image2', src : 'images/image2.png'},
+ * { name : 'image3', src : 'images/image3.png'},
+ * { name : 'image4', src : 'images/image4.png'}
+ * ]);
+ * @ignore
+ */
+ function preloadImage(img, onload, onerror) {
+ // create new Image object and add to list
+ imgList[img.name] = new Image();
+ imgList[img.name].onload = onload;
+ imgList[img.name].onerror = onerror;
+ if (typeof (api.crossOrigin) === "string") {
+ imgList[img.name].crossOrigin = api.crossOrigin;
+ }
+ imgList[img.name].src = img.src + api.nocache;
+ }
+
+ /**
+ * preload TMX files
+ * @ignore
+ */
+ function preloadTMX(tmxData, onload, onerror) {
+ function addToTMXList(data) {
+ // set the TMX content
+ tmxList[tmxData.name] = data;
+
+ // add the tmx to the levelDirector
+ if (tmxData.type === "tmx") {
+ me.levelDirector.addTMXLevel(tmxData.name);
+ }
+ }
+
+
+ //if the data is in the tmxData object, don't get it via a XMLHTTPRequest
+ if (tmxData.data) {
+ addToTMXList(tmxData.data);
+ onload();
+ return;
+ }
+
+ var xmlhttp = new XMLHttpRequest();
+ // check the data format ('tmx', 'json')
+ var format = me.utils.getFileExtension(tmxData.src);
+
+ if (xmlhttp.overrideMimeType) {
+ if (format === "json") {
+ xmlhttp.overrideMimeType("application/json");
+ }
+ else {
+ xmlhttp.overrideMimeType("text/xml");
+ }
+ }
+
+ xmlhttp.open("GET", tmxData.src + api.nocache, true);
+
+
+ // set the callbacks
+ xmlhttp.ontimeout = onerror;
+ xmlhttp.onreadystatechange = function () {
+ if (xmlhttp.readyState === 4) {
+ // status = 0 when file protocol is used, or cross-domain origin,
+ // (With Chrome use "--allow-file-access-from-files --disable-web-security")
+ if ((xmlhttp.status === 200) || ((xmlhttp.status === 0) && xmlhttp.responseText)) {
+ var result = null;
+
+ // parse response
+ switch (format) {
+ case "xml":
+ case "tmx":
+ case "tsx":
+ // ie9 does not fully implement the responseXML
+ if (me.device.ua.match(/msie/i) || !xmlhttp.responseXML) {
+ if (window.DOMParser) {
+ // manually create the XML DOM
+ result = (new DOMParser()).parseFromString(xmlhttp.responseText, "text/xml");
+ } else {
+ throw new api.Error("XML file format loading not supported, use the JSON file format instead");
+ }
+ }
+ else {
+ result = xmlhttp.responseXML;
+ }
+ // converts to a JS object
+ var data = me.TMXUtils.parse(result);
+ switch (format) {
+ case "tmx":
+ result = data.map;
+ break;
+
+ case "tsx":
+ result = data.tilesets[0];
+ break;
+ }
+
+ break;
+
+ case "json":
+ result = JSON.parse(xmlhttp.responseText);
+ break;
+
+ default:
+ throw new api.Error("TMX file format " + format + "not supported !");
+ }
+
+ //set the TMX content
+ addToTMXList(result);
+
+ // fire the callback
+ onload();
+ }
+ else {
+ onerror();
+ }
+ }
+ };
+ // send the request
+ xmlhttp.send(null);
+ }
+
+ /**
+ * preload TMX files
+ * @ignore
+ */
+ function preloadJSON(data, onload, onerror) {
+ var xmlhttp = new XMLHttpRequest();
+
+ if (xmlhttp.overrideMimeType) {
+ xmlhttp.overrideMimeType("application/json");
+ }
+
+ xmlhttp.open("GET", data.src + api.nocache, true);
+
+ // set the callbacks
+ xmlhttp.ontimeout = onerror;
+ xmlhttp.onreadystatechange = function () {
+ if (xmlhttp.readyState === 4) {
+ // status = 0 when file protocol is used, or cross-domain origin,
+ // (With Chrome use "--allow-file-access-from-files --disable-web-security")
+ if ((xmlhttp.status === 200) || ((xmlhttp.status === 0) && xmlhttp.responseText)) {
+ // get the Texture Packer Atlas content
+ jsonList[data.name] = JSON.parse(xmlhttp.responseText);
+ // fire the callback
+ onload();
+ }
+ else {
+ onerror();
+ }
+ }
+ };
+ // send the request
+ xmlhttp.send(null);
+ }
+
+ /**
+ * preload Binary files
+ * @ignore
+ */
+ function preloadBinary(data, onload, onerror) {
+ var httpReq = new XMLHttpRequest();
+
+ // load our file
+ httpReq.open("GET", data.src + api.nocache, true);
+ httpReq.responseType = "arraybuffer";
+ httpReq.onerror = onerror;
+ httpReq.onload = function () {
+ var arrayBuffer = httpReq.response;
+ if (arrayBuffer) {
+ var byteArray = new Uint8Array(arrayBuffer);
+ var buffer = [];
+ for (var i = 0; i < byteArray.byteLength; i++) {
+ buffer[i] = String.fromCharCode(byteArray[i]);
+ }
+ binList[data.name] = buffer.join("");
+ // callback
+ onload();
+ }
+ };
+ httpReq.send();
+ }
+
+ /**
+ * to enable/disable caching
+ * @ignore
+ */
+ api.nocache = "";
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * onload callback
+ * @public
+ * @function
+ * @name onload
+ * @memberOf me.loader
+ * @example
+ * // set a callback when everything is loaded
+ * me.loader.onload = this.loaded.bind(this);
+ */
+ api.onload = undefined;
+
+ /**
+ * onProgress callback
+ * each time a resource is loaded, the loader will fire the specified function,
+ * giving the actual progress [0 ... 1], as argument, and an object describing the resource loaded
+ * @public
+ * @function
+ * @name onProgress
+ * @memberOf me.loader
+ * @example
+ * // set a callback for progress notification
+ * me.loader.onProgress = this.updateProgress.bind(this);
+ */
+ api.onProgress = undefined;
+
+
+ /**
+ * crossOrigin attribute to configure the CORS requests for Image data element.
+ * By default (that is, when the attribute is not specified), CORS is not used at all.
+ * The "anonymous" keyword means that there will be no exchange of user credentials via cookies,
+ * client-side SSL certificates or HTTP authentication as described in the Terminology section of the CORS specification.
+ * @public
+ * @type String
+ * @name crossOrigin
+ * @memberOf me.loader
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes
+ * @example
+ * // allow for cross-origin texture loading in WebGL
+ * me.loader.crossOrigin = "anonymous";
+ *
+ * // set all ressources to be loaded
+ * me.loader.preload(game.resources, this.loaded.bind(this));
+ */
+ api.crossOrigin = undefined;
+
+ /**
+ * Base class for Loader exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.loader
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ api.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.loader.Error";
+ }
+ });
+
+ /**
+ * just increment the number of already loaded resources
+ * @ignore
+ */
+ api.onResourceLoaded = function (res) {
+ // increment the loading counter
+ loadCount++;
+
+ // callback ?
+ var progress = api.getLoadProgress();
+ if (api.onProgress) {
+ // pass the load progress in percent, as parameter
+ api.onProgress(progress, res);
+ }
+ me.event.publish(me.event.LOADER_PROGRESS, [progress, res]);
+ };
+
+ /**
+ * on error callback for image loading
+ * @ignore
+ */
+ api.onLoadingError = function (res) {
+ throw new api.Error("Failed loading resource " + res.src);
+ };
+
+ /**
+ * enable the nocache mechanism
+ * @ignore
+ */
+ api.setNocache = function (enable) {
+ api.nocache = enable ? "?" + ~~(Math.random() * 10000000) : "";
+ };
+
+ /**
+ * change the default baseURL for the given asset type.
+ * (this will prepend the asset URL and must finish with a '/')
+ * @name setBaseURL
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {String} type "*", "audio", binary", "image", "json", "tmx", "tsx"
+ * @param {String} [url="./"] default base URL
+ * @example
+ * // change the base URL relative address
+ * me.loader.setBaseURL("audio", "data/audio/");
+ * // change the base URL absolute address for all object types
+ * me.loader.setBaseURL("*", "http://myurl.com/")
+ */
+ api.setBaseURL = function (type, url) {
+ if (type !== "*") {
+ baseURL[type] = url;
+ } else {
+ // "wildcards"
+ baseURL["audio"] = url;
+ baseURL["binary"] = url;
+ baseURL["image"] = url;
+ baseURL["json"] = url;
+ baseURL["tmx"] = url;
+ baseURL["tsx"] = url;
+ }
+ };
+
+
+ /**
+ * set all the specified game resources to be preloaded.
+ * @name preload
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {Object[]} resources
+ * @param {String} resources.name internal name of the resource
+ * @param {String} resources.type "audio", binary", "image", "json", "tmx", "tsx"
+ * @param {String} resources.src path and/or file name of the resource (for audio assets only the path is required)
+ * @param {Boolean} [resources.stream] set to true if you don't have to wait for the audio file to be fully downloaded
+ * @param {function} [onload=me.loader.onload] function to be called when all resources are loaded
+ * @param {boolean} [switchToLoadState=true] automatically switch to the loading screen
+ * @example
+ * game_resources = [
+ * // PNG tileset
+ * {name: "tileset-platformer", type: "image", src: "data/map/tileset.png"},
+ * // PNG packed texture
+ * {name: "texture", type:"image", src: "data/gfx/texture.png"}
+ * // TSX file
+ * {name: "meta_tiles", type: "tsx", src: "data/map/meta_tiles.tsx"},
+ * // TMX level (XML & JSON)
+ * {name: "map1", type: "tmx", src: "data/map/map1.json"},
+ * {name: "map2", type: "tmx", src: "data/map/map2.tmx"},
+ * {name: "map3", type: "tmx", format: "json", data: {"height":15,"layers":[...],"tilewidth":32,"version":1,"width":20}},
+ * {name: "map4", type: "tmx", format: "xml", data: {xml representation of tmx}},
+ * // audio resources
+ * {name: "bgmusic", type: "audio", src: "data/audio/"},
+ * {name: "cling", type: "audio", src: "data/audio/"},
+ * // binary file
+ * {name: "ymTrack", type: "binary", src: "data/audio/main.ym"},
+ * // JSON file (used for texturePacker)
+ * {name: "texture", type: "json", src: "data/gfx/texture.json"}
+ * ];
+ * ...
+ * // set all resources to be loaded
+ * me.loader.preload(game.resources, this.loaded.bind(this));
+ */
+ api.preload = function (res, onload, switchToLoadState) {
+ // parse the resources
+ for (var i = 0; i < res.length; i++) {
+ resourceCount += api.load(
+ res[i],
+ api.onResourceLoaded.bind(api, res[i]),
+ api.onLoadingError.bind(api, res[i])
+ );
+ }
+ // set the onload callback if defined
+ if (typeof(onload) !== "undefined") {
+ api.onload = onload;
+ }
+
+ if (switchToLoadState !== false) {
+ // swith to the loading screen
+ me.state.change(me.state.LOADING);
+ }
+
+ // check load status
+ checkLoadStatus(onload);
+ };
+
+ /**
+ * Load a single resource (to be used if you need to load additional resource during the game)
+ * @name load
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {Object} resource
+ * @param {String} resource.name internal name of the resource
+ * @param {String} resource.type "audio", binary", "image", "json", "tmx", "tsx"
+ * @param {String} resource.src path and/or file name of the resource (for audio assets only the path is required)
+ * @param {Boolean} [resource.stream] set to true if you don't have to wait for the audio file to be fully downloaded
+ * @param {Function} onload function to be called when the resource is loaded
+ * @param {Function} onerror function to be called in case of error
+ * @example
+ * // load an image asset
+ * me.loader.load({name: "avatar", type:"image", src: "data/avatar.png"}, this.onload.bind(this), this.onerror.bind(this));
+ *
+ * // start loading music
+ * me.loader.load({
+ * name : "bgmusic",
+ * type : "audio",
+ * src : "data/audio/"
+ * }, function () {
+ * me.audio.play("bgmusic");
+ * });
+ */
+ api.load = function (res, onload, onerror) {
+ // transform the url if necessary
+ if (typeof (baseURL[res.type]) !== "undefined") {
+ res.src = baseURL[res.type] + res.src;
+ }
+ // check ressource type
+ switch (res.type) {
+ case "binary":
+ // reuse the preloadImage fn
+ preloadBinary.call(this, res, onload, onerror);
+ return 1;
+
+ case "image":
+ // reuse the preloadImage fn
+ preloadImage.call(this, res, onload, onerror);
+ return 1;
+
+ case "json":
+ preloadJSON.call(this, res, onload, onerror);
+ return 1;
+
+ case "tmx":
+ case "tsx":
+ preloadTMX.call(this, res, onload, onerror);
+ return 1;
+
+ case "audio":
+ me.audio.load(res, !!res.stream, onload, onerror);
+ return 1;
+
+ default:
+ throw new api.Error("load : unknown or invalid resource type : " + res.type);
+ }
+ };
+
+ /**
+ * unload specified resource to free memory
+ * @name unload
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {Object} resource
+ * @return {Boolean} true if unloaded
+ * @example me.loader.unload({name: "avatar", type:"image", src: "data/avatar.png"});
+ */
+ api.unload = function (res) {
+ switch (res.type) {
+ case "binary":
+ if (!(res.name in binList)) {
+ return false;
+ }
+
+ delete binList[res.name];
+ return true;
+
+ case "image":
+ if (!(res.name in imgList)) {
+ return false;
+ }
+ if (typeof(imgList[res.name].dispose) === "function") {
+ // cocoonJS implements a dispose function to free
+ // corresponding allocated texture in memory
+ imgList[res.name].dispose();
+ }
+ delete imgList[res.name];
+ return true;
+
+ case "json":
+ if (!(res.name in jsonList)) {
+ return false;
+ }
+
+ delete jsonList[res.name];
+ return true;
+
+ case "tmx":
+ case "tsx":
+ if (!(res.name in tmxList)) {
+ return false;
+ }
+
+ delete tmxList[res.name];
+ return true;
+
+ case "audio":
+ return me.audio.unload(res.name);
+
+ default:
+ throw new api.Error("unload : unknown or invalid resource type : " + res.type);
+ }
+ };
+
+ /**
+ * unload all resources to free memory
+ * @name unloadAll
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @example me.loader.unloadAll();
+ */
+ api.unloadAll = function () {
+ var name;
+
+ // unload all binary resources
+ for (name in binList) {
+ if (binList.hasOwnProperty(name)) {
+ api.unload({
+ "name" : name,
+ "type" : "binary"
+ });
+ }
+ }
+
+ // unload all image resources
+ for (name in imgList) {
+ if (imgList.hasOwnProperty(name)) {
+ api.unload({
+ "name" : name,
+ "type" : "image"
+ });
+ }
+ }
+
+ // unload all tmx resources
+ for (name in tmxList) {
+ if (tmxList.hasOwnProperty(name)) {
+ api.unload({
+ "name" : name,
+ "type" : "tmx"
+ });
+ }
+ }
+
+ // unload all in json resources
+ for (name in jsonList) {
+ if (jsonList.hasOwnProperty(name)) {
+ api.unload({
+ "name" : name,
+ "type" : "json"
+ });
+ }
+ }
+
+ // unload all audio resources
+ me.audio.unloadAll();
+ };
+
+ /**
+ * return the specified TMX/TSX object
+ * @name getTMX
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {String} tmx name of the tmx/tsx element ("map1");
+ * @return {XML|Object}
+ */
+ api.getTMX = function (elt) {
+ // force as string
+ elt = "" + elt;
+ if (elt in tmxList) {
+ return tmxList[elt];
+ }
+ else {
+ //console.log ("warning %s resource not yet loaded!",name);
+ return null;
+ }
+ };
+
+ /**
+ * return the specified Binary object
+ * @name getBinary
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {String} name of the binary object ("ymTrack");
+ * @return {Object}
+ */
+ api.getBinary = function (elt) {
+ // force as string
+ elt = "" + elt;
+ if (elt in binList) {
+ return binList[elt];
+ }
+ else {
+ //console.log ("warning %s resource not yet loaded!",name);
+ return null;
+ }
+
+ };
+
+ /**
+ * return the specified Image Object
+ * @name getImage
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {String} Image name of the Image element ("tileset-platformer");
+ * @return {Image}
+ */
+ api.getImage = function (elt) {
+ // force as string
+ elt = "" + elt;
+ if (elt in imgList) {
+ // return the corresponding Image object
+ return imgList[elt];
+ }
+ else {
+ //console.log ("warning %s resource not yet loaded!",name);
+ return null;
+ }
+
+ };
+
+ /**
+ * return the specified JSON Object
+ * @name getJSON
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @param {String} Name for the json file to load
+ * @return {Object}
+ */
+ api.getJSON = function (elt) {
+ // force as string
+ elt = "" + elt;
+ if (elt in jsonList) {
+ return jsonList[elt];
+ }
+ else {
+ return null;
+ }
+ };
+
+ /**
+ * Return the loading progress in percent
+ * @name getLoadProgress
+ * @memberOf me.loader
+ * @public
+ * @function
+ * @deprecated use callback instead
+ * @return {Number}
+ */
+ api.getLoadProgress = function () {
+ return loadCount / resourceCount;
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Font / Bitmap font
+ *
+ * ASCII Table
+ * http://www.asciitable.com/
+ * [ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz]
+ *
+ * -> first char " " 32d (0x20);
+ */
+(function () {
+
+ var runits = ["ex", "em", "pt", "px"];
+ var toPX = [12, 24, 0.75, 1];
+
+ /**
+ * a generic system font object.
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {String} font a CSS font name
+ * @param {Number|String} size size, or size + suffix (px, em, pt)
+ * @param {me.Color|String} fillStyle a CSS color value
+ * @param {String} [textAlign="left"] horizontal alignment
+ */
+ me.Font = me.Renderable.extend(
+ /** @scope me.Font.prototype */ {
+
+ /** @ignore */
+ init : function (font, size, fillStyle, textAlign) {
+ // private font properties
+ /** @ignore */
+ this.fontSize = new me.Vector2d();
+
+ /**
+ * defines the color used to draw the font.
+ * @public
+ * @type me.Color
+ * @default black
+ * @name me.Font#fillStyle
+ */
+ this.fillStyle = new me.Color().copy(fillStyle);
+
+ /**
+ * defines the color used to draw the font stroke.
+ * @public
+ * @type me.Color
+ * @default black
+ * @name me.Font#strokeStyle
+ */
+ this.strokeStyle = new me.Color(0, 0, 0);
+
+ /**
+ * sets the current line width, in pixels, when drawing stroke
+ * @public
+ * @type Number
+ * @default 1
+ * @name me.Font#lineWidth
+ */
+ this.lineWidth = 1;
+
+ /**
+ * Set the default text alignment (or justification),
+ * possible values are "left", "right", and "center".
+ * @public
+ * @type String
+ * @default "left"
+ * @name me.Font#textAlign
+ */
+ this.textAlign = textAlign || "left";
+
+ /**
+ * Set the text baseline (e.g. the Y-coordinate for the draw operation),
+ * possible values are "top", "hanging, "middle, "alphabetic, "ideographic, "bottom"
+ * @public
+ * @type String
+ * @default "top"
+ * @name me.Font#textBaseline
+ */
+ this.textBaseline = "top";
+
+ /**
+ * Set the line spacing height (when displaying multi-line strings).
+ * Current font height will be multiplied with this value to set the line height.
+ * @public
+ * @type Number
+ * @default 1.0
+ * @name me.Font#lineHeight
+ */
+ this.lineHeight = 1.0;
+
+ // super constructor
+ me.Renderable.prototype.init.apply(this, [0, 0, 0, 0]);
+
+ // font name and type
+ this.setFont(font, size, fillStyle, textAlign);
+
+ if (!this.gid) {
+ this.gid = me.utils.createGUID();
+ }
+ },
+
+ /**
+ * make the font bold
+ * @name bold
+ * @memberOf me.Font
+ * @function
+ */
+ bold : function () {
+ this.font = "bold " + this.font;
+ },
+
+ /**
+ * make the font italic
+ * @name italic
+ * @memberOf me.Font
+ * @function
+ */
+ italic : function () {
+ this.font = "italic " + this.font;
+ },
+
+ /**
+ * Change the font settings
+ * @name setFont
+ * @memberOf me.Font
+ * @function
+ * @param {String} font a CSS font name
+ * @param {Number|String} size size, or size + suffix (px, em, pt)
+ * @param {me.Color|String} [fillStyle] a CSS color value
+ * @param {String} [textAlign="left"] horizontal alignment
+ * @example
+ * font.setFont("Arial", 20, "white");
+ * font.setFont("Arial", "1.5em", "white");
+ */
+ setFont : function (font, size, fillStyle, textAlign) {
+ // font name and type
+ var font_names = font.split(",").map(function (value) {
+ value = value.trim();
+ return (
+ !/(^".*"$)|(^'.*'$)/.test(value)
+ ) ? "\"" + value + "\"" : value;
+ });
+
+ if (typeof size === "number") {
+ this.fontSize.y = size;
+ size += "px";
+ } else /* string */ {
+ // extract the units and convert if necessary
+ var CSSval = size.match(/([-+]?[\d.]*)(.*)/);
+ this.fontSize.y = parseFloat(CSSval[1]);
+ if (CSSval[2]) {
+ this.fontSize.y *= toPX[runits.indexOf(CSSval[2])];
+ } else {
+ // no unit define, assume px
+ size += "px";
+ }
+ }
+ this.height = this.fontSize.y;
+
+ this.font = size + " " + font_names.join(",");
+ if (typeof(fillStyle) !== "undefined") {
+ this.fillStyle.copy(fillStyle);
+ }
+ if (textAlign) {
+ this.textAlign = textAlign;
+ }
+ },
+
+ /**
+ * measure the given text size in pixels
+ * @name measureText
+ * @memberOf me.Font
+ * @function
+ * @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
+ * @param {String} text
+ * @return {Object} returns an object, with two attributes: width (the width of the text) and height (the height of the text).
+ */
+ measureText : function (renderer, text) {
+ var context = renderer.getFontContext();
+
+ // draw the text
+ context.font = this.font;
+ context.fillStyle = this.fillStyle.toRGBA();
+ context.textAlign = this.textAlign;
+ context.textBaseline = this.textBaseline;
+
+ this.height = this.width = 0;
+
+ var strings = ("" + text).split("\n");
+ for (var i = 0; i < strings.length; i++) {
+ this.width = Math.max(context.measureText(strings[i].trimRight()).width, this.width);
+ this.height += this.fontSize.y * this.lineHeight;
+ }
+ return {
+ width : this.width,
+ height : this.height
+ };
+ },
+
+ /**
+ * draw a text at the specified coord
+ * @name draw
+ * @memberOf me.Font
+ * @function
+ * @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
+ * @param {String} text
+ * @param {Number} x
+ * @param {Number} y
+ */
+ draw : function (renderer, text, x, y) {
+ // save the previous global alpha value
+ var _alpha = renderer.globalAlpha();
+
+ renderer.setGlobalAlpha(_alpha * this.getOpacity());
+
+ // draw the text
+ renderer.drawFont(this._drawFont(renderer.getFontContext(), text, ~~x, ~~y, false));
+
+ // restore the previous global alpha value
+ renderer.setGlobalAlpha(_alpha);
+ },
+
+ /**
+ * draw a stroke text at the specified coord, as defined
+ * by the `lineWidth` and `fillStroke` properties.
+ * Note : using drawStroke is not recommended for performance reasons
+ * @name drawStroke
+ * @memberOf me.Font
+ * @function
+ * @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
+ * @param {String} text
+ * @param {Number} x
+ * @param {Number} y
+ */
+ drawStroke : function (renderer, text, x, y) {
+ // save the previous global alpha value
+ var _alpha = renderer.globalAlpha();
+
+ renderer.setGlobalAlpha(_alpha * this.getOpacity());
+
+ // draw the text
+ renderer.drawFont(this._drawFont(renderer.getFontContext(), text, ~~x, ~~y, true));
+
+ // restore the previous global alpha value
+ renderer.setGlobalAlpha(_alpha);
+ },
+
+ /**
+ * @ignore
+ */
+ _drawFont : function (context, text, x, y, stroke) {
+ context.font = this.font;
+ context.fillStyle = this.fillStyle.toRGBA();
+ if (stroke) {
+ context.strokeStyle = this.strokeStyle.toRGBA();
+ context.lineWidth = this.lineWidth;
+ }
+ context.textAlign = this.textAlign;
+ context.textBaseline = this.textBaseline;
+
+ var strings = ("" + text).split("\n"), string = "";
+ var dw = 0;
+ var dy = y;
+ var lineHeight = this.fontSize.y * this.lineHeight;
+ for (var i = 0; i < strings.length; i++) {
+ string = strings[i].trimRight();
+ // measure the string
+ dw = Math.max(dw, context.measureText(string).width);
+ // draw the string
+ context[stroke ? "strokeText" : "fillText"](string, x, y);
+ // add leading space
+ y += lineHeight;
+ }
+
+ // compute bounds
+ // TODO : memoize me !
+ var dx = (this.textAlign === "right" ? x - dw : (
+ this.textAlign === "center" ? x - ~~(dw / 2) : x
+ ));
+ dy = (this.textBaseline.search(/^(top|hanging)$/) === 0) ? dy : (
+ this.textBaseline === "middle" ? dy - ~~(lineHeight / 2) : dy - lineHeight
+ );
+
+ // update the renderable bounds
+ return this.getBounds().setShape(
+ ~~dx,
+ ~~dy,
+ ~~(dw + 0.5),
+ ~~(strings.length * lineHeight + 0.5)
+ );
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Font / Bitmap font
+ *
+ * -> first char " " 32d (0x20);
+ */
+(function () {
+
+ /**
+ * Measures a single line of text, does not account for \n
+ * @ignore
+ */
+ var measureTextWidth = function(font, text) {
+ var characters = text.split("");
+ var width = 0;
+ var lastGlyph = null;
+ for (var i = 0; i < characters.length; i++) {
+ var ch = characters[i].charCodeAt(0);
+ var glyph = font.bitmapFontData.glyphs[ch];
+ var kerning = (lastGlyph && lastGlyph.kerning) ? lastGlyph.getKerning(ch) : 0;
+ width += (glyph.xadvance + kerning) * font.fontScale.x;
+ lastGlyph = glyph;
+ }
+
+ return width;
+ };
+
+ /**
+ * a bitmap font object
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {Object} font the font object data. Should be retrieved from the loader
+ * @param {Image} image the font image itself Should be retrieved from the loader
+ * @param {Number} [scale=1.0]
+ * @param {String} [textAlign="left"]
+ * @param {String} [textBaseline="top"]
+ * @example
+ * // Use me.loader.preload or me.loader.load to load assets
+ * me.loader.preload([
+ * { name: "arial", type: "binary" src: "data/font/arial.fnt" },
+ * { name: "arial", type: "image" src: "data/font/arial.png" },
+ * ])
+ * // Then create an instance of your bitmap font:
+ * var myFont = new me.BitmapFont(me.loader.getBinary("arial"), me.loader.getImage("arial"));
+ * // And draw it inside your Renderable, just like me.Font
+ * myFont.draw(renderer, "Hello!", 0, 0);
+ */
+ me.BitmapFont = me.Renderable.extend(
+ /** @scope me.BitmapFont.prototype */ {
+ /** @ignore */
+ init : function (data, fontImage, scale, textAlign, textBaseline) {
+ /** @ignore */
+ // scaled font size;
+ this.sSize = me.pool.pull("me.Vector2d", 0, 0);
+
+ this.fontImage = fontImage;
+
+ /**
+ * The instance of me.BitmapFontData
+ * @type {me.BitmapFontData}
+ * @name bitmapFontData
+ * @memberOf me.BitmapFont
+ */
+ this.bitmapFontData = new me.BitmapFontData(data);
+ this.fontScale = me.pool.pull("me.Vector2d", 1, 1);
+
+ this.charCount = 0;
+ me.Renderable.prototype.init.apply(this, [0, 0, 0, 0, 0, 0]);
+
+ // set a default alignement
+ this.textAlign = textAlign || "left";
+ this.textBaseline = textBaseline || "top";
+ this.lineHeight = 1;
+ // resize if necessary
+ if (scale) {
+ this.resize(scale);
+ }
+ },
+
+ /**
+ * change the font settings
+ * @name set
+ * @memberOf me.BitmapFont
+ * @function
+ * @param {String} textAlign ("left", "center", "right")
+ * @param {Number} [scale]
+ */
+ set : function (textAlign, scale) {
+ this.textAlign = textAlign;
+ // updated scaled Size
+ if (scale) {
+ this.resize(scale);
+ }
+ },
+
+ /**
+ * change the font display size
+ * @name resize
+ * @memberOf me.BitmapFont
+ * @function
+ * @param {Number} scale ratio
+ */
+ resize : function (scale) {
+ this.fontScale.set(scale, scale);
+ },
+
+
+ /**
+ * measure the given text size in pixels
+ * @name measureText
+ * @memberOf me.BitmapFont
+ * @function
+ * @param {String} text
+ * @returns {Object} an object with two properties: `width` and `height`, defining the output dimensions
+ */
+ measureText : function (text) {
+ var strings = ("" + text).split("\n");
+ var width = 0;
+ var height = 0;
+ var stringHeight = this.bitmapFontData.capHeight * this.lineHeight;
+ for (var i = 0; i < strings.length; i++) {
+ width = Math.max(measureTextWidth(this, strings[i]), width);
+ height += stringHeight;
+ }
+
+ return {width: width, height: height * this.fontScale.y};
+ },
+
+ /**
+ * draw a text at the specified coord
+ * @name draw
+ * @memberOf me.BitmapFont
+ * @function
+ * @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
+ * @param {String} text
+ * @param {Number} x
+ * @param {Number} y
+ */
+ draw : function (renderer, text, x, y) {
+ var strings = ("" + text).split("\n");
+ var lX = x;
+ var stringHeight = this.bitmapFontData.capHeight * this.lineHeight * this.fontScale.y;
+
+ // save the previous global alpha value
+ var _alpha = renderer.globalAlpha();
+ renderer.setGlobalAlpha(_alpha * this.getOpacity());
+
+ // update initial position
+ this.pos.set(x, y, this.pos.z); // TODO : z ?
+ for (var i = 0; i < strings.length; i++) {
+ x = lX;
+ var string = strings[i].trimRight();
+ // adjust x pos based on alignment value
+ var stringWidth = measureTextWidth(this, string);
+ switch (this.textAlign) {
+ case "right":
+ x -= stringWidth;
+ break;
+
+ case "center":
+ x -= stringWidth * 0.5;
+ break;
+
+ default :
+ break;
+ }
+
+ // adjust y pos based on alignment value
+ switch (this.textBaseline) {
+ case "middle":
+ y -= stringHeight * 0.5;
+ break;
+
+ case "ideographic":
+ case "alphabetic":
+ case "bottom":
+ y -= stringHeight;
+ break;
+
+ default :
+ break;
+ }
+
+ // x *= this.fontScale.x;
+ // y *= this.fontScale.y;
+
+ // draw the string
+ var lastGlyph = null;
+ for (var c = 0, len = string.length; c < len; c++) {
+ // calculate the char index
+ var ch = string.charCodeAt(c);
+ var glyph = this.bitmapFontData.glyphs[ch];
+ var kerning = (lastGlyph && lastGlyph.kerning) ? lastGlyph.getKerning(ch) : 0;
+
+ // draw it
+ if (glyph.width !== 0 && glyph.height !== 0) {
+ // some browser throw an exception when drawing a 0 width or height image
+ renderer.drawImage(this.fontImage,
+ glyph.src.x, glyph.src.y,
+ glyph.width, glyph.height,
+ x + glyph.offset.x,
+ y + glyph.offset.y * this.fontScale.y,
+ glyph.width * this.fontScale.x, glyph.height * this.fontScale.y
+ );
+ }
+
+ // increment position
+ x += (glyph.xadvance + kerning) * this.fontScale.x;
+ lastGlyph = glyph;
+ }
+ // increment line
+ y += stringHeight;
+ }
+ // restore the previous global alpha value
+ renderer.setGlobalAlpha(_alpha);
+ }
+ });
+})();
+
+/**
+ * Class for storing relevant data from the font file.
+ * @class me.BitmapFontData
+ * @memberOf me
+ * @param data {String} - The bitmap font data pulled from the resource loader using me.loader.getBinary()
+ * @constructor
+ */
+me.BitmapFontData = me.Object.extend({
+ /**
+ * @ignore
+ */
+ init: function (data) {
+ this.padTop = 0;
+ this.padRight = 0;
+ this.padBottom = 0;
+ this.padLeft = 0;
+ this.lineHeight = 0;
+ // The distance from the top of most uppercase characters to the baseline. Since the drawing position is the cap height of
+ // the first line, the cap height can be used to get the location of the baseline.
+ this.capHeight = 1;
+ // The distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative.
+ this.descent = 0;
+ this.scale = new me.Vector2d();
+
+ /**
+ * The map of glyphs, each key is a char code.
+ * @name glyphs
+ * @type {Object}
+ * @memberOf me.BitmapFontData
+ */
+ this.glyphs = {};
+
+
+ this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"];
+ this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S",
+ "T", "U", "V", "W", "X", "Y", "Z"];
+
+ // parse the data
+ this.parse(data);
+ },
+
+ /**
+ * Creates a glyph to use for the space character
+ * @private
+ * @name _createSpaceGlyph
+ * @memberOf me.BitmapFontData
+ * @function
+ */
+ _createSpaceGlyph: function () {
+ var spaceCharCode = " ".charCodeAt(0);
+ var glyph = this.glyphs[spaceCharCode];
+ if (!glyph) {
+ glyph = me.pool.pull("me.Glyph");
+ glyph.id = spaceCharCode;
+ glyph.xadvance = this._getFirstGlyph().xadvance;
+ this.glyphs[spaceCharCode] = glyph;
+ }
+ },
+
+ /**
+ * Gets the first glyph in the map that is not a space character
+ * @private
+ * @name _getFirstGlyph
+ * @memberOf me.BitmapFontData
+ * @function
+ * @returns {me.Glyph}
+ */
+ _getFirstGlyph: function () {
+ var keys = Object.keys(this.glyphs);
+ for (var i = 0; i < keys.length; i++) {
+ if (keys[i] > 32) {
+ return this.glyphs[keys[i]];
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Gets the value from a string of pairs. For example: one=1 two=2 something=hi. Can accept the regex of /one={d}/
+ * and returns the value of d
+ * @private
+ * @name _getValueFromPair
+ * @memberOf me.BitmapFontData
+ * @function
+ * @returns {String}
+ */
+ _getValueFromPair: function (string, pattern) {
+ var value = string.match(pattern);
+ if (!value) {
+ throw "Could not find pattern " + pattern + " in string: " + string;
+ }
+
+ return value[0].split("=")[1];
+ },
+
+ /**
+ * This parses the font data text and builds a map of glyphs containing the data for each character
+ * @name parse
+ * @memberOf me.BitmapFontData
+ * @function
+ * @param {String} fontData
+ */
+ parse: function (fontData) {
+ if (!fontData) {
+ throw "File containing font data was empty, cannot load the bitmap font.";
+ }
+ var lines = fontData.split(/\r\n|\n/);
+ var padding = fontData.match(/padding\=\d+,\d+,\d+,\d+/g);
+ if (!padding) {
+ throw "Padding not found in first line";
+ }
+ var paddingValues = padding[0].split("=")[1].split(",");
+ this.padTop = parseFloat(paddingValues[0]);
+ this.padLeft = parseFloat(paddingValues[1]);
+ this.padBottom = parseFloat(paddingValues[2]);
+ this.padRight = parseFloat(paddingValues[3]);
+
+ this.lineHeight = parseFloat(this._getValueFromPair(lines[1], /lineHeight\=\d+/g));
+
+ var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g));
+
+ var padY = this.padTop + this.padBottom;
+
+ var glyph = null;
+
+ for (var i = 4; i < lines.length; i++) {
+ var line = lines[i];
+ var characterValues = line.split(/=|\s+/);
+ if (!line || /^kernings/.test(line)) {
+ continue;
+ }
+ if (/^kerning\s/.test(line)) {
+ var first = parseFloat(characterValues[2]);
+ var second = parseFloat(characterValues[4]);
+ var amount = parseFloat(characterValues[6]);
+
+ glyph = this.glyphs[first];
+ if (glyph !== null && typeof glyph !== "undefined") {
+ glyph.setKerning(second, amount);
+ }
+ } else {
+ glyph = me.pool.pull("me.Glyph");
+
+ var ch = parseFloat(characterValues[2]);
+ glyph.id = ch;
+ glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6]));
+ glyph.width = parseFloat(characterValues[8]);
+ glyph.height = parseFloat(characterValues[10]);
+ var y = parseFloat(characterValues[14]);
+ glyph.offset.set(parseFloat(characterValues[12]), y);
+
+ glyph.xadvance = parseFloat(characterValues[16]);
+
+ if (glyph.width > 0 && glyph.height > 0) {
+ this.descent = Math.min(baseLine + glyph.yoffset, this.descent);
+ }
+
+ this.glyphs[ch] = glyph;
+ }
+ }
+
+ this.descent += this.padBottom;
+
+ this._createSpaceGlyph();
+
+ var xGlyph = null;
+ for (i = 0; i < this.xChars.length; i++) {
+ var xChar = this.xChars[i];
+ xGlyph = this.glyphs[xChar.charCodeAt(0)];
+ if (xGlyph) {
+ break;
+ }
+ }
+ if (!xGlyph) {
+ xGlyph = this._getFirstGlyph();
+ }
+
+ var capGlyph = null;
+ for (i = 0; i < this.capChars.length; i++) {
+ var capChar = this.capChars[i];
+ capGlyph = this.glyphs[capChar.charCodeAt(0)];
+ if (capGlyph) {
+ break;
+ }
+ }
+ if (!capGlyph) {
+ for (var charCode in this.glyphs) {
+ if (this.glyphs.hasOwnProperty(charCode)) {
+ glyph = this.glyphs[charCode];
+ if (glyph.height === 0 || glyph.width === 0) {
+ continue;
+ }
+ this.capHeight = Math.max(this.capHeight, glyph.height);
+ }
+ }
+ } else {
+ this.capHeight = capGlyph.height;
+ }
+
+ this.capHeight -= padY;
+ }
+});
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Glyph
+ */
+(function() {
+ var LOG2_PAGE_SIZE = 9;
+ var PAGE_SIZE = 1 << LOG2_PAGE_SIZE;
+ /**
+ * a glyph representing a single character in a font
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ */
+ me.Glyph = me.Object.extend({
+ /**
+ * @ignore
+ */
+ init: function () {
+ this.src = new me.Vector2d();
+ this.offset = new me.Vector2d();
+ this.onResetEvent();
+ },
+
+ /**
+ * @ignore
+ */
+ onResetEvent: function () {
+ this.id = 0;
+ this.src.set(0, 0);
+ this.width = 0;
+ this.height = 0;
+ this.u = 0;
+ this.v = 0;
+ this.u2 = 0;
+ this.v2 = 0;
+ this.offset.set(0, 0);
+ this.xadvance = 0;
+ this.fixedWidth = false;
+ },
+
+ /**
+ * @ignore
+ */
+ getKerning: function (ch) {
+ if (this.kerning) {
+ var page = this.kerning[ch >>> LOG2_PAGE_SIZE];
+ if (page) {
+ return page[ch & PAGE_SIZE - 1] || 0;
+ }
+ }
+ return 0;
+ },
+
+ /**
+ * @ignore
+ */
+ setKerning: function (ch, value) {
+ if (!this.kerning) {
+ this.kerning = {};
+ }
+ var page = this.kerning[ch >>> LOG2_PAGE_SIZE];
+ if (typeof page === "undefined") {
+ this.kerning[ch >>> LOG2_PAGE_SIZE] = {};
+ page = this.kerning[ch >>> LOG2_PAGE_SIZE];
+ }
+ page[ch & PAGE_SIZE - 1] = value;
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Audio Mngt Objects
+ *
+ *
+ */
+(function () {
+ /**
+ * There is no constructor function for me.audio.
+ * @namespace me.audio
+ * @memberOf me
+ */
+ me.audio = (function () {
+ /*
+ * PRIVATE STUFF
+ */
+
+ // hold public stuff in our singleton
+ var api = {};
+
+ // audio channel list
+ var audioTracks = {};
+
+ // current music
+ var current_track_id = null;
+
+ // a retry counter
+ var retry_counter = 0;
+
+ /**
+ * event listener callback on load error
+ * @ignore
+ */
+ function soundLoadError(sound_name, onerror_cb) {
+ // check the retry counter
+ if (retry_counter++ > 3) {
+ // something went wrong
+ var errmsg = "melonJS: failed loading " + sound_name;
+ if (me.sys.stopOnAudioError === false) {
+ // disable audio
+ me.audio.disable();
+ // call error callback if defined
+ if (onerror_cb) {
+ onerror_cb();
+ }
+ // warning
+ console.log(errmsg + ", disabling audio");
+ }
+ else {
+ // throw an exception and stop everything !
+ throw new me.audio.Error(errmsg);
+ }
+ // else try loading again !
+ }
+ else {
+ audioTracks[sound_name].load();
+ }
+ }
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * configure and initialize the audio engine
+ * melonJS will try to load audio files corresponding to the browser supported audio format(s)
+ * below is the list of supported file extentions :
+ * "mp3", "mpeg", opus", "ogg", "oga", "wav", "aac", "caf", "m4a", "mp4", "weba", "webm", "dolby", "flac"
+ * keep in mind that not all browsers can play all audio formats, and if no compatible codecs are detected, audio will be disabled.
+ * @name init
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String}
+ * [audioFormat="mp3"] audio format provided
+ * @return {Boolean} Indicates whether audio initialization was successful
+ * @example
+ * // initialize the "sound engine", giving "mp3" and "ogg" as desired audio format
+ * // i.e. on Safari, the loader will load all audio.mp3 files,
+ * // on Opera the loader will however load audio.ogg files
+ * if (!me.audio.init("mp3,ogg")) {
+ * alert("Sorry but your browser does not support html 5 audio !");
+ * return;
+ * }
+ */
+ api.init = function (audioFormat) {
+ if (!me.initialized) {
+ throw new me.audio.Error("me.audio.init() called before engine initialization.");
+ }
+ // if no param is given to init we use mp3 by default
+ audioFormat = typeof audioFormat === "string" ? audioFormat : "mp3";
+ // convert it into an array
+ this.audioFormats = audioFormat.split(",");
+
+ return !Howler.noAudio;
+ };
+
+ /**
+ * enable audio output
+ * only useful if audio supported and previously disabled through
+ *
+ * @see me.audio#disable
+ * @name enable
+ * @memberOf me.audio
+ * @public
+ * @function
+ */
+ api.enable = function () {
+ this.unmuteAll();
+ };
+
+ /**
+ * disable audio output
+ *
+ * @name disable
+ * @memberOf me.audio
+ * @public
+ * @function
+ */
+ api.disable = function () {
+ this.muteAll();
+ };
+
+ /**
+ * Load an audio file.
+ *
+ * sound item must contain the following fields :
+ * - name : name of the sound
+ * - src : source path
+ * @ignore
+ */
+ api.load = function (sound, html5, onload_cb, onerror_cb) {
+ var urls = [];
+ if (typeof(this.audioFormats) === "undefined" || this.audioFormats.length === 0) {
+ throw new me.audio.Error("target audio extension(s) should be set through me.audio.init() before calling the preloader.");
+ }
+ for (var i = 0; i < this.audioFormats.length; i++) {
+ urls.push(sound.src + sound.name + "." + this.audioFormats[i] + me.loader.nocache);
+ }
+ audioTracks[sound.name] = new Howl({
+ src : urls,
+ volume : Howler.volume(),
+ html5 : html5 === true,
+ /**
+ * @ignore
+ */
+ onloaderror : function () {
+ soundLoadError.call(me.audio, sound.name, onerror_cb);
+ },
+ /**
+ * @ignore
+ */
+ onload : function () {
+ retry_counter = 0;
+ if (onload_cb) {
+ onload_cb();
+ }
+ }
+ });
+
+ return 1;
+ };
+
+ /**
+ * play the specified sound
+ * @name play
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Boolean} [loop=false] loop audio
+ * @param {Function} [onend] Function to call when sound instance ends playing.
+ * @param {Number} [volume=default] Float specifying volume (0.0 - 1.0 values accepted).
+ * @return {Number} the sound instance ID.
+ * @example
+ * // play the "cling" audio clip
+ * me.audio.play("cling");
+ * // play & repeat the "engine" audio clip
+ * me.audio.play("engine", true);
+ * // play the "gameover_sfx" audio clip and call myFunc when finished
+ * me.audio.play("gameover_sfx", false, myFunc);
+ * // play the "gameover_sfx" audio clip with a lower volume level
+ * me.audio.play("gameover_sfx", false, null, 0.5);
+ */
+ api.play = function (sound_name, loop, onend, volume) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ var id = sound.play();
+ if (typeof loop === "boolean") {
+ // arg[0] can take different types in howler 2.0
+ sound.loop(loop, id);
+ }
+ sound.volume(typeof(volume) === "number" ? volume.clamp(0.0, 1.0) : Howler.volume(), id);
+ if (typeof(onend) === "function") {
+ if (loop === true) {
+ sound.on("end", onend, id);
+ }
+ else {
+ sound.once("end", onend, id);
+ }
+ }
+ return id;
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * Fade a currently playing sound between two volumee.
+ * @name fade
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} from Volume to fade from (0.0 to 1.0).
+ * @param {Number} to Volume to fade to (0.0 to 1.0).
+ * @param {Number} duration Time in milliseconds to fade.
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will fade.
+ */
+ api.fade = function (sound_name, from, to, duration, id) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ sound.fade(from, to, duration, id);
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * get/set the position of playback for a sound.
+ * @name seek
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} [seek] The position to move current playback to (in seconds).
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will changed.
+ * @return return the current seek position (if no extra parameters were given)
+ * @example
+ * // return the current position of the background music
+ * var current_pos = me.audio.seek("dst-gameforest");
+ * // set back the position of the background music to the beginning
+ * me.audio.seek("dst-gameforest", 0);
+ */
+ api.seek = function (sound_name, seek, id) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ return sound.seek.apply(sound, Array.prototype.slice.call(arguments, 1));
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * get or set the rate of playback for a sound.
+ * @name rate
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} [rate] playback rate : 0.5 to 4.0, with 1.0 being normal speed.
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will be changed.
+ * @return return the current playback rate (if no extra parameters were given)
+ * @example
+ * // get the playback rate of the background music
+ * var rate = me.audio.rate("dst-gameforest");
+ * // speed up the playback of the background music
+ * me.audio.rate("dst-gameforest", 2.0);
+ */
+ api.rate = function (sound_name, rate, id) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ return sound.rate.apply(sound, Array.prototype.slice.call(arguments, 1));
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * stop the specified sound on all channels
+ * @name stop
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will stop.
+ * @example
+ * me.audio.stop("cling");
+ */
+ api.stop = function (sound_name, id) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ sound.stop(id);
+ // remove the defined onend callback (if any defined)
+ sound.off("end", undefined, id);
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * pause the specified sound on all channels
+ * this function does not reset the currentTime property
+ * @name pause
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will pause.
+ * @example
+ * me.audio.pause("cling");
+ */
+ api.pause = function (sound_name, id) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ sound.pause(id);
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * resume the specified sound on all channels
+ * @name resume
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will resume.
+ * @example
+ * // play a audio clip
+ * var id = me.audio.play("myClip");
+ * ...
+ * // pause it
+ * me.audio.pause("myClip", id);
+ * ...
+ * // resume
+ * me.audio.resume("myClip", id);
+ */
+ api.resume = function (sound_name, id) {
+ var sound = audioTracks[sound_name];
+ if (sound && typeof sound !== "undefined") {
+ sound.play(id);
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * play the specified audio track
+ * this function automatically set the loop property to true
+ * and keep track of the current sound being played.
+ * @name playTrack
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio track name - case sensitive
+ * @param {Number} [volume=default] Float specifying volume (0.0 - 1.0 values accepted).
+ * @return {Number} the sound instance ID.
+ * @example
+ * me.audio.playTrack("awesome_music");
+ */
+ api.playTrack = function (sound_name, volume) {
+ current_track_id = sound_name;
+ return me.audio.play(
+ current_track_id,
+ true,
+ null,
+ volume
+ );
+ };
+
+ /**
+ * stop the current audio track
+ *
+ * @see me.audio#playTrack
+ * @name stopTrack
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @example
+ * // play a awesome music
+ * me.audio.playTrack("awesome_music");
+ * // stop the current music
+ * me.audio.stopTrack();
+ */
+ api.stopTrack = function () {
+ if (current_track_id !== null) {
+ audioTracks[current_track_id].stop();
+ current_track_id = null;
+ }
+ };
+
+ /**
+ * pause the current audio track
+ *
+ * @name pauseTrack
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @example
+ * me.audio.pauseTrack();
+ */
+ api.pauseTrack = function () {
+ if (current_track_id !== null) {
+ audioTracks[current_track_id].pause();
+ }
+ };
+
+ /**
+ * resume the previously paused audio track
+ *
+ * @name resumeTrack
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @example
+ * // play an awesome music
+ * me.audio.playTrack("awesome_music");
+ * // pause the audio track
+ * me.audio.pauseTrack();
+ * // resume the music
+ * me.audio.resumeTrack();
+ */
+ api.resumeTrack = function () {
+ if (current_track_id !== null) {
+ audioTracks[current_track_id].play();
+ }
+ };
+
+ /**
+ * returns the current track Id
+ * @name getCurrentTrack
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @return {String} audio track name
+ */
+ api.getCurrentTrack = function () {
+ return current_track_id;
+ };
+
+ /**
+ * set the default global volume
+ * @name setVolume
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {Number} volume Float specifying volume (0.0 - 1.0 values accepted).
+ */
+ api.setVolume = function (volume) {
+ Howler.volume(volume);
+ };
+
+ /**
+ * get the default global volume
+ * @name getVolume
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @returns {Number} current volume value in Float [0.0 - 1.0] .
+ */
+ api.getVolume = function () {
+ return Howler.volume();
+ };
+
+ /**
+ * mute or unmute the specified sound, but does not pause the playback.
+ * @name mute
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name - case sensitive
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will mute.
+ * @param {Boolean} [muted=true] True to mute and false to unmute
+ * @example
+ * // mute the background music
+ * me.audio.mute("awesome_music");
+ */
+ api.mute = function (sound_name, id, mute) {
+ // if not defined : true
+ mute = (typeof(mute) === "undefined" ? true : !!mute);
+ var sound = audioTracks[sound_name];
+ if (sound && typeof(sound) !== "undefined") {
+ sound.mute(mute, id);
+ } else {
+ throw new me.audio.Error("audio clip " + sound_name + " does not exist");
+ }
+ };
+
+ /**
+ * unmute the specified sound
+ * @name unmute
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio clip name
+ * @param {Number} [id] the sound instance ID. If none is passed, all sounds in group will unmute.
+ */
+ api.unmute = function (sound_name, id) {
+ api.mute(sound_name, id, false);
+ };
+
+ /**
+ * mute all audio
+ * @name muteAll
+ * @memberOf me.audio
+ * @public
+ * @function
+ */
+ api.muteAll = function () {
+ Howler.mute(true);
+ };
+
+ /**
+ * unmute all audio
+ * @name unmuteAll
+ * @memberOf me.audio
+ * @public
+ * @function
+ */
+ api.unmuteAll = function () {
+ Howler.mute(false);
+ };
+
+ /**
+ * unload specified audio track to free memory
+ *
+ * @name unload
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @param {String} sound_name audio track name - case sensitive
+ * @return {Boolean} true if unloaded
+ * @example
+ * me.audio.unload("awesome_music");
+ */
+ api.unload = function (sound_name) {
+ if (!(sound_name in audioTracks)) {
+ return false;
+ }
+
+ // destroy the Howl object
+ audioTracks[sound_name].unload();
+ if (typeof(audioTracks[sound_name].dispose) === "function") {
+ // cocoonJS implements a dispose function to free
+ // corresponding allocated audio in memory
+ audioTracks[sound_name].dispose();
+ }
+ delete audioTracks[sound_name];
+ return true;
+ };
+
+ /**
+ * unload all audio to free memory
+ *
+ * @name unloadAll
+ * @memberOf me.audio
+ * @public
+ * @function
+ * @example
+ * me.audio.unloadAll();
+ */
+ api.unloadAll = function () {
+ for (var sound_name in audioTracks) {
+ if (audioTracks.hasOwnProperty(sound_name)) {
+ api.unload(sound_name);
+ }
+ }
+ };
+
+ // return our object
+ return api;
+ })();
+
+ /**
+ * Base class for audio exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.audio
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.audio.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.audio.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * video functions
+ * There is no constructor function for me.video
+ * @namespace me.video
+ * @memberOf me
+ */
+ me.video = (function () {
+ // hold public stuff in our apig
+ var api = {};
+
+ // internal variables
+ var canvas = null;
+
+ var deferResizeId = 0;
+
+ var designRatio = 1;
+ var designWidth = 0;
+ var designHeight = 0;
+
+ // max display size
+ var maxWidth = Infinity;
+ var maxHeight = Infinity;
+
+ // default video settings
+ var settings = {
+ wrapper : undefined,
+ renderer : 0, // canvas
+ doubleBuffering : false,
+ autoScale : false,
+ scale : 1.0,
+ scaleMethod : "fit",
+ transparent : false,
+ antiAlias : false,
+ subPixel : false,
+ verbose : false
+ };
+
+
+ /**
+ * Auto-detect the best renderer to use
+ * @ignore
+ */
+ function autoDetectRenderer(c, width, height, options) {
+ try {
+ return new me.WebGLRenderer(c, width, height, options);
+ }
+ catch (e) {
+ return new me.CanvasRenderer(c, width, height, options);
+ }
+ }
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * Base class for Video exception handling.
+ * @name Error
+ * @class
+ * @constructor
+ * @memberOf me.video
+ * @param {String} msg Error message.
+ */
+ api.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.video.Error";
+ }
+ });
+
+ /**
+ * Select the HTML5 Canvas renderer
+ * @public
+ * @name CANVAS
+ * @memberOf me.video
+ * @enum {Number}
+ */
+ api.CANVAS = 0;
+
+ /**
+ * Select the WebGL renderer
+ * @public
+ * @name WEBGL
+ * @memberOf me.video
+ * @enum {Number}
+ */
+ api.WEBGL = 1;
+
+ /**
+ * Auto-select the renderer (Attempt WebGL first, with fallback to Canvas)
+ * @public
+ * @name AUTO
+ * @memberOf me.video
+ * @enum {Number}
+ */
+ api.AUTO = 2;
+
+ /**
+ * Initialize the "video" system (create a canvas based on the given arguments, and the related renderer).
+ * melonJS support various scaling mode :
+ * - `fit` : Letterboxed; content is scaled to design aspect ratio
+ * - `fill-max` : Canvas is resized to fit maximum design resolution; content is scaled to design aspect ratio
+ * - `flex-height` : Canvas height is resized to fit; content is scaled to design aspect ratio
+ * - `flex-width` : Canvas width is resized to fit; content is scaled to design aspect ratio
+ * - `stretch` : Canvas is resized to fit; content is scaled to screen aspect ratio
+ * @name init
+ * @memberOf me.video
+ * @function
+ * @param {Number} width the width of the canvas viewport
+ * @param {Number} height the height of the canvas viewport
+ * @param {Object} [options] The optional video/renderer parameters
+ * @param {String} [options.wrapper=document.body] the "div" element name to hold the canvas in the HTML file
+ * @param {Number} [options.renderer=me.video.CANVAS] renderer to use.
+ * @param {Boolean} [options.doubleBuffering=false] enable/disable double buffering
+ * @param {Number|String} [options.scale=1.0] enable scaling of the canvas ('auto' for automatic scaling)
+ * @param {String} [options.scaleMethod="fit"] ('fit','fill-min','fill-max','flex','flex-width','flex-height','stretch') screen scaling modes
+ * @param {Boolean} [options.useParentDOMSize=false] on browser devices, limit the canvas width and height to its parent container dimensions as returned by getBoundingClientRect(),
+ * as opposed to the browser window dimensions
+ * @param {Boolean} [options.transparent=false] whether to allow transparent pixels in the front buffer (screen)
+ * @param {Boolean} [options.antiAlias=false] whether to enable or not video scaling interpolation
+ * @return {Boolean} false if initialization failed (canvas not supported)
+ * @example
+ * // init the video with a 640x480 canvas
+ * me.video.init(640, 480, {
+ * wrapper : "screen",
+ * renderer : me.video.CANVAS,
+ * scale : "auto",
+ * scaleMethod : "fit",
+ * doubleBuffering : true
+ * });
+ */
+ api.init = function (game_width, game_height, options) {
+ // ensure melonjs has been properly initialized
+ if (!me.initialized) {
+ throw new api.Error("me.video.init() called before engine initialization.");
+ }
+
+ // revert to default options if not defined
+ settings = Object.assign(settings, options || {});
+
+ // sanitize potential given parameters
+ settings.doubleBuffering = !!(settings.doubleBuffering);
+ settings.useParentDOMSize = !!(settings.useParentDOMSize);
+ settings.autoScale = (settings.scale === "auto") || false;
+ if (settings.scaleMethod.search(/^(fill-(min|max)|fit|flex(-(width|height))?|stretch)$/) !== 0) {
+ settings.scaleMethod = "fit";
+ }
+ settings.transparent = !!(settings.transparent);
+
+ // override renderer settings if &webgl is defined in the URL
+ if (me.game.HASH.webgl === true) {
+ settings.renderer = api.WEBGL;
+ }
+
+ // normalize scale
+ settings.scale = (settings.autoScale) ? 1.0 : (+settings.scale || 1.0);
+ me.sys.scale = new me.Vector2d(settings.scale, settings.scale);
+
+ // force double buffering if scaling is required
+ if (settings.autoScale || (settings.scale !== 1.0)) {
+ settings.doubleBuffering = true;
+ }
+
+ // hold the requested video size ratio
+ designRatio = game_width / game_height;
+ designWidth = game_width;
+ designHeight = game_height;
+
+ // default scaled size value
+ var game_width_zoom = game_width * me.sys.scale.x;
+ var game_height_zoom = game_height * me.sys.scale.y;
+ settings.zoomX = game_width_zoom;
+ settings.zoomY = game_height_zoom;
+
+ //add a channel for the onresize/onorientationchange event
+ window.addEventListener(
+ "resize",
+ throttle(
+ 100,
+ false,
+ function (event) {
+ me.event.publish(me.event.WINDOW_ONRESIZE, [ event ]);
+ }
+ ),
+ false
+ );
+ window.addEventListener(
+ "orientationchange",
+ function (event) {
+ me.event.publish(me.event.WINDOW_ONORIENTATION_CHANGE, [ event ]);
+ },
+ false
+ );
+
+ // register to the channel
+ me.event.subscribe(
+ me.event.WINDOW_ONRESIZE,
+ me.video.onresize.bind(me.video)
+ );
+ me.event.subscribe(
+ me.event.WINDOW_ONORIENTATION_CHANGE,
+ me.video.onresize.bind(me.video)
+ );
+
+ // create the main screen canvas
+ if (me.device.ejecta === true) {
+ // a main canvas is already automatically created by Ejecta
+ canvas = document.getElementById("canvas");
+ } else {
+ canvas = api.createCanvas(game_width_zoom, game_height_zoom, true);
+ }
+
+ // add our canvas
+ if (options.wrapper) {
+ settings.wrapper = document.getElementById(options.wrapper);
+ }
+ // if wrapperid is not defined (null)
+ if (!settings.wrapper) {
+ // add the canvas to document.body
+ settings.wrapper = document.body;
+ }
+ settings.wrapper.appendChild(canvas);
+
+ // stop here if not supported
+ if (!canvas.getContext) {
+ return false;
+ }
+
+ /**
+ * A reference to the current video renderer
+ * @public
+ * @memberOf me.video
+ * @name renderer
+ * @type {me.Renderer|me.CanvasRenderer|me.WebGLRenderer}
+ */
+ switch (settings.renderer) {
+ case api.WEBGL:
+ this.renderer = new me.WebGLRenderer(canvas, game_width, game_height, settings);
+ break;
+ case api.AUTO:
+ this.renderer = autoDetectRenderer(canvas, game_width, game_height, settings);
+ break;
+ default:
+ this.renderer = new me.CanvasRenderer(canvas, game_width, game_height, settings);
+ break;
+ }
+
+ // adjust CSS style for High-DPI devices
+ var ratio = me.device.getPixelRatio();
+ if (ratio > 1) {
+ canvas.style.width = (canvas.width / ratio) + "px";
+ canvas.style.height = (canvas.height / ratio) + "px";
+ }
+
+
+ // set max the canvas max size if CSS values are defined
+ if (window.getComputedStyle) {
+ var style = window.getComputedStyle(canvas, null);
+ me.video.setMaxSize(parseInt(style.maxWidth, 10), parseInt(style.maxHeight, 10));
+ }
+
+ me.game.init();
+
+ // trigger an initial resize();
+ me.video.onresize();
+
+ return true;
+ };
+
+ /**
+ * return the relative (to the page) position of the specified Canvas
+ * @name getPos
+ * @memberOf me.video
+ * @function
+ * @param {Canvas} [canvas] system one if none specified
+ * @return {me.Vector2d}
+ */
+ api.getPos = function (c) {
+ c = c || this.renderer.getScreenCanvas();
+ return (
+ c.getBoundingClientRect ?
+ c.getBoundingClientRect() : { left : 0, top : 0 }
+ );
+ };
+
+ /**
+ * set the max canvas display size (when scaling)
+ * @name setMaxSize
+ * @memberOf me.video
+ * @function
+ * @param {Number} width width
+ * @param {Number} height height
+ */
+ api.setMaxSize = function (w, h) {
+ // max display size
+ maxWidth = w || Infinity;
+ maxHeight = h || Infinity;
+ // trigger a resize
+ // defer it to ensure everything is properly intialized
+ this.onresize.defer(this);
+
+ };
+
+ /**
+ * Create and return a new Canvas
+ * @name createCanvas
+ * @memberOf me.video
+ * @function
+ * @param {Number} width width
+ * @param {Number} height height
+ * @param {Boolean} [screencanvas=false] set to true if this canvas renders directly to the screen
+ * @return {Canvas}
+ */
+ api.createCanvas = function (width, height, screencanvas) {
+ if (width === 0 || height === 0) {
+ throw new api.Error("width or height was zero, Canvas could not be initialized !");
+ }
+
+ var _canvas = document.createElement("canvas");
+
+ if ((screencanvas === true) && (me.device.cocoon) && (me.device.android2 !== true)) {
+ // http://docs.cocoon.io/article/screencanvas/
+ _canvas.screencanvas = true;
+ }
+
+ _canvas.width = width || canvas.width;
+ _canvas.height = height || canvas.height;
+
+ return _canvas;
+ };
+
+ /**
+ * return a reference to the wrapper
+ * @name getWrapper
+ * @memberOf me.video
+ * @function
+ * @return {Document}
+ */
+ api.getWrapper = function () {
+ return settings.wrapper;
+ };
+
+ /**
+ * callback for window resize event
+ * @ignore
+ */
+ api.onresize = function () {
+ // default (no scaling)
+ var scaleX = 1, scaleY = 1;
+
+ // check for orientation information
+ if (typeof window.orientation !== "undefined") {
+ me.device.orientation = window.orientation;
+ }
+ else {
+ // is this actually not the best option since default "portrait"
+ // orientation might vary between for example an ipad and and android tab
+ me.device.orientation = (
+ window.outerWidth > window.outerHeight ?
+ 90 : 0
+ );
+ }
+
+ if (settings.autoScale) {
+ var parentNodeWidth;
+ var parentNodeHeight;
+ var parentNode = me.video.renderer.getScreenCanvas().parentNode;
+ if (typeof (parentNode) !== "undefined") {
+ if (settings.useParentDOMSize && typeof parentNode.getBoundingClientRect === "function") {
+ var rect = parentNode.getBoundingClientRect();
+ parentNodeWidth = rect.width || (rect.right - rect.left);
+ parentNodeHeight = rect.height || (rect.bottom - rect.top);
+ } else {
+ // for cased where DOM is not implemented and so parentNode (e.g. Ejecta)
+ parentNodeWidth = parentNode.width;
+ parentNodeHeight = parentNode.height;
+ }
+ }
+ var _max_width = Math.min(maxWidth, parentNodeWidth || window.innerWidth);
+ var _max_height = Math.min(maxHeight, parentNodeHeight || window.innerHeight);
+ var screenRatio = _max_width / _max_height;
+ var sWidth = Infinity;
+ var sHeight = Infinity;
+
+ if (
+ (settings.scaleMethod === "fill-min" && screenRatio > designRatio) ||
+ (settings.scaleMethod === "fill-max" && screenRatio < designRatio) ||
+ (settings.scaleMethod === "flex-width")
+ ) {
+ // resize the display canvas to fill the parent container
+ sWidth = Math.min(maxWidth, designHeight * screenRatio);
+ scaleX = scaleY = _max_width / sWidth;
+ sWidth = ~~(sWidth + 0.5);
+ this.renderer.resize(sWidth, designHeight);
+ me.game.viewport.resize(sWidth, designHeight);
+ /*
+ * XXX: Workaround for not updating container child-bounds
+ * automatically (it's expensive!)
+ */
+ me.game.world.updateChildBounds();
+ }
+ else if (
+ (settings.scaleMethod === "fill-min" && screenRatio < designRatio) ||
+ (settings.scaleMethod === "fill-max" && screenRatio > designRatio) ||
+ (settings.scaleMethod === "flex-height")
+ ) {
+ // resize the display canvas to fill the parent container
+ sHeight = Math.min(maxHeight, designWidth * (_max_height / _max_width));
+ scaleX = scaleY = _max_height / sHeight;
+ sHeight = ~~(sHeight + 0.5);
+ this.renderer.resize(designWidth, sHeight);
+ me.game.viewport.resize(designWidth, sHeight);
+ /*
+ * XXX: Workaround for not updating container child-bounds
+ * automatically (it's expensive!)
+ */
+ me.game.world.updateChildBounds();
+ }
+ else if (settings.scaleMethod === "flex") {
+ // resize the display canvas to fill the parent container
+ this.renderer.resize(_max_width, _max_height);
+ me.game.viewport.resize(_max_width, _max_height);
+ /*
+ * XXX: Workaround for not updating container child-bounds
+ * automatically (it's expensive!)
+ */
+ me.game.world.updateChildBounds();
+ }
+ else if (settings.scaleMethod === "stretch") {
+ // scale the display canvas to fit with the parent container
+ scaleX = _max_width / designWidth;
+ scaleY = _max_height / designHeight;
+ }
+ else {
+ // scale the display canvas to fit the parent container
+ // make sure we maintain the original aspect ratio
+ if (screenRatio < designRatio) {
+ scaleX = scaleY = _max_width / designWidth;
+ }
+ else {
+ scaleX = scaleY = _max_height / designHeight;
+ }
+ }
+
+ // adjust scaling ratio based on the device pixel ratio
+ scaleX *= me.device.getPixelRatio();
+ scaleY *= me.device.getPixelRatio();
+
+ if (deferResizeId) {
+ // cancel any previous pending resize
+ clearTimeout(deferResizeId);
+ }
+ deferResizeId = me.video.updateDisplaySize.defer(this, scaleX, scaleY);
+ }
+ };
+
+ /**
+ * Modify the "displayed" canvas size
+ * @name updateDisplaySize
+ * @memberOf me.video
+ * @function
+ * @param {Number} scaleX X scaling multiplier
+ * @param {Number} scaleY Y scaling multiplier
+ */
+ api.updateDisplaySize = function (scaleX, scaleY) {
+ // update the global scale variable
+ me.sys.scale.set(scaleX, scaleY);
+
+ // renderer resize logic
+ this.renderer.scaleCanvas(scaleX, scaleY);
+ me.game.repaint();
+
+ // make sure we have the correct relative canvas position cached
+ me.input._offset = me.video.getPos();
+
+ // clear the timeout id
+ deferResizeId = 0;
+ };
+
+ // return our api
+ return api;
+ })();
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+
+(function () {
+
+ /**
+ * a base renderer object
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Canvas} canvas The html canvas tag to draw to on screen.
+ * @param {Number} width The width of the canvas without scaling
+ * @param {Number} height The height of the canvas without scaling
+ * @param {Object} [options] The renderer parameters
+ * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering
+ * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing
+ * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled)
+ * @param {Boolean} [options.subPixel=false] Whether to enable subpixel rendering (performance hit when enabled)
+ * @param {Boolean} [options.verbose=false] Enable the verbose mode that provides additional details as to what the renderer is doing
+ * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied
+ * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied
+ */
+ me.Renderer = me.Object.extend(
+ /** @scope me.Renderer.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init : function (c, width, height, options) {
+ options = options || {};
+
+ // rendering options
+ this.transparent = !!(options.transparent);
+ this.doubleBuffering = !!(options.doubleBuffering);
+ this.antiAlias = !!(options.antiAlias);
+ this.subPixel = !!(options.subPixel);
+ this.verbose = !!(options.verbose);
+
+ this.gameWidthZoom = options.zoomX || width;
+ this.gameHeightZoom = options.zoomY || height;
+
+ // canvas object and context
+ this.canvas = this.backBufferCanvas = c;
+ this.context = null;
+
+ // global color
+ this.currentColor = new me.Color(255, 255, 255, 1.0);
+
+ // default uvOffset
+ this.uvOffset = 0;
+
+ return this;
+ },
+
+ /**
+ * @ignore
+ */
+ applyRGBFilter : function (object, effect, option) {
+ //create a output canvas using the given canvas or image size
+ var _context = this.getContext2d(me.video.createCanvas(object.width, object.height, false));
+ // get the pixels array of the give parameter
+ var imgpix = me.utils.getPixels(object);
+ // pointer to the pixels data
+ var pix = imgpix.data;
+
+ // apply selected effect
+ var i, n;
+ switch (effect) {
+ case "b&w":
+ for (i = 0, n = pix.length; i < n; i += 4) {
+ var grayscale = (3 * pix[i] + 4 * pix[i + 1] + pix[i + 2]) >>> 3;
+ pix[i] = grayscale; // red
+ pix[i + 1] = grayscale; // green
+ pix[i + 2] = grayscale; // blue
+ }
+ break;
+
+ case "brightness":
+ // make sure it's between 0.0 and 1.0
+ var brightness = Math.abs(option).clamp(0.0, 1.0);
+ for (i = 0, n = pix.length; i < n; i += 4) {
+
+ pix[i] *= brightness; // red
+ pix[i + 1] *= brightness; // green
+ pix[i + 2] *= brightness; // blue
+ }
+ break;
+
+ case "transparent":
+ var refColor = me.pool.pull("me.Color").parseCSS(option);
+ var pixel = me.pool.pull("me.Color");
+ for (i = 0, n = pix.length; i < n; i += 4) {
+ pixel.setColor(pix[i], pix[i + 1], pix[i + 2]);
+ if (pixel.equals(refColor)) {
+ pix[i + 3] = 0;
+ }
+ }
+ me.pool.push(refColor);
+ me.pool.push(pixel);
+
+ break;
+
+
+ default:
+ return null;
+ }
+
+ // put our modified image back in the new filtered canvas
+ _context.putImageData(imgpix, 0, 0);
+
+ // return it
+ return _context;
+ },
+
+ /**
+ * @ignore
+ */
+ clear : function () {},
+
+ /**
+ * @ignore
+ */
+ reset : function () {
+ this.resetTransform();
+ this.cache.reset();
+ },
+
+ /**
+ * return a reference to the system canvas
+ * @name getCanvas
+ * @memberOf me.Renderer
+ * @function
+ * @return {Canvas}
+ */
+ getCanvas : function () {
+ return this.backBufferCanvas;
+ },
+
+ /**
+ * return a reference to the screen canvas
+ * @name getScreenCanvas
+ * @memberOf me.Renderer
+ * @function
+ * @return {Canvas}
+ */
+ getScreenCanvas : function () {
+ return this.canvas;
+ },
+
+ /**
+ * return a reference to the screen canvas corresponding 2d Context
+ * (will return buffered context if double buffering is enabled, or a reference to the Screen Context)
+ * @name getScreenContext
+ * @memberOf me.Renderer
+ * @function
+ * @return {Context2d}
+ */
+ getScreenContext : function () {
+ return this.context;
+ },
+
+ /**
+ * Returns the 2D Context object of the given Canvas
+ * Also configures anti-aliasing based on constructor options.
+ * @name getContext2d
+ * @memberOf me.Renderer
+ * @function
+ * @param {Canvas} canvas
+ * @param {Boolean} [opaque=false] True to disable transparency
+ * @return {Context2d}
+ */
+ getContext2d : function (c, opaque) {
+ if (typeof c === "undefined" || c === null) {
+ throw new me.video.Error(
+ "You must pass a canvas element in order to create " +
+ "a 2d context"
+ );
+ }
+
+ if (typeof c.getContext === "undefined") {
+ throw new me.video.Error(
+ "Your browser does not support HTML5 canvas."
+ );
+ }
+
+ var _context;
+ if (me.device.cocoon) {
+ // cocoonJS specific extension
+ _context = c.getContext("2d", {
+ "antialias" : this.antiAlias,
+ "alpha" : !opaque
+ });
+ }
+ else {
+ _context = c.getContext("2d", {
+ "alpha" : !opaque
+ });
+ }
+ if (!_context.canvas) {
+ _context.canvas = c;
+ }
+ this.setAntiAlias(_context, this.antiAlias);
+ return _context;
+ },
+
+ /**
+ * return the width of the system Canvas
+ * @name getWidth
+ * @memberOf me.Renderer
+ * @function
+ * @return {Number}
+ */
+ getWidth : function () {
+ return this.backBufferCanvas.width;
+ },
+
+ /**
+ * return the height of the system Canvas
+ * @name getHeight
+ * @memberOf me.Renderer
+ * @function
+ * @return {Number}
+ */
+ getHeight : function () {
+ return this.backBufferCanvas.height;
+ },
+
+ /**
+ * get the current fill & stroke style color.
+ * @name getColor
+ * @memberOf me.Renderer
+ * @function
+ * @param {me.Color} current global color
+ */
+ getColor : function () {
+ return this.currentColor;
+ },
+
+ /**
+ * return the current global alpha
+ * @name globalAlpha
+ * @memberOf me.Renderer
+ * @function
+ * @return {Number}
+ */
+ globalAlpha : function () {
+ return this.currentColor.glArray[3];
+ },
+
+ /**
+ * resizes the system canvas
+ * @name resize
+ * @memberOf me.Renderer
+ * @function
+ * @param {Number} width new width of the canvas
+ * @param {Number} height new height of the canvas
+ */
+ resize : function (width, height) {
+ this.backBufferCanvas.width = width;
+ this.backBufferCanvas.height = height;
+ },
+
+ /**
+ * enable/disable image smoothing (scaling interpolation) for the specified 2d Context
+ * (!) this might not be supported by all browsers
+ * @name setAntiAlias
+ * @memberOf me.Renderer
+ * @function
+ * @param {Context2d} context
+ * @param {Boolean} [enable=false]
+ */
+ setAntiAlias : function (context, enable) {
+ if (typeof(context) !== "undefined") {
+ // enable/disable antialis on the given context
+ me.agent.setPrefixed("imageSmoothingEnabled", enable === true, context);
+ }
+
+ // disable antialias CSS scaling on the main canvas
+ var cssStyle = context.canvas.style["image-rendering"];
+ if (enable === false && (cssStyle === "" || cssStyle === "auto")) {
+ // if a specific value is set through CSS or equal to the standard "auto" one
+ context.canvas.style["image-rendering"] = "pixelated";
+ } else if (enable === true && cssStyle === "pixelated") {
+ // if set to the standard "pixelated"
+ context.canvas.style["image-rendering"] = "auto";
+ }
+ },
+
+ /**
+ * @ignore
+ */
+ drawFont : function (/*bounds*/) {}
+
+ });
+
+})();
+
+/*
+* MelonJS Game Engine
+* Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+* http://www.melonjs.org
+*
+*/
+(function () {
+
+ /**
+ * a basic texture cache object
+ * @ignore
+ */
+ me.Renderer.TextureCache = me.Object.extend({
+ /**
+ * @ignore
+ */
+ init : function (max_size) {
+ this.max_size = max_size || Infinity;
+ this.reset();
+ },
+
+ /**
+ * @ignore
+ */
+ reset : function () {
+ this.cache = new Map();
+ this.units = new Map();
+ this.length = 0;
+ },
+
+ /**
+ * @ignore
+ */
+ validate : function () {
+ if (this.length >= this.max_size) {
+ // TODO: Merge textures instead of throwing an exception
+ throw new me.video.Error(
+ "Texture cache overflow: " + this.max_size +
+ " texture units available."
+ );
+ }
+ },
+
+ /**
+ * @ignore
+ */
+ get : function (image, atlas) {
+ if (!this.cache.has(image)) {
+ if (!atlas) {
+ atlas = me.video.renderer.Texture.prototype.createAtlas.apply(
+ me.video.renderer.Texture.prototype,
+ [image.width, image.height, image.src ? me.utils.getBasename(image.src) : undefined]
+ );
+ }
+ this.put(image, new me.video.renderer.Texture(atlas, image, false));
+ }
+ return this.cache.get(image);
+ },
+
+ /**
+ * @ignore
+ */
+ put : function (image, texture) {
+ this.validate();
+ this.cache.set(image, texture);
+ this.units.set(texture, this.length++);
+ },
+
+ /**
+ * @ignore
+ */
+ getUnit : function (texture) {
+ return this.units.get(texture);
+ }
+ });
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016 Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+
+(function () {
+
+ /**
+ * a canvas renderer object
+ * @class
+ * @extends me.Renderer
+ * @memberOf me
+ * @constructor
+ * @param {Canvas} canvas The html canvas tag to draw to on screen.
+ * @param {Number} width The width of the canvas without scaling
+ * @param {Number} height The height of the canvas without scaling
+ * @param {Object} [options] The renderer parameters
+ * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering
+ * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing
+ * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled)
+ * @param {Boolean} [options.subPixel=false] Whether to enable subpixel renderering (performance hit when enabled)
+ * @param {Boolean} [options.textureSeamFix=true] enable the texture seam fix when rendering Tile when antiAlias is off for the canvasRenderer
+ * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied
+ * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied
+ */
+ me.CanvasRenderer = me.Renderer.extend(
+ /** @scope me.CanvasRenderer.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init : function (c, width, height, options) {
+ // parent constructor
+ me.Renderer.prototype.init.apply(this, [c, width, height, options]);
+
+ // defined the 2d context
+ this.context = this.getContext2d(this.canvas, !this.transparent);
+
+ // create the back buffer if we use double buffering
+ if (this.doubleBuffering) {
+ this.backBufferCanvas = me.video.createCanvas(width, height, false);
+ this.backBufferContext2D = this.getContext2d(this.backBufferCanvas);
+
+ if (this.transparent) {
+ // Clears the front buffer for each frame blit
+ this.context.globalCompositeOperation = "copy";
+ }
+ }
+ else {
+ this.backBufferCanvas = this.canvas;
+ this.backBufferContext2D = this.context;
+ }
+
+ // apply the default color to the 2d context
+ this.setColor(this.currentColor);
+
+ // create a texture cache
+ this.cache = new me.Renderer.TextureCache();
+
+ if (options.textureSeamFix !== false && !this.antiAlias) {
+ // enable the tile texture seam fix with the canvas renderer
+ this.uvOffset = 1;
+ }
+
+ return this;
+ },
+
+ /**
+ * prepare the framebuffer for drawing a new frame
+ * @name clear
+ * @memberOf me.CanvasRenderer
+ * @function
+ */
+ clear : function () {
+ if (this.transparent) {
+ this.clearColor("rgba(0,0,0,0)", true);
+ }
+ },
+
+ /**
+ * render the main framebuffer on screen
+ * @name flush
+ * @memberOf me.CanvasRenderer
+ * @function
+ */
+ flush : function () {
+ if (this.doubleBuffering) {
+ this.context.drawImage(
+ this.backBufferCanvas, 0, 0,
+ this.backBufferCanvas.width, this.backBufferCanvas.height,
+ 0, 0,
+ this.gameWidthZoom, this.gameHeightZoom
+ );
+ }
+ },
+
+ /**
+ * Clears the main framebuffer with the given color
+ * @name clearColor
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {me.Color|String} color CSS color.
+ * @param {Boolean} [opaque=false] Allow transparency [default] or clear the surface completely [true]
+ */
+ clearColor : function (col, opaque) {
+ var _ctx = this.backBufferContext2D;
+ var _canvas = _ctx.canvas;
+
+ _ctx.save();
+ _ctx.setTransform(1, 0, 0, 1, 0, 0);
+ _ctx.globalCompositeOperation = opaque ? "copy" : "source-over";
+ _ctx.fillStyle = (col instanceof me.Color) ? col.toRGBA() : col;
+ _ctx.fillRect(0, 0, _canvas.width, _canvas.height);
+ _ctx.restore();
+ },
+
+ /**
+ * Sets all pixels in the given rectangle to transparent black,
+ * erasing any previously drawn content.
+ * @name clearRect
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x x axis of the coordinate for the rectangle starting point.
+ * @param {Number} y y axis of the coordinate for the rectangle starting point.
+ * @param {Number} width The rectangle's width.
+ * @param {Number} height The rectangle's height.
+ */
+ clearRect : function (x, y, width, height) {
+ this.backBufferContext2D.clearRect(x, y, width, height);
+ },
+
+ /**
+ * Create a pattern with the specified repition
+ * @name createPattern
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {image} image Source image
+ * @param {String} repeat Define how the pattern should be repeated
+ * @return {CanvasPattern}
+ * @see me.ImageLayer#repeat
+ * @example
+ * var tileable = renderer.createPattern(image, "repeat");
+ * var horizontal = renderer.createPattern(image, "repeat-x");
+ * var vertical = renderer.createPattern(image, "repeat-y");
+ * var basic = renderer.createPattern(image, "no-repeat");
+ */
+ createPattern : function (image, repeat) {
+ return this.backBufferContext2D.createPattern(image, repeat);
+ },
+
+ /**
+ * Draw an image using the canvas api
+ * @name drawImage
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {image} image Source image
+ * @param {Number} sx Source x-coordinate
+ * @param {Number} sy Source y-coordinate
+ * @param {Number} sw Source width
+ * @param {Number} sh Source height
+ * @param {Number} dx Destination x-coordinate
+ * @param {Number} dy Destination y-coordinate
+ * @param {Number} dw Destination width
+ * @param {Number} dh Destination height
+ * @example
+ * // Can be used in three ways:
+ * renderer.drawImage(image, dx, dy);
+ * renderer.drawImage(image, dx, dy, dw, dh);
+ * renderer.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
+ * // dx, dy, dw, dh being the destination target & dimensions. sx, sy, sw, sh being the position & dimensions to take from the image
+ */
+ drawImage : function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+
+ if (this.subPixel === false) {
+ if (typeof sw === "undefined") {
+ sw = dw = image.width;
+ sh = dh = image.height;
+ dx = sx;
+ dy = sy;
+ sx = 0;
+ sy = 0;
+ }
+ else if (typeof dx === "undefined") {
+ dx = sx;
+ dy = sy;
+ dw = sw;
+ dh = sh;
+ sw = image.width;
+ sh = image.height;
+ sx = 0;
+ sy = 0;
+ }
+ this.backBufferContext2D.drawImage(image, sx, sy, sw, sh, ~~dx, ~~dy, dw, dh);
+ } else {
+ this.backBufferContext2D.drawImage.apply(this.backBufferContext2D, arguments);
+ }
+ },
+
+ /**
+ * Draw a pattern within the given rectangle.
+ * @name drawPattern
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {CanvasPattern} pattern Pattern object
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} width
+ * @param {Number} height
+ * @see me.CanvasRenderer#createPattern
+ */
+ drawPattern : function (pattern, x, y, width, height) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+ var fillStyle = this.backBufferContext2D.fillStyle;
+ this.backBufferContext2D.fillStyle = pattern;
+ this.backBufferContext2D.fillRect(x, y, width, height);
+ this.backBufferContext2D.fillStyle = fillStyle;
+ },
+
+ /**
+ * Fill an arc at the specified coordinates with given radius, start and end points
+ * @name fillArc
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x arc center point x-axis
+ * @param {Number} y arc center point y-axis
+ * @param {Number} radius
+ * @param {Number} start start angle in radians
+ * @param {Number} end end angle in radians
+ * @param {Boolean} [antiClockwise=false] draw arc anti-clockwise
+ */
+ fillArc : function (x, y, radius, start, end, antiClockwise) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+
+ this.translate(x + radius, y + radius);
+ this.backBufferContext2D.beginPath();
+ this.backBufferContext2D.arc(0, 0, radius, start, end, antiClockwise || false);
+ this.backBufferContext2D.fill();
+ this.backBufferContext2D.closePath();
+ this.translate(- (x + radius), -(y + radius));
+ },
+
+ /**
+ * Draw a filled rectangle at the specified coordinates
+ * @name fillRect
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} width
+ * @param {Number} height
+ */
+ fillRect : function (x, y, width, height) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+ this.backBufferContext2D.fillRect(x, y, width, height);
+ },
+
+ /**
+ * return a reference to the system 2d Context
+ * @name getContext
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @return {Context2d}
+ */
+ getContext : function () {
+ return this.backBufferContext2D;
+ },
+
+ /**
+ * return a reference to the font 2d Context
+ * @ignore
+ */
+ getFontContext : function () {
+ // in canvas more we can directly use the 2d context
+ return this.getContext();
+ },
+
+ /**
+ * resets the canvas transform to identity
+ * @name resetTransform
+ * @memberOf me.CanvasRenderer
+ * @function
+ */
+ resetTransform : function () {
+ this.backBufferContext2D.setTransform(1, 0, 0, 1, 0, 0);
+ },
+
+ /**
+ * scales the canvas & 2d Context
+ * @name scaleCanvas
+ * @memberOf me.CanvasRenderer
+ * @function
+ */
+ scaleCanvas : function (scaleX, scaleY) {
+ this.canvas.width = this.gameWidthZoom = this.backBufferCanvas.width * scaleX;
+ this.canvas.height = this.gameHeightZoom = this.backBufferCanvas.height * scaleY;
+
+ // adjust CSS style for High-DPI devices
+ if (me.device.getPixelRatio() > 1) {
+ this.canvas.style.width = (this.canvas.width / me.device.getPixelRatio()) + "px";
+ this.canvas.style.height = (this.canvas.height / me.device.getPixelRatio()) + "px";
+ }
+
+ if (this.doubleBuffering && this.transparent) {
+ // Clears the front buffer for each frame blit
+ this.context.globalCompositeOperation = "copy";
+ }
+ this.setAntiAlias(this.context, this.antiAlias);
+ this.flush();
+ },
+
+ /**
+ * save the canvas context
+ * @name save
+ * @memberOf me.CanvasRenderer
+ * @function
+ */
+ save : function () {
+ this.backBufferContext2D.save();
+ },
+
+ /**
+ * restores the canvas context
+ * @name restore
+ * @memberOf me.CanvasRenderer
+ * @function
+ */
+ restore : function () {
+ this.backBufferContext2D.restore();
+ this.currentColor.glArray[3] = this.backBufferContext2D.globalAlpha;
+ },
+
+ /**
+ * rotates the canvas context
+ * @name rotate
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} angle in radians
+ */
+ rotate : function (angle) {
+ this.backBufferContext2D.rotate(angle);
+ },
+
+ /**
+ * scales the canvas context
+ * @name scale
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ */
+ scale : function (x, y) {
+ this.backBufferContext2D.scale(x, y);
+ },
+
+ /**
+ * Sets the fill & stroke style colors for the context.
+ * @name setColor
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {me.Color|String} color css color value
+ */
+ setColor : function (color) {
+ this.backBufferContext2D.strokeStyle =
+ this.backBufferContext2D.fillStyle = (
+ color instanceof me.Color ?
+ color.toRGBA() :
+ color
+ );
+ },
+
+ /**
+ * Sets the global alpha on the canvas context
+ * @name setGlobalAlpha
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} alpha 0.0 to 1.0 values accepted.
+ */
+ setGlobalAlpha : function (a) {
+ this.backBufferContext2D.globalAlpha = this.currentColor.glArray[3] = a;
+ },
+
+ /**
+ * sets the line width on the context
+ * @name setLineWidth
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} width Line width
+ */
+ setLineWidth : function (width) {
+ this.backBufferContext2D.lineWidth = width;
+ },
+
+ /**
+ * Stroke an arc at the specified coordinates with given radius, start and end points
+ * @name strokeArc
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x arc center point x-axis
+ * @param {Number} y arc center point y-axis
+ * @param {Number} radius
+ * @param {Number} start start angle in radians
+ * @param {Number} end end angle in radians
+ * @param {Boolean} [antiClockwise=false] draw arc anti-clockwise
+ */
+ strokeArc : function (x, y, radius, start, end, antiClockwise) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+ this.translate(x + radius, y + radius);
+ this.backBufferContext2D.beginPath();
+ this.backBufferContext2D.arc(0, 0, radius, start, end, antiClockwise || false);
+ this.backBufferContext2D.stroke();
+ this.backBufferContext2D.closePath();
+ this.translate(-(x + radius), -(y + radius));
+ },
+
+ /**
+ * Stroke an ellipse at the specified coordinates with given radius, start and end points
+ * @name strokeEllipse
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x arc center point x-axis
+ * @param {Number} y arc center point y-axis
+ * @param {Number} w horizontal radius of the ellipse
+ * @param {Number} h vertical radius of the ellipse
+ */
+ strokeEllipse : function (x, y, w, h) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+
+ var hw = w,
+ hh = h,
+ lx = x - hw,
+ rx = x + hw,
+ ty = y - hh,
+ by = y + hh;
+
+ var xmagic = hw * 0.551784,
+ ymagic = hh * 0.551784,
+ xmin = x - xmagic,
+ xmax = x + xmagic,
+ ymin = y - ymagic,
+ ymax = y + ymagic;
+
+ this.backBufferContext2D.beginPath();
+ this.backBufferContext2D.moveTo(x, ty);
+ this.backBufferContext2D.bezierCurveTo(xmax, ty, rx, ymin, rx, y);
+ this.backBufferContext2D.bezierCurveTo(rx, ymax, xmax, by, x, by);
+ this.backBufferContext2D.bezierCurveTo(xmin, by, lx, ymax, lx, y);
+ this.backBufferContext2D.bezierCurveTo(lx, ymin, xmin, ty, x, ty);
+ this.backBufferContext2D.stroke();
+ },
+
+ /**
+ * Stroke a line of the given two points
+ * @name strokeLine
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} startX the start x coordinate
+ * @param {Number} startY the start y coordinate
+ * @param {Number} endX the end x coordinate
+ * @param {Number} endY the end y coordinate
+ */
+ strokeLine : function (startX, startY, endX, endY) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+
+ this.backBufferContext2D.beginPath();
+ this.backBufferContext2D.moveTo(startX, startY);
+ this.backBufferContext2D.lineTo(endX, endY);
+ this.backBufferContext2D.stroke();
+ },
+
+ /**
+ * Strokes a me.Polygon on the screen with a specified color
+ * @name strokePolygon
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {me.Polygon} poly the shape to draw
+ */
+ strokePolygon : function (poly) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+
+ this.translate(poly.pos.x, poly.pos.y);
+ this.backBufferContext2D.beginPath();
+ this.backBufferContext2D.moveTo(poly.points[0].x, poly.points[0].y);
+ var point;
+ for (var i = 1; i < poly.points.length; i++) {
+ point = poly.points[i];
+ this.backBufferContext2D.lineTo(point.x, point.y);
+ }
+ this.backBufferContext2D.lineTo(poly.points[0].x, poly.points[0].y);
+ this.backBufferContext2D.stroke();
+ this.backBufferContext2D.closePath();
+ this.translate(-poly.pos.x, -poly.pos.y);
+ },
+
+ /**
+ * Stroke a rectangle at the specified coordinates with a given color
+ * @name strokeRect
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} width
+ * @param {Number} height
+ */
+ strokeRect : function (x, y, width, height) {
+ if (this.backBufferContext2D.globalAlpha < 1 / 255) {
+ // Fast path: don't draw fully transparent
+ return;
+ }
+ this.backBufferContext2D.strokeRect(x, y, width, height);
+ },
+
+ /**
+ * draw the given shape
+ * @name drawShape
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object
+ */
+ drawShape : function (shape) {
+ if (shape.shapeType === "Rectangle") {
+ this.strokeRect(shape.left, shape.top, shape.width, shape.height);
+ } else if (shape instanceof me.Line || shape instanceof me.Polygon) {
+ this.strokePolygon(shape);
+ } else if (shape instanceof me.Ellipse) {
+ if (shape.radiusV.x === shape.radiusV.y) {
+ // it's a circle
+ this.strokeArc(
+ shape.pos.x - shape.radius,
+ shape.pos.y - shape.radius,
+ shape.radius,
+ 0,
+ 2 * Math.PI
+ );
+ } else {
+ // it's an ellipse
+ this.strokeEllipse(
+ shape.pos.x,
+ shape.pos.y,
+ shape.radiusV.x,
+ shape.radiusV.y
+ );
+ }
+ }
+ },
+
+ /**
+ * Resets (overrides) the renderer transformation matrix to the
+ * identity one, and then apply the given transformation matrix.
+ * @name setTransform
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {me.Matrix2d} mat2d Matrix to transform by
+ */
+ setTransform : function (mat2d) {
+ this.resetTransform();
+ this.transform(mat2d);
+ },
+
+ /**
+ * Multiply given matrix into the renderer tranformation matrix
+ * @name transform
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {me.Matrix2d} mat2d Matrix to transform by
+ */
+ transform : function (mat2d) {
+ var a = mat2d.val;
+ var tx = a[6],
+ ty = a[7];
+
+ if (this.subPixel === false) {
+ tx = ~~tx;
+ ty = ~~ty;
+ }
+
+ this.backBufferContext2D.transform(
+ a[0],
+ a[1],
+ a[3],
+ a[4],
+ tx,
+ ty
+ );
+ },
+
+ /**
+ * Translates the context to the given position
+ * @name translate
+ * @memberOf me.CanvasRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ */
+ translate : function (x, y) {
+ if (this.subPixel === false) {
+ this.backBufferContext2D.translate(~~x, ~~y);
+ } else {
+ this.backBufferContext2D.translate(x, y);
+ }
+ }
+
+ });
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a local constant for the -(Math.PI / 2) value
+ * @ignore
+ */
+ var nhPI = -(Math.PI / 2);
+
+ /**
+ * A Texture atlas object
+ * For portability, a global reference to this class is available through the default renderer: {@link me.video.renderer}.Texture
+ *
+ * Currently supports :
+ * - [TexturePacker]{@link http://www.codeandweb.com/texturepacker/} : through JSON export
+ * - [ShoeBox]{@link http://renderhjs.net/shoebox/} : through JSON export using the
+ * melonJS setting [file]{@link https://github.com/melonjs/melonJS/raw/master/media/shoebox_JSON_export.sbx}
+ * - Standard (fixed cell size) spritesheet : through a {framewidth:xx, frameheight:xx, anchorPoint:me.Vector2d} object
+ * @class
+ * @extends me.Object
+ * @memberOf me.CanvasRenderer
+ * @name Texture
+ * @constructor
+ * @param {Object} atlas atlas information. See {@link me.loader.getJSON}
+ * @param {Image} [texture=atlas.meta.image] texture name
+ * @param {Boolean} [cached=false] Use true to skip caching this Texture
+ * @example
+ * // create a texture atlas from a JSON Object
+ * texture = new me.video.renderer.Texture(
+ * me.loader.getJSON("texture"),
+ * me.loader.getImage("texture")
+ * );
+ *
+ * // create a texture atlas for a spritesheet, with (optional) an anchorPoint in the center of each frame
+ * texture = new me.video.renderer.Texture(
+ * { framewidth : 32, frameheight : 32, anchorPoint : new me.Vector2d(0.5, 0.5) },
+ * me.loader.getImage("spritesheet")
+ * );
+ */
+ me.CanvasRenderer.prototype.Texture = me.Object.extend(
+ /** @scope me.video.renderer.Texture.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init : function (atlas, texture, cache) {
+ /**
+ * to identify the atlas format (e.g. texture packer)
+ * @ignore
+ */
+ this.format = null;
+
+ /**
+ * the image texture itself (FIXME: This should be named `image`)
+ * @ignore
+ */
+ this.texture = texture || null;
+
+ /**
+ * the atlas dictionnary
+ * @ignore
+ */
+ this.atlas = null;
+
+ if (typeof (atlas) !== "undefined") {
+
+ if (typeof(atlas.meta) !== "undefined") {
+ // Texture Packer
+ if (atlas.meta.app.includes("texturepacker")) {
+ this.format = "texturepacker";
+ // set the texture
+ if (typeof(texture) === "undefined") {
+ var image = atlas.meta.image;
+ this.texture = me.utils.getImage(image);
+ if (!this.texture) {
+ throw new me.video.renderer.Texture.Error(
+ "Atlas texture '" + image + "' not found"
+ );
+ }
+ }
+ this.repeat = "no-repeat";
+ }
+ // ShoeBox
+ else if (atlas.meta.app.includes("ShoeBox")) {
+ if (!atlas.meta.exporter || !atlas.meta.exporter.includes("melonJS")) {
+ throw new me.video.renderer.Texture.Error(
+ "ShoeBox requires the JSON exporter : " +
+ "https://github.com/melonjs/melonJS/tree/master/media/shoebox_JSON_export.sbx"
+ );
+ }
+ this.format = "ShoeBox";
+ this.repeat = "no-repeat";
+ }
+ // Internal texture atlas
+ else if (atlas.meta.app.includes("melonJS")) {
+ this.format = "melonJS";
+ this.repeat = atlas.meta.repeat || "no-repeat";
+ }
+ // initialize the atlas
+ this.atlas = this.parse(atlas);
+
+ } else {
+ // a regular spritesheet ?
+ if (typeof(atlas.framewidth) !== "undefined" &&
+ typeof(atlas.frameheight) !== "undefined") {
+ this.format = "Spritesheet (fixed cell size)";
+ if (typeof(texture) !== undefined) {
+ // overwrite if specified
+ atlas.image = texture;
+ }
+ // initialize the atlas
+ this.atlas = this.parseFromSpriteSheet(atlas);
+ this.repeat = "no-repeat";
+ }
+ }
+ }
+ // if format not recognized
+ if (!this.atlas) {
+ throw new me.video.renderer.Texture.Error("texture atlas format not supported");
+ }
+
+ // Add self to TextureCache if cache !== false
+ if (cache !== false) {
+ if (cache instanceof me.Renderer.TextureCache) {
+ cache.put(this.texture, this);
+ } else {
+ me.video.renderer.cache.put(this.texture, this);
+ }
+ }
+ },
+
+ /**
+ * create a simple 1 frame texture atlas based on the given parameters
+ * @ignore
+ */
+ createAtlas : function (width, height, name, repeat) {
+ return {
+ "meta" : {
+ "app" : "melonJS",
+ "size" : { "w" : width, "h" : height },
+ "repeat" : repeat || "no-repeat"
+ },
+ "frames" : [{
+ "filename" : name || "default",
+ "frame" : { "x" : 0, "y" : 0, "w" : width, "h" : height }
+ }]
+ };
+ },
+
+ /**
+ * build an atlas from the given data
+ * @ignore
+ */
+ parse : function (data) {
+ var atlas = {};
+ data.frames.forEach(function (frame) {
+ // fix wrongly formatted JSON (e.g. last dummy object in ShoeBox)
+ if (frame.hasOwnProperty("filename")) {
+ // Source coordinates
+ var s = frame.frame;
+
+ var originX, originY;
+ // Pixel-based offset origin from the top-left of the source frame
+ var hasTextureAnchorPoint = (frame.spriteSourceSize && frame.sourceSize && frame.pivot);
+ if (hasTextureAnchorPoint) {
+ originX = (frame.sourceSize.w * frame.pivot.x) - ((frame.trimmed) ? frame.spriteSourceSize.x : 0);
+ originY = (frame.sourceSize.h * frame.pivot.y) - ((frame.trimmed) ? frame.spriteSourceSize.y : 0);
+ }
+
+ atlas[frame.filename] = {
+ name : frame.filename, // frame name
+ offset : new me.Vector2d(s.x, s.y),
+ anchorPoint : (hasTextureAnchorPoint) ? new me.Vector2d(originX / s.w, originY / s.h) : null,
+ width : s.w,
+ height : s.h,
+ angle : (frame.rotated === true) ? nhPI : 0
+ };
+ }
+ });
+ return atlas;
+ },
+
+ /**
+ * build an atlas from the given spritesheet
+ * @ignore
+ */
+ parseFromSpriteSheet : function (data) {
+ var atlas = {};
+ var image = data.image;
+ var spacing = data.spacing || 0;
+ var margin = data.margin || 0;
+
+ var width = image.width;
+ var height = image.height;
+
+ // calculate the sprite count (line, col)
+ var spritecount = new me.Vector2d(
+ ~~((width - margin + spacing) / (data.framewidth + spacing)),
+ ~~((height - margin + spacing) / (data.frameheight + spacing))
+ );
+
+ // verifying the texture size
+ if ((width % (data.framewidth + spacing)) !== 0 ||
+ (height % (data.frameheight + spacing)) !== 0) {
+ // "truncate size"
+ width = spritecount.x * (data.framewidth + spacing);
+ height = spritecount.y * (data.frameheight + spacing);
+ // warning message
+ console.warn(
+ "Spritesheet Texture for image: " + image.src +
+ " is not divisible by " + (data.framewidth + spacing) +
+ "x" + (data.frameheight + spacing) +
+ ", truncating effective size to " + width + "x" + height
+ );
+ }
+
+ // build the local atlas
+ for (var frame = 0, count = spritecount.x * spritecount.y; frame < count; frame++) {
+ atlas["" + frame] = {
+ name: "" + frame,
+ offset: new me.Vector2d(
+ margin + (spacing + data.framewidth) * (frame % spritecount.x),
+ margin + (spacing + data.frameheight) * ~~(frame / spritecount.x)
+ ),
+ anchorPoint: (data.anchorPoint || null),
+ width: data.framewidth,
+ height: data.frameheight,
+ angle: 0
+ };
+ }
+
+ return atlas;
+ },
+
+ /**
+ * return the Atlas dictionnary
+ * @name getAtlas
+ * @memberOf me.CanvasRenderer.Texture
+ * @function
+ * @return {Object}
+ */
+ getAtlas : function () {
+ return this.atlas;
+ },
+
+ /**
+ * return the Atlas texture
+ * @name getTexture
+ * @memberOf me.CanvasRenderer.Texture
+ * @function
+ * @return {Image}
+ */
+ getTexture : function () {
+ return this.texture;
+ },
+
+ /**
+ * return a normalized region/frame information for the specified sprite name
+ * @name getRegion
+ * @memberOf me.CanvasRenderer.Texture
+ * @function
+ * @param {String} name name of the sprite
+ * @return {Object}
+ */
+ getRegion : function (name) {
+ return this.atlas[name];
+ },
+
+ /**
+ * Create a sprite object using the first region found using the specified name
+ * @name createSpriteFromName
+ * @memberOf me.CanvasRenderer.Texture
+ * @function
+ * @param {String} name name of the sprite
+ * @param {Object} [settings] Additional settings passed to the {@link me.Sprite} contructor
+ * @return {me.Sprite}
+ * @example
+ * // create a new texture atlas object under the `game` namespace
+ * game.texture = new me.video.renderer.Texture(
+ * me.loader.getJSON("texture"),
+ * me.loader.getImage("texture")
+ * );
+ * ...
+ * ...
+ * // add the coin sprite as renderable for the entity
+ * this.renderable = game.texture.createSpriteFromName("coin.png");
+ * // set the renderable position to bottom center
+ * this.anchorPoint.set(0.5, 1.0);
+ */
+ createSpriteFromName : function (name, settings) {
+ // instantiate a new sprite object
+ return me.pool.pull(
+ "me.Sprite",
+ 0, 0,
+ Object.assign({
+ image: this,
+ region : name
+ }, settings || {})
+ );
+ },
+
+ /**
+ * Create an animation object using the first region found using all specified names
+ * @name createAnimationFromName
+ * @memberOf me.CanvasRenderer.Texture
+ * @function
+ * @param {String[]|Number[]} names list of names for each sprite
+ * (when manually creating a Texture out of a spritesheet, only numeric values are authorized)
+ * @param {Object} [settings] Additional settings passed to the {@link me.Sprite} contructor
+ * @return {me.Sprite}
+ * @example
+ * // create a new texture atlas object under the `game` namespace
+ * game.texture = new me.video.renderer.Texture(
+ * me.loader.getJSON("texture"),
+ * me.loader.getImage("texture")
+ * );
+ *
+ * // create a new Sprite as renderable for the entity
+ * this.renderable = game.texture.createAnimationFromName([
+ * "walk0001.png", "walk0002.png", "walk0003.png",
+ * "walk0004.png", "walk0005.png", "walk0006.png",
+ * "walk0007.png", "walk0008.png", "walk0009.png",
+ * "walk0010.png", "walk0011.png"
+ * ]);
+ *
+ * // define an additional basic walking animation
+ * this.renderable.addAnimation ("simple_walk", [0,2,1]);
+ * // you can also use frame name to define your animation
+ * this.renderable.addAnimation ("speed_walk", ["walk0007.png", "walk0008.png", "walk0009.png", "walk0010.png"]);
+ * // set the default animation
+ * this.renderable.setCurrentAnimation("simple_walk");
+ * // set the renderable position to bottom center
+ * this.anchorPoint.set(0.5, 1.0);
+ */
+ createAnimationFromName : function (names, settings) {
+ var tpAtlas = [], indices = {};
+ var width = 0, height = 0;
+ var region;
+ // iterate through the given names
+ // and create a "normalized" atlas
+ for (var i = 0; i < names.length; ++i) {
+ region = this.getRegion(names[i]);
+ if (region == null) {
+ // throw an error
+ throw new me.video.renderer.Texture.Error("Texture - region for " + names[i] + " not found");
+ }
+ tpAtlas[i] = region;
+ // save the corresponding index
+ indices[names[i]] = i;
+ // calculate the max size of a frame
+ width = Math.max(region.width, width);
+ height = Math.max(region.height, height);
+ }
+ // instantiate a new animation sheet object
+ return new me.Sprite(0, 0, Object.assign({
+ image: this,
+ framewidth: width,
+ frameheight: height,
+ margin: 0,
+ spacing: 0,
+ atlas: tpAtlas,
+ atlasIndices: indices
+ }, settings || {}));
+ }
+ });
+
+ /**
+ * Base class for Texture exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.CanvasRenderer.Texture
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.CanvasRenderer.prototype.Texture.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.CanvasRenderer.Texture.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016 Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+
+ /**
+ * The WebGL Shader singleton
+ * There is no constructor function for me.video.shader
+ * @namespace me.video.shader
+ * @memberOf me.video
+ */
+ me.video.shader = (function () {
+ /**
+ * Public API
+ * @ignore
+ */
+ var api = {};
+
+ /**
+ * Compile GLSL into a shader object
+ * @private
+ */
+ function getShader(gl, type, source) {
+ var shader = gl.createShader(type);
+ gl.shaderSource(shader, source);
+ gl.compileShader(shader);
+
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ throw new me.video.Error(gl.getShaderInfoLog(shader));
+ }
+
+ return shader;
+ }
+
+ /**
+ * Hash map of GLSL data types to WebGL Uniform methods
+ * @private
+ */
+ var fnHash = {
+ "bool" : "1i",
+ "int" : "1i",
+ "float" : "1f",
+ "vec2" : "2fv",
+ "vec3" : "3fv",
+ "vec4" : "4fv",
+ "bvec2" : "2iv",
+ "bvec3" : "3iv",
+ "bvec4" : "4iv",
+ "ivec2" : "2iv",
+ "ivec3" : "3iv",
+ "ivec4" : "4iv",
+ "mat2" : "Matrix2fv",
+ "mat3" : "Matrix3fv",
+ "mat4" : "Matrix4fv",
+ "sampler2D" : "1i"
+ };
+
+ /**
+ * Create a shader program (with bindings) using the given GLSL sources
+ * @name createShader
+ * @memberOf me.video.shader
+ * @function
+ * @param {WebGLContext} gl WebGL Context
+ * @param {String} vertex Vertex shader source
+ * @param {String} fragment Fragment shader source
+ * @return {Object} A reference to the WebGL Shader Program
+ */
+ api.createShader = function (gl, vertex, fragment) {
+ var program = {
+ "attributes" : {},
+ "uniforms" : {},
+ "handle" : null
+ },
+ handle = program.handle = gl.createProgram(),
+ attrRx = /attribute\s+\w+\s+(\w+)/g,
+ uniRx = /uniform\s+(\w+)\s+(\w+)/g,
+ attributes = [],
+ uniforms = {},
+ match,
+ descriptor = {},
+ locations = {};
+
+ gl.attachShader(handle, getShader(gl, gl.VERTEX_SHADER, vertex));
+ gl.attachShader(handle, getShader(gl, gl.FRAGMENT_SHADER, fragment));
+ gl.linkProgram(handle);
+
+ if (!gl.getProgramParameter(handle, gl.LINK_STATUS)) {
+ throw new me.video.Error(gl.getProgramInfoLog(handle));
+ }
+
+ gl.useProgram(handle);
+
+ // Detect all attribute names
+ while ((match = attrRx.exec(vertex))) {
+ attributes.push(match[1]);
+ }
+
+ // Detect all uniform names and types
+ [ vertex, fragment ].forEach(function (shader) {
+ while ((match = uniRx.exec(shader))) {
+ uniforms[match[2]] = match[1];
+ }
+ });
+
+ // Get attribute references
+ attributes.forEach(function (attr) {
+ program.attributes[attr] = gl.getAttribLocation(handle, attr);
+ gl.enableVertexAttribArray(program.attributes[attr]);
+ });
+
+ // Get uniform references
+ Object.keys(uniforms).forEach(function (name) {
+ var type = uniforms[name];
+ locations[name] = gl.getUniformLocation(handle, name);
+
+ descriptor[name] = {
+ "get" : (function (name) {
+ /**
+ * A getter for the uniform location
+ * @ignore
+ */
+ return function () {
+ return locations[name];
+ };
+ })(name),
+ "set" : (function (name, type, fn) {
+ if (type.indexOf("mat") === 0) {
+ /**
+ * A generic setter for uniform matrices
+ * @ignore
+ */
+ return function (val) {
+ gl[fn](locations[name], false, val);
+ };
+ }
+ else {
+ /**
+ * A generic setter for uniform vectors
+ * @ignore
+ */
+ return function (val) {
+ var fnv = fn;
+ if (val.length && fn.substr(-1) !== "v") {
+ fnv += "v";
+ }
+ gl[fnv](locations[name], val);
+ };
+ }
+ })(name, type, "uniform" + fnHash[type])
+ };
+ });
+ Object.defineProperties(program.uniforms, descriptor);
+
+ return program;
+ };
+
+ /**
+ * Create a texture from an image
+ * @name createTexture
+ * @memberOf me.video.shader
+ * @function
+ * @param {WebGLContext} gl WebGL Context
+ * @param {Number} unit Destination texture unit
+ * @param {Image|Canvas|ImageData|UInt8Array[]|Float32Array[]} image Source image
+ * @param {Number} filter gl.LINEAR or gl.NEAREST
+ * @param {String} [repeat="no-repeat"] Image repeat behavior (see {@link me.ImageLayer#repeat})
+ * @param {Number} [w] Source image width (Only use with UInt8Array[] or Float32Array[] source image)
+ * @param {Number} [h] Source image height (Only use with UInt8Array[] or Float32Array[] source image)
+ * @param {Number} [b] Source image border (Only use with UInt8Array[] or Float32Array[] source image)
+ * @return {WebGLTexture} A texture object
+ */
+ api.createTexture = function (gl, unit, image, filter, repeat, w, h, b) {
+ repeat = repeat || "no-repeat";
+
+ if (!me.utils.isPowerOfTwo(w || image.width) || !me.utils.isPowerOfTwo(h || image.height)) {
+ console.warn(
+ "[WebGL Renderer] " + image + " is not a POT texture " +
+ "(" + (w || image.width) + "x" + (h || image.height) + ")"
+ );
+ }
+
+ var texture = gl.createTexture(),
+ rs = (repeat.search(/^repeat(-x)?$/) === 0) ? gl.REPEAT : gl.CLAMP_TO_EDGE,
+ rt = (repeat.search(/^repeat(-y)?$/) === 0) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
+
+ gl.activeTexture(gl.TEXTURE0 + unit);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, rs);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, rt);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
+ if (w || h || b) {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, b, gl.RGBA, gl.UNSIGNED_BYTE, image);
+ }
+ else {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+ }
+
+ return texture;
+ };
+
+ return api;
+ })();
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016 Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+
+ /**
+ * a WebGL renderer object
+ * @extends me.Renderer
+ * @namespace me.WebGLRenderer
+ * @memberOf me
+ * @constructor
+ * @param {Canvas} canvas The html canvas tag to draw to on screen.
+ * @param {Number} width The width of the canvas without scaling
+ * @param {Number} height The height of the canvas without scaling
+ * @param {Object} [options] The renderer parameters
+ * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering
+ * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing
+ * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled)
+ * @param {Boolean} [options.subPixel=false] Whether to enable subpixel renderering (performance hit when enabled)
+ * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied
+ * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied
+ * @param {me.WebGLRenderer.Compositor} [options.compositor] A class that implements the compositor API
+ */
+ me.WebGLRenderer = me.Renderer.extend(
+ /** @scope me.WebGLRenderer.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init : function (c, width, height, options) {
+ me.Renderer.prototype.init.apply(this, [c, width, height, options]);
+
+ /**
+ * The WebGL context
+ * @name gl
+ * @memberOf me.WebGLRenderer
+ */
+ this.gl = this.getContextGL(c, !this.transparent);
+
+ /**
+ * @ignore
+ */
+ this.colorStack = [];
+
+ /**
+ * @ignore
+ */
+ this._matrixStack = [];
+
+ /**
+ * @ignore
+ */
+ this._linePoints = [
+ new me.Vector2d(),
+ new me.Vector2d(),
+ new me.Vector2d(),
+ new me.Vector2d()
+ ];
+
+ /**
+ * The current transformation matrix used for transformations on the overall scene
+ * @name currentTransform
+ * @type me.Matrix2d
+ * @memberOf me.WebGLRenderer
+ */
+ this.currentTransform = new me.Matrix2d();
+
+ // Create a compositor
+ var Compositor = options.compositor || me.WebGLRenderer.Compositor;
+ this.compositor = new Compositor(this);
+
+ // Create a texture cache
+ this.cache = new me.Renderer.TextureCache(
+ this.compositor.maxTextures
+ );
+
+ this.createFillTexture(this.cache);
+
+ // Configure the WebGL viewport
+ this.scaleCanvas(1, 1);
+
+ return this;
+ },
+
+ /**
+ * @ignore
+ */
+ createFillTexture : function (cache) {
+ // Create a 1x1 white texture for fill operations
+ var image = new Uint8Array([255, 255, 255, 255]);
+
+ /**
+ * @ignore
+ */
+ this.fillTexture = new this.Texture(
+ this.Texture.prototype.createAtlas.apply(
+ this.Texture.prototype,
+ [ 1, 1, "fillTexture"]
+ ),
+ image,
+ cache
+ );
+
+ this.compositor.uploadTexture(
+ this.fillTexture,
+ 1,
+ 1,
+ 0
+ );
+ },
+
+ /**
+ * @ignore
+ */
+ createFontTexture : function (cache) {
+ var image = me.video.createCanvas(
+ me.utils.nextPowerOfTwo(this.backBufferCanvas.width),
+ me.utils.nextPowerOfTwo(this.backBufferCanvas.height)
+ );
+
+ /**
+ * @ignore
+ */
+ this.fontContext2D = this.getContext2d(image);
+
+ /**
+ * @ignore
+ */
+ this.fontTexture = new this.Texture(
+ this.Texture.prototype.createAtlas.apply(
+ this.Texture.prototype,
+ [ this.backBufferCanvas.width, this.backBufferCanvas.height, "fontTexture"]
+ ),
+ image,
+ cache
+ );
+
+ this.compositor.uploadTexture(this.fontTexture);
+ },
+
+ /**
+ * Create a pattern with the specified repetition
+ * @name createPattern
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {image} image Source image
+ * @param {String} repeat Define how the pattern should be repeated
+ * @return {me.video.renderer.Texture}
+ * @see me.ImageLayer#repeat
+ * @example
+ * var tileable = renderer.createPattern(image, "repeat");
+ * var horizontal = renderer.createPattern(image, "repeat-x");
+ * var vertical = renderer.createPattern(image, "repeat-y");
+ * var basic = renderer.createPattern(image, "no-repeat");
+ */
+ createPattern : function (image, repeat) {
+
+ if (!me.utils.isPowerOfTwo(image.width) || !me.utils.isPowerOfTwo(image.height)) {
+ throw new me.video.Error(
+ "[WebGL Renderer] " + image + " is not a POT texture " +
+ "(" + image.width + "x" + image.height + ")"
+ );
+ }
+
+ var texture = new this.Texture(
+ this.Texture.prototype.createAtlas.apply(
+ this.Texture.prototype,
+ [ image.width, image.height, "pattern", repeat]
+ ),
+ image
+ );
+
+ // FIXME: Remove old cache entry and texture when changing the repeat mode
+ this.compositor.uploadTexture(texture);
+
+ return texture;
+ },
+
+ /**
+ * Flush the compositor to the frame buffer
+ * @name flush
+ * @memberOf me.WebGLRenderer
+ * @function
+ */
+ flush : function () {
+ this.compositor.flush();
+ },
+
+ /**
+ * Clears the gl context with the given color.
+ * @name clearColor
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.Color|String} color CSS color.
+ * @param {Boolean} [opaque=false] Allow transparency [default] or clear the surface completely [true]
+ */
+ clearColor : function (col, opaque) {
+ var color = this.currentColor.clone();
+ var matrix = this.currentTransform.clone();
+ this.currentColor.copy(col);
+ this.currentTransform.identity();
+
+ if (opaque) {
+ this.compositor.clear();
+ }
+ else {
+ this.fillRect(0, 0, this.canvas.width, this.canvas.height);
+ }
+
+ this.currentTransform.copy(matrix);
+ this.currentColor.copy(color);
+ me.pool.push(color);
+ },
+
+ /**
+ * Sets all pixels in the given rectangle to transparent black,
+ * erasing any previously drawn content.
+ * @name clearRect
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x x axis of the coordinate for the rectangle starting point.
+ * @param {Number} y y axis of the coordinate for the rectangle starting point.
+ * @param {Number} width The rectangle's width.
+ * @param {Number} height The rectangle's height.
+ */
+ clearRect : function (x, y, width, height) {
+ var color = this.currentColor.clone();
+ this.currentColor.copy("#0000");
+ this.fillRect(x, y, width, height);
+ this.currentColor.copy(color);
+ me.pool.push(color);
+ },
+
+ /**
+ * @ignore
+ */
+ drawFont : function (bounds) {
+ var fontContext = this.getFontContext();
+
+ // Flush the compositor so we can upload a new texture
+ this.compositor.flush();
+
+ // Force-upload the new texture
+ this.compositor.uploadTexture(this.fontTexture, 0, 0, 0, true);
+
+ // Add the new quad
+ var key = bounds.pos.x + "," + bounds.pos.y + "," + bounds.width + "," + bounds.height;
+ this.compositor.addQuad(
+ this.fontTexture,
+ key,
+ bounds.pos.x,
+ bounds.pos.y,
+ bounds.width,
+ bounds.height
+ );
+
+ // Clear font context2D
+ fontContext.clearRect(0, 0, this.backBufferCanvas.width, this.backBufferCanvas.height);
+ },
+
+ /**
+ * Draw an image to the gl context
+ * @name drawImage
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Image} image Source image
+ * @param {Number} sx Source x-coordinate
+ * @param {Number} sy Source y-coordinate
+ * @param {Number} sw Source width
+ * @param {Number} sh Source height
+ * @param {Number} dx Destination x-coordinate
+ * @param {Number} dy Destination y-coordinate
+ * @param {Number} dw Destination width
+ * @param {Number} dh Destination height
+ * @example
+ * // Can be used in three ways:
+ * renderer.drawImage(image, dx, dy);
+ * renderer.drawImage(image, dx, dy, dw, dh);
+ * renderer.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
+ * // dx, dy, dw, dh being the destination target & dimensions. sx, sy, sw, sh being the position & dimensions to take from the image
+ */
+ drawImage : function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ // TODO: Replace the function signature with:
+ // drawImage(Image|Object, sx, sy, sw, sh, dx, dy, dw, dh)
+ if (typeof sw === "undefined") {
+ sw = dw = image.width;
+ sh = dh = image.height;
+ dx = sx;
+ dy = sy;
+ sx = 0;
+ sy = 0;
+ }
+ else if (typeof dx === "undefined") {
+ dx = sx;
+ dy = sy;
+ dw = sw;
+ dh = sh;
+ sw = image.width;
+ sh = image.height;
+ sx = 0;
+ sy = 0;
+ }
+
+ if (this.subPixel === false) {
+ // clamp to pixel grid
+ dx = ~~dx;
+ dy = ~~dy;
+ }
+
+ var key = sx + "," + sy + "," + sw + "," + sh;
+ this.compositor.addQuad(this.cache.get(image), key, dx, dy, dw, dh);
+ },
+
+ /**
+ * Draw a pattern within the given rectangle.
+ * @name drawPattern
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.video.renderer.Texture} pattern Pattern object
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} width
+ * @param {Number} height
+ * @see me.WebGLRenderer#createPattern
+ */
+ drawPattern : function (pattern, x, y, width, height) {
+ var key = "0,0," + width + "," + height;
+ this.compositor.addQuad(pattern, key, x, y, width, height);
+ },
+
+ /**
+ * Draw a filled rectangle at the specified coordinates
+ * @name fillRect
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} width
+ * @param {Number} height
+ */
+ fillRect : function (x, y, width, height) {
+ this.compositor.addQuad(this.fillTexture, "default", x, y, width, height);
+ },
+
+ /**
+ * return a reference to the screen canvas corresponding WebGL Context
+ * @name getScreenContext
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @return {WebGLContext}
+ */
+ getScreenContext : function () {
+ return this.gl;
+ },
+
+ /**
+ * Returns the WebGL Context object of the given Canvas
+ * @name getContextGL
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Canvas} canvas
+ * @param {Boolean} [opaque=false] Use true to disable transparency
+ * @return {WebGLContext}
+ */
+ getContextGL : function (c, opaque) {
+ if (typeof c === "undefined" || c === null) {
+ throw new me.video.Error(
+ "You must pass a canvas element in order to create " +
+ "a GL context"
+ );
+ }
+
+ if (typeof c.getContext === "undefined") {
+ throw new me.video.Error(
+ "Your browser does not support WebGL."
+ );
+ }
+
+ var attr = {
+ antialias : this.antiAlias,
+ alpha : !opaque
+ };
+ return (
+ c.getContext("webgl", attr) ||
+ c.getContext("experimental-webgl", attr)
+ );
+ },
+
+ /**
+ * Returns the WebGLContext instance for the renderer
+ * return a reference to the system 2d Context
+ * @name getContext
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @return {WebGLContext}
+ */
+ getContext : function () {
+ return this.gl;
+ },
+
+ /**
+ * return a reference to the font 2d Context
+ * @ignore
+ */
+ getFontContext : function () {
+ if (typeof (this.fontContext2D) === "undefined" ) {
+ // warn the end user about performance impact
+ console.warn("[WebGL Renderer] WARNING : Using Standard me.Font with WebGL will severly impact performances !");
+ // create the font texture if not done yet
+ this.createFontTexture(this.cache);
+ }
+ return this.fontContext2D;
+ },
+
+ /**
+ * resets the gl transform to identity
+ * @name resetTransform
+ * @memberOf me.WebGLRenderer
+ * @function
+ */
+ resetTransform : function () {
+ this.currentTransform.identity();
+ },
+
+ /**
+ * Reset context state
+ * @name reset
+ * @memberOf me.WebGLRenderer
+ * @function
+ */
+ reset : function () {
+ this.resetTransform();
+ this.cache.reset();
+ this.compositor.reset();
+ this.createFillTexture();
+ if (typeof (this.fontContext2D) !== "undefined" ) {
+ this.createFontTexture();
+ }
+ },
+
+ /**
+ * scales the canvas & GL Context
+ * @name scaleCanvas
+ * @memberOf me.WebGLRenderer
+ * @function
+ */
+ scaleCanvas : function (scaleX, scaleY) {
+ var w = this.canvas.width * scaleX;
+ var h = this.canvas.height * scaleY;
+
+ // adjust CSS style for High-DPI devices
+ if (me.device.getPixelRatio() > 1) {
+ this.canvas.style.width = (w / me.device.getPixelRatio()) + "px";
+ this.canvas.style.height = (h / me.device.getPixelRatio()) + "px";
+ }
+ else {
+ this.canvas.style.width = w + "px";
+ this.canvas.style.height = h + "px";
+ }
+
+ this.compositor.setProjection(this.canvas.width, this.canvas.height);
+ },
+
+ /**
+ * restores the canvas context
+ * @name restore
+ * @memberOf me.WebGLRenderer
+ * @function
+ */
+ restore : function () {
+ var color = this.colorStack.pop();
+ me.pool.push(color);
+ this.currentColor.copy(color);
+ this.currentTransform.copy(this._matrixStack.pop());
+ },
+
+ /**
+ * rotates the uniform matrix
+ * @name rotate
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} angle in radians
+ */
+ rotate : function (angle) {
+ this.currentTransform.rotate(angle);
+ },
+
+ /**
+ * save the canvas context
+ * @name save
+ * @memberOf me.WebGLRenderer
+ * @function
+ */
+ save : function () {
+ this.colorStack.push(this.currentColor.clone());
+ this._matrixStack.push(this.currentTransform.clone());
+ },
+
+ /**
+ * scales the uniform matrix
+ * @name scale
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ */
+ scale : function (x, y) {
+ this.currentTransform.scale(x, y);
+ },
+
+ /**
+ * not used by this renderer?
+ * @ignore
+ */
+ setAntiAlias : function (context, enable) {
+ me.Renderer.prototype.setAntiAlias.apply(this, [context, enable]);
+ // TODO: perhaps handle GLNEAREST or other options with texture binding
+ },
+
+ /**
+ * return the current global alpha
+ * @name globalAlpha
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @return {Number}
+ */
+ setGlobalAlpha : function (a) {
+ this.currentColor.glArray[3] = a;
+ },
+
+ /**
+ * Sets the color for further draw calls
+ * @name setColor
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.Color|String} color css color string.
+ */
+ setColor : function (color) {
+ var alpha = this.currentColor.glArray[3];
+ this.currentColor.copy(color);
+ this.currentColor.glArray[3] *= alpha;
+ },
+
+ /**
+ * Set the line width
+ * @name setLineWidth
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} width Line width
+ */
+ setLineWidth : function (width) {
+ this.compositor.lineWidth(width);
+ },
+
+ /**
+ * Stroke an arc at the specified coordinates with given radius, start and end points
+ * @name strokeArc
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x arc center point x-axis
+ * @param {Number} y arc center point y-axis
+ * @param {Number} radius
+ * @param {Number} start start angle in radians
+ * @param {Number} end end angle in radians
+ * @param {Boolean} [antiClockwise=false] draw arc anti-clockwise
+ */
+ strokeArc : function (/*x, y, radius, start, end, antiClockwise*/) {
+ // TODO
+ },
+
+ /**
+ * Stroke an ellipse at the specified coordinates with given radius, start and end points
+ * @name strokeEllipse
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x arc center point x-axis
+ * @param {Number} y arc center point y-axis
+ * @param {Number} w horizontal radius of the ellipse
+ * @param {Number} h vertical radius of the ellipse
+ */
+ strokeEllipse : function (/*x, y, w, h*/) {
+ // TODO
+ },
+
+ /**
+ * Stroke a line of the given two points
+ * @name strokeLine
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} startX the start x coordinate
+ * @param {Number} startY the start y coordinate
+ * @param {Number} endX the end x coordinate
+ * @param {Number} endY the end y coordinate
+ */
+ strokeLine : function (startX, startY, endX, endY) {
+ var points = this._linePoints.slice(0, 2);
+ points[0].x = startX;
+ points[0].y = startY;
+ points[1].x = endX;
+ points[1].y = endY;
+ this.compositor.drawLine(points, true);
+ },
+
+ /**
+ * Strokes a me.Polygon on the screen with a specified color
+ * @name strokePolygon
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.Polygon} poly the shape to draw
+ */
+ strokePolygon : function (poly) {
+ var len = poly.points.length,
+ points,
+ i;
+
+ // Grow internal points buffer if necessary
+ for (i = this._linePoints.length; i < len; i++) {
+ this._linePoints.push(new me.Vector2d());
+ }
+
+ points = this._linePoints.slice(0, len);
+ for (i = 0; i < len; i++) {
+ points[i].x = poly.pos.x + poly.points[i].x;
+ points[i].y = poly.pos.y + poly.points[i].y;
+ }
+ this.compositor.drawLine(points);
+ },
+
+ /**
+ * Draw a stroke rectangle at the specified coordinates
+ * @name strokeRect
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} width
+ * @param {Number} height
+ */
+ strokeRect : function (x, y, width, height) {
+ var points = this._linePoints.slice(0, 4);
+ points[0].x = x;
+ points[0].y = y;
+ points[1].x = x + width;
+ points[1].y = y;
+ points[2].x = x + width;
+ points[2].y = y + height;
+ points[3].x = x;
+ points[3].y = y + height;
+ this.compositor.drawLine(points);
+ },
+
+ /**
+ * draw the given shape
+ * @name drawShape
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object
+ */
+ drawShape : function (shape) {
+ if (shape.shapeType === "Rectangle") {
+ this.strokeRect(shape.left, shape.top, shape.width, shape.height);
+ } else if (shape instanceof me.Line || shape instanceof me.Polygon) {
+ this.strokePolygon(shape);
+ } else if (shape instanceof me.Ellipse) {
+ if (shape.radiusV.x === shape.radiusV.y) {
+ // it's a circle
+ this.strokeArc(
+ shape.pos.x - shape.radius,
+ shape.pos.y - shape.radius,
+ shape.radius,
+ 0,
+ 2 * Math.PI
+ );
+ } else {
+ // it's an ellipse
+ this.strokeEllipse(
+ shape.pos.x,
+ shape.pos.y,
+ shape.radiusV.x,
+ shape.radiusV.y
+ );
+ }
+ }
+ },
+
+ /**
+ * Resets (overrides) the renderer transformation matrix to the
+ * identity one, and then apply the given transformation matrix.
+ * @name setTransform
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.Matrix2d} mat2d Matrix to transform by
+ */
+ setTransform : function (mat2d) {
+ this.resetTransform();
+ this.transform(mat2d);
+ },
+
+ /**
+ * Multiply given matrix into the renderer tranformation matrix
+ * @name transform
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {me.Matrix2d} mat2d Matrix to transform by
+ */
+ transform : function (mat2d) {
+ this.currentTransform.multiply(mat2d);
+ if (this.subPixel === false) {
+ // snap position values to pixel grid
+ var a = this.currentTransform.val;
+ a[6] = ~~a[6];
+ a[7] = ~~a[7];
+ }
+ },
+
+ /**
+ * Translates the uniform matrix by the given coordinates
+ * @name translate
+ * @memberOf me.WebGLRenderer
+ * @function
+ * @param {Number} x
+ * @param {Number} y
+ */
+ translate : function (x, y) {
+ if (this.subPixel === false) {
+ this.currentTransform.translate(~~x, ~~y);
+ } else {
+ this.currentTransform.translate(x, y);
+ }
+ }
+ });
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * A Texture atlas object for WebGL
+ * For portability, a global reference to this class is available through the default renderer: {@link me.video.renderer}.Texture
+ *
+ * Currently supports :
+ * - [TexturePacker]{@link http://www.codeandweb.com/texturepacker/} : through JSON export
+ * - [ShoeBox]{@link http://renderhjs.net/shoebox/} : through JSON export using the
+ * melonJS setting [file]{@link https://github.com/melonjs/melonJS/raw/master/media/shoebox_JSON_export.sbx}
+ * - Standard (fixed cell size) spritesheet : through a {framewidth:xx, frameheight:xx} object
+ * @class
+ * @extends me.CanvasRenderer
+ * @memberOf me.WebGLRenderer
+ * @name Texture
+ * @constructor
+ * @param {Object} atlas atlas information. See {@link me.loader.getJSON}
+ * @param {Image} [texture=atlas.meta.image] texture name
+ * @param {Boolean} [cached=false] Use true to skip caching this Texture
+ * @example
+ * // create a texture atlas from a JSON Object
+ * texture = new me.video.renderer.Texture(
+ * me.loader.getJSON("texture"),
+ * me.loader.getImage("texture")
+ * );
+ *
+ * // create a texture atlas for a spritesheet
+ * texture = new me.video.renderer.Texture(
+ * { framewidth : 32, frameheight : 32 },
+ * me.loader.getImage("spritesheet")
+ * );
+ */
+ me.WebGLRenderer.prototype.Texture = me.CanvasRenderer.prototype.Texture.extend(
+ /** @scope me.video.renderer.Texture.prototype */
+ {
+ /**
+ * @ignore
+ */
+ parse : function (data) {
+ var w = data.meta.size.w;
+ var h = data.meta.size.h;
+ var atlas = me.CanvasRenderer.prototype.Texture.prototype.parse.apply(this, [ data ]);
+
+ return this._addStMap(atlas, w, h);
+ },
+
+ /**
+ * @ignore
+ */
+ parseFromSpriteSheet : function (data) {
+ var w = data.image.width;
+ var h = data.image.height;
+ var atlas = me.CanvasRenderer.prototype.Texture.prototype.parseFromSpriteSheet.apply(this, [ data ]);
+
+ return this._addStMap(atlas, w, h);
+ },
+
+ /**
+ * @ignore
+ */
+ _addStMap : function (atlas, w, h) {
+ Object.keys(atlas).forEach(function (frame) {
+ // Source coordinates
+ var s = atlas[frame].offset;
+ var sw = atlas[frame].width;
+ var sh = atlas[frame].height;
+
+ // ST texture coordinates
+ atlas[frame].stMap = new Float32Array([
+ s.x / w, // Left
+ s.y / h, // Top
+ (s.x + sw) / w, // Right
+ (s.y + sh) / h // Bottom
+ ]);
+
+ // Cache source coordinates
+ // TODO: Remove this when the Batcher only accepts a region name
+ var key = s.x + "," + s.y + "," + w + "," + h;
+ atlas[key] = atlas[frame];
+ });
+ return atlas;
+ },
+
+ /**
+ * @ignore
+ */
+ _insertRegion : function (name, x, y, w, h) {
+ var dw = this.texture.width;
+ var dh = this.texture.height;
+ this.atlas[name] = {
+ name : name,
+ offset : new me.Vector2d(x, y),
+ width : w,
+ height : h,
+ angle : 0,
+ stMap : new Float32Array([
+ x / dw, // Left
+ y / dh, // Top
+ (x + w) / dw, // Right
+ (y + h) / dh // Bottom
+ ])
+ };
+
+ return this.atlas[name];
+ }
+ });
+
+ /**
+ * Base class for Texture exception handling.
+ * @ignore
+ */
+ me.WebGLRenderer.prototype.Texture.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.WebGLRenderer.Texture.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016 Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+(function () {
+
+ // Handy constants
+ var VERTEX_SIZE = 2;
+ var COLOR_SIZE = 4;
+ var TEXTURE_SIZE = 1;
+ var REGION_SIZE = 2;
+
+ var ELEMENT_SIZE = VERTEX_SIZE + COLOR_SIZE + TEXTURE_SIZE + REGION_SIZE;
+ var ELEMENT_OFFSET = ELEMENT_SIZE * Float32Array.BYTES_PER_ELEMENT;
+
+ var VERTEX_ELEMENT = 0;
+ var COLOR_ELEMENT = VERTEX_ELEMENT + VERTEX_SIZE;
+ var TEXTURE_ELEMENT = COLOR_ELEMENT + COLOR_SIZE;
+ var REGION_ELEMENT = TEXTURE_ELEMENT + TEXTURE_SIZE;
+
+ var VERTEX_OFFSET = VERTEX_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
+ var COLOR_OFFSET = COLOR_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
+ var TEXTURE_OFFSET = TEXTURE_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
+ var REGION_OFFSET = REGION_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
+
+ var ELEMENTS_PER_QUAD = 4;
+ var INDICES_PER_QUAD = 6;
+
+ var MAX_LENGTH = 16000;
+
+ /**
+ * A WebGL texture Compositor object. This class handles all of the WebGL state
+ * Pushes texture regions into WebGL buffers, automatically flushes to GPU
+ * @extends me.Object
+ * @namespace me.WebGLRenderer.Compositor
+ * @memberOf me
+ * @constructor
+ * @param {me.WebGLRenderer} renderer the current WebGL renderer session
+ */
+ me.WebGLRenderer.Compositor = me.Object.extend(
+ /** @scope me.WebGLRenderer.Compositor.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init : function (renderer) {
+ // local reference
+ var gl = renderer.gl;
+
+ /**
+ * The number of quads held in the batch
+ * @name length
+ * @memberOf me.WebGLRenderer.Compositor
+ * @type Number
+ * @readonly
+ */
+ this.length = 0;
+
+ // Hash map of texture units
+ this.units = [];
+ /*
+ * XXX: The GLSL compiler pukes with "memory exhausted" when it is
+ * given long if-then-else chains.
+ *
+ * See: http://stackoverflow.com/questions/15828966/glsl-compile-error-memory-exhausted
+ *
+ * Workaround the problem by limiting the max texture support to 24.
+ * The magic number was determined by testing under different UAs.
+ * All Desktop UAs were capable of compiling with 27 fragment shader
+ * samplers. Using 24 seems like a reasonable compromise;
+ *
+ * 24 = 2^4 + 2^3
+ *
+ * As of October 2015, approximately 4.2% of all WebGL-enabled UAs
+ * support more than 24 max textures, according to
+ * http://webglstats.com/
+ */
+ this.maxTextures = Math.min(24, gl.getParameter(
+ gl.MAX_TEXTURE_IMAGE_UNITS
+ ));
+
+ // Vector pool
+ this.v = [
+ new me.Vector2d(),
+ new me.Vector2d(),
+ new me.Vector2d(),
+ new me.Vector2d()
+ ];
+
+ // the associated renderer
+ // TODO : add a set context or whatever function, and split
+ // the constructor accordingly, so that this is easier to restore
+ // the GL context when lost
+ this.renderer = renderer;
+
+ // WebGL context
+ this.gl = renderer.gl;
+
+ // Global transformation matrix
+ this.matrix = renderer.currentTransform;
+
+ // Global color
+ this.color = renderer.currentColor;
+
+ // Uniform projection matrix
+ this.uMatrix = new me.Matrix2d();
+
+ // Detect GPU capabilities
+ var precision = (gl.getShaderPrecisionFormat(
+ gl.FRAGMENT_SHADER,
+ gl.HIGH_FLOAT
+ ).precision < 16) ? "mediump" : "highp";
+
+ // Load and create shader programs
+ /* eslint-disable */
+ this.lineShader = me.video.shader.createShader(
+ this.gl,
+ (function anonymous(ctx){var out='precision highp float;attribute vec2 aVertex;uniform mat3 uMatrix;void main(void){gl_Position=vec4((uMatrix*vec3(aVertex,1)).xy,0,1);}';return out;})(),
+ (function anonymous(ctx){var out='precision '+(ctx.precision)+' float;uniform vec4 uColor;void main(void){gl_FragColor=uColor;}';return out;})({
+ "precision" : precision
+ })
+ );
+ this.quadShader = me.video.shader.createShader(
+ this.gl,
+ (function anonymous(ctx){var out='precision highp float;attribute vec2 aVertex;attribute vec4 aColor;attribute float aTexture;attribute vec2 aRegion;uniform mat3 uMatrix;varying vec4 vColor;varying float vTexture;varying vec2 vRegion;void main(void){gl_Position=vec4((uMatrix*vec3(aVertex,1)).xy,0,1);vColor=vec4(aColor.rgb*aColor.a,aColor.a);vTexture=aTexture;vRegion=aRegion;}';return out;})(),
+ (function anonymous(ctx){var out='precision '+(ctx.precision)+' float;uniform sampler2D uSampler['+(ctx.maxTextures)+'];varying vec4 vColor;varying float vTexture;varying vec2 vRegion;void main(void){int texture=int(vTexture);if(texture==0){gl_FragColor=texture2D(uSampler[0],vRegion)*vColor;}';for(var i=1;i
+ * This is also configurable per key through the bindKey function
+ * default : true
+ * @public
+ * @type Boolean
+ * @name preventDefault
+ * @memberOf me.input
+ */
+ api.preventDefault = true;
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org/
+ *
+ */
+(function (api) {
+ /*
+ * PRIVATE STUFF
+ */
+
+ // list of binded keys
+ api._KeyBinding = {};
+
+ // corresponding actions
+ var keyStatus = {};
+
+ // lock enable flag for keys
+ var keyLock = {};
+ // actual lock status of each key
+ var keyLocked = {};
+
+ // List of binded keys being held
+ var keyRefs = {};
+
+ // whether default event should be prevented for a given keypress
+ var preventDefaultForKeys = {};
+
+ // some useful flags
+ var keyboardInitialized = false;
+
+ /**
+ * enable keyboard event
+ * @ignore
+ */
+ api._enableKeyboardEvent = function () {
+ if (!keyboardInitialized) {
+ window.addEventListener("keydown", api._keydown, false);
+ window.addEventListener("keyup", api._keyup, false);
+ keyboardInitialized = true;
+ }
+ };
+
+ /**
+ * key down event
+ * @ignore
+ */
+ api._keydown = function (e, keyCode, mouseButton) {
+
+ keyCode = keyCode || e.keyCode || e.which;
+ var action = api._KeyBinding[keyCode];
+
+ // publish a message for keydown event
+ me.event.publish(me.event.KEYDOWN, [
+ action,
+ keyCode,
+ action ? !keyLocked[action] : true
+ ]);
+
+ if (action) {
+ if (!keyLocked[action]) {
+ var trigger = mouseButton ? mouseButton : keyCode;
+ if (!keyRefs[action][trigger]) {
+ keyStatus[action]++;
+ keyRefs[action][trigger] = true;
+ }
+ }
+ // prevent event propagation
+ if (preventDefaultForKeys[keyCode]) {
+ return api._preventDefault(e);
+ }
+ else {
+ return true;
+ }
+ }
+
+ return true;
+ };
+
+
+ /**
+ * key up event
+ * @ignore
+ */
+ api._keyup = function (e, keyCode, mouseButton) {
+ keyCode = keyCode || e.keyCode || e.which;
+ var action = api._KeyBinding[keyCode];
+
+ // publish a message for keydown event
+ me.event.publish(me.event.KEYUP, [ action, keyCode ]);
+
+ if (action) {
+ var trigger = mouseButton ? mouseButton : keyCode;
+ keyRefs[action][trigger] = undefined;
+
+ if (keyStatus[action] > 0) {
+ keyStatus[action]--;
+ }
+
+ keyLocked[action] = false;
+
+ // prevent event propagation
+ if (preventDefaultForKeys[keyCode]) {
+ return api._preventDefault(e);
+ }
+ else {
+ return true;
+ }
+ }
+
+ return true;
+ };
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * standard keyboard constants
+ * @public
+ * @enum {number}
+ * @namespace KEY
+ * @memberOf me.input
+ */
+ api.KEY = {
+ /** @memberOf me.input.KEY */
+ "BACKSPACE" : 8,
+ /** @memberOf me.input.KEY */
+ "TAB" : 9,
+ /** @memberOf me.input.KEY */
+ "ENTER" : 13,
+ /** @memberOf me.input.KEY */
+ "SHIFT" : 16,
+ /** @memberOf me.input.KEY */
+ "CTRL" : 17,
+ /** @memberOf me.input.KEY */
+ "ALT" : 18,
+ /** @memberOf me.input.KEY */
+ "PAUSE" : 19,
+ /** @memberOf me.input.KEY */
+ "CAPS_LOCK" : 20,
+ /** @memberOf me.input.KEY */
+ "ESC" : 27,
+ /** @memberOf me.input.KEY */
+ "SPACE" : 32,
+ /** @memberOf me.input.KEY */
+ "PAGE_UP" : 33,
+ /** @memberOf me.input.KEY */
+ "PAGE_DOWN" : 34,
+ /** @memberOf me.input.KEY */
+ "END" : 35,
+ /** @memberOf me.input.KEY */
+ "HOME" : 36,
+ /** @memberOf me.input.KEY */
+ "LEFT" : 37,
+ /** @memberOf me.input.KEY */
+ "UP" : 38,
+ /** @memberOf me.input.KEY */
+ "RIGHT" : 39,
+ /** @memberOf me.input.KEY */
+ "DOWN" : 40,
+ /** @memberOf me.input.KEY */
+ "PRINT_SCREEN" : 42,
+ /** @memberOf me.input.KEY */
+ "INSERT" : 45,
+ /** @memberOf me.input.KEY */
+ "DELETE" : 46,
+ /** @memberOf me.input.KEY */
+ "NUM0" : 48,
+ /** @memberOf me.input.KEY */
+ "NUM1" : 49,
+ /** @memberOf me.input.KEY */
+ "NUM2" : 50,
+ /** @memberOf me.input.KEY */
+ "NUM3" : 51,
+ /** @memberOf me.input.KEY */
+ "NUM4" : 52,
+ /** @memberOf me.input.KEY */
+ "NUM5" : 53,
+ /** @memberOf me.input.KEY */
+ "NUM6" : 54,
+ /** @memberOf me.input.KEY */
+ "NUM7" : 55,
+ /** @memberOf me.input.KEY */
+ "NUM8" : 56,
+ /** @memberOf me.input.KEY */
+ "NUM9" : 57,
+ /** @memberOf me.input.KEY */
+ "A" : 65,
+ /** @memberOf me.input.KEY */
+ "B" : 66,
+ /** @memberOf me.input.KEY */
+ "C" : 67,
+ /** @memberOf me.input.KEY */
+ "D" : 68,
+ /** @memberOf me.input.KEY */
+ "E" : 69,
+ /** @memberOf me.input.KEY */
+ "F" : 70,
+ /** @memberOf me.input.KEY */
+ "G" : 71,
+ /** @memberOf me.input.KEY */
+ "H" : 72,
+ /** @memberOf me.input.KEY */
+ "I" : 73,
+ /** @memberOf me.input.KEY */
+ "J" : 74,
+ /** @memberOf me.input.KEY */
+ "K" : 75,
+ /** @memberOf me.input.KEY */
+ "L" : 76,
+ /** @memberOf me.input.KEY */
+ "M" : 77,
+ /** @memberOf me.input.KEY */
+ "N" : 78,
+ /** @memberOf me.input.KEY */
+ "O" : 79,
+ /** @memberOf me.input.KEY */
+ "P" : 80,
+ /** @memberOf me.input.KEY */
+ "Q" : 81,
+ /** @memberOf me.input.KEY */
+ "R" : 82,
+ /** @memberOf me.input.KEY */
+ "S" : 83,
+ /** @memberOf me.input.KEY */
+ "T" : 84,
+ /** @memberOf me.input.KEY */
+ "U" : 85,
+ /** @memberOf me.input.KEY */
+ "V" : 86,
+ /** @memberOf me.input.KEY */
+ "W" : 87,
+ /** @memberOf me.input.KEY */
+ "X" : 88,
+ /** @memberOf me.input.KEY */
+ "Y" : 89,
+ /** @memberOf me.input.KEY */
+ "Z" : 90,
+ /** @memberOf me.input.KEY */
+ "WINDOW_KEY" : 91,
+ /** @memberOf me.input.KEY */
+ "NUMPAD0" : 96,
+ /** @memberOf me.input.KEY */
+ "NUMPAD1" : 97,
+ /** @memberOf me.input.KEY */
+ "NUMPAD2" : 98,
+ /** @memberOf me.input.KEY */
+ "NUMPAD3" : 99,
+ /** @memberOf me.input.KEY */
+ "NUMPAD4" : 100,
+ /** @memberOf me.input.KEY */
+ "NUMPAD5" : 101,
+ /** @memberOf me.input.KEY */
+ "NUMPAD6" : 102,
+ /** @memberOf me.input.KEY */
+ "NUMPAD7" : 103,
+ /** @memberOf me.input.KEY */
+ "NUMPAD8" : 104,
+ /** @memberOf me.input.KEY */
+ "NUMPAD9" : 105,
+ /** @memberOf me.input.KEY */
+ "MULTIPLY" : 106,
+ /** @memberOf me.input.KEY */
+ "ADD" : 107,
+ /** @memberOf me.input.KEY */
+ "SUBSTRACT" : 109,
+ /** @memberOf me.input.KEY */
+ "DECIMAL" : 110,
+ /** @memberOf me.input.KEY */
+ "DIVIDE" : 111,
+ /** @memberOf me.input.KEY */
+ "F1" : 112,
+ /** @memberOf me.input.KEY */
+ "F2" : 113,
+ /** @memberOf me.input.KEY */
+ "F3" : 114,
+ /** @memberOf me.input.KEY */
+ "F4" : 115,
+ /** @memberOf me.input.KEY */
+ "F5" : 116,
+ /** @memberOf me.input.KEY */
+ "F6" : 117,
+ /** @memberOf me.input.KEY */
+ "F7" : 118,
+ /** @memberOf me.input.KEY */
+ "F8" : 119,
+ /** @memberOf me.input.KEY */
+ "F9" : 120,
+ /** @memberOf me.input.KEY */
+ "F10" : 121,
+ /** @memberOf me.input.KEY */
+ "F11" : 122,
+ /** @memberOf me.input.KEY */
+ "F12" : 123,
+ /** @memberOf me.input.KEY */
+ "NUM_LOCK" : 144,
+ /** @memberOf me.input.KEY */
+ "SCROLL_LOCK" : 145,
+ /** @memberOf me.input.KEY */
+ "SEMICOLON" : 186,
+ /** @memberOf me.input.KEY */
+ "PLUS" : 187,
+ /** @memberOf me.input.KEY */
+ "COMMA" : 188,
+ /** @memberOf me.input.KEY */
+ "MINUS" : 189,
+ /** @memberOf me.input.KEY */
+ "PERIOD" : 190,
+ /** @memberOf me.input.KEY */
+ "FORWAND_SLASH" : 191,
+ /** @memberOf me.input.KEY */
+ "GRAVE_ACCENT" : 192,
+ /** @memberOf me.input.KEY */
+ "OPEN_BRACKET" : 219,
+ /** @memberOf me.input.KEY */
+ "BACK_SLASH" : 220,
+ /** @memberOf me.input.KEY */
+ "CLOSE_BRACKET" : 221,
+ /** @memberOf me.input.KEY */
+ "SINGLE_QUOTE" : 222
+ };
+
+ /**
+ * return the key press status of the specified action
+ * @name isKeyPressed
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {String} action user defined corresponding action
+ * @return {Boolean} true if pressed
+ * @example
+ * if (me.input.isKeyPressed('left'))
+ * {
+ * //do something
+ * }
+ * else if (me.input.isKeyPressed('right'))
+ * {
+ * //do something else...
+ * }
+ *
+ */
+ api.isKeyPressed = function (action) {
+ if (keyStatus[action] && !keyLocked[action]) {
+ if (keyLock[action]) {
+ keyLocked[action] = true;
+ }
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * return the key status of the specified action
+ * @name keyStatus
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {String} action user defined corresponding action
+ * @return {Boolean} down (true) or up(false)
+ */
+ api.keyStatus = function (action) {
+ return (keyStatus[action] > 0);
+ };
+
+
+ /**
+ * trigger the specified key (simulated) event
+ * @name triggerKeyEvent
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {me.input.KEY} keycode
+ * @param {Boolean} [status=false] true to trigger a key press, or false for key release
+ * @example
+ * // trigger a key press
+ * me.input.triggerKeyEvent(me.input.KEY.LEFT, true);
+ */
+ api.triggerKeyEvent = function (keycode, status) {
+ if (status) {
+ api._keydown({}, keycode);
+ }
+ else {
+ api._keyup({}, keycode);
+ }
+ };
+
+
+ /**
+ * associate a user defined action to a keycode
+ * @name bindKey
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {me.input.KEY} keycode
+ * @param {String} action user defined corresponding action
+ * @param {Boolean} [lock=false] cancel the keypress event once read
+ * @param {Boolean} [preventDefault=me.input.preventDefault] prevent default browser action
+ * @example
+ * // enable the keyboard
+ * me.input.bindKey(me.input.KEY.LEFT, "left");
+ * me.input.bindKey(me.input.KEY.RIGHT, "right");
+ * me.input.bindKey(me.input.KEY.X, "jump", true);
+ * me.input.bindKey(me.input.KEY.F1, "options", true, true);
+ */
+ api.bindKey = function (keycode, action, lock, preventDefault) {
+ // make sure the keyboard is enable
+ api._enableKeyboardEvent();
+
+ if (typeof preventDefault !== "boolean") {
+ preventDefault = api.preventDefault;
+ }
+
+ api._KeyBinding[keycode] = action;
+ preventDefaultForKeys[keycode] = preventDefault;
+
+ keyStatus[action] = 0;
+ keyLock[action] = lock ? lock : false;
+ keyLocked[action] = false;
+ keyRefs[action] = {};
+ };
+
+ /**
+ * unlock a key manually
+ * @name unlockKey
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {String} action user defined corresponding action
+ * @example
+ * // Unlock jump when touching the ground
+ * if (!this.falling && !this.jumping) {
+ * me.input.unlockKey("jump");
+ * }
+ */
+ api.unlockKey = function (action) {
+ keyLocked[action] = false;
+ };
+
+ /**
+ * unbind the defined keycode
+ * @name unbindKey
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {me.input.KEY} keycode
+ * @example
+ * me.input.unbindKey(me.input.KEY.LEFT);
+ */
+ api.unbindKey = function (keycode) {
+ // clear the event status
+ var keybinding = api._KeyBinding[keycode];
+ keyStatus[keybinding] = 0;
+ keyLock[keybinding] = false;
+ keyRefs[keybinding] = {};
+ // remove the key binding
+ api._KeyBinding[keycode] = null;
+ preventDefaultForKeys[keycode] = null;
+ };
+})(me.input);
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org/
+ *
+ */
+(function (api) {
+ /**
+ * The built in Event Object
+ * @external Event
+ * @see {@link https://developer.mozilla.org/en/docs/Web/API/Event|Event}
+ */
+
+ /**
+ * Event normalized X coordinate within the game canvas itself
+ *
+ * @memberof! external:Event#
+ * @name external:Event#gameX
+ * @type {Number}
+ */
+
+ /**
+ * Event normalized Y coordinate within the game canvas itself
+ *
+ * @memberof! external:Event#
+ * @name external:Event#gameY
+ * @type {Number}
+ */
+
+ /**
+ * Event X coordinate relative to the viewport
+ * @memberof! external:Event#
+ * @name external:Event#gameScreenX
+ * @type {Number}
+ */
+
+ /**
+ * Event Y coordinate relative to the viewport
+ * @memberof! external:Event#
+ * @name external:Event#gameScreenY
+ * @type {Number}
+ */
+
+ /**
+ * Event X coordinate relative to the map
+ * @memberof! external:Event#
+ * @name external:Event#gameWorldX
+ * @type {Number}
+ */
+
+ /**
+ * Event Y coordinate relative to the map
+ * @memberof! external:Event#
+ * @name external:Event#gameWorldY
+ * @type {Number}
+ */
+
+ /**
+ * Event X coordinate relative to the holding container
+ * @memberof! external:Event#
+ * @name external:Event#gameLocalX
+ * @type {Number}
+ */
+
+ /**
+ * Event Y coordinate relative to the holding container
+ * @memberof! external:Event#
+ * @name external:Event#gameLocalY
+ * @type {Number}
+ */
+
+ /**
+ * The unique identifier of the contact for a touch, mouse or pen
+ * (This id is also defined on non Pointer Event Compatible platform like pure mouse or iOS-like touch event)
+ * @memberof! external:Event#
+ * @name external:Event#pointerId
+ * @type {Number}
+ * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh466123.aspx
+ */
+
+ /*
+ * PRIVATE STUFF
+ */
+
+ /**
+ * A pool of `Vector` objects to cache pointer/touch event coordinates.
+ * @type {Array.
+ * properties :
+ * LEFT : constant for left button
+ * MIDDLE : constant for middle button
+ * RIGHT : constant for right button
+ * @public
+ * @type {me.Rect}
+ * @name pointer
+ * @memberOf me.input
+ */
+ api.pointer = new me.Rect(0, 0, 1, 1);
+
+ // bind list for mouse buttons
+ api.pointer.bind = [ 0, 0, 0 ];
+
+ // W3C button constants
+ api.pointer.LEFT = 0;
+ api.pointer.MIDDLE = 1;
+ api.pointer.RIGHT = 2;
+
+ /**
+ * time interval for event throttling in milliseconds
+ * default value : "1000/me.sys.fps" ms
+ * set to 0 ms to disable the feature
+ * @public
+ * @type Number
+ * @name throttlingInterval
+ * @memberOf me.input
+ */
+ api.throttlingInterval = undefined;
+
+ /**
+ * Translate the specified x and y values from the global (absolute)
+ * coordinate to local (viewport) relative coordinate.
+ * @name globalToLocal
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {Number} x the global x coordinate to be translated.
+ * @param {Number} y the global y coordinate to be translated.
+ * @param {Number} [v] an optional vector object where to set the
+ * @return {me.Vector2d} A vector object with the corresponding translated coordinates.
+ * @example
+ * onMouseEvent : function (e) {
+ * // convert the given into local (viewport) relative coordinates
+ * var pos = me.input.globalToLocal(e.clientX, e,clientY);
+ * // do something with pos !
+ * };
+ */
+ api.globalToLocal = function (x, y, v) {
+ v = v || new me.Vector2d();
+ var offset = api._offset;
+ var pixelRatio = me.device.getPixelRatio();
+ x -= offset.left;
+ y -= offset.top;
+ var scale = me.sys.scale;
+ if (scale.x !== 1.0 || scale.y !== 1.0) {
+ x /= scale.x;
+ y /= scale.y;
+ }
+ return v.set(x * pixelRatio, y * pixelRatio);
+ };
+
+ /**
+ * Associate a pointer event to a keycode
+ * Left button â 0
+ * Middle button â 1
+ * Right button â 2
+ * @name bindPointer
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {Number} [button=me.input.pointer.LEFT] (accordingly to W3C values : 0,1,2 for left, middle and right buttons)
+ * @param {me.input.KEY} keyCode
+ * @example
+ * // enable the keyboard
+ * me.input.bindKey(me.input.KEY.X, "shoot");
+ * // map the left button click on the X key (default if the button is not specified)
+ * me.input.bindPointer(me.input.KEY.X);
+ * // map the right button click on the X key
+ * me.input.bindPointer(me.input.pointer.RIGHT, me.input.KEY.X);
+ */
+ api.bindPointer = function () {
+ var button = (arguments.length < 2) ? api.pointer.LEFT : arguments[0];
+ var keyCode = (arguments.length < 2) ? arguments[0] : arguments[1];
+
+ // make sure the mouse is initialized
+ enablePointerEvent();
+
+ // throw an exception if no action is defined for the specified keycode
+ if (!api._KeyBinding[keyCode]) {
+ throw new me.Error("no action defined for keycode " + keyCode);
+ }
+ // map the mouse button to the keycode
+ api.pointer.bind[button] = keyCode;
+ };
+ /**
+ * unbind the defined keycode
+ * @name unbindPointer
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {Number} [button=me.input.pointer.LEFT] (accordingly to W3C values : 0,1,2 for left, middle and right buttons)
+ * @example
+ * me.input.unbindPointer(me.input.pointer.LEFT);
+ */
+ api.unbindPointer = function (button) {
+ // clear the event status
+ api.pointer.bind[
+ typeof(button) === "undefined" ?
+ api.pointer.LEFT : button
+ ] = null;
+ };
+
+
+ /**
+ * allows registration of event listeners on the object target.
+ * melonJS defines the additional `gameX` and `gameY` properties when passing the Event object to the defined callback (see below)
+ * @see external:Event
+ * @see {@link http://www.w3.org/TR/pointerevents/#list-of-pointer-events|W3C Pointer Event list}
+ * @name registerPointerEvent
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {String} eventType The event type for which the object is registering
+ * melonJS currently supports:
+ *
+ *
+ * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} region a shape representing the region to register on
+ * @param {Function} callback methods to be called when the event occurs.
+ * Returning `false` from the defined callback will prevent the event to be propagated to other objects
+ * @example
+ * // onActivate function
+ * onActivateEvent: function () {
+ * // register on the 'pointerdown' event
+ * me.input.registerPointerEvent('pointerdown', this, this.pointerDown.bind(this));
+ * },
+ *
+ * // pointerDown event callback
+ * pointerDown: function (event) {
+ * // do something
+ * ....
+ * // don"t propagate the event to other objects
+ * return false;
+ * },
+ */
+ api.registerPointerEvent = function (eventType, region, callback) {
+ // make sure the mouse/touch events are initialized
+ enablePointerEvent();
+
+ if (pointerEventList.indexOf(eventType) === -1) {
+ throw new me.Error("invalid event type : " + eventType);
+ }
+
+ var eventTypes = findAllActiveEvents(activeEventList, pointerEventMap[eventType]);
+
+ // register the event
+ if (!evtHandlers.has(region)) {
+ evtHandlers.set(region, {
+ region : region,
+ callbacks : {},
+ pointerId : null
+ });
+ }
+
+ // allocate array if not defined
+ var handlers = evtHandlers.get(region);
+ for (var i = 0; i < eventTypes.length; i++) {
+ eventType = eventTypes[i];
+ if (handlers.callbacks[eventType]) {
+ handlers.callbacks[eventType].push(callback);
+ } else {
+ handlers.callbacks[eventType] = [callback];
+ }
+ }
+ };
+
+ /**
+ * allows the removal of event listeners from the object target.
+ * @see {@link http://www.w3.org/TR/pointerevents/#list-of-pointer-events|W3C Pointer Event list}
+ * @name releasePointerEvent
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {String} eventType The event type for which the object was registered. See {@link me.input.registerPointerEvent}
+ * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} region the registered region to release for this event
+ * @param {Function} [callback="all"] if specified unregister the event only for the specific callback
+ * @example
+ * // release the registered region on the 'pointerdown' event
+ * me.input.releasePointerEvent('pointerdown', this);
+ */
+ api.releasePointerEvent = function (eventType, region, callback) {
+ if (pointerEventList.indexOf(eventType) === -1) {
+ throw new me.Error("invalid event type : " + eventType);
+ }
+
+ // convert to supported event type if pointerEvent not natively supported
+ var eventTypes = findAllActiveEvents(activeEventList, pointerEventMap[eventType]);
+
+ var handlers = evtHandlers.get(region);
+ for (var i = 0; i < eventTypes.length; i++) {
+ eventType = eventTypes[i];
+ if (handlers.callbacks[eventType]) {
+ if (typeof (callback) !== "undefined") {
+ handlers.callbacks[eventType].remove(callback);
+ } else {
+ while (handlers.callbacks[eventType].length > 0) {
+ handlers.callbacks[eventType].pop();
+ }
+ }
+ // free the array if empty
+ if (handlers.callbacks[eventType].length === 0) {
+ delete handlers.callbacks[eventType];
+ }
+ }
+ }
+ if (Object.keys(handlers.callbacks).length === 0) {
+ evtHandlers.delete(region);
+ }
+ };
+
+ /**
+ * Will translate global (frequently used) pointer events
+ * which should be catched at root level, into minipubsub system events
+ * @name _translatePointerEvents
+ * @memberOf me.input
+ * @ignore
+ * @function
+ */
+ api._translatePointerEvents = function () {
+ // listen to mouse move (and touch move) events on the viewport
+ // and convert them to a system event by default
+ api.registerPointerEvent("pointermove", me.game.viewport, function (e) {
+ me.event.publish(me.event.POINTERMOVE, [e]);
+ });
+ };
+})(me.input);
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2015, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org/
+ *
+ */
+(function (api) {
+ /*
+ * PRIVATE STUFF
+ */
+
+ // Analog deadzone
+ var deadzone = 0.1;
+
+ /**
+ * A function that returns a normalized value in range [-1.0..1.0], or 0.0 if the axis is unknown.
+ * @callback me.input~normalize_fn
+ * @param {Number} value The raw value read from the gamepad driver
+ * @param {Number} axis The axis index from the standard mapping, or -1 if not an axis
+ * @param {Number} button The button index from the standard mapping, or -1 if not a button
+ */
+ function defaultNormalizeFn(value) {
+ return value;
+ }
+
+ /**
+ * Normalize axis values for wired Xbox 360
+ * @ignore
+ */
+ function wiredXbox360NormalizeFn(value, axis, button) {
+ if (button === api.GAMEPAD.BUTTONS.L2 || button === api.GAMEPAD.BUTTONS.R2) {
+ return (value + 1) / 2;
+ }
+ return value;
+ }
+
+ /**
+ * Normalize axis values for OUYA
+ * @ignore
+ */
+ function ouyaNormalizeFn(value, axis, button) {
+ if (value > 0) {
+ if (button === api.GAMEPAD.BUTTONS.L2) {
+ // L2 is wonky; seems like the deadzone is around 20000
+ // (That's over 15% of the total range!)
+ value = Math.max(0, value - 20000) / 111070;
+ }
+ else {
+ // Normalize [1..65536] => [0.0..0.5]
+ value = (value - 1) / 131070;
+ }
+ }
+ else {
+ // Normalize [-65536..-1] => [0.5..1.0]
+ value = (65536 + value) / 131070 + 0.5;
+ }
+
+ return value;
+ }
+
+ // Match vendor and product codes for Firefox
+ var vendorProductRE = /^([0-9a-f]{1,4})-([0-9a-f]{1,4})-/i;
+
+ // Match leading zeros
+ var leadingZeroRE = /^0+/;
+
+ /**
+ * Firefox reports different ids for gamepads depending on the platform:
+ * - Windows: vendor and product codes contain leading zeroes
+ * - Mac: vendor and product codes are sparse (no leading zeroes)
+ *
+ * This function normalizes the id to support both formats
+ * @ignore
+ */
+ function addMapping(id, mapping) {
+ var expanded_id = id.replace(vendorProductRE, function (_, a, b) {
+ return (
+ "000".substr(a.length - 1) + a + "-" +
+ "000".substr(b.length - 1) + b + "-"
+ );
+ });
+ var sparse_id = id.replace(vendorProductRE, function (_, a, b) {
+ return (
+ a.replace(leadingZeroRE, "") + "-" +
+ b.replace(leadingZeroRE, "") + "-"
+ );
+ });
+
+ // Normalize optional parameters
+ mapping.analog = mapping.analog || mapping.buttons.map(function () {
+ return -1;
+ });
+ mapping.normalize_fn = mapping.normalize_fn || defaultNormalizeFn;
+
+ remap.set(expanded_id, mapping);
+ remap.set(sparse_id, mapping);
+ }
+
+ // binding list
+ var bindings = {};
+
+ // mapping list
+ var remap = new Map();
+
+ /**
+ * Default gamepad mappings
+ * @ignore
+ */
+ [
+ // Firefox mappings
+ [
+ "45e-28e-Xbox 360 Wired Controller",
+ {
+ "axes" : [ 0, 1, 3, 4 ],
+ "buttons" : [ 11, 12, 13, 14, 8, 9, -1, -1, 5, 4, 6, 7, 0, 1, 2, 3, 10 ],
+ "analog" : [ -1, -1, -1, -1, -1, -1, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
+ "normalize_fn" : wiredXbox360NormalizeFn
+ }
+ ],
+ [
+ "54c-268-PLAYSTATION(R)3 Controller",
+ {
+ "axes" : [ 0, 1, 2, 3 ],
+ "buttons" : [ 14, 13, 15, 12, 10, 11, 8, 9, 0, 3, 1, 2, 4, 6, 7, 5, 16 ]
+ }
+ ],
+ [
+ "54c-5c4-Wireless Controller", // PS4 Controller
+ {
+ "axes" : [ 0, 1, 2, 3 ],
+ "buttons" : [ 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 12, 13 ]
+ }
+ ],
+ [
+ "2836-1-OUYA Game Controller",
+ {
+ "axes" : [ 0, 3, 7, 9 ],
+ "buttons" : [ 3, 6, 4, 5, 7, 8, 15, 16, -1, -1, 9, 10, 11, 12, 13, 14, -1 ],
+ "analog" : [ -1, -1, -1, -1, -1, -1, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
+ "normalize_fn" : ouyaNormalizeFn
+ }
+ ],
+
+ // Chrome mappings
+ [
+ "OUYA Game Controller (Vendor: 2836 Product: 0001)",
+ {
+ "axes" : [ 0, 1, 3, 4 ],
+ "buttons" : [ 0, 3, 1, 2, 4, 5, 12, 13, -1, -1, 6, 7, 8, 9, 10, 11, -1 ],
+ "analog" : [ -1, -1, -1, -1, -1, -1, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
+ "normalize_fn" : ouyaNormalizeFn
+ }
+ ]
+ ].forEach(function (value) {
+ addMapping(value[0], value[1]);
+ });
+
+ /**
+ * gamepad connected callback
+ * @ignore
+ */
+ window.addEventListener("gamepadconnected", function (event) {
+ me.event.publish(me.event.GAMEPAD_CONNECTED, [ event.gamepad ]);
+ }, false);
+
+ /**
+ * gamepad disconnected callback
+ * @ignore
+ */
+ window.addEventListener("gamepaddisconnected", function (event) {
+ me.event.publish(me.event.GAMEPAD_DISCONNECTED, [ event.gamepad ]);
+ }, false);
+
+ /**
+ * Update gamepad status
+ * @ignore
+ */
+ api._updateGamepads = navigator.getGamepads ? function () {
+ var gamepads = navigator.getGamepads();
+ var e = {};
+
+ // Trigger button bindings
+ Object.keys(bindings).forEach(function (index) {
+ var gamepad = gamepads[index];
+ if (!gamepad) {
+ return;
+ }
+
+ var mapping = null;
+ if (gamepad.mapping !== "standard") {
+ mapping = remap.get(gamepad.id);
+ }
+
+ var binding = bindings[index];
+
+ // Iterate all buttons that have active bindings
+ Object.keys(binding.buttons).forEach(function (button) {
+ var last = binding.buttons[button];
+ var mapped_button = button;
+ var mapped_axis = -1;
+
+ // Remap buttons if necessary
+ if (mapping) {
+ mapped_button = mapping.buttons[button];
+ mapped_axis = mapping.analog[button];
+ if (mapped_button < 0 && mapped_axis < 0) {
+ // Button is not mapped
+ return;
+ }
+ }
+
+ // Get mapped button
+ var current = gamepad.buttons[mapped_button] || {};
+
+ // Remap an axis to an analog button
+ if (mapping) {
+ if (mapped_axis >= 0) {
+ var value = mapping.normalize_fn(gamepad.axes[mapped_axis], -1, +button);
+
+ // Create a new object, because GamepadButton is read-only
+ current = {
+ "value" : value,
+ "pressed" : current.pressed || (Math.abs(value) >= deadzone)
+ };
+ }
+ }
+
+ me.event.publish(me.event.GAMEPAD_UPDATE, [ index, "buttons", +button, current ]);
+
+ // Edge detection
+ if (!last.pressed && current.pressed) {
+ api._keydown(e, last.keyCode, mapped_button + 256);
+ }
+ else if (last.pressed && !current.pressed) {
+ api._keyup(e, last.keyCode, mapped_button + 256);
+ }
+
+ // Update last button state
+ last.value = current.value;
+ last.pressed = current.pressed;
+ });
+
+ // Iterate all axes that have active bindings
+ Object.keys(binding.axes).forEach(function (axis) {
+ var last = binding.axes[axis];
+ var mapped_axis = axis;
+
+ // Remap buttons if necessary
+ if (mapping) {
+ mapped_axis = mapping.axes[axis];
+ if (mapped_axis < 0) {
+ // axe is not mapped
+ return;
+ }
+ }
+
+ // retrieve the current value and normalize if necessary
+ var value = gamepad.axes[mapped_axis];
+ if (typeof(value) === "undefined") {
+ return;
+ }
+ if (mapping) {
+ value = mapping.normalize_fn(value, +axis, -1);
+ }
+ // normalize value into a [-1, 1] range value (treat 0 as positive)
+ var range = Math.sign(value) || 1;
+ if (!last[range]) {
+ return;
+ }
+ var pressed = (Math.abs(value) >= (deadzone + Math.abs(last[range].threshold)));
+
+ me.event.publish(me.event.GAMEPAD_UPDATE, [ index, "axes", +axis, value ]);
+
+ // Edge detection
+ if (!last[range].pressed && pressed) {
+ api._keydown(e, last[range].keyCode, mapped_axis + 256);
+ }
+ else if ((last[range].pressed || (last[-range] && last[-range].pressed)) && !pressed) {
+ range = last[range].pressed ? range : -range;
+ api._keyup(e, last[range].keyCode, mapped_axis + 256);
+ }
+
+ // Update last axis state
+ last[range].value = value;
+ last[range].pressed = pressed;
+ });
+ });
+ } : function () {};
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * Namespace for standard gamepad mapping constants
+ * @public
+ * @namespace GAMEPAD
+ * @memberOf me.input
+ */
+ api.GAMEPAD = {
+ /**
+ * Standard gamepad mapping information for axes"pointermove"
"pointerdown"
"pointerup"
"pointerenter"
"pointerleave"
"pointercancel"
"mousewheel"
+ *
+ *
+ * @public
+ * @name AXES
+ * @enum {Number}
+ * @memberOf me.input.GAMEPAD
+ * @see https://w3c.github.io/gamepad/#remapping
+ */
+ "AXES" : {
+ "LX" : 0,
+ "LY" : 1,
+ "RX" : 2,
+ "RY" : 3,
+ "EXTRA_1" : 4,
+ "EXTRA_2" : 5,
+ "EXTRA_3" : 6,
+ "EXTRA_4" : 7
+ },
+
+ /**
+ * Standard gamepad mapping information for buttonsLX
(horizontal), LY
(vertical)RX
(horizontal), RY
(vertical)EXTRA_1
, EXTRA_2
, EXTRA_3
, EXTRA_4
+ *
+ *
+ * @public
+ * @name BUTTONS
+ * @enum {Number}
+ * @memberOf me.input.GAMEPAD
+ * @see https://w3c.github.io/gamepad/#remapping
+ */
+ "BUTTONS" : {
+ "FACE_1" : 0,
+ "FACE_2" : 1,
+ "FACE_3" : 2,
+ "FACE_4" : 3,
+ "L1" : 4,
+ "R1" : 5,
+ "L2" : 6,
+ "R2" : 7,
+ "SELECT" : 8,
+ "BACK" : 8,
+ "START" : 9,
+ "FORWARD" : 9,
+ "L3" : 10,
+ "R3" : 11,
+ "UP" : 12,
+ "DOWN" : 13,
+ "LEFT" : 14,
+ "RIGHT" : 15,
+ "HOME" : 16,
+ "EXTRA_1" : 17,
+ "EXTRA_2" : 18,
+ "EXTRA_3" : 19,
+ "EXTRA_4" : 20
+ }
+ };
+
+ /**
+ * Associate a gamepad event to a keycode
+ * @name bindGamepad
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {Number} index Gamepad index
+ * @param {me.input.GAMEPAD.BUTTONS|Object} button id (deprecated) or definition as below
+ * @param {String} button.type "buttons" or "axes"
+ * @param {me.input.GAMEPAD.BUTTONS|me.input.GAMEPAD.AXES} button.code button or axis code id
+ * @param {String} [button.threshold] value indicating when the axis should trigger the keycode (e.g. -0.5 or 0.5)
+ * @param {me.input.KEY} keyCode
+ * @example
+ * // enable the keyboard
+ * me.input.bindKey(me.input.KEY.X, "shoot");
+ * ...
+ * // map the lower face button on the first gamepad to the X key (deprecated use)
+ * me.input.bindGamepad(0, me.input.GAMEPAD.BUTTONS.FACE_1, me.input.KEY.X);
+ * // map the lower face button on the first gamepad to the X key
+ * me.input.bindGamepad(0, {type:"buttons", code: me.input.GAMEPAD.BUTTONS.FACE_1}, me.input.KEY.X);
+ * // map the left axis value on the first gamepad to the LEFT key
+ * me.input.bindGamepad(0, {type:"axes", code: me.input.GAMEPAD.AXES.LX, threshold: -0.5}, me.input.KEY.LEFT);
+ */
+ api.bindGamepad = function (index, button, keyCode) {
+ // Throw an exception if no action is defined for the specified keycode
+ if (!api._KeyBinding[keyCode]) {
+ throw new me.Error("no action defined for keycode " + keyCode);
+ }
+
+ // for backward compatiblity with 3.0.x
+ if (typeof (button) !== "object") {
+ button = {
+ type : "buttons",
+ code : button
+ };
+ console.warn("Deprecated: me.input.bindGamepad parameteres have changed");
+ }
+
+ // Allocate bindings if not defined
+ if (!bindings[index]) {
+ bindings[index] = {
+ "axes" : {},
+ "buttons" : {}
+ };
+ }
+
+ var mapping = {
+ "keyCode" : keyCode,
+ "value" : 0,
+ "pressed" : false,
+ "threshold" : button.threshold // can be undefined
+ };
+ var binding = bindings[index][button.type];
+
+ // Map the gamepad button or axis to the keycode
+ if (button.type === "buttons") {
+ // buttons are defined by a `gamePadButton` object
+ binding[button.code] = mapping;
+ } else if (button.type === "axes") {
+ // normalize threshold into a value that can represent both side of the axis
+ var range = (Math.sign(button.threshold) || 1);
+ // axes are defined using a double []
+ if (!binding[button.code]) {
+ binding[button.code] = {};
+ }
+ binding[button.code][range] = mapping;
+ }
+ };
+
+ /**
+ * unbind the defined keycode
+ * @name unbindGamepad
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {Number} index Gamepad index
+ * @param {me.input.GAMEPAD.BUTTONS} button
+ * @example
+ * me.input.unbindGamepad(0, me.input.GAMEPAD.BUTTONS.FACE_1);
+ */
+ api.unbindGamepad = function (index, button) {
+ if (!bindings[index]) {
+ throw new me.Error("no bindings for gamepad " + index);
+ }
+ bindings[index].buttons[button] = {};
+ };
+
+ /**
+ * Set deadzone for analog gamepad inputsFACE_1
, FACE_2
, FACE_3
, FACE_4
UP
, DOWN
, LEFT
, RIGHT
L1
, L2
, R1
, R2
L3
, R3
SELECT
(BACK
), START
(FORWARD
), HOME
EXTRA_1
, EXTRA_2
, EXTRA_3
, EXTRA_4
+ * The default deadzone is 0.1 (10%) Analog values less than this will be ignored
+ * @name setGamepadDeadzone
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {Number} value Deadzone value
+ */
+ api.setGamepadDeadzone = function (value) {
+ deadzone = value;
+ };
+
+ /**
+ * specify a custom mapping for a specific gamepad id
+ * see below for the default mapping :
+ *
+ * @name setGamepadMapping
+ * @memberOf me.input
+ * @public
+ * @function
+ * @param {String} id Gamepad id string
+ * @param {Object} mapping A hash table
+ * @param {Number[]} mapping.axes Standard analog control stick axis locations
+ * @param {Number[]} mapping.buttons Standard digital button locations
+ * @param {Number[]} [mapping.analog] Analog axis locations for buttons
+ * @param {me.input~normalize_fn} [mapping.normalize_fn] Axis normalization function
+ * @example
+ * // A weird controller that has its axis mappings reversed
+ * me.input.setGamepadMapping("Generic USB Controller", {
+ * "axes" : [ 3, 2, 1, 0 ],
+ * "buttons" : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
+ * });
+ *
+ * // Mapping extra axes to analog buttons
+ * me.input.setGamepadMapping("Generic Analog Controller", {
+ * "axes" : [ 0, 1, 2, 3 ],
+ * "buttons" : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ],
+ *
+ * // Raw axis 4 is mapped to GAMEPAD.BUTTONS.FACE_1
+ * // Raw axis 5 is mapped to GAMEPAD.BUTTONS.FACE_2
+ * // etc...
+ * // Also maps left and right triggers
+ * "analog" : [ 4, 5, 6, 7, -1, -1, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
+ *
+ * // Normalize the value of button L2: [-1.0..1.0] => [0.0..1.0]
+ * "normalize_fn" : function (value, axis, button) {
+ * return ((button === me.input.GAMEPAD.BUTTONS.L2) ? ((value + 1) / 2) : value) || 0;
+ * }
+ * });
+ */
+ api.setGamepadMapping = addMapping;
+
+})(me.input);
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * Base64 decoding
+ * @see http://www.webtoolkit.info/
+ * @ignore
+ */
+ var Base64 = (function () {
+ // hold public stuff in our singleton
+ var singleton = {};
+
+ // private property
+ var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+ // public method for decoding
+ singleton.decode = function (input) {
+
+ // make sure our input string has the right format
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ if (me.device.nativeBase64) {
+ // use native decoder
+ return window.atob(input);
+ }
+ else {
+ // use cross-browser decoding
+ var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
+
+ while (i < input.length) {
+ enc1 = _keyStr.indexOf(input.charAt(i++));
+ enc2 = _keyStr.indexOf(input.charAt(i++));
+ enc3 = _keyStr.indexOf(input.charAt(i++));
+ enc4 = _keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output.push(String.fromCharCode(chr1));
+
+ if (enc3 !== 64) {
+ output.push(String.fromCharCode(chr2));
+ }
+ if (enc4 !== 64) {
+ output.push(String.fromCharCode(chr3));
+ }
+ }
+
+ output = output.join("");
+ return output;
+ }
+ };
+
+ // public method for encoding
+ singleton.encode = function (input) {
+
+ // make sure our input string has the right format
+ input = input.replace(/\r\n/g, "\n");
+
+ if (me.device.nativeBase64) {
+ // use native encoder
+ return window.btoa(input);
+ }
+ else {
+ // use cross-browser encoding
+ var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
+
+
+ while (i < input.length) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output.push(_keyStr.charAt(enc1));
+ output.push(_keyStr.charAt(enc2));
+ output.push(_keyStr.charAt(enc3));
+ output.push(_keyStr.charAt(enc4));
+ }
+
+ output = output.join("");
+ return output;
+ }
+ };
+
+ return singleton;
+
+ })();
+
+ /**
+ * a collection of utility functions
+ * there is no constructor function for me.utils
+ * @namespace me.utils
+ * @memberOf me
+ */
+ me.utils = (function () {
+ // hold public stuff in our singleton
+ var api = {};
+
+ /*
+ * PRIVATE STUFF
+ */
+
+ // guid default value
+ var GUID_base = "";
+ var GUID_index = 0;
+
+ // regexp to deal with file name & path
+ var REMOVE_PATH = /^.*(\\|\/|\:)/;
+ var REMOVE_EXT = /\.[^\.]*$/;
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * Decode a base64 encoded string into a binary string
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name decodeBase64
+ * @param {String} input Base64 encoded data
+ * @return {String} Binary string
+ */
+ api.decodeBase64 = function (input) {
+ return Base64.decode(input);
+ };
+
+ /**
+ * Encode binary string into a base64 string
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name encodeBase64
+ * @param {String} input Binary string
+ * @return {String} Base64 encoded data
+ */
+ api.encodeBase64 = function (input) {
+ return Base64.encode(input);
+ };
+
+ /**
+ * Decode a base64 encoded string into a byte array
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name decodeBase64AsArray
+ * @param {String} input Base64 encoded data
+ * @param {Number} [bytes] number of bytes per array entry
+ * @return {Number[]} Decoded data
+ */
+ api.decodeBase64AsArray = function (input, bytes) {
+ bytes = bytes || 1;
+
+ var dec = Base64.decode(input), i, j, len;
+ var ar = new Uint32Array(dec.length / bytes);
+
+ for (i = 0, len = dec.length / bytes; i < len; i++) {
+ ar[i] = 0;
+ for (j = bytes - 1; j >= 0; --j) {
+ ar[i] += dec.charCodeAt((i * bytes) + j) << (j << 3);
+ }
+ }
+ return ar;
+ };
+
+ /**
+ * decompress zlib/gzip data (NOT IMPLEMENTED)
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name decompress
+ * @param {Number[]} data Array of bytes
+ * @param {String} format compressed data format ("gzip","zlib")
+ * @return {Number[]} Decompressed data
+ */
+ api.decompress = function () {
+ throw new me.Error("GZIP/ZLIB compressed TMX Tile Map not supported!");
+ };
+
+ /**
+ * Decode a CSV encoded array into a binary array
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name decodeCSV
+ * @param {String} input CSV formatted data
+ * @return {Number[]} Decoded data
+ */
+ api.decodeCSV = function (input) {
+ var entries = input.replace("\n", "").trim().split(",");
+
+ var result = [];
+ for (var i = 0; i < entries.length; i++) {
+ result.push(+entries[i]);
+ }
+ return result;
+ };
+
+ /**
+ * return the base name of the file without path info.
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name getBasename
+ * @param {String} path path containing the filename
+ * @return {String} the base name without path information.
+ */
+ api.getBasename = function (path) {
+ return path.replace(REMOVE_PATH, "").replace(REMOVE_EXT, "");
+ };
+
+ /**
+ * return the extension of the file in the given path
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name getFileExtension
+ * @param {String} path path containing the filename
+ * @return {String} filename extension.
+ */
+ api.getFileExtension = function (path) {
+ return path.substring(path.lastIndexOf(".") + 1, path.length);
+ };
+
+ /**
+ * Get image pixels
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name getPixels
+ * @param {Image|Canvas} image Image to read
+ * @return {ImageData} Canvas ImageData object
+ */
+ api.getPixels = function (arg) {
+ if (arg instanceof HTMLImageElement) {
+ var _context = me.CanvasRenderer.getContext2d(
+ me.video.createCanvas(arg.width, arg.height)
+ );
+ _context.drawImage(arg, 0, 0);
+ return _context.getImageData(0, 0, arg.width, arg.height);
+ }
+ else {
+ // canvas !
+ return arg.getContext("2d").getImageData(0, 0, arg.width, arg.height);
+ }
+ };
+
+ /**
+ * Normalize a String or Image to an Image reference
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name getImage
+ * @param {Image|String} image Image name or Image reference
+ * @return {Image} Image reference
+ */
+ api.getImage = function (image) {
+ return (
+ (typeof(image) === "string") ?
+ me.loader.getImage(me.utils.getBasename(image)) :
+ image
+ );
+ };
+
+ /**
+ * reset the GUID Base Name
+ * the idea here being to have a unique ID
+ * per level / object
+ * @ignore
+ */
+ api.resetGUID = function (base, index) {
+ // also ensure it's only 8bit ASCII characters
+ GUID_base = base.toString().toUpperCase().toHex();
+ GUID_index = index || 0;
+ };
+
+ /**
+ * create and return a very simple GUID
+ * Game Unique ID
+ * @ignore
+ */
+ api.createGUID = function (index) {
+ // to cover the case of undefined id for groups
+ GUID_index += index || 1;
+ return GUID_base + "-" + (index || GUID_index);
+ };
+
+ /**
+ * returns true if the given value is a power of two
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name isPowerOfTwo
+ * @param {Number} val
+ * @return {boolean}
+ */
+ api.isPowerOfTwo = function (val) {
+ return (val & (val - 1)) === 0;
+ };
+
+ /**
+ * returns the next power of two for the given value
+ * @public
+ * @function
+ * @memberOf me.utils
+ * @name nextPowerOfTwo
+ * @param {Number} val
+ * @return {boolean}
+ */
+ api.nextPowerOfTwo = function (val) {
+ val --;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val ++;
+ return val;
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+(function () {
+ var rgbaRx = /^rgba?\((\d+), ?(\d+), ?(\d+)(, ?([\d\.]+))?\)$/;
+ var hex3Rx = /^#([\da-fA-F])([\da-fA-F])([\da-fA-F])$/;
+ var hex4Rx = /^#([\da-fA-F])([\da-fA-F])([\da-fA-F])([\da-fA-F])$/;
+ var hex6Rx = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/;
+ var hex8Rx = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/;
+
+ var cssToRGB = new Map();
+
+ [
+ // CSS1
+ [ "black", [ 0, 0, 0 ] ],
+ [ "silver", [ 192, 192, 129 ] ],
+ [ "gray", [ 128, 128, 128 ] ],
+ [ "white", [ 255, 255, 255 ] ],
+ [ "maroon", [ 128, 0, 0 ] ],
+ [ "red", [ 255, 0, 0 ] ],
+ [ "purple", [ 128, 0, 128 ] ],
+ [ "fuchsia", [ 255, 0, 255 ] ],
+ [ "green", [ 0, 128, 0 ] ],
+ [ "lime", [ 0, 255, 0 ] ],
+ [ "olive", [ 128, 128, 0 ] ],
+ [ "yellow", [ 255, 255, 0 ] ],
+ [ "navy", [ 0, 0, 128 ] ],
+ [ "blue", [ 0, 0, 255 ] ],
+ [ "teal", [ 0, 128, 128 ] ],
+ [ "aqua", [ 0, 255, 255 ] ],
+
+ // CSS2
+ [ "orange", [ 255, 165, 0 ] ],
+
+ // CSS3
+ [ "aliceblue", [ 240, 248, 245 ] ],
+ [ "antiquewhite", [ 250, 235, 215 ] ],
+ [ "aquamarine", [ 127, 255, 212 ] ],
+ [ "azure", [ 240, 255, 255 ] ],
+ [ "beige", [ 245, 245, 220 ] ],
+ [ "bisque", [ 255, 228, 196 ] ],
+ [ "blanchedalmond", [ 255, 235, 205 ] ],
+ [ "blueviolet", [ 138, 43, 226 ] ],
+ [ "brown", [ 165, 42, 42 ] ],
+ [ "burlywood", [ 222, 184, 35 ] ],
+ [ "cadetblue", [ 95, 158, 160 ] ],
+ [ "chartreuse", [ 127, 255, 0 ] ],
+ [ "chocolate", [ 210, 105, 30 ] ],
+ [ "coral", [ 255, 127, 80 ] ],
+ [ "cornflowerblue", [ 100, 149, 237 ] ],
+ [ "cornsilk", [ 255, 248, 220 ] ],
+ [ "crimson", [ 220, 20, 60 ] ],
+ [ "darkblue", [ 0, 0, 139 ] ],
+ [ "darkcyan", [ 0, 139, 139 ] ],
+ [ "darkgoldenrod", [ 184, 134, 11 ] ],
+ [ "darkgray[*]", [ 169, 169, 169 ] ],
+ [ "darkgreen", [ 0, 100, 0 ] ],
+ [ "darkgrey[*]", [ 169, 169, 169 ] ],
+ [ "darkkhaki", [ 189, 183, 107 ] ],
+ [ "darkmagenta", [ 139, 0, 139 ] ],
+ [ "darkolivegreen", [ 85, 107, 47 ] ],
+ [ "darkorange", [ 255, 140, 0 ] ],
+ [ "darkorchid", [ 153, 50, 204 ] ],
+ [ "darkred", [ 139, 0, 0 ] ],
+ [ "darksalmon", [ 233, 150, 122 ] ],
+ [ "darkseagreen", [ 143, 188, 143 ] ],
+ [ "darkslateblue", [ 72, 61, 139 ] ],
+ [ "darkslategray", [ 47, 79, 79 ] ],
+ [ "darkslategrey", [ 47, 79, 79 ] ],
+ [ "darkturquoise", [ 0, 206, 209 ] ],
+ [ "darkviolet", [ 148, 0, 211 ] ],
+ [ "deeppink", [ 255, 20, 147 ] ],
+ [ "deepskyblue", [ 0, 191, 255 ] ],
+ [ "dimgray", [ 105, 105, 105 ] ],
+ [ "dimgrey", [ 105, 105, 105 ] ],
+ [ "dodgerblue", [ 30, 144, 255 ] ],
+ [ "firebrick", [ 178, 34, 34 ] ],
+ [ "floralwhite", [ 255, 250, 240 ] ],
+ [ "forestgreen", [ 34, 139, 34 ] ],
+ [ "gainsboro", [ 220, 220, 220 ] ],
+ [ "ghostwhite", [ 248, 248, 255 ] ],
+ [ "gold", [ 255, 215, 0 ] ],
+ [ "goldenrod", [ 218, 165, 32 ] ],
+ [ "greenyellow", [ 173, 255, 47 ] ],
+ [ "grey", [ 128, 128, 128 ] ],
+ [ "honeydew", [ 240, 255, 240 ] ],
+ [ "hotpink", [ 255, 105, 180 ] ],
+ [ "indianred", [ 205, 92, 92 ] ],
+ [ "indigo", [ 75, 0, 130 ] ],
+ [ "ivory", [ 255, 255, 240 ] ],
+ [ "khaki", [ 240, 230, 140 ] ],
+ [ "lavender", [ 230, 230, 250 ] ],
+ [ "lavenderblush", [ 255, 240, 245 ] ],
+ [ "lawngreen", [ 124, 252, 0 ] ],
+ [ "lemonchiffon", [ 255, 250, 205 ] ],
+ [ "lightblue", [ 173, 216, 230 ] ],
+ [ "lightcoral", [ 240, 128, 128 ] ],
+ [ "lightcyan", [ 224, 255, 255 ] ],
+ [ "lightgoldenrodyellow", [ 250, 250, 210 ] ],
+ [ "lightgray", [ 211, 211, 211 ] ],
+ [ "lightgreen", [ 144, 238, 144 ] ],
+ [ "lightgrey", [ 211, 211, 211 ] ],
+ [ "lightpink", [ 255, 182, 193 ] ],
+ [ "lightsalmon", [ 255, 160, 122 ] ],
+ [ "lightseagreen", [ 32, 178, 170 ] ],
+ [ "lightskyblue", [ 135, 206, 250 ] ],
+ [ "lightslategray", [ 119, 136, 153 ] ],
+ [ "lightslategrey", [ 119, 136, 153 ] ],
+ [ "lightsteelblue", [ 176, 196, 222 ] ],
+ [ "lightyellow", [ 255, 255, 224 ] ],
+ [ "limegreen", [ 50, 205, 50 ] ],
+ [ "linen", [ 250, 240, 230 ] ],
+ [ "mediumaquamarine", [ 102, 205, 170 ] ],
+ [ "mediumblue", [ 0, 0, 205 ] ],
+ [ "mediumorchid", [ 186, 85, 211 ] ],
+ [ "mediumpurple", [ 147, 112, 219 ] ],
+ [ "mediumseagreen", [ 60, 179, 113 ] ],
+ [ "mediumslateblue", [ 123, 104, 238 ] ],
+ [ "mediumspringgreen", [ 0, 250, 154 ] ],
+ [ "mediumturquoise", [ 72, 209, 204 ] ],
+ [ "mediumvioletred", [ 199, 21, 133 ] ],
+ [ "midnightblue", [ 25, 25, 112 ] ],
+ [ "mintcream", [ 245, 255, 250 ] ],
+ [ "mistyrose", [ 255, 228, 225 ] ],
+ [ "moccasin", [ 255, 228, 181 ] ],
+ [ "navajowhite", [ 255, 222, 173 ] ],
+ [ "oldlace", [ 253, 245, 230 ] ],
+ [ "olivedrab", [ 107, 142, 35 ] ],
+ [ "orangered", [ 255, 69, 0 ] ],
+ [ "orchid", [ 218, 112, 214 ] ],
+ [ "palegoldenrod", [ 238, 232, 170 ] ],
+ [ "palegreen", [ 152, 251, 152 ] ],
+ [ "paleturquoise", [ 175, 238, 238 ] ],
+ [ "palevioletred", [ 219, 112, 147 ] ],
+ [ "papayawhip", [ 255, 239, 213 ] ],
+ [ "peachpuff", [ 255, 218, 185 ] ],
+ [ "peru", [ 205, 133, 63 ] ],
+ [ "pink", [ 255, 192, 203 ] ],
+ [ "plum", [ 221, 160, 221 ] ],
+ [ "powderblue", [ 176, 224, 230 ] ],
+ [ "rosybrown", [ 188, 143, 143 ] ],
+ [ "royalblue", [ 65, 105, 225 ] ],
+ [ "saddlebrown", [ 139, 69, 19 ] ],
+ [ "salmon", [ 250, 128, 114 ] ],
+ [ "sandybrown", [ 244, 164, 96 ] ],
+ [ "seagreen", [ 46, 139, 87 ] ],
+ [ "seashell", [ 255, 245, 238 ] ],
+ [ "sienna", [ 160, 82, 45 ] ],
+ [ "skyblue", [ 135, 206, 235 ] ],
+ [ "slateblue", [ 106, 90, 205 ] ],
+ [ "slategray", [ 112, 128, 144 ] ],
+ [ "slategrey", [ 112, 128, 144 ] ],
+ [ "snow", [ 255, 250, 250 ] ],
+ [ "springgreen", [ 0, 255, 127 ] ],
+ [ "steelblue", [ 70, 130, 180 ] ],
+ [ "tan", [ 210, 180, 140 ] ],
+ [ "thistle", [ 216, 191, 216 ] ],
+ [ "tomato", [ 255, 99, 71 ] ],
+ [ "turquoise", [ 64, 224, 208 ] ],
+ [ "violet", [ 238, 130, 238 ] ],
+ [ "wheat", [ 245, 222, 179 ] ],
+ [ "whitesmoke", [ 245, 245, 245 ] ],
+ [ "yellowgreen", [ 154, 205, 50 ] ]
+ ].forEach(function (value) {
+ cssToRGB.set(value[0], value[1]);
+ });
+
+ /**
+ * A color manipulation object.
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Float32Array|Number} [r=0] red component or array of color components
+ * @param {Number} [g=0] green component
+ * @param {Number} [b=0] blue component
+ * @param {Number} [alpha=1.0] alpha value
+ */
+ me.Color = me.Object.extend(
+ /** @scope me.Color.prototype */
+ {
+
+ /**
+ * @ignore
+ */
+ init : function (r, g, b, alpha) {
+
+ /**
+ * Color components in a Float32Array suitable for WebGL
+ * @name glArray
+ * @memberOf me.Color
+ * @type {Float32Array}
+ * @readonly
+ */
+ if (typeof (this.glArray) === "undefined") {
+ this.glArray = new Float32Array([ 0.0, 0.0, 0.0, 1.0 ]);
+ }
+
+ return this.setColor(r, g, b, alpha);
+ },
+
+ /**
+ * Set this color to the specified value.
+ * @name setColor
+ * @memberOf me.Color
+ * @function
+ * @param {Number} r red component [0 .. 255]
+ * @param {Number} g green component [0 .. 255]
+ * @param {Number} b blue component [0 .. 255]
+ * @param {Number} [alpha=1.0] alpha value [0.0 .. 1.0]
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ setColor : function (r, g, b, alpha) {
+ // Private initialization: copy Color value directly
+ if (r instanceof me.Color) {
+ this.glArray.set(r.glArray);
+ return r;
+ }
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.alpha = alpha;
+ return this;
+ },
+
+ /**
+ * Create a new copy of this color object.
+ * @name clone
+ * @memberOf me.Color
+ * @function
+ * @return {me.Color} Reference to the newly cloned object
+ */
+ clone : function () {
+ return me.pool.pull("me.Color", this);
+ },
+
+ /**
+ * Copy a color object or CSS color into this one.
+ * @name copy
+ * @memberOf me.Color
+ * @function
+ * @param {me.Color|String} color
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ copy : function (color) {
+ if (color instanceof me.Color) {
+ this.glArray.set(color.glArray);
+ return this;
+ }
+
+ return this.parseCSS(color);
+ },
+
+ /**
+ * Blend this color with the given one using addition.
+ * @name add
+ * @memberOf me.Color
+ * @function
+ * @param {me.Color} color
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ add : function (color) {
+ this.glArray[0] = (this.glArray[0] + color.glArray[0]).clamp(0, 1);
+ this.glArray[1] = (this.glArray[1] + color.glArray[1]).clamp(0, 1);
+ this.glArray[2] = (this.glArray[2] + color.glArray[2]).clamp(0, 1);
+ this.glArray[3] = (this.glArray[3] + color.glArray[3]) / 2;
+
+ return this;
+ },
+
+ /**
+ * Darken this color value by 0..1
+ * @name darken
+ * @memberOf me.Color
+ * @function
+ * @param {Number} scale
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ darken : function (scale) {
+ scale = scale.clamp(0, 1);
+ this.glArray[0] *= scale;
+ this.glArray[1] *= scale;
+ this.glArray[2] *= scale;
+
+ return this;
+ },
+
+ /**
+ * Lighten this color value by 0..1
+ * @name lighten
+ * @memberOf me.Color
+ * @function
+ * @param {Number} scale
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ lighten : function (scale) {
+ scale = scale.clamp(0, 1);
+ this.glArray[0] = (this.glArray[0] + (1 - this.glArray[0]) * scale).clamp(0, 1);
+ this.glArray[1] = (this.glArray[1] + (1 - this.glArray[1]) * scale).clamp(0, 1);
+ this.glArray[2] = (this.glArray[2] + (1 - this.glArray[2]) * scale).clamp(0, 1);
+
+ return this;
+ },
+
+ /**
+ * Generate random r,g,b values for this color object
+ * @name random
+ * @memberOf me.Color
+ * @function
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ random : function () {
+ return this.setColor(
+ Math.random() * 256,
+ Math.random() * 256,
+ Math.random() * 256,
+ this.alpha
+ );
+ },
+
+ /**
+ * Return true if the r,g,b,a values of this color are equal with the
+ * given one.
+ * @name equals
+ * @memberOf me.Color
+ * @function
+ * @param {me.Color} color
+ * @return {Boolean}
+ */
+ equals : function (color) {
+ return (
+ (this.glArray[0] === color.glArray[0]) &&
+ (this.glArray[1] === color.glArray[1]) &&
+ (this.glArray[2] === color.glArray[2]) &&
+ (this.glArray[3] === color.glArray[3])
+ );
+ },
+
+ /**
+ * Parse a CSS color string and set this color to the corresponding
+ * r,g,b values
+ * @name parseCSS
+ * @memberOf me.Color
+ * @function
+ * @param {String} color
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ parseCSS : function (cssColor) {
+ // TODO : Memoize this function by caching its input
+
+ if (cssToRGB.has(cssColor)) {
+ return this.setColor.apply(this, cssToRGB.get(cssColor));
+ }
+
+ return this.parseRGB(cssColor);
+ },
+
+ /**
+ * Parse an RGB or RGBA CSS color string
+ * @name parseRGB
+ * @memberOf me.Color
+ * @function
+ * @param {String} color
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ parseRGB : function (rgbColor) {
+ // TODO : Memoize this function by caching its input
+
+ var match = rgbaRx.exec(rgbColor);
+ if (match) {
+ return this.setColor(+match[1], +match[2], +match[3], +match[5]);
+ }
+
+ return this.parseHex(rgbColor);
+ },
+
+ /**
+ * Parse a Hex color ("#RGB", "#RGBA" or "#RRGGBB", "#RRGGBBAA" format) and set this color to
+ * the corresponding r,g,b,a values
+ * @name parseHex
+ * @memberOf me.Color
+ * @function
+ * @param {String} color
+ * @return {me.Color} Reference to this object for method chaining
+ */
+ parseHex : function (hexColor) {
+ // TODO : Memoize this function by caching its input
+
+ var match;
+ if ((match = hex8Rx.exec(hexColor))) {
+ // #AARRGGBB
+ return this.setColor(
+ parseInt(match[1], 16),
+ parseInt(match[2], 16),
+ parseInt(match[3], 16),
+ (parseInt(match[4], 16).clamp(0, 255) / 255.0).toFixed(1)
+ );
+ }
+
+ if ((match = hex6Rx.exec(hexColor))) {
+ // #RRGGBB
+ return this.setColor(
+ parseInt(match[1], 16),
+ parseInt(match[2], 16),
+ parseInt(match[3], 16)
+ );
+ }
+
+ if ((match = hex4Rx.exec(hexColor))) {
+ // #ARGB
+ return this.setColor(
+ parseInt(match[1] + match[1], 16),
+ parseInt(match[2] + match[2], 16),
+ parseInt(match[3] + match[3], 16),
+ (parseInt(match[4] + match[4], 16).clamp(0, 255) / 255.0).toFixed(1)
+ );
+ }
+
+ if ((match = hex3Rx.exec(hexColor))) {
+ // #RGB
+ return this.setColor(
+ parseInt(match[1] + match[1], 16),
+ parseInt(match[2] + match[2], 16),
+ parseInt(match[3] + match[3], 16)
+ );
+ }
+
+ throw new me.Color.Error(
+ "invalid parameter: " + hexColor
+ );
+ },
+
+ /**
+ * Returns the private glArray
+ * @ignore
+ */
+ toGL : function () {
+ return this.glArray;
+ },
+
+ /**
+ * Get the color in "#RRGGBB" format
+ * @name toHex
+ * @memberOf me.Color
+ * @function
+ * @return {String}
+ */
+ toHex : function () {
+ // TODO : Memoize this function by caching its result until any of
+ // the r,g,b,a values are changed
+
+ return "#" + this.r.toHex() + this.g.toHex() + this.b.toHex();
+ },
+
+ /**
+ * Get the color in "#RRGGBBAA" format
+ * @name toHex8
+ * @memberOf me.Color
+ * @function
+ * @return {String}
+ */
+ toHex8 : function () {
+ // TODO : Memoize this function by caching its result until any of
+ // the r,g,b,a values are changed
+
+ return "#" + this.r.toHex() + this.g.toHex() + this.b.toHex() + this.alpha.toHex();
+ },
+
+ /**
+ * Get the color in "rgb(R,G,B)" format
+ * @name toRGB
+ * @memberOf me.Color
+ * @function
+ * @return {String}
+ */
+ toRGB : function () {
+ // TODO : Memoize this function by caching its result until any of
+ // the r,g,b,a values are changed
+
+ return "rgb(" +
+ this.r + "," +
+ this.g + "," +
+ this.b +
+ ")";
+ },
+
+ /**
+ * Get the color in "rgba(R,G,B,A)" format
+ * @name toRGBA
+ * @memberOf me.Color
+ * @function
+ * @return {String}
+ */
+ toRGBA : function () {
+ // TODO : Memoize this function by caching its result until any of
+ // the r,g,b,a values are changed
+
+ return "rgba(" +
+ this.r + "," +
+ this.g + "," +
+ this.b + "," +
+ this.alpha +
+ ")";
+ }
+ });
+
+ /**
+ * Color Red Component
+ * @type Number
+ * @name r
+ * @readonly
+ * @memberOf me.Color
+ */
+ Object.defineProperty(me.Color.prototype, "r", {
+ /**
+ * @ignore
+ */
+ get : function () { return ~~(this.glArray[0] * 255); },
+ /**
+ * @ignore
+ */
+ set : function (value) { this.glArray[0] = (~~value || 0).clamp(0, 255) / 255.0; },
+ enumerable : true,
+ configurable : true
+ });
+
+ /**
+ * Color Green Component
+ * @type Number
+ * @name g
+ * @readonly
+ * @memberOf me.Color
+ */
+ Object.defineProperty(me.Color.prototype, "g", {
+ /**
+ * @ignore
+ */
+ get : function () { return ~~(this.glArray[1] * 255); },
+ /**
+ * @ignore
+ */
+ set : function (value) { this.glArray[1] = (~~value || 0).clamp(0, 255) / 255.0; },
+ enumerable : true,
+ configurable : true
+ });
+
+ /**
+ * Color Blue Component
+ * @type Number
+ * @name b
+ * @readonly
+ * @memberOf me.Color
+ */
+ Object.defineProperty(me.Color.prototype, "b", {
+ /**
+ * @ignore
+ */
+ get : function () { return ~~(this.glArray[2] * 255); },
+ /**
+ * @ignore
+ */
+ set : function (value) { this.glArray[2] = (~~value || 0).clamp(0, 255) / 255.0; },
+ enumerable : true,
+ configurable : true
+ });
+
+ /**
+ * Color Alpha Component
+ * @type Number
+ * @name alpha
+ * @readonly
+ * @memberOf me.Color
+ */
+ Object.defineProperty(me.Color.prototype, "alpha", {
+ /**
+ * @ignore
+ */
+ get : function () { return this.glArray[3]; },
+ /**
+ * @ignore
+ */
+ set : function (value) { this.glArray[3] = typeof(value) === "undefined" ? 1.0 : (+value).clamp(0, 1); },
+ enumerable : true,
+ configurable : true
+ });
+
+ /**
+ * Base class for me.Color exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.Color
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.Color.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.Color.Error";
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * A singleton object to access the device localStorage area
+ * @example
+ * // Initialize "score" and "lives" with default values
+ * // This loads the properties from localStorage if they exist, else it sets the given defaults
+ * me.save.add({ score : 0, lives : 3 });
+ *
+ * // Print all
+ * // On first load, this prints { score : 0, lives : 3 }
+ * // On further reloads, it prints { score : 31337, lives : 3, complexObject : ... }
+ * // Because the following changes will be saved to localStorage
+ * console.log(JSON.stringify(me.save));
+ *
+ * // Save score
+ * me.save.score = 31337;
+ *
+ * // Also supports complex objects thanks to the JSON backend
+ * me.save.add({ complexObject : {} })
+ * me.save.complexObject = { a : "b", c : [ 1, 2, 3, "d" ], e : { f : [{}] } };
+ *
+ * // WARNING: Do not set any child properties of complex objects directly!
+ * // Changes made that way will not save. Always set the entire object value at once.
+ * // If you cannot live with this limitation, there's a workaround:
+ * me.save.complexObject.c.push("foo"); // Modify a child property
+ * me.save.complexObject = me.save.complexObject; // Save the entire object!
+ *
+ * // Remove "lives" from localStorage
+ * me.save.remove("lives");
+ * @namespace me.save
+ * @memberOf me
+ */
+ me.save = (function () {
+ // Variable to hold the object data
+ var data = {};
+
+ // a function to check if the given key is a reserved word
+ function isReserved(key) {
+ return (key === "add" || key === "remove");
+ }
+
+ // Public API
+ var api = {
+ /**
+ * @ignore
+ */
+ _init: function () {
+ // Load previous data if local Storage is supported
+ if (me.device.localStorage === true) {
+ var keys = JSON.parse(localStorage.getItem("me.save")) || [];
+ keys.forEach(function (key) {
+ data[key] = JSON.parse(localStorage.getItem("me.save." + key));
+ });
+ }
+ },
+
+ /**
+ * Add new keys to localStorage and set them to the given default values if they do not exist
+ * @name add
+ * @memberOf me.save
+ * @function
+ * @param {Object} props key and corresponding values
+ * @example
+ * // Initialize "score" and "lives" with default values
+ * me.save.add({ score : 0, lives : 3 });
+ */
+ add : function (props) {
+ Object.keys(props).forEach(function (key) {
+ if (isReserved(key)) {
+ return;
+ }
+
+ (function (prop) {
+ Object.defineProperty(api, prop, {
+ configurable : true,
+ enumerable : true,
+ /**
+ * @ignore
+ */
+ get : function () {
+ return data[prop];
+ },
+ /**
+ * @ignore
+ */
+ set : function (value) {
+ data[prop] = value;
+ if (me.device.localStorage === true) {
+ localStorage.setItem("me.save." + prop, JSON.stringify(value));
+ }
+ }
+ });
+ })(key);
+
+ // Set default value for key
+ if (!(key in data)) {
+ api[key] = props[key];
+ }
+ });
+
+ // Save keys
+ if (me.device.localStorage === true) {
+ localStorage.setItem("me.save", JSON.stringify(Object.keys(data)));
+ }
+ },
+
+ /**
+ * Remove a key from localStorage
+ * @name remove
+ * @memberOf me.save
+ * @function
+ * @param {String} key key to be removed
+ * @example
+ * // Remove the "score" key from localStorage
+ * me.save.remove("score");
+ */
+ remove : function (key) {
+ if (!isReserved(key)) {
+ if (typeof data[key] !== "undefined") {
+ delete data[key];
+ if (me.device.localStorage === true) {
+ localStorage.removeItem("me.save." + key);
+ localStorage.setItem("me.save", JSON.stringify(Object.keys(data)));
+ }
+ }
+ }
+ }
+ };
+
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Tile QT 0.7.x format
+ * http://www.mapeditor.org/
+ *
+ */
+(function () {
+ /**
+ * a collection of TMX utility Function
+ * @final
+ * @memberOf me
+ * @ignore
+ */
+ me.TMXUtils = (function () {
+ /*
+ * PUBLIC
+ */
+
+ // hold public stuff in our singleton
+ var api = {};
+
+ /**
+ * set and interpret a TMX property value
+ * @ignore
+ */
+ function setTMXValue(name, type, value) {
+ var match;
+
+ if (typeof(value) !== "string") {
+ // Value is already normalized (e.g. with JSON maps)
+ return value;
+ }
+
+ switch (type) {
+
+ case "int" :
+ case "float" :
+ value = Number(value);
+ break;
+
+ case "bool" :
+ value = (value === "true");
+ break;
+
+ default :
+ // try to parse it anyway
+ if (!value || value.isBoolean()) {
+ // if value not defined or boolean
+ value = value ? (value === "true") : true;
+ }
+ else if (value.isNumeric()) {
+ // check if numeric
+ value = Number(value);
+ }
+ else if (value.search(/^json:/i) === 0) {
+ // try to parse it
+ match = value.split(/^json:/i)[1];
+ try {
+ value = JSON.parse(match);
+ }
+ catch (e) {
+ throw new me.Error("Unable to parse JSON: " + match);
+ }
+ }
+ else if (value.search(/^eval:/i) === 0) {
+ // try to evaluate it
+ match = value.split(/^eval:/i)[1];
+ try {
+ // eslint-disable-next-line
+ value = eval(match);
+ }
+ catch (e) {
+ throw new me.Error("Unable to evaluate: " + match);
+ }
+ }
+ else if (
+ ((match = value.match(/^#([\da-fA-F])([\da-fA-F]{3})$/))) ||
+ ((match = value.match(/^#([\da-fA-F]{2})([\da-fA-F]{6})$/)))
+ ) {
+ value = "#" + match[2] + match[1];
+ }
+
+ // normalize values
+ if (name.search(/^(ratio|anchorPoint)$/) === 0) {
+ // convert number to vector
+ if (typeof(value) === "number") {
+ value = {
+ "x" : value,
+ "y" : value
+ };
+ }
+ }
+ }
+ // return the interpreted value
+ return value;
+ }
+
+ function parseAttributes(obj, elt) {
+ // do attributes
+ if (elt.attributes && elt.attributes.length > 0) {
+ for (var j = 0; j < elt.attributes.length; j++) {
+ var attribute = elt.attributes.item(j);
+ if (typeof(attribute.name) !== "undefined") {
+ // DOM4 (Attr no longer inherit from Node)
+ obj[attribute.name] = attribute.value;
+ } else {
+ // else use the deprecated ones
+ obj[attribute.nodeName] = attribute.nodeValue;
+ }
+ }
+ }
+ }
+
+ /**
+ * Decode the given data
+ * @ignore
+ */
+ api.decode = function (data, encoding, compression) {
+ compression = compression || "none";
+ encoding = encoding || "none";
+
+ switch (encoding) {
+ case "csv":
+ return me.utils.decodeCSV(data);
+
+ case "base64":
+ var decoded = me.utils.decodeBase64AsArray(data, 4);
+ return (
+ (compression === "none") ?
+ decoded :
+ me.utils.decompress(decoded, compression)
+ );
+
+ case "none":
+ return data;
+
+ case "xml":
+ throw new me.Error("XML encoding is deprecated, use base64 instead");
+
+ default:
+ throw new me.Error("Unknown layer encoding: " + encoding);
+ }
+ };
+
+ /**
+ * Normalize TMX format to Tiled JSON format
+ * @ignore
+ */
+ api.normalize = function (obj, item) {
+ var nodeName = item.nodeName;
+
+ switch (nodeName) {
+ case "data":
+ var data = api.parse(item);
+ // When no encoding is given, the tiles are stored as individual XML tile elements.
+ data.encoding = data.encoding || "xml";
+ obj.data = api.decode(data.text, data.encoding, data.compression);
+ obj.encoding = "none";
+ break;
+
+ case "imagelayer":
+ case "layer":
+ case "objectgroup":
+ var layer = api.parse(item);
+ layer.type = (nodeName === "layer" ? "tilelayer" : nodeName);
+ if (layer.image) {
+ layer.image = layer.image.source;
+ }
+
+ obj.layers = obj.layers || [];
+ obj.layers.push(layer);
+ break;
+
+ case "animation":
+ obj.animation = api.parse(item).frames;
+ break;
+
+ case "frame":
+ case "object":
+ var name = nodeName + "s";
+ obj[name] = obj[name] || [];
+ obj[name].push(api.parse(item));
+ break;
+
+ case "tile":
+ var tile = api.parse(item);
+ obj.tiles = obj.tiles || {};
+ obj.tiles[tile.id] = tile;
+ break;
+
+ case "tileset":
+ var tileset = api.parse(item);
+ if (tileset.image) {
+ tileset.imagewidth = tileset.image.width;
+ tileset.imageheight = tileset.image.height;
+ tileset.image = tileset.image.source;
+ }
+
+ obj.tilesets = obj.tilesets || [];
+ obj.tilesets.push(tileset);
+ break;
+
+ case "polygon":
+ case "polyline":
+ obj[nodeName] = [];
+
+ // Get a point array
+ var points = api.parse(item).points.split(" ");
+
+ // And normalize them into an array of vectors
+ for (var i = 0, v; i < points.length; i++) {
+ v = points[i].split(",");
+ obj[nodeName].push({
+ "x" : +v[0],
+ "y" : +v[1]
+ });
+ }
+
+ break;
+
+ case "properties":
+ obj.properties = api.parse(item);
+ break;
+
+ case "property":
+ var property = api.parse(item);
+ obj[property.name] = setTMXValue(
+ property.name,
+ // in XML type is undefined for "string" values
+ property.type || "string",
+ property.value
+ );
+ break;
+
+ default:
+ obj[nodeName] = api.parse(item);
+ break;
+ }
+ };
+
+ /**
+ * Parse a XML TMX object and returns the corresponding javascript object
+ * @ignore
+ */
+ api.parse = function (xml) {
+ // Create the return object
+ var obj = {};
+
+ var text = "";
+
+ if (xml.nodeType === 1) {
+ // do attributes
+ parseAttributes(obj, xml);
+ }
+
+ // do children
+ if (xml.hasChildNodes()) {
+ for (var i = 0; i < xml.childNodes.length; i++) {
+ var item = xml.childNodes.item(i);
+
+ switch (item.nodeType) {
+ case 1:
+ api.normalize(obj, item);
+ break;
+
+ case 3:
+ text += item.nodeValue.trim();
+ break;
+ }
+ }
+ }
+
+ if (text) {
+ obj.text = text;
+ }
+
+ return obj;
+ };
+
+ /**
+ * Apply TMX Properties to the given object
+ * @ignore
+ */
+ api.applyTMXProperties = function (obj, data) {
+ var properties = data.properties;
+ var types = data.propertytypes;
+ if (typeof(properties) !== "undefined") {
+ for (var name in properties) {
+ if (properties.hasOwnProperty(name)) {
+ var type = "string";
+ if (typeof(types) !== "undefined") {
+ type = types[name];
+ }
+ // set the value
+ obj[name] = setTMXValue(name, type, properties[name]);
+ }
+ }
+ }
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Tile QT 0.7.x format
+ * http://www.mapeditor.org/
+ *
+ */
+(function () {
+
+ /**
+ * TMX Object Group
+ * contains the object group definition as defined in Tiled.
+ * note : object group definition is translated into the virtual `me.game.world` using `me.Container`.
+ * @see me.Container
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ */
+ me.TMXObjectGroup = me.Object.extend({
+ /**
+ * @ignore
+ */
+ init : function (map, data, z) {
+
+ /**
+ * group name
+ * @public
+ * @type String
+ * @name name
+ * @memberOf me.TMXObjectGroup
+ */
+ this.name = data.name;
+
+ /**
+ * group width
+ * @public
+ * @type Number
+ * @name width
+ * @memberOf me.TMXObjectGroup
+ */
+ this.width = data.width;
+
+ /**
+ * group height
+ * @public
+ * @type Number
+ * @name height
+ * @memberOf me.TMXObjectGroup
+ */
+ this.height = data.height;
+
+ /**
+ * group z order
+ * @public
+ * @type Number
+ * @name z
+ * @memberOf me.TMXObjectGroup
+ */
+ this.z = z;
+
+ /**
+ * group objects list definition
+ * @see me.TMXObject
+ * @public
+ * @type Array
+ * @name name
+ * @memberOf me.TMXObjectGroup
+ */
+ this.objects = [];
+
+ var visible = typeof(data.visible) !== "undefined" ? data.visible : true;
+ this.opacity = (visible === true) ? (+data.opacity || 1.0).clamp(0.0, 1.0) : 0;
+
+ // check if we have any user-defined properties
+ me.TMXUtils.applyTMXProperties(this, data);
+
+ // parse all objects
+ var _objects = data.objects;
+ if (_objects) {
+ var self = this;
+ _objects.forEach(function (tmxObj) {
+ //self.objects.push(new me.TMXObject(tmxObj, map.orientation, map.tilesets, z));
+ self.objects.push(new me.TMXObject(map, tmxObj, z));
+ });
+ }
+ },
+
+ /**
+ * reset function
+ * @ignore
+ * @function
+ */
+
+ destroy : function () {
+ // clear all allocated objects
+ this.objects = null;
+ },
+
+ /**
+ * return the object count
+ * @ignore
+ * @function
+ */
+ getObjectCount : function () {
+ return this.objects.length;
+ },
+
+ /**
+ * returns the object at the specified index
+ * @ignore
+ * @function
+ */
+ getObjectByIndex : function (idx) {
+ return this.objects[idx];
+ }
+ });
+
+ /**
+ * a TMX Object defintion, as defined in Tiled.
+ * note : object definition are translated into the virtual `me.game.world` using `me.Entity`.
+ * @see me.Entity
+ * @class
+ * @extends Object
+ * @memberOf me
+ * @constructor
+ */
+ me.TMXObject = me.Object.extend({
+ /**
+ * @ignore
+ */
+ init : function (map, tmxObj, z) {
+
+ /**
+ * object point list (for Polygon and PolyLine)
+ * @public
+ * @type Vector2d[]
+ * @name points
+ * @memberOf me.TMXObject
+ */
+ this.points = undefined;
+
+ /**
+ * object name
+ * @public
+ * @type String
+ * @name name
+ * @memberOf me.TMXObject
+ */
+ this.name = tmxObj.name;
+
+ /**
+ * object x position
+ * @public
+ * @type Number
+ * @name x
+ * @memberOf me.TMXObject
+ */
+ this.x = +tmxObj.x;
+
+ /**
+ * object y position
+ * @public
+ * @type Number
+ * @name y
+ * @memberOf me.TMXObject
+ */
+ this.y = +tmxObj.y;
+
+ /**
+ * object z order
+ * @public
+ * @type Number
+ * @name z
+ * @memberOf me.TMXObject
+ */
+ this.z = +z;
+
+ /**
+ * object width
+ * @public
+ * @type Number
+ * @name width
+ * @memberOf me.TMXObject
+ */
+ this.width = +tmxObj.width || 0;
+
+ /**
+ * object height
+ * @public
+ * @type Number
+ * @name height
+ * @memberOf me.TMXObject
+ */
+ this.height = +tmxObj.height || 0;
+
+ /**
+ * object gid value
+ * when defined the object is a tiled object
+ * @public
+ * @type Number
+ * @name gid
+ * @memberOf me.TMXObject
+ */
+ this.gid = +tmxObj.gid || null;
+
+ /**
+ * object type
+ * @public
+ * @type String
+ * @name type
+ * @memberOf me.TMXObject
+ */
+ this.type = tmxObj.type;
+
+ /**
+ * The rotation of the object in radians clockwise (defaults to 0)
+ * @public
+ * @type Number
+ * @name rotation
+ * @memberOf me.TMXObject
+ */
+ this.rotation = Number.prototype.degToRad(+tmxObj.rotation || 0);
+
+ /**
+ * object unique identifier per level (Tiled 0.11.x+)
+ * @public
+ * @type Number
+ * @name id
+ * @memberOf me.TMXObject
+ */
+ this.id = +tmxObj.id || undefined;
+
+ /**
+ * object orientation (orthogonal or isometric)
+ * @public
+ * @type String
+ * @name orientation
+ * @memberOf me.TMXObject
+ */
+ this.orientation = map.orientation;
+
+ /**
+ * the collision shapes defined for this object
+ * @public
+ * @type Array
+ * @name shapes
+ * @memberOf me.TMXObject
+ */
+ this.shapes = undefined;
+
+ /**
+ * if true, the object is an Ellipse
+ * @public
+ * @type Boolean
+ * @name isEllipse
+ * @memberOf me.TMXObject
+ */
+ this.isEllipse = false;
+
+ /**
+ * if true, the object is a Polygon
+ * @public
+ * @type Boolean
+ * @name isPolygon
+ * @memberOf me.TMXObject
+ */
+ this.isPolygon = false;
+
+ /**
+ * if true, the object is a PolyLine
+ * @public
+ * @type Boolean
+ * @name isPolyLine
+ * @memberOf me.TMXObject
+ */
+ this.isPolyLine = false;
+
+ // check if the object has an associated gid
+ if (typeof this.gid === "number") {
+ this.setTile(map.tilesets);
+ }
+ else {
+ if (typeof(tmxObj.ellipse) !== "undefined") {
+ this.isEllipse = true;
+ }
+ else {
+ var points = tmxObj.polygon;
+ if (typeof(points) !== "undefined") {
+ this.isPolygon = true;
+ }
+ else {
+ points = tmxObj.polyline;
+ if (typeof(points) !== "undefined") {
+ this.isPolyLine = true;
+ }
+ }
+ if (typeof(points) !== "undefined") {
+ this.points = [];
+ var self = this;
+ points.forEach(function (point) {
+ self.points.push(new me.Vector2d(point.x, point.y));
+ });
+ }
+ }
+ }
+
+ // Adjust the Position to match Tiled
+ map.getRenderer().adjustPosition(this);
+
+ // set the object properties
+ me.TMXUtils.applyTMXProperties(this, tmxObj);
+
+ // define the object shapes if required
+ if (!this.shapes) {
+ this.shapes = this.parseTMXShapes();
+ }
+ },
+
+ /**
+ * set the object image (for Tiled Object)
+ * @ignore
+ * @function
+ */
+ setTile : function (tilesets) {
+ // get the corresponding tileset
+ var tileset = tilesets.getTilesetByGid(this.gid);
+
+ // set width and height equal to tile size
+ this.width = this.framewidth = tileset.tilewidth;
+ this.height = this.frameheight = tileset.tileheight;
+
+ // the object corresponding tile object
+ this.tile = new me.Tile(this.x, this.y, this.gid, tileset);
+ },
+
+ /**
+ * parses the TMX shape definition and returns a corresponding array of me.Shape object
+ * @name parseTMXShapes
+ * @memberOf me.TMXObject
+ * @private
+ * @function
+ * @return {me.Polygon[]|me.Line[]|me.Ellipse[]} an array of shape objects
+ */
+ parseTMXShapes : function () {
+ var i = 0;
+ var shapes = [];
+
+ // add an ellipse shape
+ if (this.isEllipse === true) {
+ // ellipse coordinates are the center position, so set default to the corresonding radius
+ shapes.push((new me.Ellipse(
+ this.width / 2,
+ this.height / 2,
+ this.width,
+ this.height
+ )).rotate(this.rotation));
+ }
+
+ // add a polygon
+ else if (this.isPolygon === true) {
+ shapes.push((new me.Polygon(0, 0, this.points)).rotate(this.rotation));
+ }
+
+ // add a polyline
+ else if (this.isPolyLine === true) {
+ var p = this.points;
+ var p1, p2;
+ var segments = p.length - 1;
+ for (i = 0; i < segments; i++) {
+ // clone the value before, as [i + 1]
+ // is reused later by the next segment
+ p1 = p[i];
+ p2 = p[i + 1].clone();
+ if (this.rotation !== 0) {
+ p1 = p1.rotate(this.rotation);
+ p2 = p2.rotate(this.rotation);
+ }
+ shapes.push(new me.Line(0, 0, [ p1, p2 ]));
+ }
+ }
+
+ // it's a rectangle, returns a polygon object anyway
+ else {
+ shapes.push((new me.Polygon(
+ 0, 0, [
+ new me.Vector2d(), new me.Vector2d(this.width, 0),
+ new me.Vector2d(this.width, this.height), new me.Vector2d(0, this.height)
+ ]
+ )).rotate(this.rotation));
+ }
+
+ // Apply isometric projection
+ if (this.orientation === "isometric") {
+ for (i = 0; i < shapes.length; i++) {
+ shapes[i].toIso();
+ }
+ }
+
+ return shapes;
+ },
+ /**
+ * getObjectPropertyByName
+ * @ignore
+ * @function
+ */
+ getObjectPropertyByName : function (name) {
+ return this[name];
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Tile QT 0.7.x format
+ * http://www.mapeditor.org/
+ *
+ */
+(function () {
+
+ // bitmask constants to check for flipped & rotated tiles
+ var TMX_FLIP_H = 0x80000000,
+ TMX_FLIP_V = 0x40000000,
+ TMX_FLIP_AD = 0x20000000,
+ TMX_CLEAR_BIT_MASK = ~(0x80000000 | 0x40000000 | 0x20000000);
+
+ /**
+ * a basic tile object
+ * @class
+ * @extends me.Rect
+ * @memberOf me
+ * @constructor
+ * @param {Number} x x index of the Tile in the map
+ * @param {Number} y y index of the Tile in the map
+ * @param {Number} gid tile gid
+ * @param {me.TMXTileset} tileset the corresponding tileset object
+
+ */
+ me.Tile = me.Rect.extend({
+ /** @ignore */
+ init : function (x, y, gid, tileset) {
+ /**
+ * tileset
+ * @public
+ * @type me.TMXTileset
+ * @name me.Tile#tileset
+ */
+ this.tileset = tileset;
+
+ /**
+ * the tile transformation matrix (if defined)
+ * @ignore
+ */
+ this.currentTransform = null;
+
+ // call the parent constructor
+ me.Rect.prototype.init.apply(this, [x * tileset.tilewidth, y * tileset.tileheight, tileset.tilewidth, tileset.tileheight]);
+
+ // Tile col / row pos
+ this.col = x;
+ this.row = y;
+
+ /**
+ * tileId
+ * @public
+ * @type int
+ * @name me.Tile#tileId
+ */
+ this.tileId = gid;
+ /**
+ * True if the tile is flipped horizontally
+ * @public
+ * @type Boolean
+ * @name me.Tile#flipX
+ */
+ this.flippedX = (this.tileId & TMX_FLIP_H) !== 0;
+ /**
+ * True if the tile is flipped vertically
+ * @public
+ * @type Boolean
+ * @name me.Tile#flippedY
+ */
+ this.flippedY = (this.tileId & TMX_FLIP_V) !== 0;
+ /**
+ * True if the tile is flipped anti-diagonally
+ * @public
+ * @type Boolean
+ * @name me.Tile#flippedAD
+ */
+ this.flippedAD = (this.tileId & TMX_FLIP_AD) !== 0;
+
+ /**
+ * Global flag that indicates if the tile is flipped
+ * @public
+ * @type Boolean
+ * @name me.Tile#flipped
+ */
+ this.flipped = this.flippedX || this.flippedY || this.flippedAD;
+ // create a transformation matrix if required
+ if (this.flipped === true) {
+ this.createTransform();
+ }
+
+ // clear out the flags and set the tileId
+ this.tileId &= TMX_CLEAR_BIT_MASK;
+ },
+
+ /**
+ * create a transformation matrix for this tile
+ * @ignore
+ */
+ createTransform : function () {
+ if (this.currentTransform === null) {
+ this.currentTransform = new me.Matrix2d();
+ } else {
+ // reset the matrix
+ this.currentTransform.identity();
+ }
+
+ if (this.flippedAD) {
+ // Use shearing to swap the X/Y axis
+ this.currentTransform.setTransform(
+ 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, 1
+ );
+ this.currentTransform.translate(0, this.height - this.width);
+ }
+ if (this.flippedX) {
+ this.currentTransform.translate(
+ (this.flippedAD ? 0 : this.width),
+ (this.flippedAD ? this.height : 0)
+ );
+ this.currentTransform.scaleX(-1);
+ }
+ if (this.flippedY) {
+ this.currentTransform.translate(
+ (this.flippedAD ? this.width : 0),
+ (this.flippedAD ? 0 : this.height)
+ );
+ this.currentTransform.scaleY(-1);
+ }
+ },
+
+ /**
+ * return a renderable object for this Tile object
+ * @name me.Tile#getRenderable
+ * @public
+ * @function
+ * @param {Object} [settings] see {@link me.Sprite}
+ * @return {me.Renderable} a me.Sprite object
+ */
+ getRenderable : function (settings) {
+ var renderable;
+ var tileset = this.tileset;
+
+ if (tileset.animations.has(this.tileId)) {
+ var frames = [];
+ var frameId = [];
+ (tileset.animations.get(this.tileId).frames).forEach(function (frame) {
+ frameId.push(frame.tileid);
+ frames.push({
+ name : "" + frame.tileid,
+ delay : frame.duration
+ });
+ });
+ renderable = tileset.texture.createAnimationFromName(frameId, settings);
+ renderable.addAnimation(this.tileId - tileset.firstgid, frames);
+ renderable.setCurrentAnimation(this.tileId - tileset.firstgid);
+
+ } else {
+ renderable = tileset.texture.createSpriteFromName(this.tileId - tileset.firstgid, settings);
+ }
+
+ // any H/V flipping to apply?
+ if (this.flippedX) {
+ renderable.currentTransform.scaleX(-1);
+ }
+ if (this.flippedY) {
+ renderable.currentTransform.scaleY(-1);
+ }
+
+ return renderable;
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Tile QT 0.7.x format
+ * http://www.mapeditor.org/
+ *
+ */
+(function () {
+
+ // bitmask constants to check for flipped & rotated tiles
+ var TMX_CLEAR_BIT_MASK = ~(0x80000000 | 0x40000000 | 0x20000000);
+
+ /**
+ * a TMX Tile Set Object
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Object} tileset tileset JSON definition
+ */
+ me.TMXTileset = me.Object.extend({
+ /**
+ * constructor
+ * @ignore
+ */
+ init: function (tileset) {
+ var i = 0;
+ // first gid
+
+ // tile properties (collidable, etc..)
+ this.TileProperties = [];
+
+ this.firstgid = this.lastgid = +tileset.firstgid;
+
+ // check if an external tileset is defined
+ if (typeof(tileset.source) !== "undefined") {
+ var src = tileset.source;
+ var ext = me.utils.getFileExtension(src);
+ if (ext === "tsx" || ext === "json") {
+ // load the external tileset (TSX/JSON)
+ tileset = me.loader.getTMX(me.utils.getBasename(src));
+ if (!tileset) {
+ throw new me.Error(src + " external TSX/JSON tileset not found");
+ }
+ }
+ }
+
+ this.name = tileset.name;
+ this.tilewidth = +tileset.tilewidth;
+ this.tileheight = +tileset.tileheight;
+ this.spacing = +tileset.spacing || 0;
+ this.margin = +tileset.margin || 0;
+
+ // set tile offset properties (if any)
+ this.tileoffset = new me.Vector2d();
+
+ /**
+ * Tileset contains animated tiles
+ * @public
+ * @type Boolean
+ * @name me.TMXTileset#isAnimated
+ */
+ this.isAnimated = false;
+
+ /**
+ * Tileset animations
+ * @private
+ * @type Map
+ * @name me.TMXTileset#animations
+ */
+ this.animations = new Map();
+
+ /**
+ * Remember the last update timestamp to prevent too many animation updates
+ * @private
+ * @type Map
+ * @name me.TMXTileset#_lastUpdate
+ */
+ this._lastUpdate = 0;
+
+ var tiles = tileset.tiles;
+ for (i in tiles) {
+ if (tiles.hasOwnProperty(i)) {
+ if ("animation" in tiles[i]) {
+ this.isAnimated = true;
+ this.animations.set(+i + this.firstgid, {
+ dt : 0,
+ idx : 0,
+ frames : tiles[i].animation,
+ cur : tiles[i].animation[0]
+ });
+ }
+ // set tile properties, if any (XML format)
+ if ("properties" in tiles[i]) {
+ this.setTileProperty(+i + this.firstgid, tiles[i].properties);
+ }
+ }
+ }
+
+ var offset = tileset.tileoffset;
+ if (offset) {
+ this.tileoffset.x = +offset.x;
+ this.tileoffset.y = +offset.y;
+ }
+
+ // set tile properties, if any (JSON format)
+ var tileInfo = tileset.tileproperties;
+ if (tileInfo) {
+ for (i in tileInfo) {
+ if (tileInfo.hasOwnProperty(i)) {
+ this.setTileProperty(+i + this.firstgid, tileInfo[i]);
+ }
+ }
+ }
+
+ this.image = me.utils.getImage(tileset.image);
+ if (!this.image) {
+ throw new me.TMXTileset.Error("melonJS: '" + tileset.image + "' file for tileset '" + this.name + "' not found!");
+ }
+
+ // create a texture atlas for the given tileset
+ this.texture = me.video.renderer.cache.get(this.image, {
+ framewidth : this.tilewidth,
+ frameheight : this.tileheight,
+ margin : this.margin,
+ spacing : this.spacing
+ });
+ this.atlas = this.texture.getAtlas();
+
+ // calculate the number of tiles per horizontal line
+ var hTileCount = +tileset.columns || ~~(this.image.width / (this.tilewidth + this.spacing));
+ var vTileCount = ~~(this.image.height / (this.tileheight + this.spacing));
+ // compute the last gid value in the tileset
+ this.lastgid = this.firstgid + (((hTileCount * vTileCount) - 1) || 0);
+ if (tileset.tilecount && this.lastgid - this.firstgid + 1 !== +tileset.tilecount) {
+ console.warn(
+ "Computed tilecount (" + (this.lastgid - this.firstgid + 1) +
+ ") does not match expected tilecount (" + tileset.tilecount + ")"
+ );
+ }
+ },
+
+ /**
+ * set the tile properties
+ * @ignore
+ * @function
+ */
+ setTileProperty : function (gid, prop) {
+ // set the given tile id
+ this.TileProperties[gid] = prop;
+ },
+
+ /**
+ * return true if the gid belongs to the tileset
+ * @name me.TMXTileset#contains
+ * @public
+ * @function
+ * @param {Number} gid
+ * @return {Boolean}
+ */
+ contains : function (gid) {
+ return gid >= this.firstgid && gid <= this.lastgid;
+ },
+
+ /**
+ * Get the view (local) tile ID from a GID, with animations applied
+ * @name me.TMXTileset#getViewTileId
+ * @public
+ * @function
+ * @param {Number} gid Global tile ID
+ * @return {Number} View tile ID
+ */
+ getViewTileId : function (gid) {
+ if (this.animations.has(gid)) {
+ // apply animations
+ gid = this.animations.get(gid).cur.tileid;
+ }
+ else {
+ // get the local tileset id
+ gid -= this.firstgid;
+ }
+
+ return gid;
+ },
+
+ /**
+ * return the properties of the specified tile
+ * @name me.TMXTileset#getTileProperties
+ * @public
+ * @function
+ * @param {Number} tileId
+ * @return {Object}
+ */
+ getTileProperties: function (tileId) {
+ return this.TileProperties[tileId];
+ },
+
+ // update tile animations
+ update : function (dt) {
+ var duration = 0,
+ now = me.timer.getTime(),
+ result = false;
+
+ if (this._lastUpdate !== now) {
+ this._lastUpdate = now;
+
+ this.animations.forEach(function (anim) {
+ anim.dt += dt;
+ duration = anim.cur.duration;
+ while (anim.dt >= duration) {
+ anim.dt -= duration;
+ anim.idx = (anim.idx + 1) % anim.frames.length;
+ anim.cur = anim.frames[anim.idx];
+ duration = anim.cur.duration;
+ result = true;
+ }
+ });
+ }
+
+ return result;
+ },
+
+ // draw the x,y tile
+ drawTile : function (renderer, dx, dy, tmxTile) {
+ // check if any transformation is required
+ if (tmxTile.flipped) {
+ renderer.save();
+ // apply the tile current transform
+ renderer.translate(dx, dy);
+ renderer.transform(tmxTile.currentTransform);
+ // reset both values as managed through transform();
+ dx = dy = 0;
+ }
+
+ var offset = this.atlas[this.getViewTileId(tmxTile.tileId)].offset;
+
+ // draw the tile
+ renderer.drawImage(
+ this.image,
+ offset.x, offset.y,
+ this.tilewidth, this.tileheight,
+ dx, dy,
+ this.tilewidth + renderer.uvOffset, this.tileheight + renderer.uvOffset
+ );
+
+ if (tmxTile.flipped) {
+ // restore the context to the previous state
+ renderer.restore();
+ }
+ }
+ });
+
+ /**
+ * an object containing all tileset
+ * @class
+ * @memberOf me
+ * @constructor
+ */
+ me.TMXTilesetGroup = me.Object.extend({
+ /**
+ * constructor
+ * @ignore
+ */
+ init: function () {
+ this.tilesets = [];
+ this.length = 0;
+ },
+
+ /**
+ * add a tileset to the tileset group
+ * @name me.TMXTilesetGroup#add
+ * @public
+ * @function
+ * @param {me.TMXTileset} tileset
+ */
+ add : function (tileset) {
+ this.tilesets.push(tileset);
+ this.length++;
+ },
+
+ /**
+ * return the tileset at the specified index
+ * @name me.TMXTilesetGroup#getTilesetByIndex
+ * @public
+ * @function
+ * @param {Number} i
+ * @return {me.TMXTileset} corresponding tileset
+ */
+ getTilesetByIndex : function (i) {
+ return this.tilesets[i];
+ },
+
+ /**
+ * return the tileset corresponding to the specified id
+ * will throw an exception if no matching tileset is found
+ * @name me.TMXTilesetGroup#getTilesetByGid
+ * @public
+ * @function
+ * @param {Number} gid
+ * @return {me.TMXTileset} corresponding tileset
+ */
+ getTilesetByGid : function (gid) {
+ var invalidRange = -1;
+
+ // clear the gid of all flip/rotation flags
+ gid &= TMX_CLEAR_BIT_MASK;
+
+ // cycle through all tilesets
+ for (var i = 0, len = this.tilesets.length; i < len; i++) {
+ // return the corresponding tileset if matching
+ if (this.tilesets[i].contains(gid)) {
+ return this.tilesets[i];
+ }
+ // typically indicates a layer with no asset loaded (collision?)
+ if (this.tilesets[i].firstgid === this.tilesets[i].lastgid &&
+ gid >= this.tilesets[i].firstgid) {
+ // store the id if the [firstgid .. lastgid] is invalid
+ invalidRange = i;
+ }
+ }
+ // return the tileset with the invalid range
+ if (invalidRange !== -1) {
+ return this.tilesets[invalidRange];
+ }
+ else {
+ throw new me.Error("no matching tileset found for gid " + gid);
+ }
+ }
+ });
+
+ /**
+ * Base class for TMXTileset exception handling.
+ * @name Error
+ * @class
+ * @memberOf me.TMXTileset
+ * @constructor
+ * @param {String} msg Error message.
+ */
+ me.TMXTileset.Error = me.Error.extend({
+ /**
+ * @ignore
+ */
+ init : function (msg) {
+ me.Error.prototype.init.apply(this, [ msg ]);
+ this.name = "me.TMXTileset.Error";
+ }
+ });
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Tiled (0.7+) format
+ * http://www.mapeditor.org/
+ *
+ */
+(function () {
+
+ // scope global var & constants
+ var offsetsStaggerX = [
+ {x: 0, y: 0},
+ {x: + 1, y: - 1},
+ {x: + 1, y: 0},
+ {x: + 2, y: 0}
+ ];
+ var offsetsStaggerY = [
+ {x: 0, y: 0},
+ {x: - 1, y: + 1},
+ {x: 0, y: + 1},
+ {x: 0, y: + 2}
+ ];
+
+ /**
+ * The map renderer base class
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {Number} cols width of the tilemap in tiles
+ * @param {Number} rows height of the tilemap in tiles
+ * @param {Number} tilewidth width of each tile in pixels
+ * @param {Number} tileheight height of each tile in pixels
+ */
+ me.TMXRenderer = me.Object.extend({
+ // constructor
+ init: function (cols, rows, tilewidth, tileheight) {
+ this.cols = cols;
+ this.rows = rows;
+ this.tilewidth = tilewidth;
+ this.tileheight = tileheight;
+ },
+
+ /**
+ * return true if the renderer can render the specified layer
+ * @name me.TMXRenderer#canRender
+ * @public
+ * @function
+ * @param {me.TMXTileMap|me.TMXLayer} component TMX Map or Layer
+ * @return {boolean}
+ */
+ canRender : function (component) {
+ return (
+ (this.cols === component.cols) &&
+ (this.rows === component.rows) &&
+ (this.tilewidth === component.tilewidth) &&
+ (this.tileheight === component.tileheight)
+ );
+ },
+
+ /**
+ * return the tile position corresponding to the specified pixel
+ * @name me.TMXRenderer#pixelToTileCoords
+ * @public
+ * @function
+ * @param {Number} x X coordinate
+ * @param {Number} y Y coordinate
+ * @param {me.Vector2d} [vector] an optional vector object where to put the return values
+ * @return {me.Vector2d}
+ */
+ pixelToTileCoords : function (x, y, v) {
+ return v;
+ },
+
+ /**
+ * return the pixel position corresponding of the specified tile
+ * @name me.TMXRenderer#tileToPixelCoords
+ * @public
+ * @function
+ * @param {Number} col tile horizontal position
+ * @param {Number} row tile vertical position
+ * @param {me.Vector2d} [vector] an optional vector object where to put the return values
+ * @return {me.Vector2d}
+ */
+ tileToPixelCoords : function (x, y, v) {
+ return v;
+ },
+
+ /**
+ * return the tile position corresponding for the given X coordinate
+ * @name me.TMXRenderer#pixelToTileX
+ * @public
+ * @function
+ * @param {Number} x X coordinate
+ * @return {Number} tile vertical position
+ */
+ pixelToTileX : function (x) {
+ },
+
+ /**
+ * return the tile position corresponding for the given Y coordinates
+ * @name me.TMXRenderer#pixelToTileY
+ * @public
+ * @function
+ * @param {Number} y Y coordinate
+ * @return {Number} tile horizontal position
+ */
+ pixelToTileY : function (y) {
+ },
+
+ /**
+ * draw the given tile at the specified layer
+ * @name me.TMXRenderer#drawTile
+ * @public
+ * @function
+ * @param {me.CanvasRenderer|me.WebGLRenderer} renderer a renderer object
+ * @param {Number} x X coordinate where to draw the tile
+ * @param {Number} y Y coordinate where to draw the tile
+ * @param {me.Tile} tile the tile object to draw
+ */
+ drawTile : function (renderer, x, y, tile) {
+ },
+
+ /**
+ * draw the given TMX Layer for the given area
+ * @name me.TMXRenderer#drawTileLayer
+ * @public
+ * @function
+ * @param {me.CanvasRenderer|me.WebGLRenderer} renderer a renderer object
+ * @param {me.TMXLayer} layer a TMX Layer object
+ * @param {me.Rect} rect the area of the layer to draw
+ */
+ drawTileLayer : function (renderer, layer, rect) {
+ }
+
+ });
+
+ /**
+ * an Orthogonal Map Renderder
+ * @memberOf me
+ * @extends me.TMXRenderer
+ * @memberOf me
+ * @constructor
+ * @param {Number} cols width of the tilemap in tiles
+ * @param {Number} rows height of the tilemap in tiles
+ * @param {Number} tilewidth width of each tile in pixels
+ * @param {Number} tileheight height of each tile in pixels
+ */
+ me.TMXOrthogonalRenderer = me.TMXRenderer.extend({
+ /**
+ * return true if the renderer can render the specified layer
+ * @ignore
+ */
+ canRender : function (layer) {
+ return (
+ (layer.orientation === "orthogonal") &&
+ me.TMXRenderer.prototype.canRender.apply(this, [ layer ])
+ );
+ },
+
+ /**
+ * return the tile position corresponding to the specified pixel
+ * @ignore
+ */
+ pixelToTileCoords : function (x, y, v) {
+ var ret = v || new me.Vector2d();
+ return ret.set(
+ this.pixelToTileX(x),
+ this.pixelToTileY(y)
+ );
+ },
+
+ /**
+ * return the tile position corresponding for the given X coordinate
+ * @ignore
+ */
+ pixelToTileX : function (x) {
+ return x / this.tilewidth;
+ },
+
+ /**
+ * return the tile position corresponding for the given Y coordinates
+ * @ignore
+ */
+ pixelToTileY : function (y) {
+ return y / this.tileheight;
+ },
+
+ /**
+ * return the pixel position corresponding of the specified tile
+ * @ignore
+ */
+ tileToPixelCoords : function (x, y, v) {
+ var ret = v || new me.Vector2d();
+ return ret.set(
+ x * this.tilewidth,
+ y * this.tileheight
+ );
+ },
+
+ /**
+ * fix the position of Objects to match
+ * the way Tiled places them
+ * @ignore
+ */
+ adjustPosition: function (obj) {
+ // only adjust position if obj.gid is defined
+ if (typeof(obj.gid) === "number") {
+ // Tiled objects origin point is "bottom-left" in Tiled,
+ // "top-left" in melonJS)
+ obj.y -= obj.height;
+ }
+ },
+
+ /**
+ * draw the tile map
+ * @ignore
+ */
+ drawTile : function (renderer, x, y, tmxTile) {
+ var tileset = tmxTile.tileset;
+ // draw the tile
+ tileset.drawTile(
+ renderer,
+ tileset.tileoffset.x + x * this.tilewidth,
+ tileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight,
+ tmxTile
+ );
+ },
+
+ /**
+ * draw the tile map
+ * @ignore
+ */
+ drawTileLayer : function (renderer, layer, rect) {
+ // get top-left and bottom-right tile position
+ var start = this.pixelToTileCoords(
+ Math.max(rect.pos.x - (layer.maxTileSize.width - layer.tilewidth), 0),
+ Math.max(rect.pos.y - (layer.maxTileSize.height - layer.tileheight), 0),
+ me.pool.pull("me.Vector2d")
+ ).floorSelf();
+
+ var end = this.pixelToTileCoords(
+ rect.pos.x + rect.width + this.tilewidth,
+ rect.pos.y + rect.height + this.tileheight,
+ me.pool.pull("me.Vector2d")
+ ).ceilSelf();
+
+ //ensure we are in the valid tile range
+ end.x = end.x > this.cols ? this.cols : end.x;
+ end.y = end.y > this.rows ? this.rows : end.y;
+
+ // main drawing loop
+ for (var y = start.y; y < end.y; y++) {
+ for (var x = start.x; x < end.x; x++) {
+ var tmxTile = layer.layerData[x][y];
+ if (tmxTile) {
+ this.drawTile(renderer, x, y, tmxTile);
+ }
+ }
+ }
+
+ me.pool.push(start);
+ me.pool.push(end);
+ }
+ });
+
+
+ /**
+ * an Isometric Map Renderder
+ * @memberOf me
+ * @extends me.TMXRenderer
+ * @memberOf me
+ * @constructor
+ * @param {Number} cols width of the tilemap in tiles
+ * @param {Number} rows height of the tilemap in tiles
+ * @param {Number} tilewidth width of each tile in pixels
+ * @param {Number} tileheight height of each tile in pixels
+ */
+ me.TMXIsometricRenderer = me.TMXRenderer.extend({
+ // constructor
+ init: function (cols, rows, tilewidth, tileheight) {
+ me.TMXRenderer.prototype.init.apply(this, [
+ cols,
+ rows,
+ tilewidth,
+ tileheight
+ ]);
+
+ this.hTilewidth = tilewidth / 2;
+ this.hTileheight = tileheight / 2;
+ this.originX = this.rows * this.hTilewidth;
+ },
+
+ /**
+ * return true if the renderer can render the specified layer
+ * @ignore
+ */
+ canRender : function (layer) {
+ return (
+ (layer.orientation === "isometric") &&
+ me.TMXRenderer.prototype.canRender.apply(this, [ layer ])
+ );
+ },
+
+ /**
+ * return the tile position corresponding to the specified pixel
+ * @ignore
+ */
+ pixelToTileCoords : function (x, y, v) {
+ var ret = v || new me.Vector2d();
+ return ret.set(
+ this.pixelToTileX(x, y),
+ this.pixelToTileY(y, x)
+ );
+ },
+
+ /**
+ * return the tile position corresponding for the given X coordinate
+ * @ignore
+ */
+ pixelToTileX : function (x, y) {
+ return (y / this.tileheight) + ((x - this.originX) / this.tilewidth);
+ },
+
+ /**
+ * return the tile position corresponding for the given Y coordinates
+ * @ignore
+ */
+ pixelToTileY : function (y, x) {
+ return (y / this.tileheight) - ((x - this.originX) / this.tilewidth);
+ },
+
+ /**
+ * return the pixel position corresponding of the specified tile
+ * @ignore
+ */
+ tileToPixelCoords : function (x, y, v) {
+ var ret = v || new me.Vector2d();
+ return ret.set(
+ (x - y) * this.hTilewidth + this.originX,
+ (x + y) * this.hTileheight
+ );
+ },
+
+ /**
+ * fix the position of Objects to match
+ * the way Tiled places them
+ * @ignore
+ */
+ adjustPosition: function (obj) {
+ var tileX = obj.x / this.hTilewidth;
+ var tileY = obj.y / this.tileheight;
+ var isoPos = me.pool.pull("me.Vector2d");
+
+ this.tileToPixelCoords(tileX, tileY, isoPos);
+
+ obj.x = isoPos.x;
+ obj.y = isoPos.y;
+
+ me.pool.push(isoPos);
+ },
+
+ /**
+ * draw the tile map
+ * @ignore
+ */
+ drawTile : function (renderer, x, y, tmxTile) {
+ var tileset = tmxTile.tileset;
+ // draw the tile
+ tileset.drawTile(
+ renderer,
+ ((this.cols - 1) * tileset.tilewidth + (x - y) * tileset.tilewidth >> 1),
+ (-tileset.tilewidth + (x + y) * tileset.tileheight >> 2),
+ tmxTile
+ );
+ },
+
+ /**
+ * draw the tile map
+ * @ignore
+ */
+ drawTileLayer : function (renderer, layer, rect) {
+ // cache a couple of useful references
+ var tileset = layer.tileset;
+ var offset = tileset.tileoffset;
+
+ // get top-left and bottom-right tile position
+ var rowItr = this.pixelToTileCoords(
+ rect.pos.x - tileset.tilewidth,
+ rect.pos.y - tileset.tileheight,
+ me.pool.pull("me.Vector2d")
+ ).floorSelf();
+ var TileEnd = this.pixelToTileCoords(
+ rect.pos.x + rect.width + tileset.tilewidth,
+ rect.pos.y + rect.height + tileset.tileheight,
+ me.pool.pull("me.Vector2d")
+ ).ceilSelf();
+
+ var rectEnd = this.tileToPixelCoords(TileEnd.x, TileEnd.y, me.pool.pull("me.Vector2d"));
+
+ // Determine the tile and pixel coordinates to start at
+ var startPos = this.tileToPixelCoords(rowItr.x, rowItr.y, me.pool.pull("me.Vector2d"));
+ startPos.x -= this.hTilewidth;
+ startPos.y += this.tileheight;
+
+ /* Determine in which half of the tile the top-left corner of the area we
+ * need to draw is. If we're in the upper half, we need to start one row
+ * up due to those tiles being visible as well. How we go up one row
+ * depends on whether we're in the left or right half of the tile.
+ */
+ var inUpperHalf = startPos.y - rect.pos.y > this.hTileheight;
+ var inLeftHalf = rect.pos.x - startPos.x < this.hTilewidth;
+
+ if (inUpperHalf) {
+ if (inLeftHalf) {
+ rowItr.x--;
+ startPos.x -= this.hTilewidth;
+ }
+ else {
+ rowItr.y--;
+ startPos.x += this.hTilewidth;
+ }
+ startPos.y -= this.hTileheight;
+ }
+
+ // Determine whether the current row is shifted half a tile to the right
+ var shifted = inUpperHalf ^ inLeftHalf;
+
+ // initialize the columItr vector
+ var columnItr = rowItr.clone();
+
+ // main drawing loop
+ for (var y = startPos.y * 2; y - this.tileheight * 2 < rectEnd.y * 2; y += this.tileheight) {
+ columnItr.setV(rowItr);
+ for (var x = startPos.x; x < rectEnd.x; x += this.tilewidth) {
+ //check if it's valid tile, if so render
+ if (
+ (columnItr.x >= 0) &&
+ (columnItr.y >= 0) &&
+ (columnItr.x < this.cols) &&
+ (columnItr.y < this.rows)
+ ) {
+ var tmxTile = layer.layerData[columnItr.x][columnItr.y];
+ if (tmxTile) {
+ tileset = tmxTile.tileset;
+ // offset could be different per tileset
+ offset = tileset.tileoffset;
+ // draw our tile
+ tileset.drawTile(
+ renderer,
+ offset.x + x,
+ offset.y + y / 2 - tileset.tileheight,
+ tmxTile
+ );
+ }
+ }
+ // Advance to the next column
+ columnItr.x++;
+ columnItr.y--;
+ }
+
+ // Advance to the next row
+ if (!shifted) {
+ rowItr.x++;
+ startPos.x += this.hTilewidth;
+ shifted = true;
+ }
+ else {
+ rowItr.y++;
+ startPos.x -= this.hTilewidth;
+ shifted = false;
+ }
+ }
+
+ me.pool.push(rowItr);
+ me.pool.push(TileEnd);
+ me.pool.push(rectEnd);
+ me.pool.push(startPos);
+ }
+ });
+
+
+ /**
+ * an Hexagonal Map Renderder
+ * @memberOf me
+ * @extends me.TMXRenderer
+ * @memberOf me
+ * @constructor
+ * @param {Number} cols width of the tilemap in tiles
+ * @param {Number} rows height of the tilemap in tiles
+ * @param {Number} tilewidth width of each tile in pixels
+ * @param {Number} tileheight height of each tile in pixels
+ */
+ me.TMXHexagonalRenderer = me.TMXRenderer.extend({
+ // constructor
+ init: function (cols, rows, tilewidth, tileheight, hexsidelength, staggeraxis, staggerindex) {
+ me.TMXRenderer.prototype.init.apply(this, [
+ cols,
+ rows,
+ tilewidth,
+ tileheight
+ ]);
+
+ this.hexsidelength = hexsidelength;
+ this.staggeraxis = staggeraxis;
+ this.staggerindex = staggerindex;
+
+ this.sidelengthx = 0;
+ this.sidelengthy = 0;
+
+ if (staggeraxis === "x") {
+ this.sidelengthx = hexsidelength;
+ }
+ else {
+ this.sidelengthy = hexsidelength;
+ }
+
+ this.sideoffsetx = (this.tilewidth - this.sidelengthx) / 2;
+ this.sideoffsety = (this.tileheight - this.sidelengthy) / 2;
+
+ this.columnwidth = this.sideoffsetx + this.sidelengthx;
+ this.rowheight = this.sideoffsety + this.sidelengthy;
+
+ this.centers = [
+ new me.Vector2d(),
+ new me.Vector2d(),
+ new me.Vector2d(),
+ new me.Vector2d()
+ ];
+ },
+
+ /**
+ * return true if the renderer can render the specified layer
+ * @ignore
+ */
+ canRender : function (layer) {
+ return (
+ (layer.orientation === "hexagonal") &&
+ me.TMXRenderer.prototype.canRender.apply(this, [ layer ])
+ );
+ },
+
+ /**
+ * return the tile position corresponding to the specified pixel
+ * @ignore
+ */
+ pixelToTileCoords : function (x, y, v) {
+ var q, r;
+ var ret = v || new me.Vector2d();
+
+ if (this.staggeraxis === "x") { //flat top
+ x = x - ((this.staggerindex === "odd") ? this.sideoffsetx : this.tilewidth);
+ }
+ else { //pointy top
+ y = y - ((this.staggerindex === "odd") ? this.sideoffsety : this.tileheight);
+ }
+
+ // Start with the coordinates of a grid-aligned tile
+ var referencePoint = me.pool.pull("me.Vector2d",
+ Math.floor(x / (this.columnwidth * 2)),
+ Math.floor((y / (this.rowheight * 2)))
+ );
+
+ // Relative x and y position on the base square of the grid-aligned tile
+ var rel = me.pool.pull("me.Vector2d",
+ x - referencePoint.x * (this.columnwidth * 2),
+ y - referencePoint.y * (this.rowheight * 2)
+ );
+
+ // Adjust the reference point to the correct tile coordinates
+ if (this.staggeraxis === "x") {
+ referencePoint.x = referencePoint.x * 2;
+ if (this.staggerindex === "even") {
+ ++referencePoint.x;
+ }
+ }
+ else {
+ referencePoint.y = referencePoint.y * 2;
+ if (this.staggerindex === "even") {
+ ++referencePoint.y;
+ }
+ }
+
+ // Determine the nearest hexagon tile by the distance to the center
+ var left, top, centerX, centerY;
+ if (this.staggeraxis === "x") {
+ left = this.sidelengthx / 2;
+ centerX = left + this.columnwidth;
+ centerY = this.tileheight / 2;
+
+ this.centers[0].set(left, centerY);
+ this.centers[1].set(centerX, centerY - this.rowheight);
+ this.centers[2].set(centerX, centerY + this.rowheight);
+ this.centers[3].set(centerX + this.columnwidth, centerY);
+ }
+ else {
+ top = this.sidelengthy / 2;
+ centerX = this.tilewidth / 2;
+ centerY = top + this.rowheight;
+
+ this.centers[0].set(centerX, top);
+ this.centers[1].set(centerX - this.columnwidth, centerY);
+ this.centers[2].set(centerX + this.columnwidth, centerY);
+ this.centers[3].set(centerX, centerY + this.rowheight);
+ }
+
+ var nearest = 0;
+ var minDist = Number.MAX_VALUE;
+ var dc;
+ for (var i = 0; i < 4; ++i) {
+ dc = Math.pow(this.centers[i].x - rel.x, 2) + Math.pow(this.centers[i].y - rel.y, 2);
+ if (dc < minDist) {
+ minDist = dc;
+ nearest = i;
+ }
+ }
+
+ var offsets = (this.staggeraxis === "x") ? offsetsStaggerX : offsetsStaggerY;
+
+ q = referencePoint.x + offsets[nearest].x;
+ r = referencePoint.y + offsets[nearest].y;
+
+ me.pool.push(referencePoint);
+ me.pool.push(rel);
+
+ return ret.set(q, r);
+ },
+
+ /**
+ * return the tile position corresponding for the given X coordinate
+ * @ignore
+ */
+ pixelToTileX : function (x, y) {
+ var ret = me.pool.pull("me.Vector2d");
+ this.pixelToTileCoords(x, y, ret);
+ me.pool.push(ret);
+ return ret.x;
+ },
+
+ /**
+ * return the tile position corresponding for the given Y coordinates
+ * @ignore
+ */
+ pixelToTileY : function (y, x) {
+ var ret = me.pool.pull("me.Vector2d");
+ this.pixelToTileCoords(x, y, ret);
+ me.pool.push(ret);
+ return ret.y;
+ },
+
+ /**
+ * return the pixel position corresponding of the specified tile
+ * @ignore
+ */
+ tileToPixelCoords : function (q, r, v) {
+ var x, y;
+ var ret = v || new me.Vector2d();
+ if (this.staggeraxis === "x") {
+ //flat top
+ x = q * this.columnwidth;
+ if (this.staggerindex === "odd") {
+ y = r * (this.tileheight + this.sidelengthy);
+ y = y + (this.rowheight * (q & 1));
+ }
+ else {
+ y = r * (this.tileheight + this.sidelengthy);
+ y = y + (this.rowheight * (1 - (q & 1)));
+ }
+ }
+ else {
+ //pointy top
+ y = r * this.rowheight;
+ if (this.staggerindex === "odd") {
+ x = q * (this.tilewidth + this.sidelengthx);
+ x = x + (this.columnwidth * (r & 1));
+ }
+ else {
+ x = q * (this.tilewidth + this.sidelengthx);
+ x = x + (this.columnwidth * (1 - (r & 1)));
+ }
+ }
+ return ret.set(x, y);
+ },
+
+ /**
+ * fix the position of Objects to match
+ * the way Tiled places them
+ * @ignore
+ */
+ adjustPosition: function (obj) {
+ // only adjust position if obj.gid is defined
+ if (typeof(obj.gid) === "number") {
+ // Tiled objects origin point is "bottom-left" in Tiled,
+ // "top-left" in melonJS)
+ obj.y -= obj.height;
+ }
+ },
+
+ /**
+ * draw the tile map
+ * @ignore
+ */
+ drawTile : function (renderer, x, y, tmxTile) {
+ var tileset = tmxTile.tileset;
+ var point = this.tileToPixelCoords(x, y, me.pool.pull("me.Vector2d"));
+
+ // draw the tile
+ tileset.drawTile(
+ renderer,
+ tileset.tileoffset.x + point.x,
+ tileset.tileoffset.y + point.y + (this.tileheight - tileset.tileheight),
+ tmxTile
+ );
+
+ me.pool.push(point);
+ },
+
+ /**
+ * draw the tile map
+ * @ignore
+ */
+ drawTileLayer : function (renderer, layer, rect) {
+ // get top-left and bottom-right tile position
+ var start = this.pixelToTileCoords(
+ rect.pos.x,
+ rect.pos.y
+ ).floorSelf();
+
+ var end = this.pixelToTileCoords(
+ rect.pos.x + rect.width + this.tilewidth,
+ rect.pos.y + rect.height + this.tileheight
+ ).ceilSelf();
+
+ //ensure we are in the valid tile range
+ start.x = start.x < 0 ? 0 : start.x;
+ start.y = start.y < 0 ? 0 : start.y;
+ end.x = end.x > this.cols ? this.cols : end.x;
+ end.y = end.y > this.rows ? this.rows : end.y;
+
+ // main drawing loop
+ for (var y = start.y; y < end.y; y++) {
+ for (var x = start.x; x < end.x; x++) {
+ var tmxTile = layer.layerData[x][y];
+ if (tmxTile) {
+ this.drawTile(renderer, x, y, tmxTile);
+ }
+ }
+ }
+ }
+ });
+
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+
+ /**
+ * a generic Color Layer Object
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {String} name Layer name
+ * @param {me.Color|String} color CSS color
+ * @param {Number} z z-index position
+ */
+ me.ColorLayer = me.Renderable.extend({
+ /**
+ * Constructor
+ * @ignore
+ */
+ init: function (name, color, z) {
+ // parent constructor
+ me.Renderable.prototype.init.apply(this, [0, 0, Infinity, Infinity]);
+
+ // apply given parameters
+ this.name = name;
+ this.pos.z = z;
+ this.floating = true;
+
+ // parse the given color
+ if (color instanceof me.Color) {
+ this.color = color.toRGBA();
+ } else {
+ // string (#RGB, #ARGB, #RRGGBB, #AARRGGBB)
+ var _col = me.pool.pull("me.Color");
+ this.color = _col.parseCSS(color).toRGBA();
+ me.pool.push(_col);
+ }
+
+
+ },
+
+ /**
+ * draw the color layer
+ * @ignore
+ */
+ draw : function (renderer, rect) {
+ // set layer opacity
+ var _alpha = renderer.globalAlpha();
+ renderer.setGlobalAlpha(_alpha * this.getOpacity());
+
+ var vpos = me.game.viewport.pos;
+ renderer.setColor(this.color);
+ renderer.fillRect(
+ rect.left - vpos.x, rect.top - vpos.y,
+ rect.width, rect.height
+ );
+ // restore context alpha value
+ renderer.setGlobalAlpha(_alpha);
+ renderer.setColor("#fff");
+ }
+ });
+
+ /**
+ * a generic Image Layer Object
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {Number} x x coordinate
+ * @param {Number} y y coordinate
+ * @param {Object} settings ImageLayer properties
+ * @param {Image|String} settings.image Image reference. See {@link me.loader.getImage}
+ * @param {String} [settings.name="me.ImageLayer"] Layer name
+ * @param {Number} [settings.z=0] z-index position
+ * @param {Number|me.Vector2d} [settings.ratio=1.0] Scrolling ratio to be applied
+ * @param {Number|me.Vector2d} [settings.anchorPoint=0.0] Image origin. See {@link me.ImageLayer#anchorPoint}
+ */
+ me.ImageLayer = me.Renderable.extend({
+ /**
+ * constructor
+ * @ignore
+ */
+ init: function (x, y, settings) {
+ // layer name
+ this.name = settings.name || "me.ImageLayer";
+
+ // get the corresponding image
+ this.image = me.utils.getImage(settings.image);
+
+ // XXX: Keep this check?
+ if (!this.image) {
+ throw new me.Error((
+ (typeof(settings.image) === "string") ?
+ "'" + settings.image + "'" :
+ "Image"
+ ) + " file for Image Layer '" + this.name + "' not found!");
+ }
+
+ this.imagewidth = this.image.width;
+ this.imageheight = this.image.height;
+
+ // call the constructor
+ me.Renderable.prototype.init.apply(this, [x, y, Infinity, Infinity]);
+
+ // render in screen coordinates
+ this.floating = true;
+
+ // displaying order
+ this.pos.z = settings.z || 0;
+
+ this.offset = new me.Vector2d(x, y);
+
+ /**
+ * Define the image scrolling ratio
+ * Scrolling speed is defined by multiplying the viewport delta position (e.g. followed entity) by the specified ratio.
+ * Setting this vector to <0.0,0.0> will disable automatic scrolling.
+ * To specify a value through Tiled, use one of the following format :
+ * - a number, to change the value for both axis
+ * - a json expression like `json:{"x":0.5,"y":0.5}` if you wish to specify a different value for both x and y
+ * @public
+ * @type me.Vector2d
+ * @default <1.0,1.0>
+ * @name me.ImageLayer#ratio
+ */
+ this.ratio = new me.Vector2d(1.0, 1.0);
+
+ if (typeof(settings.ratio) !== "undefined") {
+ // little hack for backward compatiblity
+ if (typeof(settings.ratio) === "number") {
+ this.ratio.set(settings.ratio, settings.ratio);
+ } else /* vector */ {
+ this.ratio.setV(settings.ratio);
+ }
+ }
+
+ if (typeof(settings.anchorPoint) === "undefined") {
+ /**
+ * Define how the image is anchored to the viewport bounds
+ * By default, its upper-left corner is anchored to the viewport bounds upper left corner.
+ * The anchorPoint is a unit vector where each component falls in range [0.0,1.0].
+ * Some common examples:
+ * * <0.0,0.0> : (Default) Anchor image to the upper-left corner of viewport bounds
+ * * <0.5,0.5> : Center the image within viewport bounds
+ * * <1.0,1.0> : Anchor image to the lower-right corner of viewport bounds
+ * To specify a value through Tiled, use one of the following format :
+ * - a number, to change the value for both axis
+ * - a json expression like `json:{"x":0.5,"y":0.5}` if you wish to specify a different value for both x and y
+ * @public
+ * @type me.Vector2d
+ * @default <0.0,0.0>
+ * @name me.ImageLayer#anchorPoint
+ */
+ this.anchorPoint.set(0, 0);
+ }
+ else {
+ if (typeof(settings.anchorPoint) === "number") {
+ this.anchorPoint.set(settings.anchorPoint, settings.anchorPoint);
+ }
+ else /* vector */ {
+ this.anchorPoint.setV(settings.anchorPoint);
+ }
+ }
+
+ /**
+ * Define if and how an Image Layer should be repeated.
+ * By default, an Image Layer is repeated both vertically and horizontally.
+ * Acceptable values :
+ * * 'repeat' - The background image will be repeated both vertically and horizontally. (default)
+ * * 'repeat-x' - The background image will be repeated only horizontally.
+ * * 'repeat-y' - The background image will be repeated only vertically.
+ * * 'no-repeat' - The background-image will not be repeated.
+ * @public
+ * @type String
+ * @name me.ImageLayer#repeat
+ */
+ Object.defineProperty(this, "repeat", {
+ /**
+ * @ignore
+ */
+ get : function get() {
+ return this._repeat;
+ },
+ /**
+ * @ignore
+ */
+ set : function set(val) {
+ this._repeat = val;
+ switch (this._repeat) {
+ case "no-repeat" :
+ this.repeatX = false;
+ this.repeatY = false;
+ break;
+ case "repeat-x" :
+ this.repeatX = true;
+ this.repeatY = false;
+ break;
+ case "repeat-y" :
+ this.repeatX = false;
+ this.repeatY = true;
+ break;
+ default : // "repeat"
+ this.repeatX = true;
+ this.repeatY = true;
+ break;
+ }
+ this.resize(me.game.viewport.width, me.game.viewport.height);
+ this.createPattern();
+ }
+ });
+
+ this.repeat = settings.repeat || "repeat";
+ },
+
+ // called when the layer is added to the game world or a container
+ onActivateEvent : function () {
+ var _updateLayerFn = this.updateLayer.bind(this);
+ // register to the viewport change notification
+ this.vpChangeHdlr = me.event.subscribe(me.event.VIEWPORT_ONCHANGE, _updateLayerFn);
+ this.vpResizeHdlr = me.event.subscribe(me.event.VIEWPORT_ONRESIZE, this.resize.bind(this));
+ this.vpLoadedHdlr = me.event.subscribe(me.event.LEVEL_LOADED, function() {
+ // force a first refresh when the level is loaded
+ _updateLayerFn(me.game.viewport.pos);
+ });
+ // in case the level is not added to the root container,
+ // the onActivateEvent call happens after the LEVEL_LOADED event
+ // so we need to force a first update
+ if (this.ancestor._root !== true) {
+ this.updateLayer(me.game.viewport.pos);
+ }
+ },
+
+ /**
+ * resize the Image Layer to match the given size
+ * @name resize
+ * @memberOf me.ImageLayer
+ * @function
+ * @param {Number} w new width
+ * @param {Number} h new height
+ */
+ resize : function (w, h) {
+ me.Renderable.prototype.resize.apply(this, [
+ this.repeatX ? Infinity : w,
+ this.repeatY ? Infinity : h
+ ]);
+ },
+
+ /**
+ * createPattern function
+ * @ignore
+ * @function
+ */
+ createPattern : function () {
+ this._pattern = me.video.renderer.createPattern(this.image, this._repeat);
+ },
+
+ /**
+ * updateLayer function
+ * @ignore
+ * @function
+ */
+ updateLayer : function (vpos) {
+ var rx = this.ratio.x,
+ ry = this.ratio.y;
+
+ if (rx === ry === 0) {
+ // static image
+ return;
+ }
+
+ var viewport = me.game.viewport,
+ width = this.imagewidth,
+ height = this.imageheight,
+ bw = viewport.bounds.width,
+ bh = viewport.bounds.height,
+ ax = this.anchorPoint.x,
+ ay = this.anchorPoint.y,
+
+ /*
+ * Automatic positioning
+ *
+ * See https://github.com/melonjs/melonJS/issues/741#issuecomment-138431532
+ * for a thorough description of how this works.
+ */
+ x = ax * (rx - 1) * (bw - viewport.width) + this.offset.x - rx * vpos.x,
+ y = ay * (ry - 1) * (bh - viewport.height) + this.offset.y - ry * vpos.y;
+
+
+ // Repeat horizontally; start drawing from left boundary
+ if (this.repeatX) {
+ this.pos.x = x % width;
+ }
+ else {
+ this.pos.x = x;
+ }
+
+ // Repeat vertically; start drawing from top boundary
+ if (this.repeatY) {
+ this.pos.y = y % height;
+ }
+ else {
+ this.pos.y = y;
+ }
+ },
+
+ /**
+ * draw the image layer
+ * @ignore
+ */
+ draw : function (renderer) {
+ var viewport = me.game.viewport,
+ width = this.imagewidth,
+ height = this.imageheight,
+ bw = viewport.bounds.width,
+ bh = viewport.bounds.height,
+ ax = this.anchorPoint.x,
+ ay = this.anchorPoint.y,
+ x = this.pos.x,
+ y = this.pos.y,
+ alpha = renderer.globalAlpha();
+
+ if (this.ratio.x === this.ratio.y === 0) {
+ x = x + ax * (bw - width);
+ y = y + ay * (bh - height);
+ }
+
+ renderer.setGlobalAlpha(alpha * this.getOpacity());
+ renderer.translate(x, y);
+ renderer.drawPattern(
+ this._pattern,
+ 0,
+ 0,
+ viewport.width * 2,
+ viewport.height * 2
+ );
+ renderer.translate(-x, -y);
+ renderer.setGlobalAlpha(alpha);
+ },
+
+ // called when the layer is removed from the game world or a container
+ onDeactivateEvent : function () {
+ // cancel all event subscriptions
+ me.event.unsubscribe(this.vpChangeHdlr);
+ me.event.unsubscribe(this.vpResizeHdlr);
+ me.event.unsubscribe(this.vpLoadedHdlr);
+ }
+
+ });
+
+ /**
+ * a TMX Tile Layer Object
+ * Tiled QT 0.7.x format
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {Number} tilewidth width of each tile in pixels
+ * @param {Number} tileheight height of each tile in pixels
+ * @param {String} orientation "isometric" or "orthogonal"
+ * @param {me.TMXTilesetGroup} tilesets tileset as defined in Tiled
+ * @param {Number} z z-index position
+ */
+ me.TMXLayer = me.Renderable.extend({
+ /**
+ * @ignore
+ */
+ init: function (tilewidth, tileheight, orientation, tilesets, z) {
+ // super constructor
+ me.Renderable.prototype.init.apply(this, [0, 0, 0, 0]);
+
+ // tile width & height
+ this.tilewidth = tilewidth;
+ this.tileheight = tileheight;
+
+ // layer orientation
+ this.orientation = orientation;
+
+ /**
+ * The Layer corresponding Tilesets
+ * @public
+ * @type me.TMXTilesetGroup
+ * @name me.TMXLayer#tilesets
+ */
+ this.tilesets = tilesets;
+
+ // the default tileset
+ // XXX: Is this even used?
+ this.tileset = (this.tilesets ? this.tilesets.getTilesetByIndex(0) : null);
+
+ // Biggest tile size to draw
+ this.maxTileSize = {
+ "width" : 0,
+ "height" : 0
+ };
+ for (var i = 0; i < this.tilesets.length; i++) {
+ var tileset = this.tilesets.getTilesetByIndex(i);
+ this.maxTileSize.width = Math.max(this.maxTileSize.width, tileset.tilewidth);
+ this.maxTileSize.height = Math.max(this.maxTileSize.height, tileset.tileheight);
+ }
+
+ /**
+ * All animated tilesets in this layer
+ * @ignore
+ * @type Array
+ * @name me.TMXLayer#animatedTilesets
+ */
+ this.animatedTilesets = [];
+
+ /**
+ * Layer contains tileset animations
+ * @public
+ * @type Boolean
+ * @name me.TMXLayer#isAnimated
+ */
+ this.isAnimated = false;
+
+ // for displaying order
+ this.pos.z = z;
+
+ // tiled default coordinates are top-left
+ this.anchorPoint.set(0, 0);
+ },
+
+ /** @ignore */
+ initFromJSON: function (layer) {
+ // additional TMX flags
+ this.name = layer.name;
+ this.cols = +layer.width;
+ this.rows = +layer.height;
+
+ // hexagonal maps only
+ this.hexsidelength = +layer.hexsidelength || undefined;
+ this.staggeraxis = layer.staggeraxis;
+ this.staggerindex = layer.staggerindex;
+
+ // layer opacity
+ var visible = typeof(layer.visible) !== "undefined" ? +layer.visible : 1;
+ this.setOpacity(visible ? +layer.opacity : 0);
+
+ // layer "real" size
+ if (this.orientation === "isometric") {
+ this.width = (this.cols + this.rows) * (this.tilewidth / 2);
+ this.height = (this.cols + this.rows) * (this.tileheight / 2);
+ } else {
+ this.width = this.cols * this.tilewidth;
+ this.height = this.rows * this.tileheight;
+ }
+ // check if we have any user-defined properties
+ me.TMXUtils.applyTMXProperties(this, layer);
+
+ // check for the correct rendering method
+ if (typeof (this.preRender) === "undefined") {
+ this.preRender = me.sys.preRender;
+ }
+
+ // if pre-rendering method is use, create an offline canvas/renderer
+ if (this.preRender === true) {
+ this.canvasRenderer = new me.CanvasRenderer(
+ me.video.createCanvas(this.width, this.height),
+ this.width, this.height,
+ { transparent : true }
+ );
+ }
+
+ //initialize the layer data array
+ this.initArray(this.cols, this.rows);
+ },
+
+ // called when the layer is added to the game world or a container
+ onActivateEvent : function () {
+
+ // (re)initialize the layer data array
+ /*if (this.layerData === undefined) {
+ this.initArray(this.cols, this.rows);
+ }*/
+
+ if (this.animatedTilesets === undefined) {
+ this.animatedTilesets = [];
+ }
+
+ if (this.tilesets) {
+ var tileset = this.tilesets.tilesets;
+ for (var i = 0; i < tileset.length; i++) {
+ if (tileset[i].isAnimated) {
+ this.animatedTilesets.push(tileset[i]);
+ }
+ }
+ }
+
+ this.isAnimated = this.animatedTilesets.length > 0;
+
+ // Force pre-render off when tileset animation is used
+ if (this.isAnimated) {
+ this.preRender = false;
+ }
+
+ // Resize the bounding rect
+ this.getBounds().resize(this.width, this.height);
+ },
+
+ // called when the layer is removed from the game world or a container
+ onDeactivateEvent : function () {
+ // clear all allocated objects
+ //this.layerData = undefined;
+ this.animatedTilesets = undefined;
+ },
+
+ /**
+ * Se the TMX renderer for this layer object
+ * @name setRenderer
+ * @memberOf me.TMXLayer
+ * @public
+ * @function
+ * @param {me.TMXRenderer} renderer
+ */
+ setRenderer : function (renderer) {
+ this.renderer = renderer;
+ },
+
+ /**
+ * Return the layer current renderer object
+ * @name getRenderer
+ * @memberOf me.TMXLayer
+ * @public
+ * @function
+ * @return {me.TMXRenderer} renderer
+ */
+ getRenderer : function (renderer) {
+ return this.renderer;
+ },
+
+ /**
+ * Create all required arrays
+ * @ignore
+ */
+ initArray : function (w, h) {
+ // initialize the array
+ this.layerData = [];
+ for (var x = 0; x < w; x++) {
+ this.layerData[x] = [];
+ for (var y = 0; y < h; y++) {
+ this.layerData[x][y] = null;
+ }
+ }
+ },
+
+ /**
+ * Return the TileId of the Tile at the specified position
+ * @name getTileId
+ * @memberOf me.TMXLayer
+ * @public
+ * @function
+ * @param {Number} x X coordinate (in world/pixels coordinates)
+ * @param {Number} y Y coordinate (in world/pixels coordinates)
+ * @return {Number} TileId or null if there is no Tile at the given position
+ */
+ getTileId : function (x, y) {
+ var tile = this.getTile(x, y);
+ return (tile ? tile.tileId : null);
+ },
+
+ /**
+ * Return the Tile object at the specified position
+ * @name getTile
+ * @memberOf me.TMXLayer
+ * @public
+ * @function
+ * @param {Number} x X coordinate (in world/pixels coordinates)
+ * @param {Number} y Y coordinate (in world/pixels coordinates)
+ * @return {me.Tile} corresponding tile or null if outside of the map area
+ * @example
+ * // get the TMX Map Layer called "Front layer"
+ * var layer = me.game.world.getChildByName("Front Layer")[0];
+ * // get the tile object corresponding to the latest pointer position
+ * var tile = layer.getTile(me.input.pointer.pos.x, me.input.pointer.pos.y);
+ */
+ getTile : function (x, y) {
+ if (this.containsPoint(x, y)) {
+ var renderer = this.renderer;
+ var col = ~~renderer.pixelToTileX(x, y);
+ var row = ~~renderer.pixelToTileY(y, x);
+ if ((col >= 0 && col < renderer.cols) && (row >= 0 && row < renderer.rows)) {
+ return this.layerData[col][row];
+ }
+ }
+ // return null if no corresponding tile
+ return null;
+ },
+
+ /**
+ * Create a new Tile at the specified position
+ * @name setTile
+ * @memberOf me.TMXLayer
+ * @public
+ * @function
+ * @param {Number} x X coordinate (in map coordinates: row/column)
+ * @param {Number} y Y coordinate (in map coordinates: row/column)
+ * @param {Number} tileId tileId
+ * @return {me.Tile} the corresponding newly created tile object
+ */
+ setTile : function (x, y, tileId) {
+ if (!this.tileset.contains(tileId)) {
+ // look for the corresponding tileset
+ this.tileset = this.tilesets.getTilesetByGid(tileId);
+ }
+ var tile = this.layerData[x][y] = new me.Tile(x, y, tileId, this.tileset);
+ // draw the corresponding tile
+ if (this.preRender) {
+ this.renderer.drawTile(this.canvasRenderer, x, y, tile);
+ }
+ return tile;
+ },
+
+ /**
+ * clear the tile at the specified position
+ * @name clearTile
+ * @memberOf me.TMXLayer
+ * @public
+ * @function
+ * @param {Number} x X coordinate (in map coordinates: row/column)
+ * @param {Number} y Y coordinate (in map coordinates: row/column)
+ * @example
+ * me.game.world.getChildByType(me.TMXLayer).forEach(function(layer) {
+ * // clear all tiles at the given x,y coordinates
+ * layer.clearTile(x, y);
+ * });
+ */
+ clearTile : function (x, y) {
+ // clearing tile
+ this.layerData[x][y] = null;
+ // erase the corresponding area in the canvas
+ if (this.preRender) {
+ this.canvasRenderer.clearRect(x * this.tilewidth, y * this.tileheight, this.tilewidth, this.tileheight);
+ }
+ },
+
+ /**
+ * update animations in a tileset layer
+ * @ignore
+ */
+ update : function (dt) {
+ if (this.isAnimated) {
+ var result = false;
+ for (var i = 0; i < this.animatedTilesets.length; i++) {
+ result = this.animatedTilesets[i].update(dt) || result;
+ }
+ return result;
+ }
+
+ return false;
+ },
+
+ /**
+ * draw a tileset layer
+ * @ignore
+ */
+ draw : function (renderer, rect) {
+ // set the layer alpha value
+ var alpha = renderer.globalAlpha();
+ renderer.setGlobalAlpha(alpha * this.getOpacity());
+
+ // use the offscreen canvas
+ if (this.preRender) {
+ var width = Math.min(rect.width, this.width);
+ var height = Math.min(rect.height, this.height);
+
+ // draw using the cached canvas
+ renderer.drawImage(
+ this.canvasRenderer.getCanvas(),
+ rect.pos.x, rect.pos.y, // sx,sy
+ width, height, // sw,sh
+ rect.pos.x, rect.pos.y, // dx,dy
+ width, height // dw,dh
+ );
+ }
+ // dynamically render the layer
+ else {
+ // draw the layer
+ this.renderer.drawTileLayer(renderer, this, rect);
+ }
+
+ // restore context to initial state
+ renderer.setGlobalAlpha(alpha);
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * Tile QT +0.7.x format
+ * http://www.mapeditor.org/
+ *
+ */
+(function () {
+
+ // constant to identify the collision object layer
+ var COLLISION_GROUP = "collision";
+
+ /**
+ * set a compatible renderer object
+ * for the specified map
+ * @ignore
+ */
+ function getNewDefaultRenderer(obj) {
+ switch (obj.orientation) {
+ case "orthogonal":
+ return new me.TMXOrthogonalRenderer(
+ obj.cols,
+ obj.rows,
+ obj.tilewidth,
+ obj.tileheight
+ );
+
+ case "isometric":
+ return new me.TMXIsometricRenderer(
+ obj.cols,
+ obj.rows,
+ obj.tilewidth,
+ obj.tileheight
+ );
+
+ case "hexagonal":
+ return new me.TMXHexagonalRenderer(
+ obj.cols,
+ obj.rows,
+ obj.tilewidth,
+ obj.tileheight,
+ obj.hexsidelength,
+ obj.staggeraxis,
+ obj.staggerindex
+ );
+
+ // if none found, throw an exception
+ default:
+ throw new me.Error(obj.orientation + " type TMX Tile Map not supported!");
+ }
+ }
+
+ /**
+ * Set tiled layer Data
+ * @ignore
+ */
+ function setLayerData(layer, data) {
+ var idx = 0;
+ // set everything
+ for (var y = 0; y < layer.rows; y++) {
+ for (var x = 0; x < layer.cols; x++) {
+ // get the value of the gid
+ var gid = data[idx++];
+ // fill the array
+ if (gid !== 0) {
+ // add a new tile to the layer
+ layer.setTile(x, y, gid);
+ }
+ }
+ }
+ }
+
+ /**
+ * read the layer Data
+ * @ignore
+ */
+ function readLayer(map, data, z) {
+ var layer = new me.TMXLayer(map.tilewidth, map.tileheight, map.orientation, map.tilesets, z);
+ // init the layer properly
+ layer.initFromJSON(data);
+ // set a renderer
+ if (!map.getRenderer().canRender(layer)) {
+ layer.setRenderer(getNewDefaultRenderer(layer));
+ }
+ else {
+ // use the default one
+ layer.setRenderer(map.getRenderer());
+ }
+ // parse the layer data
+ setLayerData(layer,
+ me.TMXUtils.decode(
+ data.data,
+ data.encoding,
+ data.compression
+ )
+ );
+ return layer;
+ }
+
+ /**
+ * read the Image Layer Data
+ * @ignore
+ */
+ function readImageLayer(map, data, z) {
+ // Normalize properties
+ me.TMXUtils.applyTMXProperties(data.properties, data);
+
+ // create the layer
+ var imageLayer = new me.ImageLayer(
+ +data.x || 0,
+ +data.y || 0,
+ Object.assign({
+ name: data.name,
+ image: data.image,
+ z: z
+ }, data.properties)
+ );
+
+ // set some additional flags
+ var visible = typeof(data.visible) !== "undefined" ? data.visible : true;
+ imageLayer.setOpacity(visible ? +data.opacity : 0);
+
+ return imageLayer;
+ }
+
+ /**
+ * read the tileset Data
+ * @ignore
+ */
+ function readTileset(data) {
+ return (new me.TMXTileset(data));
+ }
+
+ /**
+ * read the object group Data
+ * @ignore
+ */
+ function readObjectGroup(map, data, z) {
+ return (new me.TMXObjectGroup(map, data, z));
+ }
+
+
+ /**
+ * a TMX Tile Map Object
+ * Tiled QT +0.7.x format
+ * @class
+ * @extends me.Object
+ * @memberOf me
+ * @constructor
+ * @param {String} levelId name of TMX map
+ * @param {Object} data TMX map in JSON format
+ * @example
+ * // create a new level object based on the TMX JSON object
+ * var level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
+ * // add the level to the game world container
+ * level.addTo(me.game.world, true);
+ */
+ me.TMXTileMap = me.Object.extend({
+ // constructor
+ init: function (levelId, data) {
+
+ /**
+ * name of the tilemap
+ * @public
+ * @type String
+ * @name me.TMXTileMap#name
+ */
+ this.name = levelId;
+
+ /**
+ * the level data (JSON)
+ * @ignore
+ */
+ this.data = data;
+
+ /**
+ * width of the tilemap in tiles
+ * @public
+ * @type Int
+ * @name me.TMXTileMap#cols
+ */
+ this.cols = +data.width;
+ /**
+ * height of the tilemap in tiles
+ * @public
+ * @type Int
+ * @name me.TMXTileMap#rows
+ */
+ this.rows = +data.height;
+
+ /**
+ * Tile width
+ * @public
+ * @type Int
+ * @name me.TMXTileMap#tilewidth
+ */
+ this.tilewidth = +data.tilewidth;
+
+ /**
+ * Tile height
+ * @public
+ * @type Int
+ * @name me.TMXTileMap#tileheight
+ */
+ this.tileheight = +data.tileheight;
+
+ // tilesets for this map
+ this.tilesets = null;
+ // layers
+ this.layers = [];
+ // group objects
+ this.objectGroups = [];
+
+ // tilemap version
+ this.version = data.version;
+
+ // map type (orthogonal or isometric)
+ this.orientation = data.orientation;
+ if (this.orientation === "isometric") {
+ this.width = (this.cols + this.rows) * (this.tilewidth / 2);
+ this.height = (this.cols + this.rows) * (this.tileheight / 2);
+ } else {
+ this.width = this.cols * this.tilewidth;
+ this.height = this.rows * this.tileheight;
+ }
+
+
+ // objects minimum z order
+ this.z = 0;
+
+ // object id
+ this.nextobjectid = +data.nextobjectid || undefined;
+
+
+ // hex/iso properties
+ this.hexsidelength = +data.hexsidelength || undefined;
+ this.staggeraxis = data.staggeraxis;
+ this.staggerindex = data.staggerindex;
+
+ // background color
+ this.backgroundcolor = data.backgroundcolor;
+
+ // set additional map properties (if any)
+ me.TMXUtils.applyTMXProperties(this, data);
+
+ // internal flag
+ this.initialized = false;
+
+ },
+
+ /**
+ * Return the map default renderer
+ * @name getRenderer
+ * @memberOf me.TMXTileMap
+ * @public
+ * @function
+ * @return {me.TMXRenderer} renderer
+ */
+ getRenderer : function (renderer) {
+ if ((typeof(this.renderer) === "undefined") || (!this.renderer.canRender(this))) {
+ this.renderer = getNewDefaultRenderer(this);
+ }
+ return this.renderer;
+ },
+
+ /**
+ * parse the map
+ * @ignore
+ */
+ readMapObjects: function (data) {
+
+ if (this.initialized === true) {
+ return;
+ }
+
+ // to automatically increment z index
+ var zOrder = this.z;
+ var self = this;
+
+ // Tileset information
+ if (!this.tilesets) {
+ // make sure we have a TilesetGroup Object
+ this.tilesets = new me.TMXTilesetGroup();
+ }
+
+ // parse all tileset objects
+ if (typeof (data.tilesets) !== "undefined") {
+ var tilesets = data.tilesets;
+ tilesets.forEach(function (tileset) {
+ // add the new tileset
+ self.tilesets.add(readTileset(tileset));
+ });
+ }
+
+ // check if a user-defined background color is defined
+ if (this.backgroundcolor) {
+ this.layers.push(
+ new me.ColorLayer(
+ "background_color",
+ this.backgroundcolor,
+ zOrder++
+ )
+ );
+ }
+
+ // check if a background image is defined
+ if (this.background_image) {
+ // add a new image layer
+ this.layers.push(new me.ImageLayer(
+ 0, 0, {
+ name : "background_image",
+ image : this.background_image,
+ z : zOrder++
+ }
+ ));
+ }
+
+ data.layers.forEach(function (layer) {
+ switch (layer.type) {
+ case "imagelayer":
+ self.layers.push(readImageLayer(self, layer, zOrder++));
+ break;
+
+ case "tilelayer":
+ self.layers.push(readLayer(self, layer, zOrder++));
+ break;
+
+ // get the object groups information
+ case "objectgroup":
+ self.objectGroups.push(readObjectGroup(self, layer, zOrder++));
+ break;
+
+ default:
+ break;
+ }
+ });
+ this.initialized = true;
+ },
+
+
+ /**
+ * add all the map layers and objects to the given container
+ * @name me.TMXTileMap#addTo
+ * @public
+ * @function
+ * @param {me.Container} target container
+ * @param {boolean} flatten if true, flatten all objects into the given container
+ * @example
+ * // create a new level object based on the TMX JSON object
+ * var level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
+ * // add the level to the game world container
+ * level.addTo(me.game.world, true);
+ */
+ addTo : function (container, flatten) {
+ var _sort = container.autoSort;
+ var _depth = container.autoDepth;
+
+ // disable auto-sort and auto-depth
+ container.autoSort = false;
+ container.autoDepth = false;
+
+ // add all layers instances
+ this.getLayers().forEach(function (layer) {
+ container.addChild(layer);
+ });
+
+ // add all Object instances
+ this.getObjects(flatten).forEach(function (object) {
+ container.addChild(object);
+ });
+
+ // set back auto-sort and auto-depth
+ container.autoSort = _sort;
+ container.autoDepth = _depth;
+
+ // force a sort
+ container.sort(true);
+ },
+
+ /**
+ * return an Array of instantiated objects, based on the map object definition
+ * @name me.TMXTileMap#getObjects
+ * @public
+ * @function
+ * @param {boolean} flatten if true, flatten all objects into the returned array,
+ * ignoring all defined groups (no sub containers will be created)
+ * @return {me.Renderable[]} Array of Objects
+ */
+ getObjects : function (flatten) {
+ var objects = [];
+ var isCollisionGroup = false;
+ var targetContainer;
+
+ // parse the map for objects
+ this.readMapObjects(this.data);
+
+ for (var g = 0; g < this.objectGroups.length; g++) {
+ var group = this.objectGroups[g];
+
+ // check if this is the collision shape group
+ isCollisionGroup = group.name.toLowerCase().includes(COLLISION_GROUP);
+
+ if (flatten === false) {
+ // create a new container
+ targetContainer = new me.Container(0, 0, this.width, this.height);
+
+ // set additional properties
+ targetContainer.name = group.name;
+ targetContainer.pos.z = group.z;
+ targetContainer.setOpacity(group.opacity);
+
+ // disable auto-sort and auto-depth
+ targetContainer.autoSort = false;
+ targetContainer.autoDepth = false;
+ }
+
+ // iterate through the group and add all object into their
+ // corresponding target Container
+ for (var o = 0; o < group.objects.length; o++) {
+ // TMX object settings
+ var settings = group.objects[o];
+
+ var obj = me.pool.pull(
+ settings.name || "me.Entity",
+ settings.x, settings.y,
+ settings
+ );
+ // skip if the pull function does not return a corresponding object
+ if (typeof obj !== "object") {
+ continue;
+ }
+
+ // check if a me.Tile object is embedded
+ if (typeof (settings.tile) === "object" && !obj.renderable) {
+ obj.renderable = settings.tile.getRenderable(settings);
+ // adjust position if necessary
+ switch (settings.rotation) {
+ case Math.PI:
+ obj.translate(-obj.renderable.width, obj.renderable.height);
+ break;
+ case Math.PI / 2 :
+ obj.translate(0, obj.renderable.height);
+ break;
+ case -(Math.PI / 2) :
+ obj.translate(-obj.renderable.width, 0);
+ break;
+ default :
+ // this should not happen
+ break;
+ }
+ }
+
+ if (isCollisionGroup && !settings.name) {
+ // configure the body accordingly
+ obj.body.collisionType = me.collision.types.WORLD_SHAPE;
+ }
+
+ // set the obj z order correspondingly to its parent container/group
+ obj.pos.z = group.z;
+
+ //apply group opacity value to the child objects if group are merged
+ if (flatten === true) {
+ if (obj.isRenderable === true) {
+ obj.setOpacity(obj.getOpacity() * group.opacity);
+ // and to child renderables if any
+ if (obj.renderable instanceof me.Renderable) {
+ obj.renderable.setOpacity(obj.renderable.getOpacity() * group.opacity);
+ }
+ }
+ // directly add the obj into the objects array
+ objects.push(obj);
+ } else /* false*/ {
+ // add it to the new container
+ targetContainer.addChild(obj);
+ }
+
+ }
+
+ // if we created a new container
+ if ((flatten === false) && (targetContainer.children.length > 0)) {
+
+ // re-enable auto-sort and auto-depth
+ targetContainer.autoSort = true;
+ targetContainer.autoDepth = true;
+
+ // add our container to the world
+ objects.push(targetContainer);
+ }
+ }
+ return objects;
+ },
+
+ /**
+ * return all the existing layers
+ * @name me.TMXTileMap#getLayers
+ * @public
+ * @function
+ * @return {me.TMXLayer[]} Array of Layers
+ */
+ getLayers : function () {
+ // parse the map for objects
+ this.readMapObjects(this.data);
+ return this.layers;
+ },
+
+ /**
+ * destroy function, clean all allocated objects
+ * @name me.TMXTileMap#destroy
+ * @public
+ * @function
+ */
+ destroy : function () {
+ this.tilesets = undefined;
+ this.layers = [];
+ this.objectGroups = [];
+ this.initialized = false;
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * a level manager object
+ * once ressources loaded, the level director contains all references of defined levels
+ * There is no constructor function for me.levelDirector, this is a static object
+ * @namespace me.levelDirector
+ * @memberOf me
+ */
+ me.levelDirector = (function () {
+ // hold public stuff in our singletong
+ var api = {};
+
+ /*
+ * PRIVATE STUFF
+ */
+
+ // our levels
+ var levels = {};
+ // level index table
+ var levelIdx = [];
+ // current level index
+ var currentLevelIdx = 0;
+ // onresize handler
+ var onresize_handler = null;
+
+ function safeLoadLevel(levelId, options, restart) {
+ // clean the destination container
+ options.container.destroy();
+
+ // reset the renderer
+ me.video.renderer.reset();
+
+ // clean the current (previous) level
+ if (levels[api.getCurrentLevelId()]) {
+ levels[api.getCurrentLevelId()].destroy();
+ }
+
+ // update current level index
+ currentLevelIdx = levelIdx.indexOf(levelId);
+
+ // add the specified level to the game world
+ loadTMXLevel(levelId, options.container, options.flatten, options.setViewportBounds);
+
+ // publish the corresponding message
+ me.event.publish(me.event.LEVEL_LOADED, [ levelId ]);
+
+ // fire the callback
+ options.onLoaded(levelId);
+
+ if (restart) {
+ // resume the game loop if it was previously running
+ me.state.restart();
+ }
+ }
+
+ /**
+ * Load a TMX level
+ * @name loadTMXLevel
+ * @memberOf me.game
+ * @private
+ * @param {String} level level id
+ * @param {me.Container} target container
+ * @param {boolean} flatten if true, flatten all objects into the given container
+ * @param {boolean} setViewportBounds if true, set the viewport bounds to the map size
+ * @ignore
+ * @function
+ */
+ function loadTMXLevel(levelId, container, flatten, setViewportBounds) {
+ var level = levels[levelId];
+
+ // disable auto-sort for the given container
+ var autoSort = container.autoSort;
+ container.autoSort = false;
+
+ if (setViewportBounds) {
+ // update the viewport bounds
+ me.game.viewport.setBounds(
+ 0, 0,
+ Math.max(level.width, me.game.viewport.width),
+ Math.max(level.height, me.game.viewport.height)
+ );
+ }
+
+ // reset the GUID generator
+ // and pass the level id as parameter
+ me.utils.resetGUID(levelId, level.nextobjectid);
+
+ // add all level elements to the target container
+ level.addTo(container, flatten);
+
+ // sort everything (recursively)
+ container.sort(true);
+ container.autoSort = autoSort;
+
+ container.resize(level.width, level.height);
+
+ function resize_container() {
+ // center the map if smaller than the current viewport
+ container.pos.set(
+ Math.max(0, ~~((me.game.viewport.width - level.width) / 2)),
+ Math.max(0, ~~((me.game.viewport.height - level.height) / 2)),
+ 0
+ );
+ }
+
+ if (setViewportBounds) {
+ resize_container();
+
+ // Replace the resize handler
+ if (onresize_handler) {
+ me.event.unsubscribe(onresize_handler);
+ }
+ onresize_handler = me.event.subscribe(me.event.VIEWPORT_ONRESIZE, resize_container);
+ }
+ }
+
+ /*
+ * PUBLIC STUFF
+ */
+
+ /**
+ * reset the level director
+ * @ignore
+ */
+ api.reset = function () {};
+
+ /**
+ * add a level
+ * @ignore
+ */
+ api.addLevel = function () {
+ throw new me.Error("no level loader defined");
+ };
+
+ /**
+ * add a TMX level
+ * @ignore
+ */
+ api.addTMXLevel = function (levelId, callback) {
+ // just load the level with the XML stuff
+ if (levels[levelId] == null) {
+ //console.log("loading "+ levelId);
+ levels[levelId] = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
+ // level index
+ levelIdx.push(levelId);
+ }
+ else {
+ //console.log("level %s already loaded", levelId);
+ return false;
+ }
+
+ // call the callback if defined
+ if (callback) {
+ callback();
+ }
+ // true if level loaded
+ return true;
+ };
+
+ /**
+ * load a level into the game manager
+ * (will also create all level defined entities, etc..)
+ * @name loadLevel
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ * @param {String} level level id
+ * @param {Object} [options] additional optional parameters
+ * @param {me.Container} [options.container=me.game.world] container in which to load the specified level
+ * @param {function} [options.onLoaded=me.game.onLevelLoaded] callback for when the level is fully loaded
+ * @param {boolean} [options.flatten=me.game.mergeGroup] if true, flatten all objects into the given container
+ * @param {boolean} [options.setViewportBounds=true] if true, set the viewport bounds to the map size
+ * @example
+ * // the game assets to be be preloaded
+ * // TMX maps
+ * var resources = [
+ * {name: "a4_level1", type: "tmx", src: "data/level/a4_level1.tmx"},
+ * {name: "a4_level2", type: "tmx", src: "data/level/a4_level2.tmx"},
+ * {name: "a4_level3", type: "tmx", src: "data/level/a4_level3.tmx"},
+ * // ...
+ * ];
+ *
+ * // ...
+ *
+ * // load a level into the game world
+ * me.levelDirector.loadLevel("a4_level1");
+ * ...
+ * ...
+ * // load a level into a specific container
+ * var levelContainer = new me.Container();
+ * me.levelDirector.loadLevel("a4_level2", {container:levelContainer});
+ * // add a simple transformation
+ * levelContainer.currentTransform.translate(levelContainer.width / 2, levelContainer.height / 2 );
+ * levelContainer.currentTransform.rotate(0.05);
+ * levelContainer.currentTransform.translate(-levelContainer.width / 2, -levelContainer.height / 2 );
+ * // add it to the game world
+ * me.game.world.addChild(levelContainer);
+ */
+ api.loadLevel = function (levelId, options) {
+ options = Object.assign({
+ "container" : me.game.world,
+ "onLoaded" : me.game.onLevelLoaded,
+ "flatten" : me.game.mergeGroup,
+ "setViewportBounds" : true
+ }, options || {});
+
+ // throw an exception if not existing
+ if (typeof(levels[levelId]) === "undefined") {
+ throw new me.Error("level " + levelId + " not found");
+ }
+
+ if (levels[levelId] instanceof me.TMXTileMap) {
+
+ // check the status of the state mngr
+ var wasRunning = me.state.isRunning();
+
+ if (wasRunning) {
+ // stop the game loop to avoid
+ // some silly side effects
+ me.state.stop();
+
+ safeLoadLevel.defer(this, levelId, options, true);
+ }
+ else {
+ safeLoadLevel(levelId, options);
+ }
+ }
+ else {
+ throw new me.Error("no level loader defined");
+ }
+ return true;
+ };
+
+ /**
+ * return the current level id
+ * @name getCurrentLevelId
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ * @return {String}
+ */
+ api.getCurrentLevelId = function () {
+ return levelIdx[currentLevelIdx];
+ };
+
+ /**
+ * return the current level definition.
+ * for a reference to the live instantiated level,
+ * rather use the container in which it was loaded (e.g. me.game.world)
+ * @name getCurrentLevel
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ * @return {me.TMXTileMap}
+ */
+ api.getCurrentLevel = function () {
+ return levels[api.getCurrentLevelId()];
+ };
+
+ /**
+ * reload the current level
+ * @name reloadLevel
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ * @param {Object} [options] additional optional parameters
+ * @param {me.Container} [options.container=me.game.world] container in which to load the specified level
+ * @param {function} [options.onLoaded=me.game.onLevelLoaded] callback for when the level is fully loaded
+ * @param {boolean} [options.flatten=me.game.mergeGroup] if true, flatten all objects into the given container
+ */
+ api.reloadLevel = function (options) {
+ // reset the level to initial state
+ //levels[currentLevel].reset();
+ return api.loadLevel(api.getCurrentLevelId(), options);
+ };
+
+ /**
+ * load the next level
+ * @name nextLevel
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ * @param {Object} [options] additional optional parameters
+ * @param {me.Container} [options.container=me.game.world] container in which to load the specified level
+ * @param {function} [options.onLoaded=me.game.onLevelLoaded] callback for when the level is fully loaded
+ * @param {boolean} [options.flatten=me.game.mergeGroup] if true, flatten all objects into the given container
+ */
+ api.nextLevel = function (options) {
+ //go to the next level
+ if (currentLevelIdx + 1 < levelIdx.length) {
+ return api.loadLevel(levelIdx[currentLevelIdx + 1], options);
+ }
+ else {
+ return false;
+ }
+ };
+
+ /**
+ * load the previous level
+ * @name previousLevel
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ * @param {Object} [options] additional optional parameters
+ * @param {me.Container} [options.container=me.game.world] container in which to load the specified level
+ * @param {function} [options.onLoaded=me.game.onLevelLoaded] callback for when the level is fully loaded
+ * @param {boolean} [options.flatten=me.game.mergeGroup] if true, flatten all objects into the given container
+ */
+ api.previousLevel = function (options) {
+ // go to previous level
+ if (currentLevelIdx - 1 >= 0) {
+ return api.loadLevel(levelIdx[currentLevelIdx - 1], options);
+ }
+ else {
+ return false;
+ }
+ };
+
+ /**
+ * return the amount of level preloaded
+ * @name levelCount
+ * @memberOf me.levelDirector
+ * @public
+ * @function
+ */
+ api.levelCount = function () {
+ return levelIdx.length;
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/**
+ * @preserve Tween JS
+ * https://github.com/sole/Tween.js
+ */
+
+/* eslint-disable quotes, keyword-spacing, comma-spacing, no-return-assign */
+
+(function() {
+
+ /**
+ * Javascript Tweening Engine
+ * author mr.doob / http://mrdoob.com
+ * author Robert Eisele / http://www.xarg.org
+ * author Philippe / http://philippe.elsass.me
+ * author Robert Penner / http://www.robertpenner.com/easing_terms_of_use.html
+ * author Paul Lewis / http://www.aerotwist.com/
+ * author lechecacharro
+ * author Josh Faul / http://jocafa.com/
+ * @class
+ * @memberOf me
+ * @constructor
+ * @param {Object} object object on which to apply the tween
+ * @example
+ * // add a tween to change the object pos.y variable to 200 in 3 seconds
+ * tween = new me.Tween(myObject.pos).to({y: 200}, 3000).onComplete(myFunc);
+ * tween.easing(me.Tween.Easing.Bounce.Out);
+ * tween.start();
+ */
+ me.Tween = function ( object ) {
+
+ var _object = null;
+ var _valuesStart = null;
+ var _valuesEnd = null;
+ var _valuesStartRepeat = null;
+ var _duration = null;
+ var _repeat = null;
+ var _yoyo = null;
+ var _reversed = null;
+ var _delayTime = null;
+ var _startTime = null;
+ var _easingFunction = null;
+ var _interpolationFunction = null;
+ var _chainedTweens = null;
+ var _onStartCallback = null;
+ var _onStartCallbackFired = null;
+ var _onUpdateCallback = null;
+ var _onCompleteCallback = null;
+ var _tweenTimeTracker = null;
+
+ /**
+ * @ignore
+ */
+ this._resumeCallback = function (elapsed) {
+ if (_startTime) {
+ _startTime += elapsed;
+ }
+ };
+
+ /**
+ * @ignore
+ */
+ this.setProperties = function (object) {
+ _object = object;
+ _valuesStart = {};
+ _valuesEnd = {};
+ _valuesStartRepeat = {};
+ _duration = 1000;
+ _repeat = 0;
+ _yoyo = false;
+ _reversed = false;
+ _delayTime = 0;
+ _startTime = null;
+ _easingFunction = me.Tween.Easing.Linear.None;
+ _interpolationFunction = me.Tween.Interpolation.Linear;
+ _chainedTweens = [];
+ _onStartCallback = null;
+ _onStartCallbackFired = false;
+ _onUpdateCallback = null;
+ _onCompleteCallback = null;
+ _tweenTimeTracker = me.timer.lastUpdate;
+
+ // reset the persistent flag to default value
+ this.isPersistent = false;
+
+ // Set all starting values present on the target object
+ for ( var field in object ) {
+ if(typeof object !== 'object') {
+ _valuesStart[ field ] = parseFloat(object[field], 10);
+ }
+ }
+ };
+
+ this.setProperties(object);
+
+ /**
+ * reset the tween object to default value
+ * @ignore
+ */
+ this.onResetEvent = function ( object ) {
+ this.setProperties(object);
+ };
+
+ /**
+ * subscribe to the resume event when added
+ * @ignore
+ */
+ this.onActivateEvent = function () {
+ me.event.subscribe(me.event.STATE_RESUME, this._resumeCallback);
+ };
+
+ /**
+ * Unsubscribe when tween is removed
+ * @ignore
+ */
+ this.onDeactivateEvent = function () {
+ me.event.unsubscribe(me.event.STATE_RESUME, this._resumeCallback);
+ };
+
+ /**
+ * object properties to be updated and duration
+ * @name me.Tween#to
+ * @public
+ * @function
+ * @param {Object} properties hash of properties
+ * @param {Number} [duration=1000] tween duration
+ */
+ this.to = function ( properties, duration ) {
+
+ if ( duration !== undefined ) {
+
+ _duration = duration;
+
+ }
+
+ _valuesEnd = properties;
+
+ return this;
+
+ };
+
+ /**
+ * start the tween
+ * @name me.Tween#start
+ * @public
+ * @function
+ */
+ this.start = function ( _time ) {
+
+ _onStartCallbackFired = false;
+
+ // add the tween to the object pool on start
+ me.game.world.addChild(this);
+
+ _startTime = (typeof(_time) === 'undefined' ? me.timer.getTime() : _time) + _delayTime;
+
+ for ( var property in _valuesEnd ) {
+
+ // check if an Array was provided as property value
+ if ( _valuesEnd[ property ] instanceof Array ) {
+
+ if ( _valuesEnd[ property ].length === 0 ) {
+
+ continue;
+
+ }
+
+ // create a local copy of the Array with the start value at the front
+ _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] );
+
+ }
+
+ _valuesStart[ property ] = _object[ property ];
+
+ if( ( _valuesStart[ property ] instanceof Array ) === false ) {
+ _valuesStart[ property ] *= 1.0; // Ensures we're using numbers, not strings
+ }
+
+ _valuesStartRepeat[ property ] = _valuesStart[ property ] || 0;
+
+ }
+
+ return this;
+
+ };
+
+ /**
+ * stop the tween
+ * @name me.Tween#stop
+ * @public
+ * @function
+ */
+ this.stop = function () {
+ // remove the tween from the world container
+ me.game.world.removeChildNow(this);
+ return this;
+ };
+
+ /**
+ * delay the tween
+ * @name me.Tween#delay
+ * @public
+ * @function
+ * @param {Number} amount delay amount expressed in milliseconds
+ */
+ this.delay = function ( amount ) {
+
+ _delayTime = amount;
+ return this;
+
+ };
+
+ /**
+ * Repeat the tween
+ * @name me.Tween#repeat
+ * @public
+ * @function
+ * @param {Number} times amount of times the tween should be repeated
+ */
+ this.repeat = function ( times ) {
+
+ _repeat = times;
+ return this;
+
+ };
+
+ /**
+ * allows the tween to bounce back to their original value when finished
+ * @name me.Tween#yoyo
+ * @public
+ * @function
+ * @param {Boolean} yoyo
+ */
+ this.yoyo = function( yoyo ) {
+
+ _yoyo = yoyo;
+ return this;
+
+ };
+
+ /**
+ * set the easing function
+ * @name me.Tween#easing
+ * @public
+ * @function
+ * @param {me.Tween.Easing} fn easing function
+ */
+ this.easing = function ( easing ) {
+ if (typeof easing !== 'function') {
+ throw new me.Tween.Error("invalid easing function for me.Tween.easing()");
+ }
+ _easingFunction = easing;
+ return this;
+
+ };
+
+ /**
+ * set the interpolation function
+ * @name me.Tween#interpolation
+ * @public
+ * @function
+ * @param {me.Tween.Interpolation} fn interpolation function
+ */
+ this.interpolation = function ( interpolation ) {
+
+ _interpolationFunction = interpolation;
+ return this;
+
+ };
+
+ /**
+ * chain the tween
+ * @name me.Tween#chain
+ * @public
+ * @function
+ * @param {me.Tween} chainedTween Tween to be chained
+ */
+ this.chain = function () {
+
+ _chainedTweens = arguments;
+ return this;
+
+ };
+
+ /**
+ * onStart callback
+ * @name me.Tween#onStart
+ * @public
+ * @function
+ * @param {Function} onStartCallback callback
+ */
+ this.onStart = function ( callback ) {
+
+ _onStartCallback = callback;
+ return this;
+
+ };
+
+ /**
+ * onUpdate callback
+ * @name me.Tween#onUpdate
+ * @public
+ * @function
+ * @param {Function} onUpdateCallback callback
+ */
+ this.onUpdate = function ( callback ) {
+
+ _onUpdateCallback = callback;
+ return this;
+
+ };
+
+ /**
+ * onComplete callback
+ * @name me.Tween#onComplete
+ * @public
+ * @function
+ * @param {Function} onCompleteCallback callback
+ */
+ this.onComplete = function ( callback ) {
+
+ _onCompleteCallback = callback;
+ return this;
+
+ };
+
+ /** @ignore */
+ this.update = function ( dt ) {
+
+ // the original Tween implementation expect
+ // a timestamp and not a time delta
+ _tweenTimeTracker = (me.timer.lastUpdate > _tweenTimeTracker) ? me.timer.lastUpdate : _tweenTimeTracker + dt;
+ var time = _tweenTimeTracker;
+
+ var property;
+
+ if ( time < _startTime ) {
+
+ return true;
+
+ }
+
+ if ( _onStartCallbackFired === false ) {
+
+ if ( _onStartCallback !== null ) {
+
+ _onStartCallback.call( _object );
+
+ }
+
+ _onStartCallbackFired = true;
+
+ }
+
+ var elapsed = ( time - _startTime ) / _duration;
+ elapsed = elapsed > 1 ? 1 : elapsed;
+
+ var value = _easingFunction( elapsed );
+
+ for ( property in _valuesEnd ) {
+
+ var start = _valuesStart[ property ] || 0;
+ var end = _valuesEnd[ property ];
+
+ if ( end instanceof Array ) {
+
+ _object[ property ] = _interpolationFunction( end, value );
+
+ } else {
+
+ // Parses relative end values with start as base (e.g.: +10, -3)
+ if ( typeof(end) === "string" ) {
+ end = start + parseFloat(end, 10);
+ }
+
+ // protect against non numeric properties.
+ if ( typeof(end) === "number" ) {
+ _object[ property ] = start + ( end - start ) * value;
+ }
+
+ }
+
+ }
+
+ if ( _onUpdateCallback !== null ) {
+
+ _onUpdateCallback.call( _object, value );
+
+ }
+
+ if ( elapsed === 1 ) {
+
+ if ( _repeat > 0 ) {
+
+ if( isFinite( _repeat ) ) {
+ _repeat--;
+ }
+
+ // reassign starting values, restart by making startTime = now
+ for( property in _valuesStartRepeat ) {
+
+ if ( typeof( _valuesEnd[ property ] ) === "string" ) {
+ _valuesStartRepeat[ property ] = _valuesStartRepeat[ property ] + parseFloat(_valuesEnd[ property ], 10);
+ }
+
+ if (_yoyo) {
+ var tmp = _valuesStartRepeat[ property ];
+ _valuesStartRepeat[ property ] = _valuesEnd[ property ];
+ _valuesEnd[ property ] = tmp;
+ }
+ _valuesStart[ property ] = _valuesStartRepeat[ property ];
+
+ }
+
+ if (_yoyo) {
+ _reversed = !_reversed;
+ }
+
+ _startTime = time + _delayTime;
+
+ return true;
+
+ } else {
+ // remove the tween from the world container
+ me.game.world.removeChildNow(this);
+
+ if ( _onCompleteCallback !== null ) {
+
+ _onCompleteCallback.call( _object );
+
+ }
+
+ for ( var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i ++ ) {
+
+ _chainedTweens[ i ].start( time );
+
+ }
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ };
+
+ };
+
+ /**
+ * Easing Function :
+ *
+ * me.Tween.Easing.Quadratic.In
+ * me.Tween.Easing.Quadratic.Out
+ * me.Tween.Easing.Quadratic.InOut
+ * me.Tween.Easing.Cubic.In
+ * me.Tween.Easing.Cubic.Out
+ * me.Tween.Easing.Cubic.InOut
+ * me.Tween.Easing.Quartic.In
+ * me.Tween.Easing.Quartic.Out
+ * me.Tween.Easing.Quartic.InOut
+ * me.Tween.Easing.Quintic.In
+ * me.Tween.Easing.Quintic.Out
+ * me.Tween.Easing.Quintic.InOut
+ * me.Tween.Easing.Sinusoidal.In
+ * me.Tween.Easing.Sinusoidal.Out
+ * me.Tween.Easing.Sinusoidal.InOut
+ * me.Tween.Easing.Exponential.In
+ * me.Tween.Easing.Exponential.Out
+ * me.Tween.Easing.Exponential.InOut
+ * me.Tween.Easing.Circular.In
+ * me.Tween.Easing.Circular.Out
+ * me.Tween.Easing.Circular.InOut
+ * me.Tween.Easing.Elastic.In
+ * me.Tween.Easing.Elastic.Out
+ * me.Tween.Easing.Elastic.InOut
+ * me.Tween.Easing.Back.In
+ * me.Tween.Easing.Back.Out
+ * me.Tween.Easing.Back.InOut
+ * me.Tween.Easing.Bounce.In
+ * me.Tween.Easing.Bounce.Out
+ * me.Tween.Easing.Bounce.InOut
+ *
+ *
+ * me.Tween.Interpolation.Bezier
+ * me.Tween.Interpolation.CatmullRom
+ *
+ * Data passed : none
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#STATE_PAUSE
+ */
+ api.STATE_PAUSE = "me.state.onPause";
+
+ /**
+ * Channel Constant for when the game is resumed
+ * Data passed : {Number} time in ms the game was paused
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#STATE_RESUME
+ */
+ api.STATE_RESUME = "me.state.onResume";
+
+ /**
+ * Channel Constant when the game is stopped
+ * Data passed : none
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#STATE_STOP
+ */
+ api.STATE_STOP = "me.state.onStop";
+
+ /**
+ * Channel Constant for when the game is restarted
+ * Data passed : {Number} time in ms the game was stopped
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#STATE_RESTART
+ */
+ api.STATE_RESTART = "me.state.onRestart";
+
+ /**
+ * Channel Constant for when the game manager is initialized
+ * Data passed : none
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#GAME_INIT
+ */
+ api.GAME_INIT = "me.game.onInit";
+
+ /**
+ * Channel Constant for when the game manager is resetted
+ * Data passed : none
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#GAME_RESET
+ */
+ api.GAME_RESET = "me.game.onReset";
+
+ /**
+ * Channel Constant for when a level is loaded
+ * Data passed : {String} Level Name
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#LEVEL_LOADED
+ */
+ api.LEVEL_LOADED = "me.game.onLevelLoaded";
+
+ /**
+ * Channel Constant for when everything has loaded
+ * Data passed : none
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#LOADER_COMPLETE
+ */
+ api.LOADER_COMPLETE = "me.loader.onload";
+
+ /**
+ * Channel Constant for displaying a load progress indicator
+ * Data passed : {Number} [0 .. 1], {Resource} resource object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#LOADER_PROGRESS
+ */
+ api.LOADER_PROGRESS = "me.loader.onProgress";
+
+ /**
+ * Channel Constant for pressing a binded key
+ * Data passed : {String} user-defined action, {Number} keyCode,
+ * {Boolean} edge state
+ * Edge-state is for detecting "locked" key bindings. When a locked key
+ * is pressed and held, the first event will have the third argument
+ * set true. Subsequent events will continue firing with the third
+ * argument set false.
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#KEYDOWN
+ * @example
+ * me.input.bindKey(me.input.KEY.X, "jump", true); // Edge-triggered
+ * me.input.bindKey(me.input.KEY.Z, "shoot"); // Level-triggered
+ * me.event.subscribe(me.event.KEYDOWN, function (action, keyCode, edge) {
+ * // Checking bound keys
+ * if (action === "jump") {
+ * if (edge) {
+ * this.doJump();
+ * }
+ *
+ * // Make character fall slower when holding the jump key
+ * this.vel.y = this.body.gravity;
+ * }
+ * });
+ */
+ api.KEYDOWN = "me.input.keydown";
+
+ /**
+ * Channel Constant for releasing a binded key
+ * Data passed : {String} user-defined action, {Number} keyCode
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#KEYUP
+ * @example
+ * me.event.subscribe(me.event.KEYUP, function (action, keyCode) {
+ * // Checking unbound keys
+ * if (keyCode == me.input.KEY.ESC) {
+ * if (me.state.isPaused()) {
+ * me.state.resume();
+ * }
+ * else {
+ * me.state.pause();
+ * }
+ * }
+ * });
+ */
+ api.KEYUP = "me.input.keyup";
+
+ /**
+ * Channel Constant for when a gamepad is connected
+ * Data passed : {Object} gamepad object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#GAMEPAD_CONNECTED
+ */
+ api.GAMEPAD_CONNECTED = "gamepad.connected";
+
+ /**
+ * Channel Constant for when a gamepad is disconnected
+ * Data passed : {Object} gamepad object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#GAMEPAD_DISCONNECTED
+ */
+ api.GAMEPAD_DISCONNECTED = "gamepad.disconnected";
+
+ /**
+ * Channel Constant for when gamepad button/axis state is updated
+ * Data passed : {Number} index
+ * Data passed : {String} type : "axes" or "buttons"
+ * Data passed : {Number} button
+ * Data passed : {Number} current.value
+ * Data passed : {Boolean} current.pressed
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#GAMEPAD_UPDATE
+ */
+ api.GAMEPAD_UPDATE = "gamepad.update";
+
+ /**
+ * Channel Constant for pointermove events on the viewport area
+ * Data passed : {Object} the Event object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#POINTERMOVE
+ */
+ api.POINTERMOVE = "me.event.pointermove";
+
+ /**
+ * Channel Constant for dragstart events on a Draggable entity
+ * Data passed:
+ * {Object} the drag event
+ * {Object} the Draggable entity
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#DRAGSTART
+ */
+ api.DRAGSTART = "me.game.dragstart";
+
+ /**
+ * Channel Constant for dragend events on a Draggable entity
+ * Data passed:
+ * {Object} the drag event
+ * {Object} the Draggable entity
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#DRAGEND
+ */
+ api.DRAGEND = "me.game.dragend";
+
+ /**
+ * Channel Constant for when the (browser) window is resized
+ * Data passed : {Event} Event object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#WINDOW_ONRESIZE
+ */
+ api.WINDOW_ONRESIZE = "window.onresize";
+
+ /**
+ * Channel Constant for when the viewport is resized
+ * (this usually follows a WINDOW_ONRESIZE event, when using the `flex` scaling mode is used and after the viewport was updated).
+ * Data passed : {Number} viewport width
+ * Data passed : {Number} viewport height
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#VIEWPORT_ONRESIZE
+ */
+ api.VIEWPORT_ONRESIZE = "viewport.onresize";
+
+ /**
+ * Channel Constant for when the device is rotated
+ * Data passed : {Event} Event object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#WINDOW_ONORIENTATION_CHANGE
+ */
+ api.WINDOW_ONORIENTATION_CHANGE = "window.orientationchange";
+
+ /**
+ * Channel Constant for when the (browser) window is scrolled
+ * Data passed : {Event} Event object
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#WINDOW_ONSCROLL
+ */
+ api.WINDOW_ONSCROLL = "window.onscroll";
+
+ /**
+ * Channel Constant for when the viewport position is updated
+ * Data passed : {me.Vector2d} viewport position vector
+ * @public
+ * @constant
+ * @type String
+ * @name me.event#VIEWPORT_ONCHANGE
+ */
+ api.VIEWPORT_ONCHANGE = "viewport.onchange";
+
+ /**
+ * Publish some data on a channel
+ * @name me.event#publish
+ * @public
+ * @function
+ * @param {String} channel The channel to publish on
+ * @param {Array} arguments The data to publish
+ *
+ * @example Publish stuff on '/some/channel'.
+ * Anything subscribed will be called with a function
+ * signature like: function (a,b,c){ ... }
+ *
+ * me.event.publish("/some/channel", ["a","b","c"]);
+ *
+ */
+ api.publish = function (channel, args) {
+ var subs = cache[channel],
+ len = subs ? subs.length : 0;
+
+ //can change loop or reverse array if the order matters
+ while (len--) {
+ subs[len].apply(window, args || []); // is window correct here?
+ }
+ };
+
+ /**
+ * Register a callback on a named channel.
+ * @name me.event#subscribe
+ * @public
+ * @function
+ * @param {String} channel The channel to subscribe to
+ * @param {Function} callback The event handler, any time something is
+ * published on a subscribed channel, the callback will be called
+ * with the published array as ordered arguments
+ * @return {handle} A handle which can be used to unsubscribe this
+ * particular subscription
+ * @example
+ * me.event.subscribe("/some/channel", function (a, b, c){ doSomething(); });
+ */
+
+ api.subscribe = function (channel, callback) {
+ if (!cache[channel]) {
+ cache[channel] = [];
+ }
+ cache[channel].push(callback);
+ return [ channel, callback ]; // Array
+ };
+
+ /**
+ * Disconnect a subscribed function for a channel.
+ * @name me.event#unsubscribe
+ * @public
+ * @function
+ * @param {Array|String} handle The return value from a subscribe call or the
+ * name of a channel as a String
+ * @param {Function} [callback] The callback to be unsubscribed.
+ * @example
+ * var handle = me.event.subscribe("/some/channel", function (){});
+ * me.event.unsubscribe(handle);
+ *
+ * // Or alternatively ...
+ *
+ * var callback = function (){};
+ * me.event.subscribe("/some/channel", callback);
+ * me.event.unsubscribe("/some/channel", callback);
+ */
+ api.unsubscribe = function (handle, callback) {
+ var subs = cache[callback ? handle : handle[0]],
+ len = subs ? subs.length : 0;
+
+ callback = callback || handle[1];
+
+ while (len--) {
+ if (subs[len] === callback) {
+ subs.splice(len, 1);
+ }
+ }
+ };
+
+ // return our object
+ return api;
+ })();
+})();
+
+/*!
+ * howler.js v2.0.2
+ * howlerjs.com
+ *
+ * (c) 2013-2016, James Simpson of GoldFire Studios
+ * goldfirestudios.com
+ *
+ * MIT License
+ */
+
+/* eslint-disable quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new*/
+
+(function() {
+
+ 'use strict';
+
+ /** Global Methods **/
+ /***************************************************************************/
+
+ /**
+ * Create the global controller. All contained methods and properties apply
+ * to all sounds that are currently playing or will be in the future.
+ */
+ var HowlerGlobal = function() {
+ this.init();
+ };
+ HowlerGlobal.prototype = {
+ /**
+ * Initialize the global Howler object.
+ * @return {Howler}
+ */
+ init: function() {
+ var self = this || Howler;
+
+ // Internal properties.
+ self._codecs = {};
+ self._howls = [];
+ self._muted = false;
+ self._volume = 1;
+ self._canPlayEvent = 'canplaythrough';
+ self._navigator = (typeof window !== 'undefined' && window.navigator) ? window.navigator : null;
+
+ // Public properties.
+ self.masterGain = null;
+ self.noAudio = false;
+ self.usingWebAudio = true;
+ self.autoSuspend = true;
+ self.ctx = null;
+
+ // Set to false to disable the auto iOS enabler.
+ self.mobileAutoEnable = true;
+
+ // Setup the various state values for global tracking.
+ self._setup();
+
+ return self;
+ },
+
+ /**
+ * Get/set the global volume for all sounds.
+ * @param {Float} vol Volume from 0.0 to 1.0.
+ * @return {Howler/Float} Returns self or current volume.
+ */
+ volume: function(vol) {
+ var self = this || Howler;
+ vol = parseFloat(vol);
+
+ // If we don't have an AudioContext created yet, run the setup.
+ if (!self.ctx) {
+ setupAudioContext();
+ }
+
+ if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {
+ self._volume = vol;
+
+ // Don't update any of the nodes if we are muted.
+ if (self._muted) {
+ return self;
+ }
+
+ // When using Web Audio, we just need to adjust the master gain.
+ if (self.usingWebAudio) {
+ self.masterGain.gain.value = vol;
+ }
+
+ // Loop through and change volume for all HTML5 audio nodes.
+ for (var i=0; i
+ * This namespace is a container for all registered plugins.
+ * @see me.plugin.register
+ * @namespace me.plugins
+ * @memberOf me
+ */
+ me.plugins = {};
+
+ /**
+ * There is no constructor function for me.plugin
+ * @namespace me.plugin
+ * @memberOf me
+ */
+ me.plugin = (function () {
+
+ // hold public stuff inside the singleton
+ var singleton = {};
+
+ /*--------------
+ PUBLIC
+ --------------*/
+
+ /**
+ * a base Object for plugin
+ * plugin must be installed using the register function
+ * @see me.plugin
+ * @class
+ * @extends Object
+ * @name plugin.Base
+ * @memberOf me
+ * @constructor
+ */
+ singleton.Base = me.Object.extend(
+ /** @scope me.plugin.Base.prototype */
+ {
+ /** @ignore */
+ init : function () {
+ /**
+ * define the minimum required version of melonJS
+ * this can be overridden by the plugin
+ * @public
+ * @type String
+ * @default "4.1.0"
+ * @name me.plugin.Base#version
+ */
+ this.version = "4.1.0";
+ }
+ });
+
+ /**
+ * patch a melonJS function
+ * @name patch
+ * @memberOf me.plugin
+ * @public
+ * @function
+ * @param {Object} proto target object
+ * @param {String} name target function
+ * @param {Function} fn replacement function
+ * @example
+ * // redefine the me.game.update function with a new one
+ * me.plugin.patch(me.game, "update", function () {
+ * // display something in the console
+ * console.log("duh");
+ * // call the original me.game.update function
+ * this._patched();
+ * });
+ */
+ singleton.patch = function (proto, name, fn) {
+ // use the object prototype if possible
+ if (typeof proto.prototype !== "undefined") {
+ proto = proto.prototype;
+ }
+ // reuse the logic behind me.Object.extend
+ if (typeof(proto[name]) === "function") {
+ // save the original function
+ var _parent = proto[name];
+ // override the function with the new one
+ Object.defineProperty(proto, name, {
+ "configurable" : true,
+ "value" : (function (name, fn) {
+ return function () {
+ this._patched = _parent;
+ var ret = fn.apply(this, arguments);
+ this._patched = null;
+ return ret;
+ };
+ })(name, fn)
+ });
+ }
+ else {
+ console.error(name + " is not an existing function");
+ }
+ };
+
+ /**
+ * Register a plugin.
+ * @name register
+ * @memberOf me.plugin
+ * @see me.plugin.Base
+ * @public
+ * @function
+ * @param {me.plugin.Base} plugin Plugin to instiantiate and register
+ * @param {String} name
+ * @param {} [arguments...] all extra parameters will be passed to the plugin constructor
+ * @example
+ * // register a new plugin
+ * me.plugin.register(TestPlugin, "testPlugin");
+ * // the plugin then also become available
+ * // under then me.plugins namespace
+ * me.plugins.testPlugin.myfunction ();
+ */
+ singleton.register = function (plugin, name) {
+ // ensure me.plugin[name] is not already "used"
+ if (me.plugin[name]) {
+ console.error("plugin " + name + " already registered");
+ }
+
+ // get extra arguments
+ var _args = [];
+ if (arguments.length > 2) {
+ // store extra arguments if any
+ _args = Array.prototype.slice.call(arguments, 1);
+ }
+
+ // try to instantiate the plugin
+ _args[0] = plugin;
+ var instance = new (plugin.bind.apply(plugin, _args))();
+
+ // inheritance check
+ if (!instance || !(instance instanceof me.plugin.Base)) {
+ throw new me.Error("Plugin should extend the me.plugin.Base Class !");
+ }
+
+ // compatibility testing
+ if (me.sys.checkVersion(instance.version) > 0) {
+ throw new me.Error("Plugin version mismatch, expected: " + instance.version + ", got: " + me.version);
+ }
+
+ // create a reference to the new plugin
+ me.plugins[name] = instance;
+ };
+
+ // return our singleton
+ return singleton;
+ })();
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+/**
+ * Used to make a game entity draggable
+ * @class
+ * @extends me.Entity
+ * @memberOf me
+ * @constructor
+ * @param {Number} x the x coordinates of the entity object
+ * @param {Number} y the y coordinates of the entity object
+ * @param {Object} settings Entity properties (see {@link me.Entity})
+ */
+me.DraggableEntity = (function (Entity, Input, Event, Vector) {
+ "use strict";
+
+ return Entity.extend(
+ /** @scope me.DraggableEntity.prototype */
+ {
+ /**
+ * Constructor
+ * @name init
+ * @memberOf me.DraggableEntity
+ * @function
+ * @param {Number} x the x postion of the entity
+ * @param {Number} y the y postion of the entity
+ * @param {Object} settings the additional entity settings
+ */
+ init: function (x, y, settings) {
+ Entity.prototype.init.apply(this, [x, y, settings]);
+ this.dragging = false;
+ this.dragId = null;
+ this.grabOffset = new Vector(0, 0);
+ this.onPointerEvent = Input.registerPointerEvent;
+ this.removePointerEvent = Input.releasePointerEvent;
+ this.initEvents();
+ },
+
+ /**
+ * Initializes the events the modules needs to listen to
+ * It translates the pointer events to me.events
+ * in order to make them pass through the system and to make
+ * this module testable. Then we subscribe this module to the
+ * transformed events.
+ * @name initEvents
+ * @memberOf me.DraggableEntity
+ * @function
+ */
+ initEvents: function () {
+ var self = this;
+ /**
+ * @ignore
+ */
+ this.mouseDown = function (e) {
+ this.translatePointerEvent(e, Event.DRAGSTART);
+ };
+ /**
+ * @ignore
+ */
+ this.mouseUp = function (e) {
+ this.translatePointerEvent(e, Event.DRAGEND);
+ };
+ this.onPointerEvent("pointerdown", this, this.mouseDown.bind(this));
+ this.onPointerEvent("pointerup", this, this.mouseUp.bind(this));
+ Event.subscribe(Event.POINTERMOVE, this.dragMove.bind(this));
+ Event.subscribe(Event.DRAGSTART, function (e, draggable) {
+ if (draggable === self) {
+ self.dragStart(e);
+ }
+ });
+ Event.subscribe(Event.DRAGEND, function (e, draggable) {
+ if (draggable === self) {
+ self.dragEnd(e);
+ }
+ });
+ },
+
+ /**
+ * Translates a pointer event to a me.event
+ * @name translatePointerEvent
+ * @memberOf me.DraggableEntity
+ * @function
+ * @param {Object} e the pointer event you want to translate
+ * @param {String} translation the me.event you want to translate
+ * the event to
+ */
+ translatePointerEvent: function (e, translation) {
+ Event.publish(translation, [e, this]);
+ },
+
+ /**
+ * Gets called when the user starts dragging the entity
+ * @name dragStart
+ * @memberOf me.DraggableEntity
+ * @function
+ * @param {Object} x the pointer event
+ */
+ dragStart: function (e) {
+ if (this.dragging === false) {
+ this.dragging = true;
+ this.dragId = e.pointerId;
+ this.grabOffset.set(e.gameX, e.gameY);
+ this.grabOffset.sub(this.pos);
+ return false;
+ }
+ },
+
+ /**
+ * Gets called when the user drags this entity around
+ * @name dragMove
+ * @memberOf me.DraggableEntity
+ * @function
+ * @param {Object} x the pointer event
+ */
+ dragMove: function (e) {
+ if (this.dragging === true) {
+ if (this.dragId === e.pointerId) {
+ this.pos.set(e.gameX, e.gameY, this.pos.z); //TODO : z ?
+ this.pos.sub(this.grabOffset);
+ }
+ }
+ },
+
+ /**
+ * Gets called when the user stops dragging the entity
+ * @name dragEnd
+ * @memberOf me.DraggableEntity
+ * @function
+ * @param {Object} x the pointer event
+ */
+ dragEnd: function () {
+ if (this.dragging === true) {
+ this.pointerId = undefined;
+ this.dragging = false;
+ return false;
+ }
+ },
+
+ /**
+ * Destructor
+ * @name destroy
+ * @memberOf me.DraggableEntity
+ * @function
+ */
+ destroy: function () {
+ Event.unsubscribe(Event.POINTERMOVE, this.dragMove);
+ Event.unsubscribe(Event.DRAGSTART, this.dragStart);
+ Event.unsubscribe(Event.DRAGEND, this.dragEnd);
+ this.removePointerEvent("pointerdown", this);
+ this.removePointerEvent("pointerup", this);
+ }
+ });
+}(me.Entity, me.input, me.event, me.Vector2d));
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ */
+
+/**
+ * Used to make a game entity a droptarget
+ * @class
+ * @extends me.Entity
+ * @memberOf me
+ * @constructor
+ * @param {Number} x the x coordinates of the entity object
+ * @param {Number} y the y coordinates of the entity object
+ * @param {Object} settings Entity properties (see {@link me.Entity})
+ */
+me.DroptargetEntity = (function (Entity, Event) {
+ "use strict";
+
+ return Entity.extend(
+ /** @scope me.DroptargetEntity.prototype */
+ {
+ /**
+ * Constructor
+ * @name init
+ * @memberOf me.DroptargetEntity
+ * @function
+ * @param {Number} x the x postion of the entity
+ * @param {Number} y the y postion of the entity
+ * @param {Object} settings the additional entity settings
+ */
+ init: function (x, y, settings) {
+ /**
+ * constant for the overlaps method
+ * @public
+ * @constant
+ * @type String
+ * @name CHECKMETHOD_OVERLAP
+ * @memberOf me.DroptargetEntity
+ */
+ this.CHECKMETHOD_OVERLAP = "overlaps";
+ /**
+ * constant for the contains method
+ * @public
+ * @constant
+ * @type String
+ * @name CHECKMETHOD_CONTAINS
+ * @memberOf me.DroptargetEntity
+ */
+ this.CHECKMETHOD_CONTAINS = "contains";
+ /**
+ * the checkmethod we want to use
+ * @public
+ * @constant
+ * @type String
+ * @name checkMethod
+ * @memberOf me.DroptargetEntity
+ */
+ this.checkMethod = null;
+ Entity.prototype.init.apply(this, [x, y, settings]);
+ Event.subscribe(Event.DRAGEND, this.checkOnMe.bind(this));
+ this.checkMethod = this[this.CHECKMETHOD_OVERLAP];
+ },
+
+ /**
+ * Sets the collision method which is going to be used to check a valid drop
+ * @name setCheckMethod
+ * @memberOf me.DroptargetEntity
+ * @function
+ * @param {Constant} checkMethod the checkmethod (defaults to CHECKMETHOD_OVERLAP)
+ */
+ setCheckMethod: function (checkMethod) {
+ // We can improve this check,
+ // because now you can use every method in theory
+ if (typeof(this[checkMethod]) !== "undefined") {
+ this.checkMethod = this[checkMethod];
+ }
+ },
+
+ /**
+ * Checks if a dropped entity is dropped on the current entity
+ * @name checkOnMe
+ * @memberOf me.DroptargetEntity
+ * @function
+ * @param {Object} draggableEntity the draggable entity that is dropped
+ */
+ checkOnMe: function (e, draggableEntity) {
+ if (draggableEntity && this.checkMethod(draggableEntity.getBounds())) {
+ // call the drop method on the current entity
+ this.drop(draggableEntity);
+ }
+ },
+
+ /**
+ * Gets called when a draggable entity is dropped on the current entity
+ * @name drop
+ * @memberOf me.DroptargetEntity
+ * @function
+ * @param {Object} draggableEntity the draggable entity that is dropped
+ */
+ drop: function () {},
+
+ /**
+ * Destructor
+ * @name destroy
+ * @memberOf me.DroptargetEntity
+ * @function
+ */
+ destroy: function () {
+ Event.unsubscribe(Event.DRAGEND, this.checkOnMe);
+ }
+ });
+}(me.Entity, me.event));
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+
+/*
+ * A Collectable entity
+ */
+
+/**
+ * @class
+ * @extends me.Entity
+ * @memberOf me
+ * @constructor
+ * @param {Number} x the x coordinates of the entity object
+ * @param {Number} y the y coordinates of the entity object
+ * @param {Object} settings See {@link me.Entity}
+ */
+me.CollectableEntity = me.Entity.extend(
+/** @scope me.CollectableEntity.prototype */
+{
+ /** @ignore */
+ init : function (x, y, settings) {
+ // call the super constructor
+ me.Entity.prototype.init.apply(this, [x, y, settings]);
+ this.body.collisionType = me.collision.types.COLLECTABLE_OBJECT;
+ }
+});
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+
+/*
+ * A level entity
+ */
+
+/**
+ * @class
+ * @extends me.Entity
+ * @memberOf me
+ * @constructor
+ * @param {Number} x the x coordinates of the object
+ * @param {Number} y the y coordinates of the object
+ * @param {Object} settings See {@link me.Entity}
+ * @param {String} [settings.duration] Fade duration (in ms)
+ * @param {String|me.Color} [settings.color] Fade color
+ * @param {String} [settings.to] TMX level to load
+ * @param {String|me.Container} [settings.container] Target container. See {@link me.levelDirector.loadLevel}
+ * @param {Function} [settings.onLoaded] Level loaded callback. See {@link me.levelDirector.loadLevel}
+ * @param {Boolean} [settings.flatten] Flatten all objects into the target container. See {@link me.levelDirector.loadLevel}
+ * @param {Boolean} [settings.setViewportBounds] Resize the viewport to match the level. See {@link me.levelDirector.loadLevel}
+ * @example
+ * me.game.world.addChild(new me.LevelEntity(
+ * x, y, {
+ * "duration" : 250,
+ * "color" : "#000",
+ * "to" : "mymap2"
+ * }
+ * ));
+ */
+me.LevelEntity = me.Entity.extend(
+/** @scope me.LevelEntity.prototype */
+{
+ /** @ignore */
+ init : function (x, y, settings) {
+ me.Entity.prototype.init.apply(this, [x, y, settings]);
+
+ this.nextlevel = settings.to;
+
+ this.fade = settings.fade;
+ this.duration = settings.duration;
+ this.fading = false;
+
+ this.name = "levelEntity";
+
+ // a temp variable
+ this.gotolevel = settings.to;
+
+ // Collect the defined level settings
+ this.loadLevelSettings = {};
+ [ "container", "onLoaded", "flatten", "setViewportBounds" ].forEach(function (v) {
+ if (typeof(settings[v]) !== "undefined") {
+ this.loadLevelSettings[v] = settings[v];
+ }
+ }.bind(this));
+
+ this.body.collisionType = me.collision.types.ACTION_OBJECT;
+ },
+
+ /**
+ * @ignore
+ */
+ getlevelSettings : function () {
+ // Lookup for the container instance
+ if (typeof(this.loadLevelSettings.container) === "string") {
+ this.loadLevelSettings.container = me.game.world.getChildByName(this.loadLevelSettings.container)[0];
+ }
+ return this.loadLevelSettings;
+ },
+
+ /**
+ * @ignore
+ */
+ onFadeComplete : function () {
+
+ me.levelDirector.loadLevel(this.gotolevel, this.getlevelSettings());
+ me.game.viewport.fadeOut(this.fade, this.duration);
+ },
+
+ /**
+ * go to the specified level
+ * @name goTo
+ * @memberOf me.LevelEntity
+ * @function
+ * @param {String} [level=this.nextlevel] name of the level to load
+ * @protected
+ */
+ goTo : function (level) {
+ this.gotolevel = level || this.nextlevel;
+ // load a level
+ //console.log("going to : ", to);
+ if (this.fade && this.duration) {
+ if (!this.fading) {
+ this.fading = true;
+ me.game.viewport.fadeIn(this.fade, this.duration,
+ this.onFadeComplete.bind(this));
+ }
+ } else {
+ me.levelDirector.loadLevel(this.gotolevel, this.getlevelSettings());
+ }
+ },
+
+ /** @ignore */
+ onCollision : function () {
+ if (this.name === "levelEntity") {
+ this.goTo.apply(this);
+ }
+ return false;
+ }
+});
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ // generate a default image for the particles
+ var pixel = (function () {
+ var canvas = me.video.createCanvas(1, 1);
+ var context = canvas.getContext("2d");
+ context.fillStyle = "#fff";
+ context.fillRect(0, 0, 1, 1);
+ return canvas;
+ })();
+
+ /**
+ * me.ParticleEmitterSettings contains the default settings for me.ParticleEmitter.
+ *
+ * @protected
+ * @class
+ * @memberOf me
+ * @see me.ParticleEmitter
+ */
+ me.ParticleEmitterSettings = {
+ /**
+ * Width of the particle spawn area.
+ * @public
+ * @type Number
+ * @name width
+ * @memberOf me.ParticleEmitterSettings
+ * @default 0
+ */
+ width : 0,
+
+ /**
+ * Height of the particle spawn area.
+ * @public
+ * @type Number
+ * @name height
+ * @memberOf me.ParticleEmitterSettings
+ * @default 0
+ */
+ height : 0,
+
+ /**
+ * Image used for particles.
+ * @public
+ * @type CanvasImageSource
+ * @name image
+ * @memberOf me.ParticleEmitterSettings
+ * @default 1x1 white pixel
+ * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#canvasimagesource
+ */
+ image : pixel,
+
+ /**
+ * Total number of particles in the emitter.
+ * @public
+ * @type Number
+ * @name totalParticles
+ * @default 50
+ * @memberOf me.ParticleEmitterSettings
+ */
+ totalParticles : 50,
+
+ /**
+ * Start angle for particle launch in Radians.
+ * @public
+ * @type Number
+ * @name angle
+ * @default Math.PI / 2
+ * @memberOf me.ParticleEmitterSettings
+ */
+ angle : Math.PI / 2,
+
+ /**
+ * Variation in the start angle for particle launch in Radians.
+ * @public
+ * @type Number
+ * @name angleVariation
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ angleVariation : 0,
+
+ /**
+ * Minimum time each particle lives once it is emitted in ms.
+ * @public
+ * @type Number
+ * @name minLife
+ * @default 1000
+ * @memberOf me.ParticleEmitterSettings
+ */
+ minLife : 1000,
+
+ /**
+ * Maximum time each particle lives once it is emitted in ms.
+ * @public
+ * @type Number
+ * @name maxLife
+ * @default 3000
+ * @memberOf me.ParticleEmitterSettings
+ */
+ maxLife : 3000,
+
+ /**
+ * Start speed of particles.
+ * @public
+ * @type Number
+ * @name speed
+ * @default 2
+ * @memberOf me.ParticleEmitterSettings
+ */
+ speed : 2,
+
+ /**
+ * Variation in the start speed of particles.
+ * @public
+ * @type Number
+ * @name speedVariation
+ * @default 1
+ * @memberOf me.ParticleEmitterSettings
+ */
+ speedVariation : 1,
+
+ /**
+ * Minimum start rotation for particles sprites in Radians.
+ * @public
+ * @type Number
+ * @name minRotation
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ minRotation : 0,
+
+ /**
+ * Maximum start rotation for particles sprites in Radians.
+ * @public
+ * @type Number
+ * @name maxRotation
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ maxRotation : 0,
+
+ /**
+ * Minimum start scale ratio for particles (1 = no scaling).
+ * @public
+ * @type Number
+ * @name minStartScale
+ * @default 1
+ * @memberOf me.ParticleEmitterSettings
+ */
+ minStartScale : 1,
+
+ /**
+ * Maximum start scale ratio for particles (1 = no scaling).
+ * @public
+ * @type Number
+ * @name maxStartScale
+ * @default 1
+ * @memberOf me.ParticleEmitterSettings
+ */
+ maxStartScale : 1,
+
+ /**
+ * Minimum end scale ratio for particles.
+ * @public
+ * @type Number
+ * @name minEndScale
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ minEndScale : 0,
+
+ /**
+ * Maximum end scale ratio for particles.
+ * @public
+ * @type Number
+ * @name maxEndScale
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ maxEndScale : 0,
+
+ /**
+ * Vertical force (Gravity) for each particle.
+ * @public
+ * @type Number
+ * @name gravity
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ * @see me.sys.gravity
+ */
+ gravity : 0,
+
+ /**
+ * Horizontal force (like a Wind) for each particle.
+ * @public
+ * @type Number
+ * @name wind
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ wind : 0,
+
+ /**
+ * Update the rotation of particle in accordance the particle trajectory.
+ * The particle sprite should aim at zero angle (draw from left to right).
+ * Override the particle minRotation and maxRotation.
+ * @public
+ * @type Boolean
+ * @name followTrajectory
+ * @default false
+ * @memberOf me.ParticleEmitterSettings
+ */
+ followTrajectory : false,
+
+ /**
+ * Enable the Texture Additive by canvas composite operation (lighter).
+ * WARNING: Composite Operation may decreases performance!.
+ * @public
+ * @type Boolean
+ * @name textureAdditive
+ * @default false
+ * @memberOf me.ParticleEmitterSettings
+ */
+ textureAdditive : false,
+
+ /**
+ * Update particles only in the viewport, remove it when out of viewport.
+ * @public
+ * @type Boolean
+ * @name onlyInViewport
+ * @default true
+ * @memberOf me.ParticleEmitterSettings
+ */
+ onlyInViewport : true,
+
+ /**
+ * Render particles in screen space.
+ * @public
+ * @type Boolean
+ * @name floating
+ * @default false
+ * @memberOf me.ParticleEmitterSettings
+ */
+ floating : false,
+
+ /**
+ * Maximum number of particles launched each time in this emitter (used only if emitter is Stream).
+ * @public
+ * @type Number
+ * @name maxParticles
+ * @default 10
+ * @memberOf me.ParticleEmitterSettings
+ */
+ maxParticles : 10,
+
+ /**
+ * How often a particle is emitted in ms (used only if emitter is Stream).
+ * Necessary that value is greater than zero.
+ * @public
+ * @type Number
+ * @name frequency
+ * @default 100
+ * @memberOf me.ParticleEmitterSettings
+ */
+ frequency : 100,
+
+ /**
+ * Duration that the emitter releases particles in ms (used only if emitter is Stream).
+ * After this period, the emitter stop the launch of particles.
+ * @public
+ * @type Number
+ * @name duration
+ * @default Infinity
+ * @memberOf me.ParticleEmitterSettings
+ */
+ duration : Infinity,
+
+ /**
+ * Skip n frames after updating the particle system once.
+ * This can be used to reduce the performance impact of emitters with many particles.
+ * @public
+ * @type Number
+ * @name framesToSkip
+ * @default 0
+ * @memberOf me.ParticleEmitterSettings
+ */
+ framesToSkip : 0
+ };
+
+ /**
+ * Particle Emitter Object.
+ * @class
+ * @extends Rect
+ * @memberOf me
+ * @constructor
+ * @param {Number} x x-position of the particle emitter
+ * @param {Number} y y-position of the particle emitter
+ * @param {object} settings An object containing the settings for the particle emitter. See {@link me.ParticleEmitterSettings}
+ * @example
+ *
+ * // Create a basic emitter at position 100, 100
+ * var emitter = new me.ParticleEmitter(100, 100);
+ *
+ * // Adjust the emitter properties
+ * emitter.totalParticles = 200;
+ * emitter.minLife = 1000;
+ * emitter.maxLife = 3000;
+ * emitter.z = 10;
+ *
+ * // Add the emitter to the game world
+ * me.game.world.addChild(emitter);
+ *
+ * // Launch all particles one time and stop, like a explosion
+ * emitter.burstParticles();
+ *
+ * // Launch constantly the particles, like a fountain
+ * emitter.streamParticles();
+ *
+ * // At the end, remove emitter from the game world
+ * // call this in onDestroyEvent function
+ * me.game.world.removeChild(emitter);
+ *
+ */
+ me.ParticleEmitter = me.Rect.extend(
+ /** @scope me.ParticleEmitter.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init: function (x, y, settings) {
+ // Emitter is Stream, launch particles constantly
+ /** @ignore */
+ this._stream = false;
+
+ // Frequency timer (in ms) for emitter launch new particles
+ // used only in stream emitter
+ /** @ignore */
+ this._frequencyTimer = 0;
+
+ // Time of live (in ms) for emitter launch new particles
+ // used only in stream emitter
+ /** @ignore */
+ this._durationTimer = 0;
+
+ // Emitter is emitting particles
+ /** @ignore */
+ this._enabled = false;
+ // Emitter will always update
+ this.isRenderable = false;
+ // call the super constructor
+ me.Rect.prototype.init.apply(this,
+ [x, y,
+ Infinity,
+ Infinity]
+ );
+
+ // don't sort the particles by z-index
+ this.autoSort = false;
+
+ this.container = new me.ParticleContainer(this);
+
+ /**
+ * @ignore
+ */
+ Object.defineProperty(this.pos, "z", {
+ /**
+ * @ignore
+ */
+ get : (function () { return this.container.pos.z; }).bind(this),
+ /**
+ * @ignore
+ */
+ set : (function (value) { this.container.pos.z = value; }).bind(this),
+ enumerable : true,
+ configurable : true
+ });
+
+ /**
+ * Floating property for particles, value is forwarded to the particle container
+ * @type Boolean
+ * @name floating
+ * @memberOf me.ParticleEmitter
+ */
+ Object.defineProperty(this, "floating", {
+ /**
+ * @ignore
+ */
+ get : function () { return this.container.floating; },
+ /**
+ * @ignore
+ */
+ set : function (value) { this.container.floating = value; },
+ enumerable : true,
+ configurable : true
+ });
+
+ // Reset the emitter to defaults
+ this.reset(settings);
+ },
+
+ /**
+ * @ignore
+ */
+ onActivateEvent: function() {
+ this.ancestor.addChild(this.container);
+ this.container.pos.z = this.pos.z;
+ if (!this.ancestor.autoSort) {
+ this.ancestor.sort();
+ }
+ },
+
+ /**
+ * @ignore
+ */
+ onDeactivateEvent: function() {
+ if (this.ancestor.hasChild(this.container)) {
+ this.ancestor.removeChildNow(this.container);
+ }
+ },
+
+ /**
+ * @ignore
+ */
+ destroy: function () {
+ this.reset();
+ },
+
+ /**
+ * returns a random point inside the bounds x axis of this emitter
+ * @name getRandomPointX
+ * @memberOf me.ParticleEmitter
+ * @function
+ * @return {Number}
+ */
+ getRandomPointX: function () {
+ return this.pos.x + (0).randomFloat(this.width);
+ },
+
+ /**
+ * returns a random point inside the bounds y axis of this emitter
+ * @name getRandomPointY
+ * @memberOf me.ParticleEmitter
+ * @function
+ * @return {Number}
+ */
+ getRandomPointY: function () {
+ return this.pos.y + (0).randomFloat(this.height);
+ },
+
+ /**
+ * Reset the emitter with default values.
+ * @function
+ * @param {Object} settings [optional] object with emitter settings. See {@link me.ParticleEmitterSettings}
+ * @name reset
+ * @memberOf me.ParticleEmitter
+ */
+ reset: function (settings) {
+ // check if settings exists and create a dummy object if necessary
+ settings = settings || {};
+ var defaults = me.ParticleEmitterSettings;
+
+ var width = (typeof settings.width === "number") ? settings.width : defaults.width;
+ var height = (typeof settings.height === "number") ? settings.height : defaults.height;
+ this.resize(width, height);
+
+ Object.assign(this, defaults, settings);
+
+ // reset particle container values
+ this.container.destroy();
+ },
+
+ // Add count particles in the game world
+ /** @ignore */
+ addParticles: function (count) {
+ for (var i = 0; i < ~~count; i++) {
+ // Add particle to the container
+ var particle = me.pool.pull("me.Particle", this);
+ this.container.addChild(particle);
+ }
+ },
+
+ /**
+ * Emitter is of type stream and is launching particles
+ * @function
+ * @returns {Boolean} Emitter is Stream and is launching particles
+ * @name isRunning
+ * @memberOf me.ParticleEmitter
+ */
+ isRunning: function () {
+ return this._enabled && this._stream;
+ },
+
+ /**
+ * Launch particles from emitter constantly
+ * Particles example: Fountains
+ * @param {Number} duration [optional] time that the emitter releases particles in ms
+ * @function
+ * @name streamParticles
+ * @memberOf me.ParticleEmitter
+ */
+ streamParticles: function (duration) {
+ this._enabled = true;
+ this._stream = true;
+ this.frequency = Math.max(this.frequency, 1);
+ this._durationTimer = (typeof duration === "number") ? duration : this.duration;
+ },
+
+ /**
+ * Stop the emitter from generating new particles (used only if emitter is Stream)
+ * @function
+ * @name stopStream
+ * @memberOf me.ParticleEmitter
+ */
+ stopStream: function () {
+ this._enabled = false;
+ },
+
+ /**
+ * Launch all particles from emitter and stop
+ * Particles example: Explosions
+ * @param {Number} total [optional] number of particles to launch
+ * @function
+ * @name burstParticles
+ * @memberOf me.ParticleEmitter
+ */
+ burstParticles: function (total) {
+ this._enabled = true;
+ this._stream = false;
+ this.addParticles((typeof total === "number") ? total : this.totalParticles);
+ this._enabled = false;
+ },
+
+ /**
+ * @ignore
+ */
+ update: function (dt) {
+ // Launch new particles, if emitter is Stream
+ if ((this._enabled) && (this._stream)) {
+ // Check if the emitter has duration set
+ if (this._durationTimer !== Infinity) {
+ this._durationTimer -= dt;
+
+ if (this._durationTimer <= 0) {
+ this.stopStream();
+ return false;
+ }
+ }
+
+ // Increase the emitter launcher timer
+ this._frequencyTimer += dt;
+
+ // Check for new particles launch
+ var particlesCount = this.container.children.length;
+ if ((particlesCount < this.totalParticles) && (this._frequencyTimer >= this.frequency)) {
+ if ((particlesCount + this.maxParticles) <= this.totalParticles) {
+ this.addParticles(this.maxParticles);
+ }
+ else {
+ this.addParticles(this.totalParticles - particlesCount);
+ }
+
+ this._frequencyTimer = 0;
+ }
+ }
+ return true;
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * Particle Container Object.
+ * @class
+ * @extends me.Container
+ * @memberOf me
+ * @constructor
+ * @param {me.ParticleEmitter} emitter the emitter which owns this container
+ */
+ me.ParticleContainer = me.Container.extend(
+ /** @scope ParticleContainer */
+ {
+ /**
+ * @ignore
+ */
+ init: function (emitter) {
+ // cache a reference to the viewport to use as our bounding box
+ this._viewport = me.game.viewport;
+
+ // call the super constructor
+ me.Container.prototype.init.apply(this);
+
+ // don't sort the particles by z-index
+ this.autoSort = false;
+
+ // count the updates
+ this._updateCount = 0;
+
+ // internally store how much time was skipped when frames are skipped
+ this._dt = 0;
+
+ // cache the emitter for later use
+ this._emitter = emitter;
+ },
+
+ /**
+ * @ignore
+ */
+ getBounds : function () {
+ return this._viewport;
+ },
+
+ /**
+ * @ignore
+ */
+ update: function (dt) {
+ // skip frames if necessary
+ if (++this._updateCount > this._emitter.framesToSkip) {
+ this._updateCount = 0;
+ }
+ if (this._updateCount > 0) {
+ this._dt += dt;
+ return false;
+ }
+
+ // apply skipped delta time
+ dt += this._dt;
+ this._dt = 0;
+
+ // Update particles and remove them if they are dead
+ var viewport = me.game.viewport;
+ for (var i = this.children.length - 1; i >= 0; --i) {
+ var particle = this.children[i];
+ particle.isRenderable = true;
+ // particle.inViewport = viewport.isVisible(particle);
+ particle.inViewport = this.floating || (
+ particle.pos.x < viewport.pos.x + viewport.width &&
+ viewport.pos.x < particle.pos.x + particle.width &&
+ particle.pos.y < viewport.pos.y + viewport.height &&
+ viewport.pos.y < particle.pos.y + particle.height
+ );
+ if (!particle.update(dt)) {
+ this.removeChildNow(particle);
+ }
+ }
+ return true;
+ },
+
+ /**
+ * @ignore
+ */
+ draw : function (renderer, rect) {
+ if (this.children.length > 0) {
+ var context = renderer.getContext(),
+ gco;
+ // Check for additive draw
+ if (this._emitter.textureAdditive) {
+ gco = context.globalCompositeOperation;
+ context.globalCompositeOperation = "lighter";
+ }
+
+ me.Container.prototype.draw.apply(this, [renderer, rect]);
+
+ // Restore globalCompositeOperation
+ if (this._emitter.textureAdditive) {
+ context.globalCompositeOperation = gco;
+ }
+ }
+ }
+ });
+})();
+
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ */
+(function () {
+ /**
+ * Single Particle Object.
+ * @class
+ * @extends me.Renderable
+ * @memberOf me
+ * @constructor
+ * @param {me.ParticleEmitter} particle emitter
+ */
+ me.Particle = me.Renderable.extend(
+ /** @scope me.Particle.prototype */
+ {
+ /**
+ * @ignore
+ */
+ init : function (emitter) {
+ // Call the super constructor
+ me.Renderable.prototype.init.apply(this, [
+ emitter.getRandomPointX(),
+ emitter.getRandomPointY(),
+ emitter.image.width,
+ emitter.image.height
+ ]);
+
+ // Particle will always update
+ this.alwaysUpdate = true;
+
+ // Particle will not act as a rednerable
+ // FIXME: This is probably not needed. It's a hack that tries to
+ // workaround performance issues within container.
+ this.isRenderable = false;
+
+ // Cache the image reference
+ this.image = emitter.image;
+
+ // Set the start particle Angle and Speed as defined in emitter
+ var angle = emitter.angle + ((emitter.angleVariation > 0) ? ((0).randomFloat(2) - 1) * emitter.angleVariation : 0);
+ var speed = emitter.speed + ((emitter.speedVariation > 0) ? ((0).randomFloat(2) - 1) * emitter.speedVariation : 0);
+
+ // Set the start particle Velocity
+ this.vel = new me.Vector2d(speed * Math.cos(angle), -speed * Math.sin(angle));
+
+ // Set the start particle Time of Life as defined in emitter
+ this.life = emitter.minLife.randomFloat(emitter.maxLife);
+ this.startLife = this.life;
+
+ // Set the start and end particle Scale as defined in emitter
+ // clamp the values as minimum and maximum scales range
+ this.startScale = emitter.minStartScale.randomFloat(
+ emitter.maxStartScale
+ ).clamp(emitter.minStartScale, emitter.maxStartScale);
+ this.endScale = emitter.minEndScale.randomFloat(
+ emitter.maxEndScale
+ ).clamp(emitter.minEndScale, emitter.maxEndScale);
+
+ // Set the particle Gravity and Wind (horizontal gravity) as defined in emitter
+ this.gravity = emitter.gravity;
+ this.wind = emitter.wind;
+
+ // Set if the particle update the rotation in accordance the trajectory
+ this.followTrajectory = emitter.followTrajectory;
+
+ // Set if the particle update only in Viewport
+ this.onlyInViewport = emitter.onlyInViewport;
+
+ // Set the particle Z Order
+ this.pos.z = emitter.z;
+
+ // cache inverse of the expected delta time
+ this._deltaInv = me.sys.fps / 1000;
+
+ // Set the start particle rotation as defined in emitter
+ // if the particle not follow trajectory
+ if (!emitter.followTrajectory) {
+ this.angle = emitter.minRotation.randomFloat(emitter.maxRotation);
+ }
+ },
+
+ /**
+ * Update the Particle
+ * This is automatically called by the game manager {@link me.game}
+ * @name update
+ * @memberOf me.Particle
+ * @function
+ * @ignore
+ * @param {Number} dt time since the last update in milliseconds
+ */
+ update : function (dt) {
+ // move things forward independent of the current frame rate
+ var skew = dt * this._deltaInv;
+
+ // Decrease particle life
+ this.life = this.life > dt ? this.life - dt : 0;
+
+ // Calculate the particle Age Ratio
+ var ageRatio = this.life / this.startLife;
+
+ // Resize the particle as particle Age Ratio
+ var scale = this.startScale;
+ if (this.startScale > this.endScale) {
+ scale *= ageRatio;
+ scale = (scale < this.endScale) ? this.endScale : scale;
+ }
+ else if (this.startScale < this.endScale) {
+ scale /= ageRatio;
+ scale = (scale > this.endScale) ? this.endScale : scale;
+ }
+
+ // Set the particle opacity as Age Ratio
+ this.alpha = ageRatio;
+
+ // Adjust the particle velocity
+ this.vel.x += this.wind * skew;
+ this.vel.y += this.gravity * skew;
+
+ // If necessary update the rotation of particle in accordance the particle trajectory
+ var angle = this.followTrajectory ? Math.atan2(this.vel.y, this.vel.x) : this.angle;
+
+ this.pos.x += this.vel.x * skew;
+ this.pos.y += this.vel.y * skew;
+
+ // Update particle transform
+ this.currentTransform.setTransform(
+ scale, 0, 0,
+ 0, scale, 0,
+ this.pos.x, this.pos.y, 1
+ ).rotate(angle);
+
+ // Return true if the particle is not dead yet
+ return (this.inViewport || !this.onlyInViewport) && (this.life > 0);
+ },
+
+ /**
+ * @ignore
+ */
+ draw : function (renderer) {
+ renderer.save();
+
+ // particle alpha value
+ renderer.setGlobalAlpha(renderer.globalAlpha() * this.alpha);
+
+ // translate to the defined anchor point and scale it
+ renderer.transform(this.currentTransform);
+
+ var w = this.width, h = this.height;
+ renderer.drawImage(
+ this.image,
+ 0, 0,
+ w, h,
+ -w / 2, -h / 2,
+ w, h
+ );
+
+ renderer.restore();
+ }
+ });
+
+
+ /*---------------------------------------------------------*/
+ // END END END
+ /*---------------------------------------------------------*/
+})(window);
diff --git a/Games/Contra-Shooter/lib/plugins/debug/debugPanel.js b/Games/Contra-Shooter/lib/plugins/debug/debugPanel.js
new file mode 100644
index 0000000000..2dee306646
--- /dev/null
+++ b/Games/Contra-Shooter/lib/plugins/debug/debugPanel.js
@@ -0,0 +1,701 @@
+/*
+ * MelonJS Game Engine
+ * Copyright (C) 2011 - 2015, Olivier Biot, Jason Oster, Aaron McLeod
+ * http://www.melonjs.org
+ *
+ * a simple debug panel plugin
+ * usage : me.plugin.register.defer(this, me.debug.Panel, "debug");
+ *
+ * you can then use me.plugins.debug.show() or me.plugins.debug.hide()
+ * to show or hide the panel, or press respectively the "S" and "H" keys.
+ *
+ * note :
+ * Heap Memory information is available under Chrome when using
+ * the "--enable-memory-info" parameter to launch Chrome
+ */
+
+(function () {
+
+ // ensure that me.debug is defined
+ me.debug = me.debug || {};
+
+ var DEBUG_HEIGHT = 50;
+
+ var Counters = me.Object.extend({
+ init : function (stats) {
+ this.stats = {};
+ this.reset(stats);
+ },
+
+ reset : function (stats) {
+ var self = this;
+ (stats || Object.keys(this.stats)).forEach(function (stat) {
+ self.stats[stat] = 0;
+ });
+ },
+
+ inc : function (stat, value) {
+ this.stats[stat] += (value || 1);
+ },
+
+ get : function (stat) {
+ return this.stats[stat];
+ }
+ });
+
+ // embedded bitmap font data
+ var fontDataSource =
+ "info face=\"PressStart2P\" size=10 bold=0 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=0,0 outline=0\n" +
+ "common lineHeight=10 base=10 scaleW=128 scaleH=128 pages=1 packed=0\n" +
+ "page id=0 file=\"PressStart2P.png\"\n" +
+ "chars count=95\n" +
+ "char id=32 x=1 y=1 width=0 height=0 xoffset=0 yoffset=10 xadvance=10 page=0 chnl=15\n" +
+ "char id=33 x=1 y=2 width=5 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=34 x=1 y=13 width=8 height=5 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=35 x=7 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=36 x=1 y=19 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=37 x=1 y=30 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=38 x=1 y=41 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=39 x=10 y=12 width=4 height=5 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=40 x=1 y=52 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=41 x=1 y=63 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=42 x=1 y=74 width=10 height=8 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15\n" +
+ "char id=43 x=8 y=52 width=9 height=8 xoffset=1 yoffset=1 xadvance=10 page=0 chnl=15\n" +
+ "char id=44 x=8 y=61 width=5 height=5 xoffset=1 yoffset=6 xadvance=10 page=0 chnl=15\n" +
+ "char id=45 x=8 y=67 width=9 height=2 xoffset=1 yoffset=4 xadvance=10 page=0 chnl=15\n" +
+ "char id=46 x=14 y=61 width=4 height=4 xoffset=3 yoffset=6 xadvance=10 page=0 chnl=15\n" +
+ "char id=47 x=12 y=18 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=48 x=18 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=49 x=12 y=29 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=50 x=12 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=51 x=22 y=29 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=52 x=23 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=53 x=29 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=54 x=1 y=83 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=55 x=1 y=94 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=56 x=1 y=105 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=57 x=1 y=116 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=58 x=18 y=51 width=4 height=8 xoffset=3 yoffset=1 xadvance=10 page=0 chnl=15\n" +
+ "char id=59 x=12 y=70 width=5 height=9 xoffset=1 yoffset=1 xadvance=10 page=0 chnl=15\n" +
+ "char id=60 x=12 y=80 width=8 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=61 x=23 y=23 width=10 height=5 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=62 x=18 y=66 width=8 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=63 x=23 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=64 x=33 y=29 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=65 x=23 y=51 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=66 x=34 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=67 x=40 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=68 x=12 y=91 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=69 x=21 y=77 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=70 x=27 y=62 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=71 x=34 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=72 x=34 y=51 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=73 x=44 y=23 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=74 x=45 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=75 x=51 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=76 x=12 y=102 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=77 x=12 y=113 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=78 x=22 y=102 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=79 x=23 y=88 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=80 x=32 y=73 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=81 x=38 y=62 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=82 x=23 y=113 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=83 x=33 y=99 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=84 x=34 y=84 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=85 x=43 y=73 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=86 x=34 y=110 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=87 x=44 y=84 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=88 x=44 y=95 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=89 x=45 y=106 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=90 x=45 y=117 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=91 x=45 y=34 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=92 x=45 y=45 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=93 x=52 y=34 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=94 x=34 y=23 width=8 height=4 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=95 x=34 y=121 width=10 height=2 xoffset=0 yoffset=9 xadvance=10 page=0 chnl=15\n" +
+ "char id=96 x=15 y=12 width=4 height=4 xoffset=4 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=97 x=54 y=23 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=98 x=56 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=99 x=62 y=1 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=100 x=49 y=56 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=101 x=56 y=45 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=102 x=59 y=31 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=103 x=54 y=67 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=104 x=60 y=54 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=105 x=67 y=42 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=106 x=67 y=9 width=8 height=11 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=107 x=69 y=21 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=108 x=76 y=1 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=109 x=76 y=12 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=110 x=69 y=32 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=111 x=86 y=1 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=112 x=97 y=1 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=113 x=108 y=1 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=114 x=87 y=10 width=9 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=115 x=97 y=11 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=116 x=108 y=11 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=117 x=87 y=19 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=118 x=98 y=19 width=9 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=119 x=80 y=27 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=120 x=108 y=22 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=121 x=91 y=28 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=122 x=80 y=35 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=123 x=118 y=11 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=124 x=102 y=28 width=4 height=10 xoffset=4 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=125 x=119 y=22 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" +
+ "char id=126 x=91 y=38 width=10 height=5 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" +
+ "char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15";
+
+ var fontImageSource = "data:image/png;base64,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";
+
+ var DebugPanel = me.Renderable.extend({
+ /** @private */
+ init : function (showKey, hideKey) {
+ // call the super constructor
+ this._super(me.Renderable, "init", [ 0, 0, me.game.viewport.width, DEBUG_HEIGHT ]);
+
+ // minimum melonJS version expected
+ this.version = "4.0.0";
+
+ // to hold the debug options
+ // clickable rect area
+ this.area = {};
+
+ // Useful counters
+ this.counters = new Counters([
+ "shapes",
+ "sprites",
+ "velocity",
+ "bounds",
+ "children"
+ ]);
+
+ // for z ordering
+ // make it ridiculously high
+ this.pos.z = Infinity;
+
+ // visibility flag
+ this.visible = false;
+
+ // frame update time in ms
+ this.frameUpdateTime = 0;
+
+ // frame draw time in ms
+ this.frameDrawTime = 0;
+
+ // set the object GUID value
+ this.GUID = "debug-" + me.utils.createGUID();
+
+ // set the object entity name
+ this.name = "me.debugPanel";
+
+ // persistent
+ this.isPersistent = true;
+
+ // a floating object
+ this.floating = true;
+
+ // renderable
+ this.isRenderable = true;
+
+ // always update, even when not visible
+ this.alwaysUpdate = true;
+
+ // WebGL/Canvas compatibility
+ this.canvas = me.video.createCanvas(this.width, this.height, true);
+
+ // create a default font, with fixed char width
+ this.font_size = 10;
+ this.mod = 2;
+ if (this.width < 500) {
+ this.font_size = 7;
+ this.mod = this.mod * (this.font_size / 10);
+ }
+
+ // create the bitmapfont
+ var fontImage = new Image();
+ fontImage.src = fontImageSource;
+
+ this.font = new me.BitmapFont(
+ fontDataSource,
+ fontImage
+ );
+
+ // free static ressources
+ fontImageSource = null;
+ fontDataSource = null;
+
+ // clickable areas
+ var size = 10 * this.mod;
+ this.area.renderHitBox = new me.Rect(250, 2, size, size);
+ this.area.renderVelocity = new me.Rect(250, 17, size, size);
+ this.area.renderQuadTree = new me.Rect(410, 2, size, size);
+
+ // some internal string/length
+ this.help_str = "(s)how/(h)ide";
+ this.help_str_len = this.font.measureText(me.video.renderer, this.help_str).width;
+ this.fps_str_len = this.font.measureText(me.video.renderer, "00/00 fps").width;
+ this.memoryPositionX = 325 * this.mod;
+
+ // enable the FPS counter
+ me.debug.displayFPS = true;
+
+ var self = this;
+
+ // add some keyboard shortcuts
+ this.showKey = showKey || me.input.KEY.S;
+ this.hideKey = hideKey || me.input.KEY.H;
+ this.keyHandler = me.event.subscribe(me.event.KEYDOWN, function (action, keyCode) {
+ if (keyCode === self.showKey) {
+ me.plugins.debugPanel.show();
+ } else if (keyCode === self.hideKey) {
+ me.plugins.debugPanel.hide();
+ }
+ });
+
+ // resize the panel if the browser is resized
+ me.event.subscribe(me.event.VIEWPORT_ONRESIZE, function (w) {
+ self.resize(w, DEBUG_HEIGHT);
+ });
+
+ //patch patch patch !
+ this.patchSystemFn();
+ },
+
+ /**
+ * patch system fn to draw debug information
+ */
+ patchSystemFn : function () {
+
+ // add a few new debug flag (if not yet defined)
+ me.debug.renderHitBox = me.debug.renderHitBox || me.game.HASH.hitbox || false;
+ me.debug.renderVelocity = me.debug.renderVelocity || me.game.HASH.velocity || false;
+ me.debug.renderQuadTree = me.debug.renderQuadTree || me.game.HASH.quadtree || false;
+
+ var _this = this;
+ var bounds = new me.Rect(0, 0, 0, 0);
+
+ // patch timer.js
+ me.plugin.patch(me.timer, "update", function (dt) {
+ // call the original me.timer.update function
+ this._patched(dt);
+
+ // call the FPS counter
+ me.timer.countFPS();
+ });
+
+ // patch me.game.update
+ me.plugin.patch(me.game, "update", function (dt) {
+ var frameUpdateStartTime = window.performance.now();
+
+ this._patched(dt);
+
+ // calculate the update time
+ _this.frameUpdateTime = window.performance.now() - frameUpdateStartTime;
+ });
+
+ // patch me.game.draw
+ me.plugin.patch(me.game, "draw", function () {
+ var frameDrawStartTime = window.performance.now();
+
+ _this.counters.reset();
+
+ this._patched();
+
+ // calculate the drawing time
+ _this.frameDrawTime = window.performance.now() - frameDrawStartTime;
+ });
+
+ // patch sprite.js
+ me.plugin.patch(me.Sprite, "draw", function (renderer) {
+ // call the original me.Sprite.draw function
+ this._patched(renderer);
+
+ if (!_this.visible) {
+ // don't do anything else if the panel is hidden
+ return;
+ }
+
+ // increment the sprites counter
+ _this.counters.inc("sprites");
+
+ // draw the sprite rectangle
+ if (me.debug.renderHitBox) {
+ var bounds = this.getBounds();
+ var x = - (this.anchorPoint.x * this.width) - ((bounds.width - this.width) / 2);
+ var y = - (this.anchorPoint.y * this.height) - ((bounds.height - this.height) / 2);
+
+ renderer.save();
+ renderer.setColor("green");
+ renderer.translate(x, y);
+ renderer.drawShape(bounds);
+ renderer.restore();
+ }
+ });
+
+ /*
+ // patch font.js
+ me.plugin.patch(me.Font, "draw", function (renderer, text, x, y) {
+ // call the original me.Sprite.draw function
+ this._patched(renderer, text, x, y);
+
+ // draw the font rectangle
+ if (me.debug.renderHitBox) {
+ renderer.save();
+ renderer.setColor("orange");
+ renderer.drawShape(this.getBounds());
+ _this.counters.inc("bounds");
+ renderer.restore();
+ }
+ });
+
+ // patch font.js
+ me.plugin.patch(me.Font, "drawStroke", function (renderer, text, x, y) {
+ // call the original me.Sprite.draw function
+ this._patched(renderer, text, x, y);
+
+ // draw the font rectangle
+ if (me.debug.renderHitBox) {
+ renderer.save();
+ renderer.setColor("orange");
+ renderer.drawShape(this.getBounds());
+ _this.counters.inc("bounds");
+ renderer.restore();
+ }
+ });
+ */
+
+ // patch entities.js
+ me.plugin.patch(me.Entity, "draw", function (renderer) {
+ // call the original me.Entity.draw function
+ this._patched(renderer);
+
+ if (!_this.visible) {
+ // don't do anything else if the panel is hidden
+ return;
+ }
+
+ // increment the bounds counter
+ _this.counters.inc("bounds");
+
+ // check if debug mode is enabled
+ if (me.debug.renderHitBox) {
+ renderer.save();
+ renderer.setLineWidth(1);
+
+ // draw the bounding rect shape
+ renderer.setColor("orange");
+ bounds.copy(this.getBounds());
+ bounds.pos.sub(this.ancestor._absPos);
+ renderer.drawShape(bounds);
+
+ // draw all defined shapes
+ renderer.setColor("red");
+ renderer.translate(this.pos.x, this.pos.y);
+ }
+ for (var i = this.body.shapes.length, shape; i--, (shape = this.body.shapes[i]);) {
+ if (me.debug.renderHitBox) {
+ renderer.drawShape(shape);
+ }
+ _this.counters.inc("shapes");
+ }
+
+ if (me.debug.renderHitBox) {
+ renderer.restore();
+ }
+
+ if (me.debug.renderVelocity && (this.body.vel.x || this.body.vel.y)) {
+ bounds.copy(this.getBounds());
+ bounds.pos.sub(this.ancestor._absPos);
+ // draw entity current velocity
+ var x = ~~(bounds.pos.x + (bounds.width / 2));
+ var y = ~~(bounds.pos.y + (bounds.height / 2));
+
+ renderer.save();
+ renderer.setLineWidth(1);
+
+ renderer.setColor("blue");
+ renderer.translate(x, y);
+ renderer.strokeLine(0, 0, ~~(this.body.vel.x * (bounds.width / 2)), ~~(this.body.vel.y * (bounds.height / 2)));
+ _this.counters.inc("velocity");
+
+ renderer.restore();
+ }
+ });
+
+ // patch container.js
+ me.plugin.patch(me.Container, "draw", function (renderer, rect) {
+ // call the original me.Container.draw function
+ this._patched(renderer, rect);
+
+ // check if debug mode is enabled
+ if (!_this.visible) {
+ // don't do anything else if the panel is hidden
+ return;
+ }
+
+ // increment counters
+ _this.counters.inc("bounds");
+ _this.counters.inc("children");
+
+ if (me.debug.renderHitBox) {
+ renderer.save();
+ renderer.setLineWidth(1);
+
+ // draw the bounding rect shape
+ renderer.setColor("orange");
+ bounds.copy(this.getBounds());
+ if (this.ancestor) {
+ bounds.pos.sub(this.ancestor._absPos);
+ }
+ renderer.drawShape(bounds);
+
+ // draw the children bounding rect shape
+ renderer.setColor("purple");
+ bounds.copy(this.childBounds);
+ if (this.ancestor) {
+ bounds.pos.sub(this.ancestor._absPos);
+ }
+ renderer.drawShape(bounds);
+
+ renderer.restore();
+ }
+ });
+ },
+
+ /**
+ * show the debug panel
+ */
+ show : function () {
+ if (!this.visible) {
+ // add the debug panel to the game world
+ me.game.world.addChild(this, Infinity);
+ // register a mouse event for the checkboxes
+ me.input.registerPointerEvent("pointerdown", this, this.onClick.bind(this));
+ // mark it as visible
+ this.visible = true;
+ // force repaint
+ me.game.repaint();
+ }
+ },
+
+ /**
+ * hide the debug panel
+ */
+ hide : function () {
+ if (this.visible) {
+ // release the mouse event for the checkboxes
+ me.input.releasePointerEvent("pointerdown", this);
+ // remove the debug panel from the game world
+ me.game.world.removeChild(this, true);
+ // mark it as invisible
+ this.visible = false;
+ // force repaint
+ me.game.repaint();
+ }
+ },
+
+
+ /** @private */
+ update : function () {
+ return this.visible;
+ },
+
+ /** @private */
+ onClick : function (e) {
+ // check the clickable areas
+ if (this.area.renderHitBox.containsPoint(e.gameX, e.gameY)) {
+ me.debug.renderHitBox = !me.debug.renderHitBox;
+ } else if (this.area.renderVelocity.containsPoint(e.gameX, e.gameY)) {
+ // does nothing for now, since velocity is
+ // rendered together with hitboxes (is a global debug flag required?)
+ me.debug.renderVelocity = !me.debug.renderVelocity;
+ } else if (this.area.renderQuadTree.containsPoint(e.gameX, e.gameY)) {
+ me.debug.renderQuadTree = !me.debug.renderQuadTree;
+ }
+ // force repaint
+ me.game.repaint();
+ },
+
+ /** @private */
+ drawQuadTreeNode : function (renderer, node) {
+ var bounds = node.bounds;
+
+ // draw the current bounds
+ if (node.nodes.length === 0) {
+ // cap the alpha value to 0.4 maximum
+ var _alpha = (node.objects.length * 0.4) / me.collision.maxChildren;
+ if (_alpha > 0.0) {
+ renderer.save();
+ renderer.setColor("rgba(255,0,0," + _alpha + ")");
+ renderer.fillRect(bounds.pos.x, bounds.pos.y, bounds.width, bounds.height);
+ renderer.restore();
+ }
+ } else {
+ //has subnodes? drawQuadtree them!
+ for (var i = 0; i < node.nodes.length; i++) {
+ this.drawQuadTreeNode(renderer, node.nodes[i]);
+ }
+ }
+ },
+
+ /** @private */
+ drawQuadTree : function (renderer) {
+ // save the current globalAlpha value
+ var _alpha = renderer.globalAlpha();
+ var x = me.game.viewport.pos.x;
+ var y = me.game.viewport.pos.y;
+
+ renderer.translate(-x, -y);
+
+ this.drawQuadTreeNode(renderer, me.collision.quadTree);
+
+ renderer.translate(x, y);
+
+ renderer.setGlobalAlpha(_alpha);
+ },
+
+ /** @private */
+ drawMemoryGraph : function (renderer, endX) {
+ if (window.performance && window.performance.memory) {
+ var usedHeap = Number.prototype.round(window.performance.memory.usedJSHeapSize / 1048576, 2);
+ var totalHeap = Number.prototype.round(window.performance.memory.totalJSHeapSize / 1048576, 2);
+ var maxLen = ~~(endX - this.memoryPositionX - 5);
+ var len = maxLen * (usedHeap / totalHeap);
+
+ renderer.setColor("#0065AD");
+ renderer.fillRect(this.memoryPositionX, 0, maxLen, 20);
+ renderer.setColor("#3AA4F0");
+ renderer.fillRect(this.memoryPositionX + 1, 1, len - 1, 17);
+
+ this.font.draw(renderer, "Heap : " + usedHeap + "/" + totalHeap + " MB", this.memoryPositionX + 5, 2 * this.mod);
+ } else {
+ // Heap Memory information not available
+ this.font.draw(renderer, "Heap : ??/?? MB", this.memoryPositionX, 2 * this.mod);
+ }
+ },
+
+ /** @private */
+ draw : function (renderer) {
+ renderer.save();
+
+ // draw the QuadTree (before the panel)
+ if (me.debug.renderQuadTree === true) {
+ this.drawQuadTree(renderer);
+ }
+
+ // draw the panel
+ renderer.setGlobalAlpha(0.5);
+ renderer.setColor("black");
+ renderer.fillRect(
+ this.left, this.top,
+ this.width, this.height
+ );
+ renderer.setGlobalAlpha(1.0);
+ renderer.setColor("white");
+
+ this.font.textAlign = "left";
+
+ this.font.draw(renderer, "#objects : " + me.game.world.children.length, 5 * this.mod, 2 * this.mod);
+ this.font.draw(renderer, "#draws : " + me.game.world.drawCount, 5 * this.mod, 10 * this.mod);
+
+ // debug checkboxes
+ this.font.draw(renderer, "?hitbox [" + (me.debug.renderHitBox ? "x" : " ") + "]", 75 * this.mod, 2 * this.mod);
+ this.font.draw(renderer, "?velocity [" + (me.debug.renderVelocity ? "x" : " ") + "]", 75 * this.mod, 10 * this.mod);
+
+ this.font.draw(renderer, "?QuadTree [" + (me.debug.renderQuadTree ? "x" : " ") + "]", 150 * this.mod, 2 * this.mod);
+
+ // draw the update duration
+ this.font.draw(renderer, "Update : " + this.frameUpdateTime.toFixed(2) + " ms", 225 * this.mod, 2 * this.mod);
+ // draw the draw duration
+ this.font.draw(renderer, "Draw : " + this.frameDrawTime.toFixed(2) + " ms", 225 * this.mod, 10 * this.mod);
+
+
+ // Draw color code hints (not supported with bitmapfont)
+ //this.font.fillStyle.copy("red");
+ this.font.draw(renderer, "Shapes : " + this.counters.get("shapes"), 5 * this.mod, 17 * this.mod);
+
+ //this.font.fillStyle.copy("green");
+ this.font.draw(renderer, "Sprites : " + this.counters.get("sprites"), 75 * this.mod, 17 * this.mod);
+
+ //this.font.fillStyle.copy("blue");
+ this.font.draw(renderer, "Velocity : " + this.counters.get("velocity"), 150 * this.mod, 17 * this.mod);
+
+ //this.font.fillStyle.copy("orange");
+ this.font.draw(renderer, "Bounds : " + this.counters.get("bounds"), 225 * this.mod, 17 * this.mod);
+
+ //this.font.fillStyle.copy("purple");
+ this.font.draw(renderer, "Children : " + this.counters.get("children"), 325 * this.mod, 17 * this.mod);
+
+ // Reset font style
+ //this.font.setFont("courier", this.font_size, "white");
+
+ // draw the memory heap usage
+ var endX = this.width - 5;
+ this.drawMemoryGraph(renderer, endX - this.help_str_len);
+
+ this.font.textAlign = "right";
+
+ // some help string
+ this.font.draw(renderer, this.help_str, endX, 17 * this.mod);
+
+ //fps counter
+ var fps_str = me.timer.fps + "/" + me.sys.fps + " fps";
+ this.font.draw(renderer, fps_str, endX, 2 * this.mod);
+
+ renderer.restore();
+ },
+
+ /** @private */
+ onDestroyEvent : function () {
+ // hide the panel
+ this.hide();
+ // unbind keys event
+ me.input.unbindKey(me.input.KEY.S);
+ me.input.unbindKey(me.input.KEY.H);
+ me.event.unsubscribe(this.keyHandler);
+ }
+ });
+
+ /**
+ * @class
+ * @public
+ * @extends me.plugin.Base
+ * @memberOf me
+ * @constructor
+ */
+ me.debug.Panel = me.plugin.Base.extend(
+ /** @scope me.debug.Panel.prototype */
+ {
+ /** @private */
+ init : function (showKey, hideKey) {
+ // call the super constructor
+ this._super(me.plugin.Base, "init");
+ this.panel = new DebugPanel(showKey, hideKey);
+
+ // if "#debug" is present in the URL
+ if (me.game.HASH.debug === true) {
+ this.show();
+ } // else keep it hidden
+ },
+
+ /** @private */
+ show : function () {
+ this.panel.show();
+ },
+
+ /** @private */
+ hide : function () {
+ this.panel.hide();
+ }
+ });
+
+ // automatically register the debug panel
+ window.onReady(function () {
+ me.plugin.register.defer(this, me.debug.Panel, "debugPanel");
+ });
+
+ /*---------------------------------------------------------*/
+ // END END END
+ /*---------------------------------------------------------*/
+})();
diff --git a/Games/Contra-Shooter/main.js b/Games/Contra-Shooter/main.js
new file mode 100644
index 0000000000..9c0bdceda6
--- /dev/null
+++ b/Games/Contra-Shooter/main.js
@@ -0,0 +1,48 @@
+const electron = require('electron');
+const app = electron.app; // Module to control application life.
+const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
+
+const path = require('path');
+const url = require('url');
+
+// Keep a global reference of the window object, if you don't, the window will
+// be closed automatically when the javascript object is GCed.
+let mainWindow;
+
+// Quit when all windows are closed.
+app.on('window-all-closed', () => {
+ if (process.platform != 'darwin')
+ app.quit();
+});
+
+function createWindow() {
+ // Create the browser window.
+ mainWindow = new BrowserWindow({ width: 960, height: 660 });
+
+ // and load the index.html of the app.
+ mainWindow.loadURL(url.format({
+ pathname: path.join(__dirname, 'index.html'),
+ protocol: 'file:',
+ slashes: true
+ }));
+
+ // Emitted when the window is closed.
+ mainWindow.on('closed', () => {
+ // Dereference the window object, usually you would store windows
+ // in an array if your app supports multi windows, this is the time
+ // when you should delete the corresponding element.
+ mainWindow = null;
+ });
+}
+
+// This method will be called when atom-shell has done everything
+// initialization and ready for creating browser windows.
+app.on('ready', createWindow);
+
+app.on('activate', function () {
+ // On OS X it's common to re-create a window in the app when the
+ // dock icon is clicked and there are no other windows open.
+ if (mainWindow === null) {
+ createWindow();
+ }
+});
diff --git a/Games/Contra-Shooter/manifest.json b/Games/Contra-Shooter/manifest.json
new file mode 100644
index 0000000000..eaa10a7bd9
--- /dev/null
+++ b/Games/Contra-Shooter/manifest.json
@@ -0,0 +1,27 @@
+{
+ "short_name": "contra2000",
+ "name": "Contra 2000",
+ "orientation": "landscape",
+ "developer": {
+ "name": "chris",
+ "url": "https://github.com/chrisDaniel"
+ },
+ "start_url": "./index.html",
+ "display": "fullscreen",
+ "icons": [{
+ "src": "/icons/touch-icon-iphone-60x60.png",
+ "sizes": "60x60"
+ },{
+ "src": "/icons/touch-icon-ipad-76x76.png",
+ "sizes": "76x76"
+ },{
+ "src": "/icons/touch-icon-iphone-retina-120x120",
+ "sizes": "120x120"
+ },{
+ "src": "/icons/touch-icon-marketplace-128x128",
+ "sizes": "128x128"
+ },{
+ "src": "/icons/touch-icon-ipad-retina-152x152",
+ "sizes": "152x152"
+ }]
+}
diff --git a/Games/Contra-Shooter/package-lock.json b/Games/Contra-Shooter/package-lock.json
new file mode 100644
index 0000000000..850a135f4b
--- /dev/null
+++ b/Games/Contra-Shooter/package-lock.json
@@ -0,0 +1,3319 @@
+{
+ "name": "contra2000",
+ "version": "2.1.3",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
+ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.18",
+ "negotiator": "0.6.1"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
+ "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.22",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
+ "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.38.0"
+ }
+ }
+ }
+ },
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2",
+ "longest": "^1.0.1",
+ "repeat-string": "^1.5.2"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "applause": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/applause/-/applause-1.2.2.tgz",
+ "integrity": "sha1-qEaFeegfZzl7tWNMKZU77c0PVsA=",
+ "dev": true,
+ "requires": {
+ "cson-parser": "^1.1.0",
+ "js-yaml": "^3.3.0",
+ "lodash": "^3.10.0"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+ "dev": true
+ }
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ }
+ }
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "asar": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/asar/-/asar-0.8.3.tgz",
+ "integrity": "sha1-wuA/kFRRbbv1Z1noVOnOjRqdOdM=",
+ "dev": true,
+ "requires": {
+ "chromium-pickle-js": "0.1.0",
+ "commander": "2.3.0",
+ "cuint": "0.1.5",
+ "glob": "^5.0.5",
+ "minimatch": "2.0.4",
+ "mkdirp": "^0.5.0",
+ "mksnapshot": "0.1.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.4.tgz",
+ "integrity": "sha1-g76hFYA+egl6eAIkJyh+23Yvr+0=",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.0.0"
+ }
+ }
+ }
+ },
+ "asn1": {
+ "version": "0.1.11",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz",
+ "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=",
+ "dev": true
+ },
+ "assert-plus": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz",
+ "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz",
+ "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+ "dev": true
+ },
+ "binary": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
+ "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
+ "dev": true,
+ "requires": {
+ "buffers": "~0.1.1",
+ "chainsaw": "~0.1.0"
+ }
+ },
+ "bl": {
+ "version": "0.9.5",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz",
+ "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~1.0.26"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ }
+ }
+ },
+ "bluebird": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
+ "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=",
+ "dev": true
+ },
+ "body": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz",
+ "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=",
+ "dev": true,
+ "requires": {
+ "continuable-cache": "^0.3.1",
+ "error": "^7.0.0",
+ "raw-body": "~1.1.0",
+ "safe-json-parse": "~1.0.1"
+ }
+ },
+ "boom": {
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
+ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
+ "dev": true,
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+ "dev": true,
+ "requires": {
+ "pako": "~0.2.0"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "buffers": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
+ "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=",
+ "dev": true
+ },
+ "bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
+ "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "caseless": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz",
+ "integrity": "sha1-t7Zc5r8UE4hlOc/VM/CzDv+pz4g=",
+ "dev": true
+ },
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "dev": true,
+ "requires": {
+ "align-text": "^0.1.3",
+ "lazy-cache": "^1.0.3"
+ }
+ },
+ "chainsaw": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
+ "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
+ "dev": true,
+ "requires": {
+ "traverse": ">=0.3.0 <0.4"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chromium-pickle-js": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.1.0.tgz",
+ "integrity": "sha1-HUixB9ghJqLz4hHC6iX4A7pVGyE=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "dev": true,
+ "requires": {
+ "center-align": "^0.1.1",
+ "right-align": "^0.1.1",
+ "wordwrap": "0.0.2"
+ }
+ },
+ "coffeescript": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
+ "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
+ "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "0.0.5"
+ }
+ },
+ "commander": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
+ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "connect": {
+ "version": "3.6.6",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
+ "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.0",
+ "parseurl": "~1.3.2",
+ "utils-merge": "1.0.1"
+ }
+ },
+ "connect-livereload": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.5.4.tgz",
+ "integrity": "sha1-gBV9E3HJ83zBQDmrGJWXDRGdw7w=",
+ "dev": true
+ },
+ "continuable-cache": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz",
+ "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "cryptiles": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
+ "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
+ "dev": true,
+ "requires": {
+ "boom": "2.x.x"
+ }
+ },
+ "cson-parser": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz",
+ "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=",
+ "dev": true,
+ "requires": {
+ "coffee-script": "^1.10.0"
+ },
+ "dependencies": {
+ "coffee-script": {
+ "version": "1.12.7",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
+ "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==",
+ "dev": true
+ }
+ }
+ },
+ "ctype": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz",
+ "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=",
+ "dev": true
+ },
+ "cuint": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.1.5.tgz",
+ "integrity": "sha1-uEixhGbz8YD5bR624HzLfs8Sai4=",
+ "dev": true
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "dateformat": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.3.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "decompress-zip": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.1.0.tgz",
+ "integrity": "sha1-vOYMEWZPLWYPykvPY0r23l1sFMc=",
+ "dev": true,
+ "requires": {
+ "binary": "^0.3.0",
+ "graceful-fs": "^3.0.0",
+ "mkpath": "^0.1.0",
+ "nopt": "^3.0.1",
+ "q": "^1.1.2",
+ "readable-stream": "^1.1.8",
+ "touch": "0.0.3"
+ },
+ "dependencies": {
+ "graceful-fs": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
+ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
+ "dev": true,
+ "requires": {
+ "natives": "^1.1.0"
+ }
+ }
+ }
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "delayed-stream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz",
+ "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "error": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz",
+ "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=",
+ "dev": true,
+ "requires": {
+ "string-template": "~0.2.1",
+ "xtend": "~4.0.0"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+ "dev": true
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "faye-websocket": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "file-sync-cmp": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz",
+ "integrity": "sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs=",
+ "dev": true
+ },
+ "finalhandler": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
+ "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.1",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.3.1",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+ "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+ "dev": true,
+ "requires": {
+ "glob": "~5.0.0"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz",
+ "integrity": "sha1-Jvi8JtpkQOKZy9z7aQNcT3em5GY=",
+ "dev": true,
+ "requires": {
+ "async": "~0.9.0",
+ "combined-stream": "~0.0.4",
+ "mime-types": "~2.0.3"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
+ "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
+ "dev": true
+ }
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "0.18.2",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.18.2.tgz",
+ "integrity": "sha1-rwXKcCsLbfp96AOh96tHnsXCFSU=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^3.0.5",
+ "jsonfile": "^2.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "dependencies": {
+ "graceful-fs": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
+ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
+ "dev": true,
+ "requires": {
+ "natives": "^1.1.0"
+ }
+ }
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "gaze": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
+ "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+ "dev": true,
+ "requires": {
+ "globule": "^1.0.0"
+ }
+ },
+ "generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "dev": true,
+ "requires": {
+ "is-property": "^1.0.2"
+ }
+ },
+ "generate-object-property": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
+ "dev": true,
+ "requires": {
+ "is-property": "^1.0.0"
+ }
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+ "dev": true
+ },
+ "github-releases": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/github-releases/-/github-releases-0.3.2.tgz",
+ "integrity": "sha1-lFCHdRhaXrFuimt4fGTAIlAFquI=",
+ "dev": true,
+ "requires": {
+ "grunt": "0.4",
+ "minimatch": "0.2.12",
+ "optimist": "~0.4.0",
+ "prettyjson": "^1.1.3",
+ "request": "~2.51.0"
+ },
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=",
+ "dev": true,
+ "requires": {
+ "underscore": "~1.7.0",
+ "underscore.string": "~2.4.0"
+ },
+ "dependencies": {
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz",
+ "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=",
+ "dev": true
+ }
+ }
+ },
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz",
+ "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=",
+ "dev": true
+ },
+ "boom": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz",
+ "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=",
+ "dev": true,
+ "requires": {
+ "hoek": "0.9.x"
+ }
+ },
+ "caseless": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.8.0.tgz",
+ "integrity": "sha1-W8oogdQUN/VLJAfr40iIx7mtT30=",
+ "dev": true
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz",
+ "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=",
+ "dev": true
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
+ "dev": true
+ },
+ "cryptiles": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz",
+ "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=",
+ "dev": true,
+ "requires": {
+ "boom": "0.4.x"
+ }
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz",
+ "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
+ "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=",
+ "dev": true
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=",
+ "dev": true,
+ "requires": {
+ "glob": "~3.2.9",
+ "lodash": "~2.4.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
+ "dev": true,
+ "requires": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ }
+ }
+ },
+ "forever-agent": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz",
+ "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=",
+ "dev": true
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "~1.2.0",
+ "inherits": "1",
+ "minimatch": "~0.2.11"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
+ "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=",
+ "dev": true
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
+ "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=",
+ "dev": true
+ },
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=",
+ "dev": true,
+ "requires": {
+ "async": "~0.1.22",
+ "coffee-script": "~1.3.3",
+ "colors": "~0.6.2",
+ "dateformat": "1.0.2-1.2.3",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.1.2",
+ "getobject": "~0.1.0",
+ "glob": "~3.1.21",
+ "grunt-legacy-log": "~0.1.0",
+ "grunt-legacy-util": "~0.2.0",
+ "hooker": "~0.2.3",
+ "iconv-lite": "~0.2.11",
+ "js-yaml": "~2.0.5",
+ "lodash": "~0.9.2",
+ "minimatch": "~0.2.12",
+ "nopt": "~1.0.10",
+ "rimraf": "~2.2.8",
+ "underscore.string": "~2.2.1",
+ "which": "~1.0.5"
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz",
+ "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=",
+ "dev": true,
+ "requires": {
+ "colors": "~0.6.2",
+ "grunt-legacy-log-utils": "~0.1.1",
+ "hooker": "~0.2.3",
+ "lodash": "~2.4.1",
+ "underscore.string": "~2.3.3"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
+ "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz",
+ "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=",
+ "dev": true,
+ "requires": {
+ "colors": "~0.6.2",
+ "lodash": "~2.4.1",
+ "underscore.string": "~2.3.3"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
+ "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz",
+ "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=",
+ "dev": true,
+ "requires": {
+ "async": "~0.1.22",
+ "exit": "~0.1.1",
+ "getobject": "~0.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~0.9.2",
+ "underscore.string": "~2.2.1",
+ "which": "~1.0.5"
+ }
+ },
+ "hawk": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz",
+ "integrity": "sha1-h81JH5tG5OKurKM1QWdmiF0tHtk=",
+ "dev": true,
+ "requires": {
+ "boom": "0.4.x",
+ "cryptiles": "0.2.x",
+ "hoek": "0.9.x",
+ "sntp": "0.2.x"
+ }
+ },
+ "hoek": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz",
+ "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
+ "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=",
+ "dev": true,
+ "requires": {
+ "argparse": "~ 0.1.11",
+ "esprima": "~ 1.0.2"
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz",
+ "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz",
+ "integrity": "sha1-mVrhOSq4r/y/yyZB3QVOlDwNXc4=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.12.tgz",
+ "integrity": "sha1-6oKgEqxmLH3fqhRPHBR+aUb12vs=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "oauth-sign": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.5.0.tgz",
+ "integrity": "sha1-12f1FpMlYg6rLgh+8MRy53PbZGE=",
+ "dev": true
+ },
+ "qs": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz",
+ "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=",
+ "dev": true
+ },
+ "request": {
+ "version": "2.51.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.51.0.tgz",
+ "integrity": "sha1-NdALvswBLlX5B7G9ng29V3v+8m4=",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.5.0",
+ "bl": "~0.9.0",
+ "caseless": "~0.8.0",
+ "combined-stream": "~0.0.5",
+ "forever-agent": "~0.5.0",
+ "form-data": "~0.2.0",
+ "hawk": "1.1.1",
+ "http-signature": "~0.10.0",
+ "json-stringify-safe": "~5.0.0",
+ "mime-types": "~1.0.1",
+ "node-uuid": "~1.4.0",
+ "oauth-sign": "~0.5.0",
+ "qs": "~2.3.1",
+ "stringstream": "~0.0.4",
+ "tough-cookie": ">=0.12.0",
+ "tunnel-agent": "~0.4.0"
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+ "dev": true
+ },
+ "sntp": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz",
+ "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=",
+ "dev": true,
+ "requires": {
+ "hoek": "0.9.x"
+ }
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz",
+ "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz",
+ "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=",
+ "dev": true
+ }
+ }
+ },
+ "glob": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "globule": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
+ "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
+ "dev": true,
+ "requires": {
+ "glob": "~7.1.1",
+ "lodash": "~4.17.10",
+ "minimatch": "~3.0.2"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "grunt": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz",
+ "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==",
+ "dev": true,
+ "requires": {
+ "coffeescript": "~1.10.0",
+ "dateformat": "~1.0.12",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.3.0",
+ "glob": "~7.0.0",
+ "grunt-cli": "~1.2.0",
+ "grunt-known-options": "~1.1.0",
+ "grunt-legacy-log": "~2.0.0",
+ "grunt-legacy-util": "~1.1.1",
+ "iconv-lite": "~0.4.13",
+ "js-yaml": "~3.13.0",
+ "minimatch": "~3.0.2",
+ "mkdirp": "~0.5.1",
+ "nopt": "~3.0.6",
+ "path-is-absolute": "~1.0.0",
+ "rimraf": "~2.6.2"
+ },
+ "dependencies": {
+ "grunt-cli": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+ "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "~0.3.0",
+ "grunt-known-options": "~1.1.0",
+ "nopt": "~3.0.6",
+ "resolve": "~1.1.0"
+ }
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-asar": {
+ "version": "github:bwin/grunt-asar#7406ecbfb16817d0af6ca1a737f74a8acc2f8c61",
+ "from": "github:bwin/grunt-asar#update-asar",
+ "dev": true,
+ "requires": {
+ "asar": "^0.8.3",
+ "glob": "^6.0.1",
+ "mkdirp": "^0.5.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
+ "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
+ "dev": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "grunt-contrib-clean": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-1.1.0.tgz",
+ "integrity": "sha1-Vkq/LQN4qYOhW54/MO51tzjEBjg=",
+ "dev": true,
+ "requires": {
+ "async": "^1.5.2",
+ "rimraf": "^2.5.1"
+ }
+ },
+ "grunt-contrib-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz",
+ "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "source-map": "^0.5.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-connect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-connect/-/grunt-contrib-connect-1.0.2.tgz",
+ "integrity": "sha1-XPkzuRpnOGBEJzwLJERgPNmIebo=",
+ "dev": true,
+ "requires": {
+ "async": "^1.5.2",
+ "connect": "^3.4.0",
+ "connect-livereload": "^0.5.0",
+ "http2": "^3.3.4",
+ "morgan": "^1.6.1",
+ "opn": "^4.0.0",
+ "portscanner": "^1.0.0",
+ "serve-index": "^1.7.1",
+ "serve-static": "^1.10.0"
+ }
+ },
+ "grunt-contrib-copy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz",
+ "integrity": "sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.1",
+ "file-sync-cmp": "^0.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-uglify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-2.3.0.tgz",
+ "integrity": "sha1-s9AmDr3WzvoS/y+Onh4ln33kIW8=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "maxmin": "^1.1.0",
+ "object.assign": "^4.0.4",
+ "uglify-js": "~2.8.21",
+ "uri-path": "^1.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz",
+ "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.0",
+ "gaze": "^1.1.0",
+ "lodash": "^4.17.10",
+ "tiny-lr": "^1.1.1"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ }
+ }
+ },
+ "grunt-download-electron": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/grunt-download-electron/-/grunt-download-electron-2.1.4.tgz",
+ "integrity": "sha1-BVbwoLDs9qW21hNy85CNxz1RIcU=",
+ "dev": true,
+ "requires": {
+ "decompress-zip": "0.0.4",
+ "github-releases": "0.3.2",
+ "grunt": "0.4",
+ "progress": "1.1.2",
+ "wrench": "1.5.4"
+ },
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=",
+ "dev": true,
+ "requires": {
+ "underscore": "~1.7.0",
+ "underscore.string": "~2.4.0"
+ },
+ "dependencies": {
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz",
+ "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=",
+ "dev": true
+ }
+ }
+ },
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz",
+ "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=",
+ "dev": true
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz",
+ "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=",
+ "dev": true
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
+ "dev": true
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz",
+ "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=",
+ "dev": true
+ },
+ "decompress-zip": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.0.4.tgz",
+ "integrity": "sha1-xWJDH3aviXCvr1AibkLhxHpXsIY=",
+ "dev": true,
+ "requires": {
+ "binary": "~0.3.0",
+ "mkpath": "~0.1.0",
+ "nopt": "~2.1.2",
+ "q": "~0.9.6",
+ "readable-stream": "~1.1.8",
+ "touch": "0.0.2"
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
+ "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=",
+ "dev": true
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=",
+ "dev": true,
+ "requires": {
+ "glob": "~3.2.9",
+ "lodash": "~2.4.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
+ "dev": true,
+ "requires": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ }
+ }
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "~1.2.0",
+ "inherits": "1",
+ "minimatch": "~0.2.11"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
+ "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=",
+ "dev": true
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
+ "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=",
+ "dev": true
+ },
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=",
+ "dev": true,
+ "requires": {
+ "async": "~0.1.22",
+ "coffee-script": "~1.3.3",
+ "colors": "~0.6.2",
+ "dateformat": "1.0.2-1.2.3",
+ "eventemitter2": "~0.4.13",
+ "exit": "~0.1.1",
+ "findup-sync": "~0.1.2",
+ "getobject": "~0.1.0",
+ "glob": "~3.1.21",
+ "grunt-legacy-log": "~0.1.0",
+ "grunt-legacy-util": "~0.2.0",
+ "hooker": "~0.2.3",
+ "iconv-lite": "~0.2.11",
+ "js-yaml": "~2.0.5",
+ "lodash": "~0.9.2",
+ "minimatch": "~0.2.12",
+ "nopt": "~1.0.10",
+ "rimraf": "~2.2.8",
+ "underscore.string": "~2.2.1",
+ "which": "~1.0.5"
+ },
+ "dependencies": {
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz",
+ "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=",
+ "dev": true,
+ "requires": {
+ "colors": "~0.6.2",
+ "grunt-legacy-log-utils": "~0.1.1",
+ "hooker": "~0.2.3",
+ "lodash": "~2.4.1",
+ "underscore.string": "~2.3.3"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
+ "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz",
+ "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=",
+ "dev": true,
+ "requires": {
+ "colors": "~0.6.2",
+ "lodash": "~2.4.1",
+ "underscore.string": "~2.3.3"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz",
+ "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz",
+ "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=",
+ "dev": true,
+ "requires": {
+ "async": "~0.1.22",
+ "exit": "~0.1.1",
+ "getobject": "~0.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~0.9.2",
+ "underscore.string": "~2.2.1",
+ "which": "~1.0.5"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
+ "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=",
+ "dev": true,
+ "requires": {
+ "argparse": "~ 0.1.11",
+ "esprima": "~ 1.0.2"
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz",
+ "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ },
+ "nopt": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz",
+ "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "q": {
+ "version": "0.9.7",
+ "resolved": "https://registry.npmjs.org/q/-/q-0.9.7.tgz",
+ "integrity": "sha1-TeLmyzspCIyeTLwDv51C+5bOL3U=",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+ "dev": true
+ },
+ "touch": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.2.tgz",
+ "integrity": "sha1-plp3d5Xly74SmUmb3EIoH/shtfQ=",
+ "dev": true,
+ "requires": {
+ "nopt": "~1.0.10"
+ },
+ "dependencies": {
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ }
+ }
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz",
+ "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz",
+ "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-known-options": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz",
+ "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==",
+ "dev": true
+ },
+ "grunt-legacy-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz",
+ "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==",
+ "dev": true,
+ "requires": {
+ "colors": "~1.1.2",
+ "grunt-legacy-log-utils": "~2.0.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.5"
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz",
+ "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==",
+ "dev": true,
+ "requires": {
+ "chalk": "~2.4.1",
+ "lodash": "~4.17.10"
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz",
+ "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==",
+ "dev": true,
+ "requires": {
+ "async": "~1.5.2",
+ "exit": "~0.1.1",
+ "getobject": "~0.1.0",
+ "hooker": "~0.2.3",
+ "lodash": "~4.17.10",
+ "underscore.string": "~3.3.4",
+ "which": "~1.3.0"
+ }
+ },
+ "grunt-processhtml": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/grunt-processhtml/-/grunt-processhtml-0.4.2.tgz",
+ "integrity": "sha512-kOEBUHmDKauUcg0q5FMJbhCTJajzdCEcREhnNzfCehYpKNBiOKD8CMz24lJEOKECXL+ekUux7a0/F/XgRSBcbA==",
+ "dev": true,
+ "requires": {
+ "async": "^1.5.2",
+ "htmlprocessor": "^0.2.4",
+ "lodash": "^4.17.5"
+ }
+ },
+ "grunt-replace": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-replace/-/grunt-replace-1.0.1.tgz",
+ "integrity": "sha1-kKeVMvuJBB/kJ8h9QlI4sPiGZRo=",
+ "dev": true,
+ "requires": {
+ "applause": "1.2.2",
+ "chalk": "^1.1.0",
+ "file-sync-cmp": "^0.1.0",
+ "lodash": "^4.11.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "gzip-size": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz",
+ "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=",
+ "dev": true,
+ "requires": {
+ "browserify-zlib": "^0.1.4",
+ "concat-stream": "^1.4.1"
+ }
+ },
+ "har-validator": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-1.8.0.tgz",
+ "integrity": "sha1-2DhCsOtMQ1lgrrEIoGejqpTA7rI=",
+ "dev": true,
+ "requires": {
+ "bluebird": "^2.9.30",
+ "chalk": "^1.0.0",
+ "commander": "^2.8.1",
+ "is-my-json-valid": "^2.12.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
+ "hawk": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz",
+ "integrity": "sha1-HnMc45RH+h0PbXB/e87r7A/R7B8=",
+ "dev": true,
+ "requires": {
+ "boom": "2.x.x",
+ "cryptiles": "2.x.x",
+ "hoek": "2.x.x",
+ "sntp": "1.x.x"
+ }
+ },
+ "hoek": {
+ "version": "2.16.3",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
+ "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
+ "dev": true
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "htmlprocessor": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/htmlprocessor/-/htmlprocessor-0.2.6.tgz",
+ "integrity": "sha1-rJ9HfsU3g7jXprZ9e2w1HqXXPTU=",
+ "dev": true,
+ "requires": {
+ "lodash": "~2.4.1"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
+ "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=",
+ "dev": true
+ }
+ }
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "dependencies": {
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "dev": true
+ }
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz",
+ "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==",
+ "dev": true
+ },
+ "http-signature": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz",
+ "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=",
+ "dev": true,
+ "requires": {
+ "asn1": "0.1.11",
+ "assert-plus": "^0.1.5",
+ "ctype": "0.5.3"
+ }
+ },
+ "http2": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/http2/-/http2-3.3.7.tgz",
+ "integrity": "sha512-puSi8M8WNlFJm9Pk4c/Mbz9Gwparuj3gO9/RRO5zv6piQ0FY+9Qywp0PdWshYgsMJSalixFY7eC6oPu0zRxLAQ==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-my-ip-valid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz",
+ "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==",
+ "dev": true
+ },
+ "is-my-json-valid": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz",
+ "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==",
+ "dev": true,
+ "requires": {
+ "generate-function": "^2.0.0",
+ "generate-object-property": "^1.1.0",
+ "is-my-ip-valid": "^1.0.0",
+ "jsonpointer": "^4.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
+ "dev": true
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonpointer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
+ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+ "dev": true
+ },
+ "livereload-js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz",
+ "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+ "dev": true
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
+ "dev": true
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "maxmin": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz",
+ "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0",
+ "figures": "^1.0.1",
+ "gzip-size": "^1.0.0",
+ "pretty-bytes": "^1.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ }
+ },
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.12.0.tgz",
+ "integrity": "sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc=",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.14.tgz",
+ "integrity": "sha1-MQ4VnbI+B3+Lsit0jav6SVcUCqY=",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.12.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ }
+ }
+ },
+ "mkpath": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz",
+ "integrity": "sha1-dVSm+Nhxg0zJe1RisSLEwSTW3pE=",
+ "dev": true
+ },
+ "mksnapshot": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/mksnapshot/-/mksnapshot-0.1.0.tgz",
+ "integrity": "sha1-99CavKgGrYw3gNpwG7GHeNfOaaw=",
+ "dev": true,
+ "requires": {
+ "decompress-zip": "0.1.0",
+ "fs-extra": "0.18.2",
+ "request": "2.55.0"
+ }
+ },
+ "morgan": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
+ "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==",
+ "dev": true,
+ "requires": {
+ "basic-auth": "~2.0.0",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.1"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "natives": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz",
+ "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==",
+ "dev": true
+ },
+ "negotiator": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
+ "dev": true
+ },
+ "node-uuid": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+ "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
+ "dev": true
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.6.0.tgz",
+ "integrity": "sha1-fb6uRPbKRU4fFoRR1jB0ZzWBPOM=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "opn": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz",
+ "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "optimist": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.4.0.tgz",
+ "integrity": "sha1-y47Dfy/jqphky2eidSUOfhliCiU=",
+ "dev": true,
+ "requires": {
+ "wordwrap": "~0.0.2"
+ }
+ },
+ "pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "portscanner": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-1.2.0.tgz",
+ "integrity": "sha1-sUu9olfRTDEPqcwJaCrwLUCWGAI=",
+ "dev": true,
+ "requires": {
+ "async": "1.5.2"
+ }
+ },
+ "pretty-bytes": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
+ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.1.0"
+ }
+ },
+ "prettyjson": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz",
+ "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=",
+ "dev": true,
+ "requires": {
+ "colors": "^1.1.2",
+ "minimist": "^1.2.0"
+ }
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true
+ },
+ "progress": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.2.tgz",
+ "integrity": "sha1-h/28fHanhAIIl7XpZlVUsF/FjNE=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.1.31",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+ "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+ "dev": true
+ },
+ "qs": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz",
+ "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=",
+ "dev": true
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz",
+ "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=",
+ "dev": true,
+ "requires": {
+ "bytes": "1",
+ "string_decoder": "0.10"
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ }
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "request": {
+ "version": "2.55.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.55.0.tgz",
+ "integrity": "sha1-11wc32eddrsQD5v/4f5VG1wk6T0=",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.5.0",
+ "bl": "~0.9.0",
+ "caseless": "~0.9.0",
+ "combined-stream": "~0.0.5",
+ "forever-agent": "~0.6.0",
+ "form-data": "~0.2.0",
+ "har-validator": "^1.4.0",
+ "hawk": "~2.3.0",
+ "http-signature": "~0.10.0",
+ "isstream": "~0.1.1",
+ "json-stringify-safe": "~5.0.0",
+ "mime-types": "~2.0.1",
+ "node-uuid": "~1.4.0",
+ "oauth-sign": "~0.6.0",
+ "qs": "~2.4.0",
+ "stringstream": "~0.0.4",
+ "tough-cookie": ">=0.12.0",
+ "tunnel-agent": "~0.4.0"
+ }
+ },
+ "resolve": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "dev": true,
+ "requires": {
+ "align-text": "^0.1.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safe-json-parse": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz",
+ "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
+ "dev": true
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "dependencies": {
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ }
+ }
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "mime-db": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
+ "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.22",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
+ "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.38.0"
+ }
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "sntp": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
+ "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
+ "dev": true,
+ "requires": {
+ "hoek": "2.x.x"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
+ "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+ "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
+ "dev": true
+ },
+ "string-template": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
+ "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "stringstream": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
+ "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "tiny-lr": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz",
+ "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==",
+ "dev": true,
+ "requires": {
+ "body": "^5.1.0",
+ "debug": "^3.1.0",
+ "faye-websocket": "~0.10.0",
+ "livereload-js": "^2.3.0",
+ "object-assign": "^4.1.0",
+ "qs": "^6.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+ "dev": true
+ }
+ }
+ },
+ "touch": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz",
+ "integrity": "sha1-Ua7z1ElXHU8oel2Hyci0kYGg2x0=",
+ "dev": true,
+ "requires": {
+ "nopt": "~1.0.10"
+ },
+ "dependencies": {
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ }
+ }
+ },
+ "tough-cookie": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
+ "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+ "dev": true,
+ "requires": {
+ "ip-regex": "^2.1.0",
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "traverse": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
+ "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=",
+ "dev": true
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
+ "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.5.1",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.10.0"
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "dev": true,
+ "optional": true
+ },
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
+ "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=",
+ "dev": true
+ },
+ "underscore.string": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
+ "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "^1.0.3",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "uri-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
+ "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
+ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.4.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
+ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "wrench": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.4.tgz",
+ "integrity": "sha1-Lo2dPbNWjMHAAaTI3OyncuXSFkM=",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^1.0.2",
+ "cliui": "^2.1.0",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "dev": true
+ }
+ }
+ }
+ }
+}
diff --git a/Games/Contra-Shooter/package.json b/Games/Contra-Shooter/package.json
new file mode 100644
index 0000000000..199efc29df
--- /dev/null
+++ b/Games/Contra-Shooter/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "contra2000",
+ "version": "2.1.3",
+ "main": "main.js",
+ "devDependencies": {
+ "grunt": "^1.0.1",
+ "grunt-asar": "bwin/grunt-asar#update-asar",
+ "grunt-contrib-clean": "^1.0.0",
+ "grunt-contrib-concat": "^1.0.1",
+ "grunt-contrib-connect": "^1.0.2",
+ "grunt-contrib-copy": "^1.0.0",
+ "grunt-contrib-uglify": "^2.0.0",
+ "grunt-contrib-watch": "^1.0.0",
+ "grunt-download-electron": "^2.1.4",
+ "grunt-processhtml": "^0.4.1",
+ "grunt-replace": "^1.0.1"
+ }
+}
diff --git a/Games/Contra-Shooter/tasks/grunt-resources.js b/Games/Contra-Shooter/tasks/grunt-resources.js
new file mode 100644
index 0000000000..8080e1f418
--- /dev/null
+++ b/Games/Contra-Shooter/tasks/grunt-resources.js
@@ -0,0 +1,43 @@
+module.exports = function (grunt) {
+ function task() {
+ var path = require("path"),
+ options = this.options({
+ "varname" : "game.resources"
+ });
+ res = [],
+ audio = {};
+
+ grunt.log.debug("options: " + JSON.stringify(options));
+ grunt.log.debug("files: " + JSON.stringify(this.files));
+
+ this.files.forEach(function (file) {
+ file.src.forEach(function (src) {
+ var name = path.basename(src, path.extname(src));
+ if ((file.type !== "audio") || (!audio.hasOwnProperty(name))) {
+ if (file.type === "audio") {
+ audio[name] = true;
+ }
+ res.push({
+ "name" : name,
+ "type" : file.type,
+ "src" : (
+ file.type === "audio" ?
+ path.dirname(src) + "/" :
+ src
+ )
+ });
+ }
+ });
+ });
+
+ grunt.log.debug(JSON.stringify(res));
+
+ grunt.file.write(
+ options.dest,
+ options.varname + " = " + JSON.stringify(res, null, 4) + ";"
+ );
+ grunt.log.ok(options.dest)
+ }
+
+ grunt.registerMultiTask("resources", "Build melonJS resources.js", task);
+};
diff --git a/README.md b/README.md
index 02880e98e7..6290c38336 100644
--- a/README.md
+++ b/README.md
@@ -200,6 +200,7 @@ This repository also provides one such platforms where contributers come over an
| [Maze_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Maze_Game) | [TriHand_Tactics](https://github.com/kunjgit/GameZone/tree/main/Games/TriHand_Tactics) | [Earth_Guardian](https://github.com/kunjgit/GameZone/tree/main/Games/Earth_Guardian) | [Ball_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Ball_Shooting_Game) |
| [Ball_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Ball_Shooting_Game) | [CatchTheBall](https://github.com/kunjgit/GameZone/tree/main/Games/CatchTheBall) |
+| [Contra-Shooter](https://github.com/VesperAkshay/GameZone/tree/main/Games/Contra-Shooter)|
| [Memory_Cards_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Memory_Cards_Game) |
@@ -208,6 +209,7 @@ This repository also provides one such platforms where contributers come over an
| [Rock_paper_scissor](https://github.com/kunjgit/GameZone/tree/main/Games/Rock_paper_scissor) |
+
diff --git a/assets/images/Contra-Shooter.png b/assets/images/Contra-Shooter.png
new file mode 100644
index 0000000000..dde2203b9c
Binary files /dev/null and b/assets/images/Contra-Shooter.png differ
diff --git a/assets/images/PressStart2P.png b/assets/images/PressStart2P.png
new file mode 100644
index 0000000000..73766690aa
Binary files /dev/null and b/assets/images/PressStart2P.png differ
diff --git a/assets/images/bad-guys.png b/assets/images/bad-guys.png
new file mode 100644
index 0000000000..bad714b4bc
Binary files /dev/null and b/assets/images/bad-guys.png differ
diff --git a/assets/images/bill-lance.png b/assets/images/bill-lance.png
new file mode 100644
index 0000000000..7e66088d5e
Binary files /dev/null and b/assets/images/bill-lance.png differ
diff --git a/assets/images/contra-tile-map.png b/assets/images/contra-tile-map.png
new file mode 100644
index 0000000000..6182a13966
Binary files /dev/null and b/assets/images/contra-tile-map.png differ
diff --git a/assets/images/explosion-sprite.png b/assets/images/explosion-sprite.png
new file mode 100644
index 0000000000..118beb66f1
Binary files /dev/null and b/assets/images/explosion-sprite.png differ
diff --git a/assets/images/icon_badge_24_48.png b/assets/images/icon_badge_24_48.png
new file mode 100644
index 0000000000..871702a3bd
Binary files /dev/null and b/assets/images/icon_badge_24_48.png differ
diff --git a/assets/images/icon_select_30_25.png b/assets/images/icon_select_30_25.png
new file mode 100644
index 0000000000..597987de88
Binary files /dev/null and b/assets/images/icon_select_30_25.png differ
diff --git a/assets/images/img_captured.png b/assets/images/img_captured.png
new file mode 100644
index 0000000000..629990969d
Binary files /dev/null and b/assets/images/img_captured.png differ
diff --git a/assets/images/img_neslogo.png b/assets/images/img_neslogo.png
new file mode 100644
index 0000000000..914c130fb9
Binary files /dev/null and b/assets/images/img_neslogo.png differ
diff --git a/assets/images/img_splashpage.png b/assets/images/img_splashpage.png
new file mode 100644
index 0000000000..6b110ca5a2
Binary files /dev/null and b/assets/images/img_splashpage.png differ
diff --git a/assets/images/power-ups-sprite.png b/assets/images/power-ups-sprite.png
new file mode 100644
index 0000000000..fd417d8bae
Binary files /dev/null and b/assets/images/power-ups-sprite.png differ
diff --git a/assets/images/rock_platform-sprite.png b/assets/images/rock_platform-sprite.png
new file mode 100644
index 0000000000..c66433d45f
Binary files /dev/null and b/assets/images/rock_platform-sprite.png differ
diff --git a/assets/images/touch-icon-ipad-76x76.png b/assets/images/touch-icon-ipad-76x76.png
new file mode 100644
index 0000000000..142f22f241
Binary files /dev/null and b/assets/images/touch-icon-ipad-76x76.png differ
diff --git a/assets/images/touch-icon-ipad-retina-152x152.png b/assets/images/touch-icon-ipad-retina-152x152.png
new file mode 100644
index 0000000000..960fae6c33
Binary files /dev/null and b/assets/images/touch-icon-ipad-retina-152x152.png differ
diff --git a/assets/images/touch-icon-iphone-60x60.png b/assets/images/touch-icon-iphone-60x60.png
new file mode 100644
index 0000000000..9453e148ed
Binary files /dev/null and b/assets/images/touch-icon-iphone-60x60.png differ
diff --git a/assets/images/touch-icon-iphone-retina-120x120.png b/assets/images/touch-icon-iphone-retina-120x120.png
new file mode 100644
index 0000000000..9c27b0856c
Binary files /dev/null and b/assets/images/touch-icon-iphone-retina-120x120.png differ
diff --git a/assets/images/touch-icon-marketplace-128x128.png b/assets/images/touch-icon-marketplace-128x128.png
new file mode 100644
index 0000000000..e6796420d7
Binary files /dev/null and b/assets/images/touch-icon-marketplace-128x128.png differ
diff --git a/assets/js/gamesData.json b/assets/js/gamesData.json
index 10de2b4aa3..0e3d416feb 100644
--- a/assets/js/gamesData.json
+++ b/assets/js/gamesData.json
@@ -1949,10 +1949,15 @@
"gameUrl": "Earth_Guardian",
"thumbnailUrl": "Earth_Guardian.png"
},
- "387":{
+ "388":{
"gameTitle": "Memory Cards Game",
"gameUrl": "Memory_Cards_Game",
"thumbnailUrl": "Memory_Cards_Game.png"
+ },
+ "389":{
+ "gameTitle": "Contra-Shooter",
+ "gameUrl": "Contra-Shooter",
+ "thumbnailUrl": "Contra-Shooter.png"
}