├── .babelrc ├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── examples ├── assets │ ├── baddie.png │ ├── button_sprite_sheet.png │ ├── diamond.png │ ├── dude.png │ ├── firstaid.png │ ├── platform.png │ ├── sky.png │ ├── star.png │ ├── states_player.png │ └── states_win.png ├── button │ ├── button.js │ └── index.html ├── graphics-button │ ├── graphics-button.js │ └── index.html ├── graphics │ ├── graphics.js │ └── index.html ├── part5 │ ├── index.html │ └── part5.js ├── part6 │ ├── index.html │ └── part6.js ├── part7 │ ├── index.html │ └── part7.js ├── part8 │ ├── index.html │ └── part8.js ├── phaser │ ├── phaser.js │ └── phaser.map └── states │ ├── index.html │ └── states.js ├── gulpfile.js ├── package.json ├── src ├── examples │ ├── button.js │ ├── graphics-button.js │ ├── graphics.js │ ├── part5.js │ ├── part6.js │ ├── part7.js │ ├── part8.js │ └── states.js ├── impl │ ├── nodes.js │ ├── physic-system-name.js │ ├── properties │ │ ├── base │ │ │ ├── PIXI.DisplayObject.js │ │ │ ├── PIXI.DisplayObjectContainer.js │ │ │ ├── PIXI.Graphics.js │ │ │ ├── PIXI.Sprite.js │ │ │ ├── Phaser.Button.js │ │ │ ├── Phaser.Component.Angle.js │ │ │ ├── Phaser.Component.Animation.js │ │ │ ├── Phaser.Component.AutoCull.js │ │ │ ├── Phaser.Component.Bounds.js │ │ │ ├── Phaser.Component.BringToTop.js │ │ │ ├── Phaser.Component.Core.js │ │ │ ├── Phaser.Component.Crop.js │ │ │ ├── Phaser.Component.Delta.js │ │ │ ├── Phaser.Component.Destroy.js │ │ │ ├── Phaser.Component.FixedToCamera.js │ │ │ ├── Phaser.Component.Health.js │ │ │ ├── Phaser.Component.InCamera.js │ │ │ ├── Phaser.Component.InWorld.js │ │ │ ├── Phaser.Component.InputEnabled.js │ │ │ ├── Phaser.Component.LifeSpan.js │ │ │ ├── Phaser.Component.LoadTexture.js │ │ │ ├── Phaser.Component.Overlap.js │ │ │ ├── Phaser.Component.PhysicsBody.js │ │ │ ├── Phaser.Component.Reset.js │ │ │ ├── Phaser.Component.ScaleMinMax.js │ │ │ ├── Phaser.Component.Smoothed.js │ │ │ ├── Phaser.Game.js │ │ │ ├── Phaser.Graphics.js │ │ │ ├── Phaser.Group.js │ │ │ ├── Phaser.Image.js │ │ │ ├── Phaser.Sprite.js │ │ │ └── Phaser.Text.js │ │ ├── custom │ │ │ ├── body.js │ │ │ └── key.js │ │ └── utils.js │ ├── tree-utils.js │ └── types │ │ ├── animation.js │ │ ├── button.js │ │ ├── collides.js │ │ ├── game.js │ │ ├── graphics │ │ ├── create-graphics-item.js │ │ ├── graphics.js │ │ ├── index.js │ │ ├── renderers.js │ │ ├── renderimage.js │ │ ├── rendertexture.js │ │ └── texturetext.js │ │ ├── group.js │ │ ├── index.js │ │ ├── input │ │ ├── index.js │ │ ├── input.js │ │ └── key.js │ │ ├── overlaps.js │ │ ├── sprite.js │ │ ├── state.js │ │ └── text.js ├── index.js ├── native.js ├── node-management │ ├── node-init-adapter.js │ └── node-tree-adapter.js └── phaser-implementation.js └── tests └── node-management ├── node-init-adapter-test.js └── node-tree-adapter-test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["react", "es2015"] } 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/.npmignore -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-present, Eloy Villasclaras 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-phaser 2 | 3 | A React-based wrapper for phaser. 4 | 5 | React-phaser allows game developers to create Phaser games using React. Main features: 6 | 7 | - Phaser objects are represented as React VDOM nodes; react-phaser reacts to VDOM changes by adding, updating or removing Phaser objects. 8 | - React-phaser users a React version unhooked from browser DOM manipulation. 9 | 10 | See the [example code](https://github.com/evilfer/react-phaser/tree/master/src/examples) to see an implementation of the Phaser [Making your first game tutorial](http://phaser.io/tutorials/making-your-first-phaser-game). 11 | 12 | ## Getting started 13 | 14 | Install with: 15 | ```bash 16 | npm install react-phaser 17 | ``` 18 | 19 | In your code: 20 | 21 | ```javascript 22 | var React = require('react-phaser'), 23 | 24 | MyGame = React.createClass({ 25 | render: function () { 26 | return ; 27 | } 28 | }); 29 | 30 | React.render(, 'game'); 31 | ``` 32 | 33 | - `require('react-phaser')` provides the same object as `require('react')`; however, when react-phaser is first loaded it removes the DOM manipulation functionality from React, and inject the Phaser wrapper. 34 | - `React.render` is a method added by react-phaser. that initializes the React lifecycle for the passed element. The second parameter (a string) identifies the virtual container where the game is rendered. This is not a DOM element, but simply a react-phaser internal unique string. 35 | - `` is one of the tags that react-phaser makes available (see [list of available tags](https://github.com/evilfer/react-phaser/wiki/Tags)). 36 | 37 | 38 | 39 | ## Further reading 40 | 41 | - [react-phaser wiki](https://github.com/evilfer/react-phaser/wiki) -------------------------------------------------------------------------------- /examples/assets/baddie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/baddie.png -------------------------------------------------------------------------------- /examples/assets/button_sprite_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/button_sprite_sheet.png -------------------------------------------------------------------------------- /examples/assets/diamond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/diamond.png -------------------------------------------------------------------------------- /examples/assets/dude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/dude.png -------------------------------------------------------------------------------- /examples/assets/firstaid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/firstaid.png -------------------------------------------------------------------------------- /examples/assets/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/platform.png -------------------------------------------------------------------------------- /examples/assets/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/sky.png -------------------------------------------------------------------------------- /examples/assets/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/star.png -------------------------------------------------------------------------------- /examples/assets/states_player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/states_player.png -------------------------------------------------------------------------------- /examples/assets/states_win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evilfer/react-phaser/29ebb4b3d1fb468774ef2e8cef292672689d1e2f/examples/assets/states_win.png -------------------------------------------------------------------------------- /examples/button/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/graphics-button/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/graphics/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/part5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/part6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/part7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/part8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/states/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | path = require('path'), 3 | gutil = require('gulp-util'), 4 | 5 | webpack = require('webpack'), 6 | 7 | jsTaskGen = function (mode) { 8 | var isProd = mode === 'prod', 9 | doWatch = mode === 'watch', 10 | output = './examples/'; 11 | 12 | return function () { 13 | return webpack({ 14 | entry: { 15 | 'part5/part5': './src/examples/part5.js', 16 | 'part6/part6': './src/examples/part6.js', 17 | 'part7/part7': './src/examples/part7.js', 18 | 'part8/part8': './src/examples/part8.js', 19 | 'button/button': './src/examples/button.js', 20 | 'graphics/graphics': './src/examples/graphics.js', 21 | 'graphics-button/graphics-button': './src/examples/graphics-button.js', 22 | 'states/states': './src/examples/states.js' 23 | }, 24 | watch: doWatch, 25 | module: { 26 | loaders: [{ 27 | test: /.jsx?$/, 28 | loader: 'babel-loader', 29 | exclude: /node_modules/, 30 | query: { 31 | presets: ['es2015', 'react'] 32 | } 33 | }] 34 | }, 35 | plugins: isProd ? [new webpack.optimize.UglifyJsPlugin()] : [], 36 | output: { 37 | path: path.join(__dirname, output), 38 | filename: '[name].' + (isProd ? 'min.js' : 'js') 39 | }, 40 | devtool: !isProd && '#inline-source-map' 41 | }, function (err, stats) { 42 | if (err) throw new gutil.PluginError("webpack", err); 43 | gutil.log("[webpack]", stats.toString({})); 44 | }); 45 | } 46 | }; 47 | 48 | 49 | gulp.task('js-dev', jsTaskGen('dev', 'web')); 50 | gulp.task('js-prod', jsTaskGen('prod', 'web')); 51 | gulp.task('js-watch', jsTaskGen('watch', 'web')); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-phaser", 3 | "version": "0.1.6", 4 | "description": "Phaser implementation for React", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/evilfer/react-phaser.git" 8 | }, 9 | "main": "src/index.js", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "Eloy Villasclaras ", 14 | "license": "BSD", 15 | "bugs": { 16 | "url": "https://github.com/evilfer/react-phaser/issues" 17 | }, 18 | "keywords": [ 19 | "React", 20 | "Phaser" 21 | ], 22 | "dependencies": { 23 | "extend": "^3.0.0", 24 | "invariant": "^2.2.1", 25 | "p2": "^0.7.1", 26 | "phaser": "^2.4.6", 27 | "react-anything": "^0.1.5" 28 | }, 29 | "devDependencies": { 30 | "babel-core": "^6.7.6", 31 | "babel-loader": "^6.2.4", 32 | "babel-preset-es2015": "6.6.0", 33 | "babel-preset-react": "6.5.0", 34 | "chai": "^3.5.0", 35 | "gulp": "^3.9.1", 36 | "gulp-util": "^3.0.7", 37 | "mocha": "^2.4.5", 38 | "script": "^0.1.4", 39 | "sinon": "^1.17.3", 40 | "sinon-chai": "^2.8.0", 41 | "webpack": "^1.12.14" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/examples/button.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | assets = { 4 | 'sky': {type: 'image', src: '../assets/sky.png'}, 5 | 'ground': {type: 'image', src: '../assets/platform.png'}, 6 | 'star': {type: 'image', src: '../assets/star.png'}, 7 | 'dude': {type: 'spritesheet', src: '../assets/dude.png', width: 32, height: 48}, 8 | 'button': {type: 'spritesheet', src: '../assets/button_sprite_sheet.png', width: 193, height: 71} 9 | }, 10 | 11 | scoreStyle = { 12 | fontSize: '32px', 13 | fill: '#000' 14 | }, 15 | 16 | MyGame = React.createClass({ 17 | getInitialState: function () { 18 | return { 19 | stars: Array.apply(null, {length: 12}).map(function (_, i) { 20 | return [i, 0.7 + Math.random() * 0.2]; 21 | }), 22 | score: 0 23 | }; 24 | }, 25 | 26 | onInput: function (context) { 27 | var player = context.nodes.player.obj, 28 | cursors = context.input.cursors; 29 | 30 | if (cursors.left.isDown) { 31 | player.body.velocity.x = -150; 32 | player.animations.play('left'); 33 | } else if (cursors.right.isDown) { 34 | player.body.velocity.x = 150; 35 | player.animations.play('right'); 36 | } else { 37 | player.body.velocity.x = 0; 38 | player.animations.stop(); 39 | player.frame = 4; 40 | } 41 | 42 | if (cursors.up.isDown && player.body.touching.down) { 43 | player.body.velocity.y = -350; 44 | } 45 | }, 46 | 47 | collectStar: function (playerNode, starNode) { 48 | this.setState({ 49 | stars: this.state.stars.filter(function (_, i) { 50 | return i !== starNode.props.i; 51 | }), 52 | score: this.state.score + 10 53 | }); 54 | }, 55 | 56 | render: function () { 57 | var stars = this.state.stars.map(function (star, i) { 58 | return 60 | }); 61 | 62 | 63 | return ( 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | {stars} 74 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | 85 | 88 | 89 | 90 | ); 91 | } 92 | }); 93 | 94 | 95 | React.render(, 'game'); -------------------------------------------------------------------------------- /src/examples/graphics-button.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | buttonShape = { 4 | width: 200, 5 | height: 40, 6 | radius: 5 7 | }, 8 | 9 | MyGame = React.createClass({ 10 | 11 | render: function () { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 29 | 30 | 31 | 33 | 34 | 35 | 37 | 38 | 39 | 42 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 108 | 110 | 111 | 113 | 114 | 115 | 116 | 117 | ); 118 | } 119 | }); 120 | 121 | 122 | React.render(, 'game'); -------------------------------------------------------------------------------- /src/examples/part5.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | assets = { 4 | 'sky': {type: 'image', src: '../assets/sky.png'}, 5 | 'ground': {type: 'image', src: '../assets/platform.png'}, 6 | 'star': {type: 'image', src: '../assets/star.png'}, 7 | 'dude': {type: 'spritesheet', src: '../assets/dude.png', width: 32, height: 48} 8 | }; 9 | 10 | React.render(( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ), 'game'); -------------------------------------------------------------------------------- /src/examples/part6.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | assets = { 4 | 'sky': {type: 'image', src: '../assets/sky.png'}, 5 | 'ground': {type: 'image', src: '../assets/platform.png'}, 6 | 'star': {type: 'image', src: '../assets/star.png'}, 7 | 'dude': {type: 'spritesheet', src: '../assets/dude.png', width: 32, height: 48} 8 | }, 9 | 10 | onInput = function (context) { 11 | var player = context.nodes.player.obj, 12 | cursors = context.input.cursors; 13 | 14 | if (cursors.left.isDown) { 15 | player.body.velocity.x = -150; 16 | player.animations.play('left'); 17 | } else if (cursors.right.isDown) { 18 | player.body.velocity.x = 150; 19 | player.animations.play('right'); 20 | } else { 21 | player.body.velocity.x = 0; 22 | player.animations.stop(); 23 | player.frame = 4; 24 | } 25 | 26 | if (cursors.up.isDown && player.body.touching.down) { 27 | player.body.velocity.y = -350; 28 | } 29 | }; 30 | 31 | React.render(( 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ), 'game'); -------------------------------------------------------------------------------- /src/examples/part7.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | assets = { 4 | 'sky': {type: 'image', src: '../assets/sky.png'}, 5 | 'ground': {type: 'image', src: '../assets/platform.png'}, 6 | 'star': {type: 'image', src: '../assets/star.png'}, 7 | 'dude': {type: 'spritesheet', src: '../assets/dude.png', width: 32, height: 48} 8 | }, 9 | 10 | MyGame = React.createClass({ 11 | getInitialState: function () { 12 | return { 13 | stars: Array.apply(null, {length: 12}).map(function (_, i) { 14 | return [i, 0.7 + Math.random() * 0.2]; 15 | }) 16 | }; 17 | }, 18 | 19 | onInput: function (context) { 20 | var player = context.nodes.player.obj, 21 | cursors = context.input.cursors; 22 | 23 | if (cursors.left.isDown) { 24 | player.body.velocity.x = -150; 25 | player.animations.play('left'); 26 | } else if (cursors.right.isDown) { 27 | player.body.velocity.x = 150; 28 | player.animations.play('right'); 29 | } else { 30 | player.body.velocity.x = 0; 31 | player.animations.stop(); 32 | player.frame = 4; 33 | } 34 | 35 | if (cursors.up.isDown && player.body.touching.down) { 36 | player.body.velocity.y = -350; 37 | } 38 | }, 39 | 40 | collectStar: function (playerNode, starNode) { 41 | this.setState({ 42 | stars: this.state.stars.filter(function (_, i) { 43 | return i !== starNode.props.i; 44 | }) 45 | }); 46 | }, 47 | 48 | render: function () { 49 | var stars = this.state.stars.map(function (star, i) { 50 | return 52 | }); 53 | 54 | 55 | return ( 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {stars} 66 | 67 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | ); 78 | } 79 | }); 80 | 81 | 82 | React.render(, 'game'); -------------------------------------------------------------------------------- /src/examples/part8.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | assets = { 4 | 'sky': {type: 'image', src: '../assets/sky.png'}, 5 | 'ground': {type: 'image', src: '../assets/platform.png'}, 6 | 'star': {type: 'image', src: '../assets/star.png'}, 7 | 'dude': {type: 'spritesheet', src: '../assets/dude.png', width: 32, height: 48} 8 | }, 9 | 10 | scoreStyle = { 11 | fontSize: '32px', 12 | fill: '#000' 13 | }, 14 | 15 | MyGame = React.createClass({ 16 | getInitialState: function () { 17 | return { 18 | stars: Array.apply(null, {length: 12}).map(function (_, i) { 19 | return [i, 0.7 + Math.random() * 0.2]; 20 | }), 21 | score: 0 22 | }; 23 | }, 24 | 25 | onInput: function (context) { 26 | var player = context.nodes.player.obj, 27 | cursors = context.input.cursors; 28 | 29 | if (cursors.left.isDown) { 30 | player.body.velocity.x = -150; 31 | player.animations.play('left'); 32 | } else if (cursors.right.isDown) { 33 | player.body.velocity.x = 150; 34 | player.animations.play('right'); 35 | } else { 36 | player.body.velocity.x = 0; 37 | player.animations.stop(); 38 | player.frame = 4; 39 | } 40 | 41 | if (cursors.up.isDown && player.body.touching.down) { 42 | player.body.velocity.y = -350; 43 | } 44 | }, 45 | 46 | collectStar: function (playerNode, starNode) { 47 | this.setState({ 48 | stars: this.state.stars.filter(function (_, i) { 49 | return i !== starNode.props.i; 50 | }), 51 | score: this.state.score + 10 52 | }); 53 | }, 54 | 55 | render: function () { 56 | var stars = this.state.stars.map(function (star, i) { 57 | return 59 | }); 60 | 61 | 62 | return ( 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {stars} 73 | 74 | 77 | 78 | 79 | 80 | 81 | 82 | 84 | 85 | 86 | ); 87 | } 88 | }); 89 | 90 | 91 | React.render(, 'game'); -------------------------------------------------------------------------------- /src/examples/states.js: -------------------------------------------------------------------------------- 1 | var React = require('../native'), 2 | 3 | textStyle = { 4 | fontSize: '12px', 5 | fill: '#fff' 6 | }, 7 | 8 | MyGame = React.createClass({ 9 | getInitialState: function () { 10 | return { 11 | name: "stateA" 12 | }; 13 | }, 14 | 15 | startState: function (stateName) { 16 | this.setState({name: stateName}); 17 | }, 18 | 19 | render: function () { 20 | return ( 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | }); 40 | 41 | 42 | React.render(, 'game'); -------------------------------------------------------------------------------- /src/impl/nodes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nodeTypes = require('./types'), 4 | 5 | Nodes = function () { 6 | this.gameNode = null; 7 | this.ids = {}; 8 | this.name2node = {}; 9 | this.transactionListeners = []; 10 | }; 11 | 12 | Nodes.prototype.setGameNode = function (node) { 13 | this.gameNode = node; 14 | }; 15 | 16 | 17 | Nodes.prototype.requestTransactionNofitication = function (nodeid) { 18 | if (this.transactionListeners.indexOf(nodeid) < 0) { 19 | this.transactionListeners.push(nodeid); 20 | } 21 | }; 22 | 23 | Nodes.prototype.cancelTransactionNofitication = function (nodeid) { 24 | var index = this.transactionListeners.indexOf(nodeid); 25 | if (index >= 0) { 26 | this.transactionListeners.splice(index, 1); 27 | } 28 | }; 29 | 30 | Nodes.prototype.game = function () { 31 | return this.gameNode.obj; 32 | }; 33 | 34 | 35 | Nodes.prototype._registerName = function (node) { 36 | this.ids[node.id] = node; 37 | if (node.props.name) { 38 | this.name2node[node.props.name] = node; 39 | } 40 | }; 41 | 42 | Nodes.prototype._updateName = function (node, lastProps) { 43 | if (lastProps.name !== node.props.name) { 44 | delete this.name2node[lastProps.name]; 45 | this.name2node[node.props.name] = node; 46 | } 47 | }; 48 | 49 | Nodes.prototype._unregisterName = function (node) { 50 | delete this.ids[node.id]; 51 | if (node.props.name) { 52 | delete this.name2node[node.props.name]; 53 | } 54 | }; 55 | 56 | Nodes.prototype.byId = function (id) { 57 | return this.ids[id]; 58 | }; 59 | 60 | 61 | Nodes.prototype.byName = function (name) { 62 | return this.name2node[name]; 63 | }; 64 | 65 | Nodes.prototype.parent = function (node, tag) { 66 | while (true) { 67 | var parent = this.ids[node.parent] || null; 68 | if (!parent || parent.tag === tag) { 69 | return parent; 70 | } else { 71 | node = parent; 72 | } 73 | } 74 | }; 75 | 76 | 77 | Nodes.prototype.gameState = function (node) { 78 | while (true) { 79 | var parent = this.ids[node.parent] || null; 80 | if (!parent || parent.tag === 'game' || parent.tag === 'state') { 81 | return parent; 82 | } else { 83 | node = parent; 84 | } 85 | } 86 | }; 87 | 88 | Nodes.prototype.context = function (node) { 89 | return this.gameState(node).context; 90 | }; 91 | 92 | 93 | Nodes.prototype.parentInitd = function (node) { 94 | var parent = this.ids[node.parent]; 95 | 96 | return !parent || parent.initialized; 97 | }; 98 | 99 | Nodes.prototype._invoke = function (node, method) { 100 | var nodeType = nodeTypes[node.tag]; 101 | if (nodeType && nodeType[method]) { 102 | nodeType[method](this, node); 103 | } 104 | }; 105 | 106 | Nodes.prototype.mountNode = function (node) { 107 | this.ids[node.id] = node; 108 | if (node.tag === 'game') { 109 | this.setGameNode(node); 110 | } 111 | 112 | if (!node.initialized && this.parentInitd(node) && this.initImmediately(node)) { 113 | this._initNode(node, 'init'); 114 | } 115 | }; 116 | 117 | Nodes.prototype._initNode = function (node, method) { 118 | node.initialized = true; 119 | this._registerName(node); 120 | this._invoke(node, method); 121 | if (node.obj) { 122 | node.obj.rnodeid = node.id; 123 | } 124 | }; 125 | 126 | Nodes.prototype.updateNode = function (node, prevProps) { 127 | this._updateName(node, prevProps); 128 | 129 | var nodeType = nodeTypes[node.tag]; 130 | if (nodeType && nodeType.update) { 131 | var changedProps = Object.keys(node.props); 132 | changedProps = changedProps.filter(function (key) { 133 | return key !== 'children' && node.props[key] !== prevProps[key]; 134 | }); 135 | 136 | Object.keys(prevProps).forEach(function (key) { 137 | if (key !== 'children' && !(key in node.props)) { 138 | changedProps.push(key); 139 | } 140 | }); 141 | 142 | if (changedProps.length > 0) { 143 | nodeType.update(this, node, changedProps, prevProps); 144 | } 145 | } 146 | }; 147 | 148 | Nodes.prototype.unmountNode = function (node) { 149 | this._unregisterName(node); 150 | this._invoke(node, 'kill'); 151 | }; 152 | 153 | Nodes.prototype.onChildrenMount = function (node) { 154 | if (this.parentInitd(node)) { 155 | if (node.initialized) { 156 | this._invoke(node, 'onChildrenInit') 157 | } else { 158 | this._initNode(node, 'onChildrenInit'); 159 | } 160 | } 161 | }; 162 | 163 | Nodes.prototype.notifyTransaction = function () { 164 | if (this.transactionListeners.length > 0) { 165 | for (var i = 0; i < this.transactionListeners.length; i++) { 166 | var node = this.ids[this.transactionListeners[i]]; 167 | if (node) { 168 | this._invoke(node, 'notifyTransaction'); 169 | } 170 | } 171 | } 172 | }; 173 | 174 | Nodes.prototype.clearChildren = function (children, options) { 175 | var options = options || {}, 176 | include = options.include, 177 | exclude = options.exclude; 178 | 179 | for (var i = 0; i < children.length; i++) { 180 | var child = this.ids[children[i]], 181 | shouldClear = child.initialized && 182 | (!include || include.indexOf(child.tag) >= 0) && 183 | (!exclude || exclude.indexOf(child.tag) < 0); 184 | 185 | if (shouldClear) { 186 | this._invoke(child, 'clear'); 187 | child.initialized = false; 188 | if (child.children.length > 0) { 189 | this.clearChildren(child.children); 190 | } 191 | } 192 | } 193 | }; 194 | 195 | Nodes.prototype.initChildren = function (children, options) { 196 | var _options = options || {}, 197 | include = _options.include, 198 | exclude = _options.exclude; 199 | 200 | for (var i = 0; i < children.length; i++) { 201 | var child = this.ids[children[i]], 202 | shouldInit = this.parentInitd(child) && !child.initialized && 203 | (!include || include.indexOf(child.tag) >= 0) && 204 | (!exclude || exclude.indexOf(child.tag) < 0); 205 | 206 | if (shouldInit) { 207 | if (this.initImmediately(child)) { 208 | this._initNode(child, 'init'); 209 | if (child.children.length > 0) { 210 | this.initChildren(child.children); 211 | this._invoke(child, 'onChildrenInit'); 212 | } 213 | } else { 214 | this._initNode(child, 'onChildrenInit'); 215 | } 216 | } 217 | } 218 | }; 219 | 220 | Nodes.prototype.initImmediately = function (node) { 221 | var nodeType = nodeTypes[node.tag]; 222 | return nodeType && !nodeType.initOnChildrenMount; 223 | }; 224 | 225 | module.exports = Nodes; 226 | -------------------------------------------------------------------------------- /src/impl/physic-system-name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var names = {}; 4 | 5 | names[Phaser.Physics.ARCADE] = 'arcade'; 6 | names[Phaser.Physics.BOX2D] = 'box2d'; 7 | names[Phaser.Physics.CHIPMUNK] = 'chipmunk'; 8 | names[Phaser.Physics.MATTERJS] = 'matter'; 9 | names[Phaser.Physics.NINJA] = 'ninja'; 10 | names[Phaser.Physics.P2JS] = 'p2'; 11 | 12 | module.exports = function (system) { 13 | return names[system] || 'arcade'; 14 | }; 15 | -------------------------------------------------------------------------------- /src/impl/properties/base/PIXI.DisplayObject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = utils.generatePointPropMap(['scale']); 6 | -------------------------------------------------------------------------------- /src/impl/properties/base/PIXI.DisplayObjectContainer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | 5 | module.exports = extend( 6 | {}, 7 | require('./PIXI.DisplayObject') 8 | ); 9 | -------------------------------------------------------------------------------- /src/impl/properties/base/PIXI.Graphics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | 5 | module.exports = extend( 6 | {}, 7 | require('./PIXI.DisplayObjectContainer') 8 | ); 9 | -------------------------------------------------------------------------------- /src/impl/properties/base/PIXI.Sprite.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | 5 | module.exports = extend( 6 | {}, 7 | require('./PIXI.DisplayObjectContainer') 8 | ); 9 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Button.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var extend = require('extend'); 5 | 6 | module.exports = extend( 7 | {}, 8 | require('./Phaser.Image') 9 | ); 10 | 11 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Angle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | var generateBasicPropMap = require('../utils').generateBasicPropMap; 6 | 7 | module.exports = generateBasicPropMap(['angle']); 8 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Animation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.AutoCull.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | var generateBasicPropMap = require('../utils').generateBasicPropMap; 6 | 7 | module.exports = generateBasicPropMap(['autocull']); 8 | /** 9 | * inCamera 10 | */ 11 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Bounds.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.BringToTop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Core.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = utils.generateAliasPropMap({ 6 | assetKey: 'key' 7 | }); 8 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Crop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = {}; 3 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Delta.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Destroy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.FixedToCamera.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Health.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.InCamera.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.InWorld.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | var generateBasicPropMap = require('../utils').generateBasicPropMap; 6 | 7 | module.exports = generateBasicPropMap(['checkWorldBounds', 'outOfBoundsKill']); 8 | /** 9 | * inWorld 10 | */ 11 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.InputEnabled.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.LifeSpan.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var generateBasicPropMap = require('../utils').generateBasicPropMap; 4 | 5 | module.exports = generateBasicPropMap(['alive', 'lifespan']); 6 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.LoadTexture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var generateBasicPropMap = require('../utils').generateBasicPropMap; 4 | 5 | module.exports = generateBasicPropMap(['frame', 'frameName']); 6 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Overlap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.PhysicsBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'), 4 | utils = require('../utils'); 5 | 6 | module.exports = extend( 7 | {}, 8 | utils.generateBasicPropMap(['x', 'y']), 9 | require('../custom/body') 10 | ); 11 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Reset.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.ScaleMinMax.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = {}; 4 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Component.Smoothed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var generateBasicPropMap = require('../utils').generateBasicPropMap; 4 | 5 | module.exports = generateBasicPropMap(['smoothed']) -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Game.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'), 4 | utils = require('../utils'), 5 | 6 | generateCustomPropMap = utils.generateCustomPropMap, 7 | generatePrefixedBasicPropMap = utils.generatePrefixedBasicPropMap; 8 | 9 | 10 | module.exports = extend( 11 | generateCustomPropMap({ 12 | 'stateName': function (node, value) { 13 | if (value) { 14 | node.obj.state.start(value); 15 | } 16 | } 17 | }), 18 | generatePrefixedBasicPropMap('stage', ['backgroundColor']) 19 | ); 20 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Graphics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | 5 | module.exports = extend( 6 | {}, 7 | require('./PIXI.Graphics'), 8 | require('./Phaser.Component.Core'), 9 | require('./Phaser.Component.Angle'), 10 | require('./Phaser.Component.AutoCull'), 11 | require('./Phaser.Component.Bounds'), 12 | require('./Phaser.Component.Destroy'), 13 | require('./Phaser.Component.FixedToCamera'), 14 | require('./Phaser.Component.InputEnabled'), 15 | require('./Phaser.Component.InWorld'), 16 | require('./Phaser.Component.LifeSpan'), 17 | require('./Phaser.Component.PhysicsBody'), 18 | require('./Phaser.Component.Reset') 19 | ); 20 | 21 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Group.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var extend = require('extend'), 5 | generateBasicPropMap = require('../utils').generateBasicPropMap; 6 | 7 | module.exports = extend( 8 | {}, 9 | require('./PIXI.DisplayObjectContainer'), 10 | generateBasicPropMap(['enableBody']) 11 | ); 12 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Image.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var extend = require('extend'); 5 | 6 | module.exports = extend( 7 | {}, 8 | require('./PIXI.Sprite'), 9 | require('./Phaser.Component.Core'), 10 | require('./Phaser.Component.Angle'), 11 | require('./Phaser.Component.Animation'), 12 | require('./Phaser.Component.AutoCull'), 13 | require('./Phaser.Component.Bounds'), 14 | require('./Phaser.Component.BringToTop'), 15 | require('./Phaser.Component.Crop'), 16 | require('./Phaser.Component.Delta'), 17 | require('./Phaser.Component.Destroy'), 18 | require('./Phaser.Component.FixedToCamera'), 19 | require('./Phaser.Component.InputEnabled'), 20 | require('./Phaser.Component.LifeSpan'), 21 | require('./Phaser.Component.LoadTexture'), 22 | require('./Phaser.Component.Overlap'), 23 | require('./Phaser.Component.Reset'), 24 | require('./Phaser.Component.Smoothed') 25 | ); 26 | 27 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Sprite.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var extend = require('extend'); 5 | 6 | module.exports = extend( 7 | {}, 8 | require('./PIXI.Sprite'), 9 | require('./Phaser.Component.Core'), 10 | require('./Phaser.Component.Angle'), 11 | require('./Phaser.Component.Animation'), 12 | require('./Phaser.Component.AutoCull'), 13 | require('./Phaser.Component.Bounds'), 14 | require('./Phaser.Component.BringToTop'), 15 | require('./Phaser.Component.Crop'), 16 | require('./Phaser.Component.Delta'), 17 | require('./Phaser.Component.Destroy'), 18 | require('./Phaser.Component.FixedToCamera'), 19 | require('./Phaser.Component.Health'), 20 | require('./Phaser.Component.InCamera'), 21 | require('./Phaser.Component.InputEnabled'), 22 | require('./Phaser.Component.InWorld'), 23 | require('./Phaser.Component.LifeSpan'), 24 | require('./Phaser.Component.LoadTexture'), 25 | require('./Phaser.Component.Overlap'), 26 | require('./Phaser.Component.PhysicsBody'), 27 | require('./Phaser.Component.Reset'), 28 | require('./Phaser.Component.ScaleMinMax'), 29 | require('./Phaser.Component.Smoothed') 30 | ); 31 | 32 | -------------------------------------------------------------------------------- /src/impl/properties/base/Phaser.Text.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var extend = require('extend'), 5 | utils = require('../utils'); 6 | 7 | module.exports = extend( 8 | {}, 9 | require('./Phaser.Sprite'), 10 | utils.generateBasicPropMap(['text']) 11 | ); 12 | 13 | -------------------------------------------------------------------------------- /src/impl/properties/custom/body.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'), 4 | utils = require('../utils'), 5 | treeUtils = require('../../tree-utils'); 6 | 7 | module.exports = extend( 8 | {}, 9 | utils.generatePrefixedBasicPropMap('body', ['immovable', 'collideWorldBounds']), 10 | utils.generatePrefixedPointPropMap('body', ['bounce', 'gravity']), 11 | utils.generateMountOnlyPropMap({ 12 | bodyPhysics: function (node, value, tree) { 13 | var physics = treeUtils.physics(node, tree), 14 | system = value !== true ? value : physics.system; 15 | 16 | physics.enable(node.obj, system); 17 | } 18 | }) 19 | ); 20 | -------------------------------------------------------------------------------- /src/impl/properties/custom/key.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = utils.generateFixedPropMap(['keyName', 'keyCode']); 6 | -------------------------------------------------------------------------------- /src/impl/properties/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var invariant = require('invariant'), 4 | 5 | generateBasicPropMap = function (props) { 6 | return props.reduce(function (acc, prop) { 7 | acc[prop] = function (node, value) { 8 | node.obj[prop] = value; 9 | }; 10 | return acc; 11 | }, {}); 12 | }, 13 | 14 | generatePrefixedBasicPropMap = function (prefix, props) { 15 | return props.reduce(function (acc, prop) { 16 | acc[prefix + prop.charAt(0).toUpperCase() + prop.slice(1)] = function (node, value) { 17 | console.log(node.obj); 18 | console.log(node.obj[prefix]); 19 | console.log(prefix, prop, value); 20 | node.obj[prefix][prop] = value; 21 | }; 22 | return acc; 23 | }, {}); 24 | }, 25 | 26 | 27 | generatePointPropMap = function (props) { 28 | return props.reduce(function (acc, prop) { 29 | acc[prop] = function (node, value, prevValue, isNew) { 30 | var point = node.obj[prop]; 31 | if (isNew || value.x !== prevValue.x) { 32 | point.x = value.x; 33 | } 34 | if (isNew || value.y !== prevValue.y) { 35 | point.y = value.y; 36 | } 37 | }; 38 | acc[prop + 'X'] = function (node, value) { 39 | node.obj[prop].x = value; 40 | }; 41 | acc[prop + 'Y'] = function (node, value) { 42 | node.obj[prop].y = value; 43 | }; 44 | return acc; 45 | }, {}); 46 | }, 47 | 48 | generatePrefixedPointPropMap = function (prefix, props) { 49 | return props.reduce(function (acc, prop) { 50 | var prefixedProp = prefix + prop.charAt(0).toUpperCase() + prop.slice(1); 51 | acc[prefixedProp] = function (node, value, prevValue, isNew) { 52 | var point = node.obj[prefix][prop]; 53 | if (isNew || value.x !== prevValue.x) { 54 | point.x = value.x; 55 | } 56 | if (isNew || value.y !== prevValue.y) { 57 | point.y = value.y; 58 | } 59 | }; 60 | acc[prefixedProp + 'X'] = function (node, value) { 61 | node.obj[prefix][prop].x = value; 62 | }; 63 | acc[prefixedProp + 'Y'] = function (node, value) { 64 | node.obj[prefix][prop].y = value; 65 | }; 66 | return acc; 67 | }, {}); 68 | }, 69 | 70 | generateAliasPropMap = function (aliases) { 71 | return Object.keys(aliases).reduce(function (acc, alias) { 72 | var prop = aliases[alias]; 73 | acc[alias] = function (node, value) { 74 | node.obj[prop] = value; 75 | }; 76 | return acc; 77 | }, {}); 78 | }, 79 | generateMountOnlyPropMap = function (propMap) { 80 | return Object.keys(propMap).reduce(function (acc, prop) { 81 | var impl = propMap[prop]; 82 | acc[prop] = function (node, value, prevValue, isNew, deleted, tree) { 83 | if (isNew) { 84 | impl(node, value, tree); 85 | } 86 | }; 87 | return acc; 88 | }, {}); 89 | }, 90 | 91 | generateCustomPropMap = function (props) { 92 | return props; 93 | }, 94 | 95 | generateFixedPropMap = function (props) { 96 | return props.reduce(function (acc, prop) { 97 | acc[prop] = function (node, value, prevValue, isNew) { 98 | invariant(isNew, "Property '%s' of '%s' cannot change.", prop, node.tag); 99 | }; 100 | return acc; 101 | }, {}); 102 | }; 103 | 104 | module.exports = { 105 | generateBasicPropMap: generateBasicPropMap, 106 | generatePrefixedBasicPropMap: generatePrefixedBasicPropMap, 107 | generatePointPropMap: generatePointPropMap, 108 | generatePrefixedPointPropMap: generatePrefixedPointPropMap, 109 | generateAliasPropMap: generateAliasPropMap, 110 | generateMountOnlyPropMap: generateMountOnlyPropMap, 111 | generateCustomPropMap: generateCustomPropMap, 112 | generateFixedPropMap: generateFixedPropMap 113 | }; 114 | -------------------------------------------------------------------------------- /src/impl/tree-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var propsChanged = function (nextProps, lastProps) { 4 | if (!lastProps) { 5 | return true; 6 | } 7 | 8 | var npl = Object.keys(nextProps), 9 | lpl = Object.keys(lastProps); 10 | 11 | if (npl.length !== lpl.length) { 12 | return true; 13 | } 14 | 15 | for (var i = 0; i < npl.length; i++) { 16 | var prop = npl[i]; 17 | if (nextProps[prop] !== lastProps[prop]) { 18 | return true; 19 | } 20 | } 21 | 22 | return false; 23 | }, 24 | 25 | genPropertyMapUpdate = function (props) { 26 | return function (node, prevProps, tree) { 27 | if (prevProps) { 28 | Object.keys(prevProps).forEach(function (prop) { 29 | if (props[prop] && typeof node.props[prop] === 'undefined') { 30 | props[prop](node, node.props[prop], prevProps[prop], false, true, tree); 31 | } 32 | }); 33 | } 34 | Object.keys(node.props).forEach(function (prop) { 35 | if (props[prop] && (!prevProps || node.props[prop] !== prevProps[prop])) { 36 | props[prop](node, node.props[prop], prevProps && prevProps[prop], !prevProps, false, tree); 37 | } 38 | }); 39 | } 40 | }, 41 | 42 | game = function (tree) { 43 | return tree.root && tree.root.obj; 44 | }, 45 | 46 | parent = function (node, tree, type) { 47 | while (true) { 48 | node = tree.nodes[node.parent]; 49 | if (!node || !type || type === node.tag) { 50 | return node; 51 | } 52 | } 53 | }, 54 | 55 | stateNode = function (node, tree) { 56 | while (true) { 57 | node = tree.nodes[node.parent]; 58 | if (!node || node.tag === 'game' || node.tag === 'state') { 59 | return node; 60 | } 61 | } 62 | }, 63 | 64 | addDisplayObject = function (node, tree, obj) { 65 | var parent = tree.nodes[node.parent], 66 | group = parent.obj.world || parent.obj; 67 | 68 | group.add(obj || node.obj); 69 | }, 70 | 71 | physics = function (node, tree) { 72 | var physicsNode = stateNode(node, tree); 73 | return physicsNode && physicsNode.obj.physics; 74 | }; 75 | 76 | module.exports = { 77 | propsChanged: propsChanged, 78 | genPropertyMapUpdate: genPropertyMapUpdate, 79 | game: game, 80 | parent: parent, 81 | addDisplayObject: addDisplayObject, 82 | stateNode: stateNode, 83 | physics: physics 84 | }; 85 | -------------------------------------------------------------------------------- /src/impl/types/animation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | 5 | initAnimation = function (node, tree) { 6 | var parentNode = treeUtils.parent(node, tree); 7 | node.obj = parentNode.obj.animations.add(node.props.id, node.props.frames, node.props.fps, node.props.loop); 8 | }; 9 | 10 | module.exports = { 11 | init: initAnimation, 12 | kill: null, 13 | update: null 14 | }; 15 | -------------------------------------------------------------------------------- /src/impl/types/button.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | buttonPropertes = require('../properties/base/Phaser.Button'), 5 | 6 | updateButton = treeUtils.genPropertyMapUpdate(buttonPropertes), 7 | 8 | initButton = function (node, tree) { 9 | var props = node.props, 10 | key = props.assetKey, 11 | game = treeUtils.game(tree); 12 | 13 | node.button = new Phaser.Button( 14 | game, 15 | props.x, 16 | props.y, 17 | key, 18 | props.onClick, 19 | node, 20 | props.frames[0], 21 | props.frames[1], 22 | props.frames[2], 23 | props.frames[3] 24 | ); 25 | 26 | if (node.props.children) { 27 | node.obj = new Phaser.Group(game); 28 | node.obj.add(node.button); 29 | } else { 30 | node.obj = node.button; 31 | } 32 | 33 | treeUtils.addDisplayObject(node, tree); 34 | updateButton(node, null, tree); 35 | }, 36 | 37 | killButton = function (node, tree) { 38 | if (node.obj !== node.button) { 39 | node.obj.destroy(); 40 | } else { 41 | node.obj.kill(); 42 | } 43 | }; 44 | 45 | module.exports = { 46 | init: initButton, 47 | kill: killButton, 48 | update: updateButton 49 | }; 50 | -------------------------------------------------------------------------------- /src/impl/types/collides.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | systemName = require('../physic-system-name'), 5 | 6 | initCollides = function (node, tree) { 7 | var a = tree.nodes[node.parent], 8 | b = tree.byname[node.props.with], 9 | name = systemName(node.props.system), 10 | stateNode = treeUtils.stateNode(node, tree); 11 | 12 | node.obj = { 13 | a: a, 14 | b: b, 15 | onUpdate: function (context) { 16 | context.game.physics[name].collide(a.obj, b.obj); 17 | } 18 | }; 19 | 20 | stateNode.addUpdateListener(node.obj.onUpdate); 21 | }, 22 | 23 | killCollides = function (node, tree) { 24 | var stateNode = treeUtils.stateNode(node, tree); 25 | stateNode.removeUpdateListener(node.obj.onUpdate); 26 | }; 27 | 28 | module.exports = { 29 | init: initCollides, 30 | kill: killCollides, 31 | update: null 32 | }; 33 | -------------------------------------------------------------------------------- /src/impl/types/game.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | gamePropertes = require('../properties/base/Phaser.Game'), 5 | 6 | updateGame = treeUtils.genPropertyMapUpdate(gamePropertes), 7 | 8 | onChildrenInit = function (node, tree, nodeMethods) { 9 | node._updateMethods = []; 10 | node.addUpdateListener = function (listener) { 11 | node._updateMethods.push(listener); 12 | }; 13 | node.removeUpdateListener = function (listener) { 14 | var index = node._updateMethods.indexOf(listener); 15 | if (index >= 0) { 16 | node._updateMethods.splice(index, 1); 17 | } 18 | }; 19 | 20 | var props = node.props, 21 | gameImpl = { 22 | preload: function () { 23 | updateGame(node, null, tree); 24 | if (props.assets) { 25 | Object.keys(props.assets).forEach(function (key) { 26 | var asset = props.assets[key]; 27 | switch (asset.type) { 28 | case 'image': 29 | game.load.image(key, asset.src); 30 | break; 31 | case 'spritesheet': 32 | game.load.spritesheet(key, asset.src, asset.width, asset.height); 33 | break; 34 | } 35 | }); 36 | } 37 | nodeMethods.initChildren(node, tree, {include: ['assets']}); 38 | }, 39 | create: function () { 40 | 41 | if (node.props.hasOwnProperty('physics')) { 42 | node.obj.physics.startSystem(node.props.physics); 43 | } 44 | 45 | nodeMethods.initChildren(node, tree); 46 | 47 | if (node.props.stateName) { 48 | game.state.start(node.props.stateName); 49 | } 50 | }, 51 | update: function () { 52 | for (var i = 0; i < node._updateMethods.length; i++) { 53 | node._updateMethods[i](context); 54 | } 55 | } 56 | }, 57 | 58 | game = new Phaser.Game(props.width, props.height, props.mode || Phaser.AUTO, '', gameImpl), 59 | context = { 60 | game: game, 61 | nodes: tree.byname 62 | }; 63 | 64 | node.obj = game; 65 | node.context = context; 66 | }; 67 | 68 | module.exports = { 69 | init: null, 70 | onChildrenInit: onChildrenInit, 71 | update: updateGame, 72 | kill: null, 73 | deferredInit: true 74 | }; 75 | -------------------------------------------------------------------------------- /src/impl/types/graphics/create-graphics-item.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../../tree-utils'), 4 | create = function (draw) { 5 | 6 | var requestNotification = function (node, tree, treeMethods) { 7 | var graphics = treeUtils.parent(node, tree, 'graphics'); 8 | if (graphics) { 9 | treeMethods.requestTransactionNofitication(graphics.id); 10 | } 11 | }, 12 | 13 | update = function (node, prevProps, tree, treeMethods) { 14 | if (treeUtils.propsChanged(node.props, prevProps)) { 15 | requestNotification(node, tree, treeMethods); 16 | } 17 | }, 18 | 19 | 20 | drawWrapper = function (node, tree, graphics, x0, y0) { 21 | var fill = typeof node.props.fill !== 'undefined', 22 | line = typeof node.props.stroke!== 'undefined' || 23 | typeof node.props.strokeWidth !== 'undefined' || 24 | typeof node.props.strokeAlpha !== 'undefined'; 25 | 26 | if (fill) { 27 | var fillColor = typeof node.props.fill !== 'undefined' ? node.props.fill : 0x000000, 28 | fillAlpha = typeof node.props.fillAlpha === 'number' ? node.props.fillAlpha : 1; 29 | graphics.beginFill(fillColor, fillAlpha); 30 | } 31 | if (line) { 32 | var lineColor = typeof node.props.stroke !== 'undefined' ? node.props.stroke : 0x000000, 33 | lineAlpha = typeof node.props.strokeAlpha === 'number' ? node.props.strokeAlpha : 1, 34 | lineWidth = typeof node.props.strokeWidth === 'number' ? node.props.strokeWidth : 1; 35 | graphics.lineStyle(lineWidth, lineColor, lineAlpha); 36 | } else { 37 | graphics.lineStyle(0); 38 | } 39 | 40 | draw(node, tree, graphics, x0, y0); 41 | 42 | if (fill) { 43 | graphics.endFill(); 44 | } 45 | }; 46 | 47 | return { 48 | init: requestNotification, 49 | kill: requestNotification, 50 | update: update, 51 | draw: drawWrapper 52 | }; 53 | }; 54 | 55 | module.exports = create; 56 | -------------------------------------------------------------------------------- /src/impl/types/graphics/graphics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../../tree-utils'), 4 | graphicsPropertes = require('../../properties/base/Phaser.Graphics'), 5 | 6 | updateGraphics = treeUtils.genPropertyMapUpdate(graphicsPropertes), 7 | 8 | itemTypes = require('./renderers'), 9 | 10 | initGraphics = function (node, tree) { 11 | var props = node.props; 12 | node.obj = new Phaser.Graphics(treeUtils.game(tree), props.x, props.y); 13 | treeUtils.addDisplayObject(node, tree); 14 | updateGraphics(node, tree); 15 | }, 16 | 17 | killGraphics = function (node) { 18 | node.obj.kill(); 19 | }, 20 | 21 | onChildrenInit = function (node, tree, treeMethods) { 22 | treeMethods.cancelTransactionNofitication(node.id); 23 | draw(node, tree); 24 | }, 25 | 26 | redraw = function (node, tree) { 27 | node.obj.clear(); 28 | draw(node, tree); 29 | }, 30 | 31 | draw = function (node, tree) { 32 | for (var i = 0; i < node.children.length; i++) { 33 | var child = tree.nodes[node.children[i]]; 34 | if (itemTypes[child.tag]) { 35 | itemTypes[child.tag].draw(child, tree, node.obj, 0, 0); 36 | } 37 | } 38 | }; 39 | 40 | module.exports = { 41 | init: initGraphics, 42 | onChildrenInit: onChildrenInit, 43 | kill: killGraphics, 44 | update: updateGraphics, 45 | notifyTransaction: redraw 46 | }; 47 | -------------------------------------------------------------------------------- /src/impl/types/graphics/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | 5 | module.exports = extend( 6 | { 7 | graphics: require('./graphics'), 8 | rendertexture: require('./rendertexture'), 9 | renderimage: require('./renderimage') 10 | }, 11 | require('./renderers') 12 | ); 13 | -------------------------------------------------------------------------------- /src/impl/types/graphics/renderers.js: -------------------------------------------------------------------------------- 1 | var createGraphicsNode = require('./create-graphics-item'), 2 | 3 | renderers = { 4 | arc: function (node, tree, graphics, x0, y0) { 5 | graphics.arc( 6 | x0 + (node.props.x || 0), 7 | y0 + (node.props.y || 0), 8 | node.props.radius || 0, 9 | node.props.startAngle || 0, 10 | node.props.endAngle || 2 * Math.PI, 11 | node.props.anticlockwise || false, 12 | node.props.segments 13 | ); 14 | }, 15 | circle: function (node, tree, graphics, x0, y0) { 16 | graphics.drawCircle( 17 | x0 + (node.props.x || 0), 18 | y0 + (node.props.y || 0), 19 | node.props.diameter || 0 20 | ); 21 | }, 22 | ellipse: function (node, tree, graphics, x0, y0) { 23 | graphics.drawEllipse( 24 | x0 + (node.props.x || 0), 25 | y0 + (node.props.y || 0), 26 | node.props.width || 0, 27 | node.props.height || 0 28 | ); 29 | }, 30 | rect: function (node, tree, graphics, x0, y0) { 31 | graphics.drawRect( 32 | x0 + (node.props.x || 0), 33 | y0 + (node.props.y || 0), 34 | node.props.width || 0, 35 | node.props.height || 0 36 | ); 37 | }, 38 | roundedrect: function (node, tree, graphics, x0, y0) { 39 | graphics.drawRoundedRect( 40 | x0 + (node.props.x || 0), 41 | y0 + (node.props.y || 0), 42 | node.props.width || 0, 43 | node.props.height || 0, 44 | node.props.radius || 0 45 | ); 46 | }, 47 | line: function (node, tree, graphics, x0, y0) { 48 | graphics.moveTo( 49 | x0 + (node.props.x1 || 0), 50 | y0 + (node.props.y1 || 0) 51 | ); 52 | graphics.lineTo( 53 | x0 + (node.props.x2 || 0), 54 | y0 + (node.props.y2 || 0) 55 | ); 56 | }, 57 | lineto: function (node, tree, graphics, x0, y0) { 58 | graphics.lineTo( 59 | x0 + (node.props.x || 0), 60 | y0 + (node.props.y || 0) 61 | ); 62 | }, 63 | quadraticcurveto: function (node, tree, graphics, x0, y0) { 64 | graphics.quadraticCurveTo( 65 | node.props.cpx + x0, 66 | node.props.cpy + y0, 67 | node.props.x + x0, 68 | node.props.y + y0 69 | ); 70 | }, 71 | beziercurveto: function (node, tree, graphics, x0, y0) { 72 | graphics.quadraticCurveTo( 73 | node.props.cpx + x0, 74 | node.props.cpy + y0, 75 | node.props.cpx2 + x0, 76 | node.props.cpy2 + y0, 77 | node.props.x + x0, 78 | node.props.y + y0 79 | ); 80 | }, 81 | shape: function (node, tree, graphics, x0, y0) { 82 | var sx0 = x0 + (node.props.x || 0), 83 | sy0 = y0 + (node.props.y || 0); 84 | 85 | if (node.props.s) { 86 | var parts = node.props.s.replace(/\s/g, '').match(/([a-z][0-9,]+)/g); 87 | for (var i = 0; i < parts.length; i++) { 88 | var part = parts[i], 89 | command = part.charAt(0), 90 | v = part.match(/[0-9]+/g).map(function (v) { 91 | return parseFloat(v); 92 | }); 93 | 94 | switch (command) { 95 | case 'a': 96 | graphics.arc(v[0] + sx0, v[1] + sy0, v[2], v[3], v[4], !!v[5], v[6]); 97 | break; 98 | case 'l': 99 | graphics.lineTo(v[0] + sx0, v[1] + sy0); 100 | break; 101 | case 'c': 102 | graphics.drawCircle(v[0] + sx0, v[1] + sy0, v[2]); 103 | break; 104 | case 'e': 105 | graphics.drawEllipse(v[0] + sx0, v[1] + sy0, v[2], v[3]); 106 | break; 107 | case 'r': 108 | graphics.drawRect(v[0] + sx0, v[1] + sy0, v[2], v[3]); 109 | break; 110 | case 'd': 111 | graphics.drawRoundedRect(v[0] + sx0, v[1] + sy0, v[2], v[3], v[4]); 112 | break; 113 | case 'm': 114 | graphics.moveTo(v[0] + sx0, v[1] + sy0); 115 | break; 116 | case 'b': 117 | graphics.bezierCurveTo(v[0] + sx0, v[1] + sy0, v[2] + sx0, v[3] + sy0, v[4] + sx0, v[5] + sy0); 118 | break; 119 | case 'q': 120 | graphics.quadraticCurveTo(v[0] + sx0, v[1] + sy0, v[2] + sx0, v[3] + sy0); 121 | break; 122 | } 123 | } 124 | } 125 | 126 | for (i = 0; i < node.children.length; i++) { 127 | var child = tree.nodes[node.children[i]]; 128 | if (renderers[child.tag]) { 129 | renderers[child.tag](child, tree, graphics, sx0, sy0); 130 | } 131 | } 132 | } 133 | }; 134 | 135 | module.exports = Object.keys(renderers).reduce(function (acc, type) { 136 | acc[type] = createGraphicsNode(renderers[type]); 137 | return acc; 138 | }, {}); 139 | -------------------------------------------------------------------------------- /src/impl/types/graphics/renderimage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../../tree-utils'), 4 | graphicsPropertes = require('../../properties/base/Phaser.Graphics'), 5 | 6 | updateGraphics = treeUtils.genPropertyMapUpdate(graphicsPropertes), 7 | 8 | itemTypes = require('./renderers'), 9 | onTextureTypes = { 10 | 'texturetext': require('./texturetext') 11 | }, 12 | 13 | 14 | initGraphics = function (node, tree) { 15 | node.obj = new Phaser.Graphics(treeUtils.game(tree), 0, 0); 16 | updateGraphics(node, null, tree); 17 | }, 18 | 19 | killGraphics = function (node) { 20 | node.obj.kill(); 21 | }, 22 | 23 | onChildrenInit = function (node, tree) { 24 | draw(node, tree); 25 | 26 | var game = treeUtils.game(tree), 27 | texture = new Phaser.RenderTexture(game, node.obj.width, node.obj.height); 28 | 29 | texture.renderXY(node.obj, 0, 0); 30 | renderOnTexture(node, tree, texture); 31 | 32 | var url = texture.getBase64(); 33 | 34 | texture.destroy(); 35 | node.obj.destroy(); 36 | 37 | if (node.props.frameWidth || node.props.frameHeight) { 38 | var w = node.props.frameWidth || texture.width, 39 | h = node.props.frameHeight || texture.height; 40 | 41 | node.obj = game.load.spritesheet(node.props.assetKey, url, w, h); 42 | } else { 43 | node.obj = game.load.image(node.props.assetKey, url); 44 | } 45 | }, 46 | 47 | draw = function (node, tree) { 48 | for (var i = 0; i < node.children.length; i++) { 49 | var child = tree.nodes[node.children[i]]; 50 | if (itemTypes[child.tag]) { 51 | itemTypes[child.tag].draw(child, tree, node.obj, 0, 0); 52 | } 53 | } 54 | }, 55 | 56 | renderOnTexture = function (node, tree, texture) { 57 | for (var i = 0; i < node.children.length; i++) { 58 | var child = tree.nodes[node.children[i]]; 59 | console.log(child.tag, onTextureTypes[child.tag]); 60 | if (onTextureTypes[child.tag]) { 61 | onTextureTypes[child.tag].draw(child, tree, texture); 62 | } 63 | } 64 | }; 65 | 66 | module.exports = { 67 | init: initGraphics, 68 | onChildrenInit: onChildrenInit, 69 | kill: killGraphics, 70 | update: updateGraphics 71 | }; 72 | -------------------------------------------------------------------------------- /src/impl/types/graphics/rendertexture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../../tree-utils'), 4 | graphicsPropertes = require('../../properties/base/Phaser.Graphics'), 5 | 6 | updateGraphics = treeUtils.genPropertyMapUpdate(graphicsPropertes), 7 | 8 | itemTypes = require('./renderers'), 9 | onTextureTypes = { 10 | 'texturetext': require('./texturetext') 11 | }, 12 | 13 | initGraphics = function (node, tree) { 14 | node.obj = new Phaser.Graphics(treeUtils.game(tree), 0, 0); 15 | updateGraphics(node, null, tree); 16 | }, 17 | 18 | killGraphics = function (node) { 19 | node.obj.kill(); 20 | }, 21 | 22 | onChildrenInit = function (node, tree) { 23 | draw(node, tree); 24 | 25 | var game = treeUtils.game(tree), 26 | texture = new Phaser.RenderTexture(game, node.obj.width, node.obj.height); 27 | 28 | texture.renderXY(node.obj, 0, 0); 29 | node.obj.destroy(); 30 | 31 | renderOnTexture(node, tree, texture); 32 | 33 | game.cache.addRenderTexture(node.props.assetKey, texture); 34 | 35 | }, 36 | 37 | draw = function (node, tree) { 38 | for (var i = 0; i < node.children.length; i++) { 39 | var child = tree.nodes[node.children[i]]; 40 | if (itemTypes[child.tag]) { 41 | itemTypes[child.tag].draw(child, tree, node.obj, 0, 0); 42 | } 43 | } 44 | }, 45 | 46 | renderOnTexture = function (node, tree, texture) { 47 | for (var i = 0; i < node.children.length; i++) { 48 | var child = tree.nodes[node.children[i]]; 49 | if (onTextureTypes[child.tag]) { 50 | onTextureTypes[child.tag].draw(child, tree, texture); 51 | } 52 | } 53 | }; 54 | 55 | module.exports = { 56 | init: initGraphics, 57 | onChildrenInit: onChildrenInit, 58 | kill: killGraphics, 59 | update: updateGraphics 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /src/impl/types/graphics/texturetext.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../../tree-utils'), 4 | 5 | drawTextureText = function (node, tree, texture) { 6 | var props = node.props, 7 | text = new Phaser.Text(treeUtils.game(tree), 0, 0, props.text, props.style), 8 | x = (props.x || 0) + 9 | (props.align ? (props.align.indexOf('right') >= 0 ? -text.width : 10 | (props.align.indexOf('center') >= 0 ? -0.5 * text.width : 0)) : 0), 11 | y = (props.y || 0) + 12 | (props.align ? (props.align.indexOf('bottom') >= 0 ? -text.height: 13 | (props.align.indexOf('middle') >= 0 ? -0.5 * text.height : 0)) : 0); 14 | 15 | texture.renderXY(text, x, y); 16 | text.destroy(); 17 | }; 18 | 19 | module.exports = { 20 | draw: drawTextureText 21 | }; 22 | -------------------------------------------------------------------------------- /src/impl/types/group.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | groupPropertes = require('../properties/base/Phaser.Group'), 5 | 6 | updateGroup = treeUtils.genPropertyMapUpdate(groupPropertes), 7 | 8 | initGroup = function (node, tree) { 9 | node.obj = new Phaser.Group(treeUtils.game(tree)); 10 | treeUtils.addDisplayObject(node, tree); 11 | updateGroup(node, null, tree); 12 | }, 13 | 14 | killGroup = function (node) { 15 | node.obj.destroy(); 16 | }; 17 | 18 | module.exports = { 19 | init: initGroup, 20 | kill: killGroup, 21 | update: updateGroup 22 | }; 23 | -------------------------------------------------------------------------------- /src/impl/types/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | 5 | module.exports = extend( 6 | { 7 | game: require('./game'), 8 | state: require('./state'), 9 | sprite: require('./sprite'), 10 | group: require('./group'), 11 | animation: require('./animation'), 12 | collides: require('./collides'), 13 | overlaps: require('./overlaps'), 14 | text: require('./text'), 15 | button: require('./button') 16 | }, 17 | require('./graphics'), 18 | require('./input') 19 | ); 20 | 21 | -------------------------------------------------------------------------------- /src/impl/types/input/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | input: require('./input'), 5 | key: require('./key') 6 | }; 7 | -------------------------------------------------------------------------------- /src/impl/types/input/input.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../../tree-utils'), 4 | 5 | defaultPointerNumber = 2, 6 | events = ["onDown", "onUp", "onTap", "onHold"], 7 | 8 | clearInput = function (node, tree) { 9 | var context = treeUtils.stateNode(node, tree).context; 10 | delete context.input; 11 | }, 12 | 13 | initInput = function (node, tree) { 14 | var stateNode = treeUtils.stateNode(node, tree); 15 | if (!stateNode.context.input) { 16 | var phaserInput = stateNode.obj.input, 17 | pointerCount = node.props.pointers || defaultPointerNumber, 18 | input = { 19 | mousePointer: phaserInput.mousePointer, 20 | activePointer: phaserInput.activePointer, 21 | pointers: [] 22 | }; 23 | 24 | node.obj = { 25 | input: input 26 | }; 27 | 28 | stateNode.context.input = input; 29 | 30 | for (var i = 0; i < pointerCount; i++) { 31 | if (i >= defaultPointerNumber) { 32 | phaserInput.addPointer(); 33 | } 34 | input.pointers[i] = phaserInput['pointer' + (i + 1)]; 35 | } 36 | 37 | events.forEach(function (event) { 38 | var listener = node.props[event]; 39 | if (listener) { 40 | phaserInput[event].add(function (pointer) { 41 | listener(pointer, stateNode.context); 42 | }); 43 | } 44 | }); 45 | 46 | if (node.props.cursors) { 47 | input.cursors = phaserInput.keyboard.createCursorKeys(); 48 | } 49 | 50 | if (node.props.keys) { 51 | input.keys = Object.keys(node.props.keys).reduce(function (acc, key) { 52 | acc[key] = phaserInput.keyboard.addKey(node.props.keys[key]); 53 | }, {}); 54 | } 55 | 56 | if (node.props.onInput) { 57 | stateNode.addUpdateListener(node.props.onInput); 58 | } 59 | } 60 | }; 61 | 62 | module.exports = { 63 | init: initInput, 64 | clear: clearInput, 65 | kill: null, 66 | update: null 67 | }; 68 | -------------------------------------------------------------------------------- /src/impl/types/input/key.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils= require('../../tree-utils'), 4 | keyPropertes = require('../../properties/custom/key'), 5 | 6 | updateKey = treeUtils.genPropertyMapUpdate(keyPropertes), 7 | 8 | events = ["onDown", "onUp", "onHoldCallback"], 9 | 10 | initKey = function (node, tree) { 11 | var stateNode = treeUtils.stateNode(node, tree); 12 | if (stateNode.context.input && node.props.keyName && node.props.keyCode) { 13 | var phaserInput = stateNode.obj.input, 14 | input = stateNode.context.input, 15 | key = phaserInput.keyboard.addKey(node.props.keyCode); 16 | 17 | if (!input.keys) { 18 | input.keys = {}; 19 | } 20 | 21 | node.obj = key; 22 | input.keys[node.props.keyName] = node.obj; 23 | 24 | events.forEach(function (event) { 25 | var listener = node.props[event]; 26 | if (listener) { 27 | key[event].add(function (key) { 28 | listener(key, stateNode.context); 29 | }); 30 | } 31 | }); 32 | } 33 | }, 34 | 35 | killKey = function (node, tree) { 36 | if (node.obj) { 37 | var stateNode = treeUtils.stateNode(node, tree); 38 | 39 | stateNode.obj.keyboard.removeKey(node.obj.keyCode); 40 | delete stateNode.context.input[node.props.keyName]; 41 | } 42 | }; 43 | 44 | module.exports = { 45 | init: initKey, 46 | kill: killKey, 47 | update: updateKey 48 | }; 49 | -------------------------------------------------------------------------------- /src/impl/types/overlaps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | systemName = require('../physic-system-name'), 5 | 6 | initOverlaps = function (node, tree) { 7 | var a = tree.nodes[node.parent], 8 | b = tree.byname[node.props.with], 9 | name = systemName(node.props.system), 10 | stateNode = treeUtils.stateNode(node, tree), 11 | onOverlap = node.props.onOverlap; 12 | 13 | node.obj = { 14 | a: a, 15 | b: b, 16 | onUpdate: function (context) { 17 | context.game.physics[name].overlap(a.obj, b.obj, function (overlappingA, overlappingB) { 18 | onOverlap(tree.nodes[overlappingA.rnodeid], tree.nodes[overlappingB.rnodeid], context, a, b); 19 | }); 20 | } 21 | }; 22 | 23 | stateNode.addUpdateListener(node.obj.onUpdate); 24 | }, 25 | 26 | killOverlaps = function (nodes, node) { 27 | nodes.gameNode.removeUpdateListener(node.obj.onUpdate); 28 | }; 29 | 30 | module.exports = { 31 | init: initOverlaps, 32 | kill: killOverlaps, 33 | update: null 34 | }; 35 | -------------------------------------------------------------------------------- /src/impl/types/sprite.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | spritePropertes = require('../properties/base/Phaser.Sprite'), 5 | 6 | updateSprite = treeUtils.genPropertyMapUpdate(spritePropertes), 7 | 8 | initSprite = function (node, tree) { 9 | var props = node.props; 10 | node.obj = new Phaser.Sprite(treeUtils.game(tree), props.x, props.y, props.assetKey); 11 | treeUtils.addDisplayObject(node, tree); 12 | updateSprite(node, null, tree); 13 | }, 14 | 15 | killSprite = function (node, tree) { 16 | node.obj.kill(); 17 | }; 18 | 19 | module.exports = { 20 | init: initSprite, 21 | kill: killSprite, 22 | update: updateSprite 23 | }; 24 | -------------------------------------------------------------------------------- /src/impl/types/state.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | 5 | onChildrenInit = function (node, tree, treeMethods) { 6 | node._updateMethods = []; 7 | node.addUpdateListener = function (listener) { 8 | node._updateMethods.push(listener); 9 | }; 10 | node.removeUpdateListener = function (listener) { 11 | var index = node._updateMethods.indexOf(listener); 12 | if (index >= 0) { 13 | node._updateMethods.splice(index, 1); 14 | } 15 | }; 16 | 17 | var props = node.props, 18 | stateImpl = { 19 | create: function () { 20 | if (node.props.hasOwnProperty('physics')) { 21 | node.obj.physics.startSystem(node.props.physics); 22 | } 23 | 24 | treeMethods.clearChildren(node, tree); 25 | treeMethods.initChildren(node, tree); 26 | }, 27 | update: function () { 28 | for (var i = 0; i < node._updateMethods.length; i++) { 29 | node._updateMethods[i](context); 30 | } 31 | } 32 | }, 33 | game = treeUtils.game(tree), 34 | state = game.state.add(props.name, stateImpl), 35 | context = { 36 | game: game, 37 | state: state, 38 | nodes: tree.byname 39 | }; 40 | 41 | node.obj = state; 42 | node.context = context; 43 | }; 44 | 45 | module.exports = { 46 | init: null, 47 | onChildrenInit: onChildrenInit, 48 | kill: null, 49 | deferredInit: true 50 | }; 51 | -------------------------------------------------------------------------------- /src/impl/types/text.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var treeUtils = require('../tree-utils'), 4 | textPropertes = require('../properties/base/Phaser.Text'), 5 | 6 | updateText = treeUtils.genPropertyMapUpdate(textPropertes), 7 | 8 | initText = function (node, tree) { 9 | var props = node.props, 10 | text = new Phaser.Text(treeUtils.game(tree), 0, 0, props.text, props.style), 11 | container = treeUtils.parent(node, tree).obj, 12 | x = props.x || (props.align && (container.width - text.width) * (props.align.indexOf('right') >= 0 ? 1 : 13 | (props.align.indexOf('center') >= 0 ? .5 : 0))) || 0, 14 | y = props.y || (props.align && (container.height - text.height) * (props.align.indexOf('bottom') >= 0 ? 1 : 15 | (props.align.indexOf('middle') >= 0 ? .5 : 0))) || 0; 16 | 17 | text.x = x; 18 | text.y = y; 19 | node.obj = text; 20 | treeUtils.addDisplayObject(node, tree); 21 | }, 22 | 23 | killText = function (node, tree) { 24 | node.obj.kill(); 25 | }; 26 | 27 | module.exports = { 28 | init: initText, 29 | kill: killText, 30 | update: updateText 31 | }; 32 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | var createReactAnything = require('react-anything'); 3 | var phaserImplementation = require('./phaser-implementation'); 4 | 5 | var ReactPhaser = createReactAnything(phaserImplementation); 6 | var React = ReactPhaser.React; 7 | 8 | React.render = ReactPhaser.render; 9 | 10 | module.exports = React; 11 | -------------------------------------------------------------------------------- /src/native.js: -------------------------------------------------------------------------------- 1 | 2 | var createReactAnything = require('react-anything/src/native'); 3 | var phaserImplementation = require('./phaser-implementation'); 4 | 5 | var ReactPhaser = createReactAnything(phaserImplementation); 6 | var React = ReactPhaser.React; 7 | 8 | React.render = ReactPhaser.render; 9 | 10 | module.exports = React; 11 | -------------------------------------------------------------------------------- /src/node-management/node-init-adapter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016-present, Eloy Villasclaras 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | var create = function (nodeTypes) { 12 | 13 | var transactionListeners = [], 14 | 15 | parentInitd = function (node, tree) { 16 | var parent = tree.nodes[node.parent]; 17 | return !parent || parent.initialized; 18 | }, 19 | 20 | invoke = function (method, node, a, b, c) { 21 | var nodeType = nodeTypes[node.tag], 22 | f = nodeType && nodeType[method]; 23 | 24 | if (f) { 25 | f(node, a, b, c); 26 | } 27 | }, 28 | 29 | initImmediately = function (node) { 30 | var nodeType = nodeTypes[node.tag]; 31 | return !nodeType || !nodeTypes[node.tag].deferredInit; 32 | }, 33 | 34 | init = function (method, node, tree) { 35 | node.initialized = true; 36 | invoke(method, node, tree, methods); 37 | if (node.obj) { 38 | node.obj.rnodeid = node.id; 39 | } 40 | }, 41 | 42 | initChildren = function (parent, tree, options) { 43 | var _options = options || {}, 44 | include = _options.include, 45 | exclude = _options.exclude; 46 | 47 | for (var i = 0; i < parent.children.length; i++) { 48 | var child = tree.nodes[parent.children[i]], 49 | doInit = !child.initialized && 50 | (!include || include.indexOf(child.tag) >= 0) && 51 | (!exclude || exclude.indexOf(child.tag) < 0); 52 | 53 | if (doInit) { 54 | if (initImmediately(child)) { 55 | init('init', child, tree); 56 | methods.initChildren(child, tree); 57 | invoke('onChildrenInit', child, tree, methods); 58 | } else { 59 | init('onChildrenInit', child, tree, methods); 60 | } 61 | } 62 | } 63 | }, 64 | 65 | clearChildren = function (parent, tree, options) { 66 | var _options = options || {}, 67 | include = _options.include, 68 | exclude = _options.exclude; 69 | 70 | for (var i = 0; i < parent.children.length; i++) { 71 | var child = tree.nodes[parent.children[i]], 72 | shouldClear = child.initialized && 73 | (!include || include.indexOf(child.tag) >= 0) && 74 | (!exclude || exclude.indexOf(child.tag) < 0); 75 | 76 | if (shouldClear) { 77 | invoke('clear', tree, methods); 78 | child.initialized = false; 79 | if (child.children.length > 0) { 80 | clearChildren(child, tree); 81 | } 82 | } 83 | } 84 | }, 85 | 86 | requestTransactionNofitication = function (nodeid) { 87 | if (transactionListeners.indexOf(nodeid) < 0) { 88 | transactionListeners.push(nodeid); 89 | } 90 | }, 91 | 92 | cancelTransactionNofitication = function (nodeid) { 93 | var index = transactionListeners.indexOf(nodeid); 94 | if (index >= 0) { 95 | transactionListeners.splice(index, 1); 96 | } 97 | }, 98 | 99 | methods = { 100 | initChildren: initChildren, 101 | clearChildren: clearChildren, 102 | requestTransactionNofitication: requestTransactionNofitication, 103 | cancelTransactionNofitication: cancelTransactionNofitication 104 | }, 105 | 106 | impl = { 107 | components: { 108 | mount: function (node, tree) { 109 | if (initImmediately(node) && parentInitd(node, tree)) { 110 | init('init', node, tree); 111 | } 112 | }, 113 | childrenMount: function (node, tree) { 114 | if (node.initialized) { 115 | invoke('onChildrenInit', node, tree, methods); 116 | } else if (parentInitd(node, tree)) { 117 | init('onChildrenInit', node, tree, methods); 118 | } 119 | }, 120 | unmount: function (node, tree) { 121 | invoke('kill', node, tree); 122 | }, 123 | update: function (node, prevProps, tree) { 124 | invoke('update', node, prevProps, tree, methods); 125 | } 126 | }, 127 | transaction: { 128 | close: function (tree) { 129 | if (transactionListeners.length > 0) { 130 | for (var i = 0; i < transactionListeners.length; i++) { 131 | var node = tree.nodes[transactionListeners[i]]; 132 | if (node) { 133 | invoke('notifyTransaction', node, tree); 134 | } 135 | } 136 | transactionListeners = []; 137 | } 138 | } 139 | } 140 | }; 141 | 142 | return impl; 143 | } 144 | ; 145 | 146 | module.exports = create; 147 | -------------------------------------------------------------------------------- /src/node-management/node-tree-adapter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016-present, Eloy Villasclaras 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | var create = function (impl) { 12 | var tree = { 13 | root: null, 14 | nodes: {}, 15 | byname: {} 16 | }, 17 | treeImpl = { 18 | components: { 19 | mount: function (id, tag, props, parent) { 20 | var node = { 21 | id: id, 22 | tag: tag, 23 | props: props, 24 | parent: parent && parent.id, 25 | children: [], 26 | initialized: false 27 | }; 28 | 29 | if (parent) { 30 | parent.children.push(id); 31 | } else { 32 | tree.root = node; 33 | } 34 | tree.nodes[id] = node; 35 | 36 | if (props.name) { 37 | tree.byname[props.name] = node; 38 | } 39 | 40 | impl.components.mount(node, tree); 41 | return node; 42 | }, 43 | childrenMount: function (node) { 44 | impl.components.childrenMount(node, tree); 45 | }, 46 | unmount: function (node) { 47 | impl.components.unmount(node, tree); 48 | delete tree.nodes[node.id]; 49 | if (node.parent) { 50 | var parent = tree.nodes[node.parent]; 51 | parent.children.splice(parent.children.indexOf(node.id), 1); 52 | } 53 | delete tree.nodes[node.id]; 54 | if (node === tree.root) { 55 | tree.root = null; 56 | } 57 | 58 | if (node.props.name) { 59 | delete tree.byname[node.props.name]; 60 | } 61 | }, 62 | update: function (node, nextProps, prevProps) { 63 | node.props = nextProps; 64 | impl.components.update(node, prevProps, tree); 65 | if (nextProps.name !== prevProps.name) { 66 | if (prevProps.name) { 67 | delete tree.byname[prevProps.name]; 68 | } 69 | if (nextProps.name) { 70 | tree.byname[nextProps.name] = node; 71 | } 72 | } 73 | } 74 | }, 75 | transaction: { 76 | initialize: impl.transaction.initialize && function () { 77 | impl.transaction.initialize(tree); 78 | }, 79 | close: impl.transaction.close && function () { 80 | impl.transaction.close(tree); 81 | } 82 | } 83 | }; 84 | 85 | return treeImpl; 86 | }; 87 | 88 | module.exports = create; 89 | -------------------------------------------------------------------------------- /src/phaser-implementation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016-present, Eloy Villasclaras 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | var createTreeImpl = require('./node-management/node-tree-adapter'), 12 | createInitAdapter = require('./node-management/node-init-adapter'), 13 | nodeTypes = require('./impl/types'); 14 | 15 | module.exports = createTreeImpl(createInitAdapter(nodeTypes)); 16 | -------------------------------------------------------------------------------- /tests/node-management/node-init-adapter-test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var chai = require("chai"); 3 | var sinon = require("sinon"); 4 | var sinonChai = require("sinon-chai"); 5 | var expect = chai.expect; 6 | chai.use(sinonChai); 7 | 8 | var createInitmpl = require('../../src/node-management/node-init-adapter'); 9 | var createTreeImpl = require('../../src/node-management/node-tree-adapter'); 10 | 11 | describe('node init adapter', function () { 12 | var nodeTypes, impl, treeImpl; 13 | 14 | beforeEach(function () { 15 | nodeTypes = { 16 | a: { 17 | init: sinon.spy(), 18 | onChildrenInit: sinon.spy() 19 | }, 20 | b: { 21 | init: sinon.spy(), 22 | onChildrenInit: sinon.spy() 23 | }, 24 | c: { 25 | init: sinon.spy(), 26 | onChildrenInit: sinon.spy() 27 | } 28 | }; 29 | impl = createInitmpl(nodeTypes); 30 | treeImpl = createTreeImpl(impl); 31 | }); 32 | 33 | 34 | it('should invoke node init by default', function () { 35 | var a = treeImpl.components.mount(1, 'a', {}, null); 36 | expect(nodeTypes.a.init).to.have.been.calledOnce; 37 | expect(nodeTypes.a.init).to.have.been.calledWith(a); 38 | }); 39 | 40 | it('should invoke node init on children by default', function () { 41 | var a = treeImpl.components.mount(1, 'a', {}, null), 42 | b = treeImpl.components.mount(2, 'a', {}, a); 43 | 44 | expect(nodeTypes.a.init).to.have.been.calledTwice; 45 | expect(nodeTypes.a.init.getCall(0)).to.have.been.calledWith(a); 46 | expect(nodeTypes.a.init.getCall(1)).to.have.been.calledWith(b); 47 | }); 48 | 49 | it('should invoke mount children', function () { 50 | var a = treeImpl.components.mount(1, 'a', {}, null), 51 | b = treeImpl.components.mount(2, 'a', {}, a); 52 | 53 | treeImpl.components.childrenMount(a); 54 | 55 | expect(nodeTypes.a.onChildrenInit).to.have.been.calledOnce; 56 | expect(nodeTypes.a.onChildrenInit.getCall(0)).to.have.been.calledWith(a); 57 | }); 58 | 59 | it('should not init children for deferred parents', function () { 60 | nodeTypes.a.deferredInit = true; 61 | 62 | var a = treeImpl.components.mount(1, 'a', {}, null), 63 | b = treeImpl.components.mount(2, 'b', {}, a); 64 | treeImpl.components.childrenMount(a); 65 | 66 | expect(nodeTypes.a.init).not.to.have.been.calledOnce; 67 | expect(nodeTypes.a.onChildrenInit).to.have.been.calledOnce; 68 | expect(nodeTypes.b.init).not.to.have.been.called; 69 | }); 70 | 71 | it('should not init children for deferred parents', function () { 72 | nodeTypes.a.deferredInit = true; 73 | 74 | var a = treeImpl.components.mount(1, 'a', {}, null), 75 | b = treeImpl.components.mount(2, 'b', {}, a); 76 | treeImpl.components.childrenMount(a); 77 | 78 | expect(nodeTypes.a.init).not.to.have.been.calledOnce; 79 | expect(nodeTypes.a.onChildrenInit).to.have.been.calledOnce; 80 | expect(nodeTypes.b.init).not.to.have.been.called; 81 | 82 | expect(a.initialized).to.equal(true); 83 | expect(b.initialized).to.equal(false); 84 | }); 85 | 86 | 87 | it('should init children for deferred parents on request', function () { 88 | nodeTypes.a.deferredInit = true; 89 | nodeTypes.a.onChildrenInit = sinon.spy(function (node, tree, implMethods) { 90 | implMethods.initChildren(node, tree); 91 | }); 92 | 93 | var a = treeImpl.components.mount(1, 'a', {}, null), 94 | b = treeImpl.components.mount(2, 'b', {}, a); 95 | treeImpl.components.childrenMount(a); 96 | 97 | expect(nodeTypes.a.init).not.to.have.been.calledOnce; 98 | expect(nodeTypes.a.onChildrenInit).to.have.been.calledOnce; 99 | expect(nodeTypes.b.init).to.have.been.calledOnce; 100 | }); 101 | 102 | it('should not init nested children by default, when deferred', function () { 103 | nodeTypes.a.deferredInit = true; 104 | nodeTypes.a.onChildrenInit = sinon.spy(function (node, tree, implMethods) { 105 | implMethods.initChildren(node, tree); 106 | }); 107 | 108 | nodeTypes.b.deferredInit = true; 109 | nodeTypes.b.onChildrenInit = sinon.spy(function (node, tree, implMethods) { 110 | }); 111 | 112 | var a1 = treeImpl.components.mount(1, 'a', {}, null), 113 | a2 = treeImpl.components.mount(2, 'b', {}, a1), 114 | b = treeImpl.components.mount(3, 'c', {}, a2); 115 | treeImpl.components.childrenMount(a1); 116 | 117 | expect(nodeTypes.a.init).not.to.have.been.called; 118 | expect(nodeTypes.a.onChildrenInit).to.have.been.calledOnce; 119 | expect(nodeTypes.b.init).not.to.have.been.called; 120 | expect(nodeTypes.b.onChildrenInit).to.have.been.calledOnce; 121 | expect(nodeTypes.b.init).not.to.have.been.calledOnce; 122 | }); 123 | 124 | it('should filter children on deferred init children'); 125 | 126 | }); 127 | -------------------------------------------------------------------------------- /tests/node-management/node-tree-adapter-test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var chai = require("chai"); 3 | var sinon = require("sinon"); 4 | var sinonChai = require("sinon-chai"); 5 | var expect = chai.expect; 6 | chai.use(sinonChai); 7 | 8 | var createNativeImpl = require('../../src/node-management/node-tree-adapter'); 9 | 10 | describe('node tree adapter', function () { 11 | 12 | var nodes, nativeImpl; 13 | 14 | beforeEach(function () { 15 | nodes = { 16 | components: { 17 | mount: sinon.spy(), 18 | childrenMount: sinon.spy(), 19 | update: sinon.spy(), 20 | unmount: sinon.spy() 21 | }, 22 | transaction: {} 23 | }; 24 | nativeImpl = createNativeImpl(nodes); 25 | }); 26 | 27 | it('should mount root node', function () { 28 | var node = nativeImpl.components.mount(1, 'a', {}, null); 29 | 30 | expect(node).to.deep.eql({ 31 | id: 1, 32 | tag: 'a', 33 | props: {}, 34 | parent: null, 35 | children: [], 36 | initialized: false 37 | }); 38 | 39 | expect(nodes.components.mount).to.have.been.calledOnce; 40 | expect(nodes.components.mount).to.have.been.calledWith({ 41 | id: 1, 42 | tag: 'a', 43 | props: {}, 44 | parent: null, 45 | children: [], 46 | initialized: false 47 | }); 48 | }); 49 | 50 | it('should mount child root node', function () { 51 | var a = nativeImpl.components.mount(1, 'a', {}, null), 52 | b = nativeImpl.components.mount(2, 'b', {}, a); 53 | 54 | expect(a).to.deep.eql({ 55 | id: 1, 56 | tag: 'a', 57 | props: {}, 58 | parent: null, 59 | children: [2], 60 | initialized: false 61 | }); 62 | 63 | expect(b).to.deep.eql({ 64 | id: 2, 65 | tag: 'b', 66 | props: {}, 67 | parent: 1, 68 | children: [], 69 | initialized: false 70 | }); 71 | 72 | expect(nodes.components.mount).to.have.been.calledTwice; 73 | expect(nodes.components.mount.getCall(0)).to.have.been.calledWith(a); 74 | expect(nodes.components.mount.getCall(1)).to.have.been.calledWith(b); 75 | }); 76 | 77 | it('should notify after children mounted', function () { 78 | var a = nativeImpl.components.mount(1, 'a', {}, null), 79 | b = nativeImpl.components.mount(2, 'b', {}, a); 80 | 81 | nativeImpl.components.childrenMount(a); 82 | 83 | 84 | expect(nodes.components.childrenMount).to.have.been.calledOnce; 85 | expect(nodes.components.childrenMount).to.have.been.calledWith(a); 86 | }); 87 | 88 | it('should notify node update', function () { 89 | var a = nativeImpl.components.mount(1, 'a', {p: 0}, null); 90 | nativeImpl.components.update(a, {p: 1}, a.props); 91 | 92 | expect(nodes.components.update).to.have.been.calledOnce; 93 | expect(nodes.components.update).to.have.been.calledWith(a, {p: 0}); 94 | expect(a.props).to.eql({p: 1}); 95 | }); 96 | 97 | it('should unmount node from tree', function () { 98 | var a = nativeImpl.components.mount(1, 'a', {}, null), 99 | b = nativeImpl.components.mount(2, 'b', {}, a); 100 | 101 | expect(a.children).to.eql([2]); 102 | 103 | nativeImpl.components.unmount(b); 104 | 105 | expect(a.children).to.eql([]); 106 | 107 | expect(nodes.components.unmount).to.have.been.calledOnce; 108 | expect(nodes.components.unmount).to.have.been.calledWith(b); 109 | }); 110 | }); 111 | --------------------------------------------------------------------------------