# Contributing

This document is intended for developers interest in making contributions to Preact and document our internal processes like releasing a new version.

## Getting Started

This steps will help you to set up your development environment. That includes all dependencies we use to build Preact and developer tooling like git commit hooks.

1. Clone the git repository: `git clone git@github.com:preactjs/preact.git`
2. Go into the cloned folder: `cd preact/`
3. Install all dependencies: `npm install`

## The Repo Structure

This repository contains Preact itself, as well as several addons like the debugging package for example. This is reflected in the directory structure of this repository. Each package has a `src/` folder where the source code can be found, a `test` folder for all sorts of tests that check if the code in `src/` is correct, and a `dist/` folder where you can find the bundled artifacts. Note that the `dist/` folder may not be present initially. It will be created as soon as you run any of the build scripts inside `package.json`. More on that later ;)

A quick overview of our repository:

```bash
# The repo root (folder where you cloned the repo into)
/
  src/  # Source code of our core
	test/ # Unit tests for core
	dist/ # Build artifacts for publishing on npm (may not be present)

	# Sub-package, can be imported via `preact/compat` by users.
	# Compat stands for react-compatibility layer which tries to mirror the
	# react API as close as possible (mostly legacy APIs)
	compat/
	  src/  # Source code of the compat addon
		test/ # Tests related to the compat addon
		dist/ # Build artifacts for publishing on npm (may not be present)

	# Sub-package, can be imported via `preact/hooks` by users.
	# The hooks API is an effect based API to deal with component lifcycles.
	# It's similar to hooks in React
	hooks/
	  src/  # Source code of the hooks addon
		test/ # Tests related to the hooks addon
		dist/ # Build artifacts for publishing on npm (may not be present)

	# Sub-package, can be imported via `preact/debug` by users.
	# Includes debugging warnings and error messages for common mistakes found
	# in Preact application. Also hosts the devtools bridge
	debug/
	  src/  # Source code of the debug addon
		test/ # Tests related to the debug addon
		dist/ # Build artifacts for publishing on npm (may not be present)

	# Sub-package, can be imported via `preact/test-utils` by users.
	# Provides helpers to make testing Preact applications easier
	test-utils/
	  src/  # Source code of the test-utils addon
		test/ # Tests related to the test-utils addon
		dist/ # Build artifacts for publishing on npm (may not be present)

	# A demo application that we use to debug tricky errors and play with new
	# features.
	demo/

	# Contains build scripts and dependencies for development
	package.json
```

_Note: The code for rendering Preact on the server lives in another repo and is a completely separate npm package. It can be found here: [https://github.com/preactjs/preact-render-to-string](https://github.com/preactjs/preact-render-to-string)_

### What does `mangle.json` do?

It's a special file that can be used to specify how `terser` (previously known as `uglify`) will minify variable names. Because each sub-package has it's own distribution files we need to ensure that the variable names stay consistent across bundles.

## What does `options.js` do?

Unique to Preact we do support several ways to hook into our renderer. All our addons use that to inject code at different stages of a render process. They are documented in our typings in `internal.d.ts`. The core itself doesn't make use of them, which is why the file only contains an empty `object`.

## Important Branches

We merge every PR into the `master` branch which is the one that we'll use to publish code to npm. For the previous Preact release line we have a branch called `8` which is in maintenance mode. As a new contributor you won't have to deal with that ;)

## Creating your first Pull-Request

We try to make it as easy as possible to contribute to Preact and make heavy use of GitHub's "Draft PR" feature which tags Pull-Requests (short = PR) as work in progress. PRs tend to be published as soon as there is an idea that the developer deems worthwile to include into Preact and has written some rough code. The PR doesn't have to be perfect or anything really ;)

Once a PR or a Draft PR has been created our community typically joins the discussion about the proposed change. Sometimes that includes ideas for test cases or even different ways to go about implementing a feature. Often this also includes ideas on how to make the code smaller. We usually refer to the latter as "code-golfing" or just "golfing".

When everything is good to go someone will approve the PR and the changes will be merged into the `master` branch and we usually cut a release a few days/ a week later.

_The big takeaway for you here is, that we will guide you along the way. We're here to help to make a PR ready for approval!_

The short summary is:

1. Make changes and submit a PR
2. Modify change according to feedback (if there is any)
3. PR will be merged into `master`
4. A new release will be cut (every 2-3 weeks).

## Commonly used scripts for contributions

Scripts can be executed via `npm run [script]` or `yarn [script]` respectively.

- `build` - compiles all packages ready for publishing to npm
- `build:core` - builds just Preact itself
- `build:debug` - builds the debug addon only
- `build:hooks` - builds the hook addon only
- `build:test-utils` - builds the test-utils addon only
- `test:ts` - Run all tests for TypeScript definitions
- `test:karma` - Run all unit/integration tests.
- `test:karma:watch` - Same as above, but it will automatically re-run the test suite if a code change was detected.

But to be fair, the only ones we use ourselves are `build` and `test:karma:watch`. The other ones are mainly used on our CI pipeline and we rarely use them.

_Note: Both `test:karma` and `test:karma:watch` listen to the environment variable `COVERAGE=true`. Disabling code coverage can significantly speed up the time it takes to complete the test suite._

_Note2: The test suite is based on `karma` and `mocha`. Individual tests can be executed by appending `.only`:_

```jsx
it.only('should test something', () => {
	expect(1).to.equal(1);
});
```

## Common terminology and variable names

- `vnode` -> shorthand for `virtual-node` which is an object that specifies how a Component or DOM-node looks like
- `commit` -> A commit is the moment in time when you flush all changes to the DOM
- `c` -> The variable `c` always refers to a `component` instance throughout our code base.
- `diff/diffing` -> Diffing describes the process of comparing two "things". In our case we compare the previous `vnode` tree with the new one and apply the delta to the DOM.
- `root` -> The topmost node of a `vnode` tree

## Tips for getting to know the code base

- Check the JSDoc block right above the function definition to understand what it does. It contains a short description of each function argument and what it does.
- Check the callsites of a function to understand how it's used. Modern editors/IDEs allow you to quickly find those, or use the plain old search feature instead.

## FAQ

### Why does the JSDoc use TypeScript syntax to specify types?

Several members of the team are very fond of TypeScript and we wanted to leverage as many of its advantages, like improved autocompletion, for Preact. We even attempted to port Preact to TypeScript a few times, but we ran into many issues with the DOM typings. Those would force us to fill our codebase with many `any` castings, making our code very noisy.

Luckily TypeScript has a mode where it can somewhat reliably typecheck JavaScript code by reusing the types defined in JSDoc blocks. It's not perfect and it often has trouble inferring the correct types the further one strays away from the function arguments, but it's good enough that it helps us a lot with autocompletion. Another plus is that we can make sure that our TypeScript definitons are correct at the same time.

Check out the [official TypeScript documentation](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) for more information.

_Note that we have separate tests for our TypeScript definition files. We only use `ts-check` for local development and don't check it anywhere else like on the CI._

### Why does the code base often use `let` instead of `const`?

There is no real reason for that other a historical one. Back before auto-formatting via prettier was a thing and minifiers weren't as advanced as they are today we used a pretty terse code-style. The code-style deliberately was aimed at making code look as concise and short as possible. The `let` keyword is a bit shorter than `const` to write, so we only used that. This was done only for stylistic reasons.

This helped our minds to not lose sight of focusing on size, but made it difficult for newcomers to start contributing to Preact. For that reason alone we switched to `prettier` and loosened our rule regarding usage of `let` or `const`. Today we use both, but you can still find many existing places where `let` is still in use.

In the end there is no effect on size regardless if you use `const`, `let` or use both. Our code is downtranspiled to `ES5` for npm so both will be replaced with `var` anyways. Therefore it doesn't really matter at all which one is used in our codebase.

This will only become important once shipping modern JavaScript code on npm becomes a thing and bundlers follow suit.

## How to create a good bug report

To be able to fix issues we need to see them on our machine. This is only possible when we can reproduce the error. The easiest way to do that is narrow down the problem to specific components or combination of them. This can be done by removing as much unrelated code as possible.

The perfect way to do that is to make a [codesandbox](https://codesandbox.io/). That way you can easily share the problematic code and ensure that others can see the same issue you are seeing.

For us a [codesandbox](https://codesandbox.io/) says more than a 1000 words :tada:

## I have more questions on how to contribute to Preact. How can I reach you?

We closely watch our issues and have a pretty active [Slack workspace](https://preact-slack.now.sh/). Nearly all our communication happens via these two forms of communication.

## Releasing Preact (Maintainers only)

This guide is intended for core team members that have the necessary
rights to publish new releases on npm.

1. [Write the release notes](#writing-release-notes) and keep them as a draft in GitHub
   1. I'd recommend writing them in an offline editor because each edit to a draft will change the URL in GitHub.
2. Make a PR where **only** the version number is incremented in `package.json` (note: We follow `SemVer` conventions)
3. Wait until the PR is approved and merged.
4. Switch back to the `master` branch and pull the merged PR
5. Run `npm run build && npm publish`
   1. Make sure you have 2FA enabled in npm, otherwise the above command will fail.
   2. If you're doing a pre-release add `--tag next` to the `npm publish` command to publish it under a different tag (default is `latest`)
6. Publish the release notes and create the correct git tag.
7. Tweet it out

## Legacy Releases (8.x)

> **ATTENTION:** Make sure that you've cleared the project correctly
> when switching from a 10.x branch.

0. Run `rm -rf dist node_modules && npm i` to make sure to have the correct dependencies.

Apart from that it's the same as above.

## Writing release notes

The release notes have become a sort of tiny blog post about what's
happening in preact-land. The title usually has this format:

```txt
Version Name
```

Example:

```txt
10.0.0-beta.1 Los Compresseros
```

The name is optional, we just have fun finding creative names :wink:

To keep them interesting we try to be as
concise as possible and to just reflect where we are. There are some
rules we follow while writing them:

- Be nice, use a positive tone. Avoid negative words
- Show, don't just tell.
- Be honest.
- Don't write too much, keep it simple and short.
- Avoid making promises and don't overpromise. That leads to unhappy users
- Avoid framework comparisons if possible
- Highlight awesome community contributions (or great issue reports)
- If in doubt, praise the users.

After this section we typically follow with a changelog part that's
divided into 4 groups in order of importance for the user:

- Features
- Bug Fixes
- Typings
- Maintenance

We generate it via this handy cli program: [changelogged](https://github.com/marvinhagemeister/changelogged). It will collect and format
the descriptions of all PRs that have been merged between two tags.
The usual command is `changelogged 10.0.0-rc.2..HEAD` similar to how
you'd diff two points in time with git. This will get you 90% there,
but you still need to divide it into groups. It's also a good idea
to unify the formatting of the descriptions, so that they're easier
to read and don't look like a mess.


it('should mask value on password input elements', () => {

render(<input value="xyz" type="password" />, scratch);

keep keep keep keep add keep keep keep keep

it('should set value attribute on custom elements without value property', () => {

render(<o-input value="test" />, scratch);

import { act } from 'preact/test-utils';
import { createElement, render, Fragment, Component } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useEffect, useState } from 'preact/hooks';
import { useEffectAssertions } from './useEffectAssertions.test';
import { scheduleEffectAssert } from '../_util/useEffectUtil';

/** @jsx createElement */

describe('useEffect', () => {
	/** @type {HTMLDivElement} */
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	useEffectAssertions(useEffect, scheduleEffectAssert);

	it('calls the effect immediately if another render is about to start', () => {
		const cleanupFunction = sinon.spy();
		const callback = sinon.spy(() => cleanupFunction);

		function Comp() {
			useEffect(callback);
			return null;
		}

		render(<Comp />, scratch);
		render(<Comp />, scratch);

		expect(cleanupFunction).to.be.not.called;
		expect(callback).to.be.calledOnce;

		render(<Comp />, scratch);

		expect(cleanupFunction).to.be.calledOnce;
		expect(callback).to.be.calledTwice;
	});

	it('cancels the effect when the component get unmounted before it had the chance to run it', () => {
		const cleanupFunction = sinon.spy();
		const callback = sinon.spy(() => cleanupFunction);

		function Comp() {
			useEffect(callback);
			return null;
		}

		render(<Comp />, scratch);
		render(null, scratch);

		return scheduleEffectAssert(() => {
			expect(cleanupFunction).to.not.be.called;
			expect(callback).to.not.be.called;
		});
	});

	it('should execute multiple effects in same component in the right order', () => {
		let executionOrder = [];
		const App = ({ i }) => {
			executionOrder = [];
			useEffect(() => {
				executionOrder.push('action1');
				return () => executionOrder.push('cleanup1');
			}, [i]);
			useEffect(() => {
				executionOrder.push('action2');
				return () => executionOrder.push('cleanup2');
			}, [i]);
			return
Test
;
		};
		act(() => render(<App i={0} />, scratch));
		act(() => render(<App i={2} />, scratch));
		expect(executionOrder).to.deep.equal([
			'cleanup1',
			'cleanup2',
			'action1',
			'action2'
		]);
	});

	it('should throw an error upwards', () => {
		const spy = sinon.spy();
		let errored = false;

		const Page1 = () => {
			const [state, setState] = useState('loading');
			useEffect(() => {
				setState('loaded');
			}, []);
			return
{state}
;
		};

		const Page2 = () => {
			useEffect(() => {
				throw new Error('err');
			}, []);
			return
invisible
;
		};

		class App extends Component {
			componentDidCatch(err) {
				spy();
				errored = err;
			}

			render(props, state) {
				if (errored) {
					return
Error
;
				}

				return <Fragment>{props.page === 1 ? <Page1 /> : <Page2 />}</Fragment>;
			}
		}

		act(() => render(<App page={1} />, scratch));
		expect(spy).to.not.be.called;
		expect(scratch.innerHTML).to.equal('
loaded
');

		act(() => render(<App page={2} />, scratch));
		expect(spy).to.be.calledOnce;
		expect(scratch.innerHTML).to.equal('
Error
');
		errored = false;

		act(() => render(<App page={1} />, scratch));
		expect(spy).to.be.calledOnce;
		expect(scratch.innerHTML).to.equal('
loaded
');
	});

	it('should throw an error upwards from return', () => {
		const spy = sinon.spy();
		let errored = false;

		const Page1 = () => {
			const [state, setState] = useState('loading');
			useEffect(() => {
				setState('loaded');
			}, []);
			return
{state}
;
		};

		const Page2 = () => {
			useEffect(() => {
				return () => {
					throw new Error('err');
				};
			}, []);
			return
Load
;
		};

		class App extends Component {
			componentDidCatch(err) {
				spy();
				errored = err;
			}

			render(props, state) {
				if (errored) {
					return
Error
;
				}

				return <Fragment>{props.page === 1 ? <Page1 /> : <Page2 />}</Fragment>;
			}
		}

		act(() => render(<App page={2} />, scratch));
		expect(scratch.innerHTML).to.equal('
Load
');

		act(() => render(<App page={1} />, scratch));
		expect(spy).to.be.calledOnce;
		expect(scratch.innerHTML).to.equal('
Error
');
	});

	it('catches errors when error is invoked during render', () => {
		const spy = sinon.spy();
		let errored;

		function Comp() {
			useEffect(() => {
				throw new Error('hi');
			});
			return null;
		}

		class App extends Component {
			componentDidCatch(err) {
				spy();
				errored = err;
			}

			render(props, state) {
				if (errored) {
					return
Error
;
				}

				return <Comp />;
			}
		}

		render(<App />, scratch);
		act(() => {
			render(<App />, scratch);
		});
		expect(spy).to.be.calledOnce;
		expect(scratch.innerHTML).to.equal('
Error
');
	});
});


import { createElement, Component, render } from 'preact';
import { setupScratch, teardown } from '../_util/helpers';
import { logCall, clearLog, getLog } from '../_util/logCall';
import { div } from '../_util/dom';

/** @jsx createElement */

describe('keys', () => {
	/** @type {HTMLDivElement} */
	let scratch;

	/** @type {string[]} */
	let ops;

	function createStateful(name) {
		return class Stateful extends Component {
			componentDidUpdate() {
				ops.push(`Update ${name}`);
			}
			componentDidMount() {
				ops.push(`Mount ${name}`);
			}
			componentWillUnmount() {
				ops.push(`Unmount ${name}`);
			}
			render() {
				return
{name}
;
			}
		};
	}

	/** @type {(props: {values: any[]}) => any} */
	const List = props => (

			{props.values.map(value => (

{value}

			))}

	);

	/**
	 * Move an element in an array from one index to another
	 * @param {any[]} values The array of values
	 * @param {number} from The index to move from
	 * @param {number} to The index to move to
	 */
	function move(values, from, to) {
		const value = values[from];
		values.splice(from, 1);
		values.splice(to, 0, value);
	}

	before(() => {
		logCall(Element.prototype, 'appendChild');
		logCall(Element.prototype, 'insertBefore');
		logCall(Element.prototype, 'removeChild');
		logCall(Element.prototype, 'remove');
	});

	beforeEach(() => {
		scratch = setupScratch();
		ops = [];
	});

	afterEach(() => {
		teardown(scratch);
		clearLog();
	});

	// https://fb.me/react-special-props
	it('should not pass key in props', () => {
		const Foo = sinon.spy(() => null);
		render(<Foo key="foo" />, scratch);
		expect(Foo.args[0][0]).to.deep.equal({});
	});

	// See preactjs/preact-compat#21
	it('should remove orphaned keyed nodes', () => {
		render(

1

a

b

,
			scratch
		);

		render(

2

b

c

,
			scratch
		);

		expect(scratch.innerHTML).to.equal(
			'

2

b

c

'
		);
	});

	it('should remove keyed nodes (#232)', () => {
		class App extends Component {
			componentDidMount() {
				setTimeout(() => this.setState({ opened: true, loading: true }), 10);
				setTimeout(() => this.setState({ opened: true, loading: false }), 20);
			}

			render({ opened, loading }) {
				return (
					<BusyIndicator id="app" busy={loading}>

This div needs to be here for this to break

						{opened && !loading &&
{[]}
}
					</BusyIndicator>
				);
			}
		}

		class BusyIndicator extends Component {
			render({ children, busy }) {
				return (

						{children && children.length ? (
							children
						) : (

						)}

indicator

indicator

indicator

				);
			}
		}

		render(<App />, scratch);
		render(<App opened loading />, scratch);
		render(<App opened />, scratch);

		const html = String(scratch.firstChild.innerHTML).replace(/ class=""/g, '');
		expect(html).to.equal(
			'
This div needs to be here for this to break

indicator

indicator

indicator

'
		);
	});

	it('should append new keyed elements', () => {
		const values = ['a', 'b'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('ab');

		values.push('c');
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abc');
		expect(getLog()).to.deep.equal([
			'
.appendChild(#text)',
			'
ab.appendChild(
c)'
		]);
	});

	it('should remove keyed elements from the end', () => {
		const values = ['a', 'b', 'c', 'd'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abcd');

		values.pop();
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abc');
		expect(getLog()).to.deep.equal(['
d.remove()']);
	});

	it('should prepend keyed elements to the beginning', () => {
		const values = ['b', 'c'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('bc');

		values.unshift('a');
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abc');
		expect(getLog()).to.deep.equal([
			'
.appendChild(#text)',
			'
bc.insertBefore(
a,
b)'
		]);
	});

	it('should remove keyed elements from the beginning', () => {
		const values = ['z', 'a', 'b', 'c'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('zabc');

		values.shift();
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abc');
		expect(getLog()).to.deep.equal(['
z.remove()']);
	});

	it('should insert new keyed children in the middle', () => {
		const values = ['a', 'c'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('ac');

		values.splice(1, 0, 'b');
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abc');
		expect(getLog()).to.deep.equal([
			'
.appendChild(#text)',
			'
ac.insertBefore(
b,
c)'
		]);
	});

	it('should remove keyed children from the middle', () => {
		const values = ['a', 'b', 'x', 'y', 'z', 'c', 'd'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abxyzcd');

		values.splice(2, 3);
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abcd');
		expect(getLog()).to.deep.equal([
			'
z.remove()',
			'
y.remove()',
			'
x.remove()'
		]);
	});

	it('should swap keyed children efficiently', () => {
		render(<List values={['a', 'b']} />, scratch);
		expect(scratch.textContent).to.equal('ab');

		clearLog();

		render(<List values={['b', 'a']} />, scratch);
		expect(scratch.textContent).to.equal('ba');

		expect(getLog()).to.deep.equal(['
ab.appendChild(
a)']);
	});

	it('should swap existing keyed children in the middle of a list efficiently', () => {
		const values = ['a', 'b', 'c', 'd'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abcd');

		// swap
		move(values, 1, 2);
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('acbd', 'initial swap');
		expect(getLog()).to.deep.equal(
			['
abcd.insertBefore(
b,
d)'],
			'initial swap'
		);

		// swap back
		move(values, 2, 1);
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abcd', 'swap back');
		expect(getLog()).to.deep.equal(
			['
acbd.insertBefore(
c,
d)'],
			'swap back'
		);
	});

	it('should move keyed children to the end of the list', () => {
		const values = ['a', 'b', 'c', 'd'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abcd');

		// move to end
		move(values, 0, values.length - 1);
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('bcda', 'move to end');
		expect(getLog()).to.deep.equal(
			['
abcd.appendChild(
a)'],
			'move to end'
		);

		// move to beginning
		move(values, values.length - 1, 0);
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal('abcd', 'move to beginning');
		expect(getLog()).to.deep.equal(
			['
bcda.insertBefore(
a,
b)'],
			'move to beginning'
		);
	});

	it('should reverse keyed children effectively', () => {
		const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal(values.join(''));

		// reverse list
		values.reverse();
		clearLog();

		render(<List values={values} />, scratch);
		expect(scratch.textContent).to.equal(values.join(''));
		expect(getLog()).to.deep.equal([
			'
abcdefghij.insertBefore(
j,
a)',
			'
jabcdefghi.insertBefore(
i,
a)',
			'
jiabcdefgh.insertBefore(
h,
a)',
			'
jihabcdefg.insertBefore(
g,
a)',
			'
jihgabcdef.appendChild(
e)',
			'
jihgabcdfe.appendChild(
d)',
			'
jihgabcfed.appendChild(
c)',
			'
jihgabfedc.appendChild(
b)',
			'
jihgafedcb.appendChild(
a)'
		]);
	});

	it("should not preserve state when a component's keys are different", () => {
		const Stateful = createStateful('Stateful');

		function Foo({ condition }) {
			return condition ? <Stateful key="a" /> : <Stateful key="b" />;
		}

		ops = [];
		render(<Foo condition />, scratch);
		expect(scratch.innerHTML).to.equal('
Stateful
');
		expect(ops).to.deep.equal(['Mount Stateful'], 'initial mount');

		ops = [];
		render(<Foo condition={false} />, scratch);
		expect(scratch.innerHTML).to.equal('
Stateful
');
		expect(ops).to.deep.equal(
			['Unmount Stateful', 'Mount Stateful'],
			'switching keys 1'
		);

		ops = [];
		render(<Foo condition />, scratch);
		expect(scratch.innerHTML).to.equal('
Stateful
');
		expect(ops).to.deep.equal(
			['Unmount Stateful', 'Mount Stateful'],
			'switching keys 2'
		);
	});

	it('should not preserve state between an unkeyed and keyed component', () => {
		// React and Preact v8 behavior: https://codesandbox.io/s/57prmy5mx

		const Stateful = createStateful('Stateful');

		function Foo({ keyed }) {
			return keyed ? <Stateful key="a" /> : <Stateful />;
		}

		ops = [];
		render(<Foo keyed />, scratch);
		expect(scratch.innerHTML).to.equal('
Stateful
');
		expect(ops).to.deep.equal(['Mount Stateful'], 'initial mount with key');

		ops = [];
		render(<Foo keyed={false} />, scratch);
		expect(scratch.innerHTML).to.equal('
Stateful
');
		expect(ops).to.deep.equal(
			['Unmount Stateful', 'Mount Stateful'],
			'switching from keyed to unkeyed'
		);

		ops = [];
		render(<Foo keyed />, scratch);
		expect(scratch.innerHTML).to.equal('
Stateful
');
		expect(ops).to.deep.equal(
			['Unmount Stateful', 'Mount Stateful'],
			'switching from unkeyed to keyed'
		);
	});

	it('should not preserve state when keys change with multiple children', () => {
		// React & Preact v8 behavior: https://codesandbox.io/s/8l3p6lz9kj

		const Stateful1 = createStateful('Stateful1');
		const Stateful2 = createStateful('Stateful2');

		let Stateful1Ref;
		let Stateful2Ref;
		let Stateful1MovedRef;
		let Stateful2MovedRef;

		function Foo({ moved }) {
			return moved ? (

1

					<Stateful1 key="c" ref={c => (Stateful1MovedRef = c)} />

2

					<Stateful2 key="d" ref={c => (Stateful2MovedRef = c)} />

			) : (

1

					<Stateful1 key="a" ref={c => (Stateful1Ref = c)} />

2

					<Stateful2 key="b" ref={c => (Stateful2Ref = c)} />

			);
		}

		const expectedHtml = div([
			div(1),
			div('Stateful1'),
			div(2),
			div('Stateful2')
		]);

		ops = [];
		render(<Foo moved={false} />, scratch);

		expect(scratch.innerHTML).to.equal(expectedHtml);
		expect(ops).to.deep.equal(['Mount Stateful1', 'Mount Stateful2']);
		expect(Stateful1Ref).to.exist;
		expect(Stateful2Ref).to.exist;

		ops = [];
		render(<Foo moved />, scratch);

		expect(scratch.innerHTML).to.equal(expectedHtml);
		expect(ops).to.deep.equal([
			'Unmount Stateful2',
			'Unmount Stateful1',
			'Mount Stateful1',
			'Mount Stateful2'
		]);
		expect(Stateful1MovedRef).to.not.equal(Stateful1Ref);
		expect(Stateful2MovedRef).to.not.equal(Stateful2Ref);

		ops = [];
		render(<Foo moved={false} />, scratch);

		expect(scratch.innerHTML).to.equal(expectedHtml);
		expect(ops).to.deep.equal([
			'Unmount Stateful2',
			'Unmount Stateful1',
			'Mount Stateful1',
			'Mount Stateful2'
		]);
		expect(Stateful1Ref).to.not.equal(Stateful1MovedRef);
		expect(Stateful2Ref).to.not.equal(Stateful2MovedRef);
	});

	it('should preserve state when moving keyed children components', () => {
		// React & Preact v8 behavior: https://codesandbox.io/s/8l3p6lz9kj

		const Stateful1 = createStateful('Stateful1');
		const Stateful2 = createStateful('Stateful2');

		let Stateful1Ref;
		let Stateful2Ref;
		let Stateful1MovedRef;
		let Stateful2MovedRef;

		function Foo({ moved }) {
			return moved ? (

1

					<Stateful2
						key="b"
						ref={c => (c ? (Stateful2MovedRef = c) : undefined)}
					/>

2

					<Stateful1
						key="a"
						ref={c => (c ? (Stateful1MovedRef = c) : undefined)}
					/>

			) : (

1

					<Stateful1 key="a" ref={c => (c ? (Stateful1Ref = c) : undefined)} />

2

					<Stateful2 key="b" ref={c => (c ? (Stateful2Ref = c) : undefined)} />

			);
		}

		const htmlForFalse = div([
			div(1),
			div('Stateful1'),
			div(2),
			div('Stateful2')
		]);

		const htmlForTrue = div([
			div(1),
			div('Stateful2'),
			div(2),
			div('Stateful1')
		]);

		ops = [];
		render(<Foo moved={false} />, scratch);

		expect(scratch.innerHTML).to.equal(htmlForFalse);
		expect(ops).to.deep.equal(['Mount Stateful1', 'Mount Stateful2']);
		expect(Stateful1Ref).to.exist;
		expect(Stateful2Ref).to.exist;

		ops = [];
		render(<Foo moved />, scratch);

		expect(scratch.innerHTML).to.equal(htmlForTrue);
		expect(ops).to.deep.equal(['Update Stateful2', 'Update Stateful1']);
		expect(Stateful1MovedRef).to.equal(Stateful1Ref);
		expect(Stateful2MovedRef).to.equal(Stateful2Ref);

		ops = [];
		render(<Foo moved={false} />, scratch);

		expect(scratch.innerHTML).to.equal(htmlForFalse);
		expect(ops).to.deep.equal(['Update Stateful1', 'Update Stateful2']);
		expect(Stateful1Ref).to.equal(Stateful1MovedRef);
		expect(Stateful2Ref).to.equal(Stateful2MovedRef);
	});

	it('should not preserve state when switching between keyed and unkeyed components as children', () => {
		// React & Preact v8 behavior: https://codesandbox.io/s/8l3p6lz9kj

		const Stateful1 = createStateful('Stateful1');
		const Stateful2 = createStateful('Stateful2');

		let Stateful1Ref;
		let Stateful2Ref;
		let Stateful1MovedRef;
		let Stateful2MovedRef;

		function Foo({ unkeyed }) {
			return unkeyed ? (

1

					<Stateful1 ref={c => (Stateful2MovedRef = c)} />

2

					<Stateful2 ref={c => (Stateful1MovedRef = c)} />

			) : (

1

					<Stateful1 key="a" ref={c => (Stateful1Ref = c)} />

2

					<Stateful2 key="b" ref={c => (Stateful2Ref = c)} />

			);
		}

		const expectedHtml = div([
			div(1),
			div('Stateful1'),
			div(2),
			div('Stateful2')
		]);

		ops = [];
		render(<Foo unkeyed={false} />, scratch);

		expect(scratch.innerHTML).to.equal(expectedHtml);
		expect(ops).to.deep.equal(['Mount Stateful1', 'Mount Stateful2']);
		expect(Stateful1Ref).to.exist;
		expect(Stateful2Ref).to.exist;

		ops = [];
		render(<Foo unkeyed />, scratch);

		expect(scratch.innerHTML).to.equal(expectedHtml);
		expect(ops).to.deep.equal([
			'Unmount Stateful2',
			'Unmount Stateful1',
			'Mount Stateful1',
			'Mount Stateful2'
		]);
		expect(Stateful1MovedRef).to.not.equal(Stateful1Ref);
		expect(Stateful2MovedRef).to.not.equal(Stateful2Ref);

		ops = [];
		render(<Foo unkeyed={false} />, scratch);

		expect(scratch.innerHTML).to.equal(expectedHtml);
		expect(ops).to.deep.equal([
			'Unmount Stateful2',
			'Unmount Stateful1',
			'Mount Stateful1',
			'Mount Stateful2'
		]);
		expect(Stateful1Ref).to.not.equal(Stateful1MovedRef);
		expect(Stateful2Ref).to.not.equal(Stateful2MovedRef);
	});
});


//  A frame based animation (as opposed to a bone based animation)

//  Extract all the frame data into the frames array

this.frames = GetFrames(manager.textureManager, GetValue(config, 'frames', []));

//  The frame rate of playback in frames per second (default 24 if duration is null)

this.frameRate = GetValue(config, 'frameRate', null);

keep keep keep keep keep replace keep keep keep keep

this.frames = GetFrames(manager.textureManager, GetValue(config, 'frames', []), GetValue(config, 'defaultTextureKey', null));

var Frame = require('../AnimationFrame');
var GetValue = require('../../../utils/object/GetValue');

/**
 * [description]
 *
 * @method Phaser.Animations.Animation#getFrames
 * @since 3.0.0
 *
 * @param {[type]} textureManager - [description]
 * @param {[type]} frames - [description]
 *
 * @return {Phaser.Animations.AnimationFrame[]} [description]
 */
var GetFrames = function (textureManager, frames)
{
    //      frames: [
    //          { key: textureKey, frame: textureFrame },
    //          { key: textureKey, frame: textureFrame, duration: float },
    //          { key: textureKey, frame: textureFrame, onUpdate: function }
    //          { key: textureKey, frame: textureFrame, visible: boolean }
    //      ],

    var out = [];
    var prev;
    var animationFrame;
    var index = 1;
    var i;
    var textureKey;

    //  if frames is a string, we'll get all the frames from the texture manager as if it's a sprite sheet
    if (typeof frames === 'string')
    {
        textureKey = frames;

        var texture = textureManager.get(textureKey);
        var frameKeys = texture.getFrameNames();

        frames = [];

        frameKeys.forEach(function (idx, value) {
            frames.push({ key: textureKey, frame: value });
        });
    }

    // console.table(frames);

    if (!Array.isArray(frames) || frames.length === 0)
    {
        return out;
    }

    for (i = 0; i < frames.length; i++)
    {
        var item = frames[i];

        var key = GetValue(item, 'key', null);

        if (!key)
        {
            continue;
        }

        var frame = GetValue(item, 'frame', 0);

        var textureFrame = textureManager.getFrame(key, frame);

        animationFrame = new Frame(key, frame, index, textureFrame);

        animationFrame.duration = GetValue(item, 'duration', 0);
        animationFrame.onUpdate = GetValue(item, 'onUpdate', null);

        var visible = GetValue(item, 'visible', null);

        if (visible !== null)
        {
            animationFrame.setVisible = true;
            animationFrame.visible = visible;
        }

        animationFrame.isFirst = (!prev);

        //  The previously created animationFrame
        if (prev)
        {
            prev.nextFrame = animationFrame;

            animationFrame.prevFrame = prev;
        }

        out.push(animationFrame);

        prev = animationFrame;

        index++;
    }

    if (out.length > 0)
    {
        animationFrame.isLast = true;

        //  Link them end-to-end, so they loop
        animationFrame.nextFrame = out[0];

        out[0].prevFrame = animationFrame;

        //  Generate the progress data

        var slice = 1 / (out.length - 1);

        for (i = 0; i < out.length; i++)
        {
            out[i].progress = i * slice;
        }
    }

    return out;
};

module.exports = GetFrames;


var Debug = {

    setDebug: function (showBody, showVelocity, bodyColor)
    {
        this.debugShowBody = showBody;
        this.debugShowVelocity = showVelocity;
        this.debugBodyColor = bodyColor;

        return this;
    },

    setDebugBodyColor: function (value)
    {
        this.body.debugBodyColor = value;

        return this;
    },

    debugShowBody: {

        get: function ()
        {
            return this.body.debugShowBody;
        },

        set: function (value)
        {
            this.body.debugShowBody = value;
        }

    },

    debugShowVelocity: {

        get: function ()
        {
            return this.body.debugShowVelocity;
        },

        set: function (value)
        {
            this.body.debugShowVelocity = value;
        }

    },

    debugBodyColor: {

        get: function ()
        {
            return this.body.debugBodyColor;
        },

        set: function (value)
        {
            this.body.debugBodyColor = value;
        }

    }

};

module.exports = Debug;


/**
 * [description]
 *
 * @function Phaser.Geom.Point.Invert
 * @since 3.0.0
 *
 * @param {Phaser.Geom.Point} point - [description]
 *
 * @return {Phaser.Geom.Point} [description]
 */
var Invert = function (point)
{
    return point.setTo(point.y, point.x);
};

module.exports = Invert;


//  Phaser.Display.Align

module.exports = {

    In: require('./in'),
    To: require('./to')

};


* @param {[type]} textureManager - [description]

* @param {[type]} frames - [description]

* @return {Phaser.Animations.AnimationFrame[]} [description]

var GetFrames = function (textureManager, frames)

//          { key: textureKey, frame: textureFrame },

//          { key: textureKey, frame: textureFrame, duration: float },

//          { key: textureKey, frame: textureFrame, onUpdate: function }

keep keep keep keep keep replace keep keep keep keep keep

var GetFrames = function (textureManager, frames, defaultTextureKey)


/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* The Stage controls root level display objects upon which everything is displayed.
* It also handles browser visibility handling and the pausing due to loss of focus.
*
* @class Phaser.GameObject.Stage
* @extends PIXI.DisplayObjectContainer
* @constructor
* @param {Phaser.Game} game - Game reference to the currently running game.
 */
Phaser.Stage = function (game) {

    /**
    * @property {Phaser.Game} game - A reference to the currently running Game.
    */
    this.game = game;

    // Phaser.GameObject.Container.call(this, game);

    /**
    * @property {string} name - The name of this object.
    * @default
    */
    this.name = '_stage_root';

    /**
    * By default if the browser tab loses focus the game will pause.
    * You can stop that behavior by setting this property to true.
    * Note that the browser can still elect to pause your game if it wishes to do so,
    * for example swapping to another browser tab. This will cause the RAF callback to halt,
    * effectively pausing your game, even though no in-game pause event is triggered if you enable this property.
    * @property {boolean} disableVisibilityChange
    * @default
    */
    this.disableVisibilityChange = false;

    /**
    * @property {boolean} exists - If exists is true the Stage and all children are updated, otherwise it is skipped.
    * @default
    */
    // this.exists = true;

    /**
    * @property {Phaser.Matrix} worldTransform - Current transform of the object based on world (parent) factors
    * @private
    * @readOnly
    */
    // this.worldTransform = new Phaser.Matrix();

    /**
    * @property {Phaser.Stage} stage - The stage reference (the Stage is its own stage)
    * @private
    * @readOnly
    */
    // this.stage = this;

    /**
    * @property {number} currentRenderOrderID - Reset each frame, keeps a count of the total number of objects updated.
    */
    this.currentRenderOrderID = 0;

    /**
    * @property {string} hiddenVar - The page visibility API event name.
    * @private
    */
    this._hiddenVar = 'hidden';

    /**
    * @property {function} _onChange - The blur/focus event handler.
    * @private
    */
    this._onChange = null;

    this.color = new Phaser.Component.Color(this);

    this.children = new Phaser.Component.Children(this);

    this.alpha = 1;
    this.visible = true;

    /**
    * @property {number} _bgColor - Stage background color object. Populated by setBackgroundColor.
    * @private
    this._bgColor = { r: 255, g: 0, b: 255, a: 0, color: 0, rgba: 'rgba(0,0,0,0.1)' };

    if (!this.game.transparent)
    {
        //  transparent = 0,0,0,0 - otherwise r,g,b,1
        // this._bgColor.a = 1;
    }
    */

    if (game.config)
    {
        this.parseConfig(game.config);
    }

};

// Phaser.Stage.prototype = Object.create(Phaser.GameObject.Container.prototype);
Phaser.Stage.prototype.constructor = Phaser.Stage;

/**
* Parses a Game configuration object.
*
* @method Phaser.Stage#parseConfig
* @protected
* @param {object} config -The configuration object to parse.
*/
Phaser.Stage.prototype.parseConfig = function (config) {

    if (config['disableVisibilityChange'])
    {
        this.disableVisibilityChange = config['disableVisibilityChange'];
    }

    if (config['backgroundColor'])
    {
        this.setBackgroundColor(config['backgroundColor']);
    }

};

/**
* Initialises the stage and adds the event listeners.
* @method Phaser.Stage#boot
* @private
*/
Phaser.Stage.prototype.boot = function () {

    Phaser.DOM.getOffset(this.game.canvas, this.offset);

    Phaser.Canvas.setUserSelect(this.game.canvas, 'none');
    Phaser.Canvas.setTouchAction(this.game.canvas, 'none');

    this.checkVisibility();

    this.setBackgroundColor('#000000');

};

/**
* This is called automatically after the plugins preUpdate and before the State.update.
* Most objects have preUpdate methods and it's where initial movement and positioning is done.
*
* @method Phaser.Stage#preUpdate
*/
Phaser.Stage.prototype.preUpdate = function ()
{
    // this.worldAlpha = this.alpha;

    // this.currentRenderOrderID = 0;

    //  This can't loop in reverse, we need the renderOrderID to be in sequence
    for (var i = 0; i < this.children.list.length; i++)
    {
        this.children.list[i].preUpdate();
    }
};

/**
* This is called automatically after the State.update, but before particles or plugins update.
*
* @method Phaser.Stage#update
*/
Phaser.Stage.prototype.update = function () {

    //  Goes in reverse, because it's highly likely the child will destroy itself in `update`
    var i = this.children.list.length;

    while (i--)
    {
        this.children.list[i].update();
    }

};

/**
* This is called automatically before the renderer runs and after the plugins have updated.
* In postUpdate this is where all the final physics calculations and object positioning happens.
* The objects are processed in the order of the display list.
*
* @method Phaser.Stage#postUpdate
*/
Phaser.Stage.prototype.postUpdate = function () {

    /*
    //  Apply the camera shake, fade, bounds, etc
    this.game.camera.update();

    //  Camera target first?
    if (this.game.camera.target)
    {
        this.game.camera.target.postUpdate();

        this.updateTransform();

        this.game.camera.updateTarget();
    }
    */

    for (var i = 0; i < this.children.list.length; i++)
    {
        this.children.list[i].postUpdate();
    }

    // this.updateTransform();

};

/**
* Starts a page visibility event listener running, or window.onpagehide/onpageshow if not supported by the browser.
* Also listens for window.onblur and window.onfocus.
*
* @method Phaser.Stage#checkVisibility
*/
Phaser.Stage.prototype.checkVisibility = function () {

    if (document.hidden !== undefined)
    {
        this._hiddenVar = 'visibilitychange';
    }
    else if (document.webkitHidden !== undefined)
    {
        this._hiddenVar = 'webkitvisibilitychange';
    }
    else if (document.mozHidden !== undefined)
    {
        this._hiddenVar = 'mozvisibilitychange';
    }
    else if (document.msHidden !== undefined)
    {
        this._hiddenVar = 'msvisibilitychange';
    }
    else
    {
        this._hiddenVar = null;
    }

    var _this = this;

    this._onChange = function (event) {
        return _this.visibilityChange(event);
    };

    //  Does browser support it? If not (like in IE9 or old Android) we need to fall back to blur/focus
    if (this._hiddenVar)
    {
        document.addEventListener(this._hiddenVar, this._onChange, false);
    }

    window.onblur = this._onChange;
    window.onfocus = this._onChange;

    window.onpagehide = this._onChange;
    window.onpageshow = this._onChange;

    if (this.game.device.cocoonJSApp)
    {
        CocoonJS.App.onSuspended.addEventListener(function () {
            Phaser.Stage.prototype.visibilityChange.call(_this, { type: "pause" });
        });

        CocoonJS.App.onActivated.addEventListener(function () {
            Phaser.Stage.prototype.visibilityChange.call(_this, { type: "resume" });
        });
    }

};

/**
* This method is called when the document visibility is changed.
*
* @method Phaser.Stage#visibilityChange
* @param {Event} event - Its type will be used to decide whether the game should be paused or not.
*/
Phaser.Stage.prototype.visibilityChange = function (event) {

    if (event.type === 'pagehide' || event.type === 'blur' || event.type === 'pageshow' || event.type === 'focus')
    {
        if (event.type === 'pagehide' || event.type === 'blur')
        {
            this.game.focusLoss(event);
        }
        else if (event.type === 'pageshow' || event.type === 'focus')
        {
            this.game.focusGain(event);
        }

        return;
    }

    if (this.disableVisibilityChange)
    {
        return;
    }

    if (document.hidden || document.mozHidden || document.msHidden || document.webkitHidden || event.type === "pause")
    {
        this.game.gamePaused(event);
    }
    else
    {
        this.game.gameResumed(event);
    }

};

/**
* Sets the background color for the Stage.
*
* The color can be given as a hex string (`'#RRGGBB'`), a CSS color string (`'rgb(r,g,b)'`), or a numeric value (`0xRRGGBB`).
*
* An alpha channel is _not_ supported and will be ignored.
*
* If you've set your game to be transparent then calls to setBackgroundColor are ignored.
*
* @method Phaser.Stage#setBackgroundColor
* @param {number|string} color - The color of the background.
*/
Phaser.Stage.prototype.setBackgroundColor = function (color)
{
    this.game.canvas.style.backgroundColor = color;

    /*
    Phaser.Color.valueToColor(color, this._bgColor);
    Phaser.Color.updateColor(this._bgColor);

    //  For gl.clearColor (canvas uses _bgColor.rgba)
    this._bgColor.r /= 255;
    this._bgColor.g /= 255;
    this._bgColor.b /= 255;
    this._bgColor.a = 1;
    */

};

/**
* Destroys the Stage and removes event listeners.
*
* @method Phaser.Stage#destroy
*/
Phaser.Stage.prototype.destroy = function () {

    if (this._hiddenVar)
    {
        document.removeEventListener(this._hiddenVar, this._onChange, false);
    }

    window.onpagehide = null;
    window.onpageshow = null;

    window.onblur = null;
    window.onfocus = null;

};

/**
* @name Phaser.Stage#backgroundColor
* @property {number|string} backgroundColor - Gets and sets the background color of the stage. The color can be given as a number: 0xff0000 or a hex string: '#ff0000'
*/
Object.defineProperty(Phaser.Stage.prototype, "backgroundColor", {

    get: function () {

        return this._bgColor.color;

    },

    set: function (color) {

        this.setBackgroundColor(color);

    }

});

/**
* Enable or disable texture smoothing for all objects on this Stage. Only works for bitmap/image textures. Smoothing is enabled by default.
*
* @name Phaser.Stage#smoothed
* @property {boolean} smoothed - Set to true to smooth all sprites rendered on this Stage, or false to disable smoothing (great for pixel art)
*/
Object.defineProperty(Phaser.Stage.prototype, "smoothed", {

    get: function () {

        return Phaser.scaleModes.DEFAULT === Phaser.scaleModes.LINEAR;

    },

    set: function (value) {

        if (value)
        {
            Phaser.scaleModes.DEFAULT = Phaser.scaleModes.LINEAR;
        }
        else
        {
            Phaser.scaleModes.DEFAULT = Phaser.scaleModes.NEAREST;
        }
    }

});


//  Remove frame if it matches the given frame
/**
 * [description]
 *
 * @method Phaser.Animations.Animation#removeFrame
 * @since 3.0.0
 *
 * @param {Phaser.Animations.AnimationFrame} frame - [description]
 *
 * @return {Phaser.Animations.Animation} [description]
 */
var RemoveFrame = function (frame)
{
    var index = this.frames.indexOf(frame);

    if (index !== -1)
    {
        this.removeFrameAt(index);
    }

    return this;
};

module.exports = RemoveFrame;


/**
 * [description]
 *
 * @method Phaser.Tweens.TweenManager#resumeAll
 * @since 3.0.0
 *
 * @return {Phaser.Tweens.TweenManager} [description]
 */
var ResumeAll = function ()
{
    var list = this._active;

    for (var i = 0; i < list.length; i++)
    {
        list[i].resume();
    }

    return this;
};

module.exports = ResumeAll;


var texture = textureManager.get(textureKey);

var frameKeys = texture.getFrameNames();

frameKeys.forEach(function (idx, value) {

frames.push({ key: textureKey, frame: value });


/**
* @author       Miller Medeiros http://millermedeiros.github.com/js-signals/
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* Object that represents a binding between a Signal and a listener function.
* This is an internal constructor and shouldn't be created directly.
* Inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.
*
* @class Phaser.SignalBinding
* @constructor
* @param {Phaser.Signal} signal - Reference to Signal object that listener is currently bound to.
* @param {function} listener - Handler function bound to the signal.
* @param {boolean} isOnce - If binding should be executed just once.
* @param {object} [listenerContext=null] - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
* @param {number} [priority] - The priority level of the event listener. (default = 0).
* @param {...any} [args=(none)] - Additional arguments to pass to the callback (listener) function. They will be appended after any arguments usually dispatched.
*/
Phaser.SignalBinding = function (signal, listener, isOnce, listenerContext, priority, args) {

    /**
    * @property {Phaser.Game} _listener - Handler function bound to the signal.
    * @private
    */
    this._listener = listener;

    if (isOnce)
    {
        this._isOnce = true;
    }

    if (listenerContext != null) /* not null/undefined */
    {
        this.context = listenerContext;
    }

    /**
    * @property {Phaser.Signal} _signal - Reference to Signal object that listener is currently bound to.
    * @private
    */
    this._signal = signal;

    if (priority)
    {
        this._priority = priority;
    }

    if (args && args.length)
    {
        this._args = args;
    }

};

Phaser.SignalBinding.prototype = {

    /**
    * @property {?object} context - Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    */
    context: null,

    /**
    * @property {boolean} _isOnce - If binding should be executed just once.
    * @private
    */
    _isOnce: false,

    /**
    * @property {number} _priority - Listener priority.
    * @private
    */
    _priority: 0,

    /**
    * @property {array} _args - Listener arguments.
    * @private
    */
    _args: null,

    /**
    * @property {number} callCount - The number of times the handler function has been called.
    */
    callCount: 0,

    /**
    * If binding is active and should be executed.
    * @property {boolean} active
    * @default
    */
    active: true,

    /**
    * Default parameters passed to listener during `Signal.dispatch` and `SignalBinding.execute` (curried parameters).
    * @property {array|null} params
    * @default
    */
    params: null,

    /**
    * Call listener passing arbitrary parameters.
    * If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.
    * @method Phaser.SignalBinding#execute
    * @param {any[]} [paramsArr] - Array of parameters that should be passed to the listener.
    * @return {any} Value returned by the listener.
    */
    execute: function(paramsArr) {

        var handlerReturn, params;

        if (this.active && !!this._listener)
        {
            params = this.params ? this.params.concat(paramsArr) : paramsArr;

            if (this._args)
            {
                params = params.concat(this._args);
            }

            handlerReturn = this._listener.apply(this.context, params);

            this.callCount++;

            if (this._isOnce)
            {
                this.detach();
            }
        }

        return handlerReturn;

    },

    /**
    * Detach binding from signal.
    * alias to: @see mySignal.remove(myBinding.getListener());
    * @method Phaser.SignalBinding#detach
    * @return {function|null} Handler function bound to the signal or `null` if binding was previously detached.
    */
    detach: function () {
        return this.isBound() ? this._signal.remove(this._listener, this.context) : null;
    },

    /**
    * @method Phaser.SignalBinding#isBound
    * @return {boolean} True if binding is still bound to the signal and has a listener.
    */
    isBound: function () {
        return (!!this._signal && !!this._listener);
    },

    /**
    * @method Phaser.SignalBinding#isOnce
    * @return {boolean} If SignalBinding will only be executed once.
    */
    isOnce: function () {
        return this._isOnce;
    },

    /**
    * @method Phaser.SignalBinding#getListener
    * @return {function} Handler function bound to the signal.
    */
    getListener: function () {
        return this._listener;
    },

    /**
    * @method Phaser.SignalBinding#getSignal
    * @return {Phaser.Signal} Signal that listener is currently bound to.
    */
    getSignal: function () {
        return this._signal;
    },

    /**
    * Delete instance properties
    * @method Phaser.SignalBinding#_destroy
    * @private
    */
    _destroy: function () {
        delete this._signal;
        delete this._listener;
        delete this.context;
    },

    /**
    * @method Phaser.SignalBinding#toString
    * @return {string} String representation of the object.
    */
    toString: function () {
        return '[Phaser.SignalBinding isOnce:' + this._isOnce +', isBound:'+ this.isBound() +', active:' + this.active + ']';
    }

};

Phaser.SignalBinding.prototype.constructor = Phaser.SignalBinding;


/**
 * [description]
 *
 * @method Phaser.Curves.Curve#draw
 * @since 3.0.0
 *
 * @param {Phaser.GameObjects.Graphics} graphics - [description]
 * @param {integer} [pointsTotal=32] - [description]
 *
 * @return {Phaser.GameObjects.Graphics} [description]
 */
var Draw = function (graphics, pointsTotal)
{
    if (pointsTotal === undefined) { pointsTotal = 32; }

    //  So you can chain graphics calls
    return graphics.strokePoints(this.getPoints(pointsTotal));
};

module.exports = Draw;


//  Graphics

Phaser.Renderer.WebGL.GameObjects.Graphics = {

    TYPES: [
        Phaser.GameObject.Graphics.prototype
    ],

    circleSegments: 40,

    graphicsDataPool: [],

    render: function (renderer, src)
    {
        if (src.visible === false || src.alpha === 0 || src.isMask === true)
        {
            return;
        }

        if (src._cacheAsBitmap)
        {
            if (src.dirty || src.cachedSpriteDirty)
            {
                src._generateCachedSprite();

                // we will also need to update the texture on the gpu too!
                src.updateCachedSpriteTexture();

                src.cachedSpriteDirty = false;
                src.dirty = false;
            }

            src._cachedSprite.worldAlpha = src.worldAlpha;

            // PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession);

            return;
        }

        renderer.spriteBatch.stop();
        renderer.setBlendMode(src.blendMode);

        if (src._mask)
        {
            renderer.pushMask(src._mask);
        }

        if (src._filters)
        {
            renderer.filterManager.pushFilter(src._filterBlock);
        }

        // check blend mode
        if (src.blendMode !== renderer.spriteBatch.currentBlendMode)
        {
            renderer.spriteBatch.currentBlendMode = src.blendMode;

            var blendModeWebGL = renderer.blendModes[renderer.spriteBatch.currentBlendMode];

            renderer.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);
        }

        // check if the webgl graphic needs to be updated
        if (src.webGLDirty)
        {
            src.dirty = true;
            src.webGLDirty = false;
        }

        var gl = renderer.gl;
        var offset = renderer.offset;
        var projection = renderer.projection;
        var shader = renderer.shaderManager.primitiveShader;
        var webGLData;

        if (src.dirty)
        {
            Phaser.Renderer.WebGL.GameObjects.Graphics.updateGraphics(renderer, src);
        }

        for (var i = 0; i < src._webGL.data.length; i++)
        {
            webGLData = src._webGL.data[i];

            if (webGLData.mode === 1)
            {
                renderer.stencilManager.pushStencil(src, webGLData);

                gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, (webGLData.indices.length - 4) * 2);

                renderer.stencilManager.popStencil(src, webGLData);
            }
            else
            {
                renderer.shaderManager.setShader(shader);

                // shader = renderSession.shaderManager.primitiveShader;

                gl.uniformMatrix3fv(shader.translationMatrix, false, src.worldTransform.toArray(true));

                gl.uniform1f(shader.flipY, 1);

                gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
                gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);

                gl.uniform3fv(shader.tintColor, Phaser.Color.hexToRGBArray(src.tint));

                gl.uniform1f(shader.alpha, src.worldAlpha);

                gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer);

                gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0);

                gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4);

                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer);

                gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0);
            }
        }

        //  Only render if it has children!
        if (src.children.length)
        {
            renderer.spriteBatch.start();

            for (var i = 0; i < src.children.length; i++)
            {
                var child = src.children[i];
                child.render(renderer, child);
            }

            renderer.spriteBatch.stop();
        }

        if (src._filters)
        {
            renderer.filterManager.popFilter();
        }

        if (src._mask)
        {
            renderer.popMask(src.mask);
        }

        renderer.drawCount++;

        renderer.spriteBatch.start();
    },

    /**
     * Updates the graphics object
     *
     * @static
     * @private
     * @method updateGraphics
     * @param graphicsData {Graphics} The graphics object to update
     * @param gl {WebGLContext} the current WebGL drawing context
     */
    updateGraphics: function (renderer, graphics)
    {
        var gl = renderer.gl;
        var webGL = graphics._webGL;

        //  If the graphics object does not exist in the webGL context time to create it!
        if (!webGL)
        {
            webGL = graphics._webGL = { lastIndex: 0, data: [], gl: gl };
        }

        //  Flag the graphics as not dirty as we are about to update it
        graphics.dirty = false;

        var i;

        //  If the user cleared the graphics object we will need to clear every object
        if (graphics.clearDirty)
        {
            graphics.clearDirty = false;

            // loop through and return all the webGLDatas to the object pool so than can be reused later on
            for (i = 0; i < webGL.data.length; i++)
            {
                var graphicsData = webGL.data[i];

                graphicsData.reset();

                Phaser.Renderer.WebGL.GameObjects.Graphics.graphicsDataPool.push(graphicsData);
            }

            //  Clear the array and reset the index
            webGL.data = [];
            webGL.lastIndex = 0;
        }

        var webGLData;

        //  Loop through the Graphics data.
        //  If the object is complex, use the Stencil Buffer.
        //  Otherwise add the object into the batch.

        for (i = webGL.lastIndex; i < graphics.graphicsData.length; i++)
        {
            var data = graphics.graphicsData[i];

            switch (data.type)
            {
                case Phaser.RECTANGLE:

                    webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 0);
                    Phaser.Renderer.WebGL.GameObjects.Graphics.buildRectangle(data, webGLData);

                    break;

                case Phaser.CIRCLE:
                case Phaser.ELLIPSE:

                    webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 0);
                    Phaser.Renderer.WebGL.GameObjects.Graphics.buildCircle(data, webGLData);

                    break;

                case Phaser.ROUNDEDRECTANGLE:

                    webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 0);
                    Phaser.Renderer.WebGL.GameObjects.Graphics.buildRoundedRectangle(data, webGLData);

                    break;

                case Phaser.POLYGON:

                    //  Need to add the points the the graphics object
                    data.points = data.shape.points.slice();

                    if (data.shape.closed)
                    {
                        //  Close the poly if the value is true!
                        if (data.points[0] !== data.points[data.points.length - 2] || data.points[1] !== data.points[data.points.length - 1])
                        {
                            data.points.push(data.points[0], data.points[1]);
                        }
                    }

                    //  Check the type
                    if (data.fill)
                    {
                        if (data.points.length >= renderer.stencilBufferLimit)
                        {
                            if (data.points.length < renderer.stencilBufferLimit * 2)
                            {
                                webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 0);

                                var canDrawUsingSimple = Phaser.Renderer.WebGL.GameObjects.Graphics.buildPoly(data, webGLData);

                                if (!canDrawUsingSimple)
                                {
                                    webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 1);
                                    Phaser.Renderer.WebGL.GameObjects.Graphics.buildComplexPoly(data, webGLData);
                                }
                            }
                            else
                            {
                                webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 1);
                                Phaser.Renderer.WebGL.GameObjects.Graphics.buildComplexPoly(data, webGLData);
                            }
                        }
                    }

                    if (data.lineWidth > 0)
                    {
                        webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.switchMode(webGL, 0);
                        Phaser.Renderer.WebGL.GameObjects.Graphics.buildLine(data, webGLData);
                    }

                    break;
            }

            webGL.lastIndex++;
        }

        //  Upload all the dirty data
        for (i = 0; i < webGL.data.length; i++)
        {
            webGLData = webGL.data[i];

            if (webGLData.dirty)
            {
                webGLData.upload();
            }
        }
    },

    switchMode: function (webGL, type)
    {
        var webGLData;

        if (!webGL.data.length)
        {
            webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.graphicsDataPool.pop() || new Phaser.Renderer.WebGL.GameObjects.GraphicsData(webGL.gl);
            webGLData.mode = type;
            webGL.data.push(webGLData);
        }
        else
        {
            webGLData = webGL.data[webGL.data.length - 1];

            if (webGLData.mode !== type || type === 1)
            {
                webGLData = Phaser.Renderer.WebGL.GameObjects.Graphics.graphicsDataPool.pop() || new Phaser.Renderer.WebGL.GameObjects.GraphicsData(webGL.gl);
                webGLData.mode = type;
                webGL.data.push(webGLData);
            }
        }

        webGLData.dirty = true;

        return webGLData;
    },

    buildRectangle: function (graphicsData, webGLData)
    {
        var rectData = graphicsData.shape;
        var x = rectData.x;
        var y = rectData.y;
        var width = rectData.width;
        var height = rectData.height;

        if (graphicsData.fill)
        {
            var color = Phaser.Color.hexToRGBArray(graphicsData.fillColor);
            var alpha = graphicsData.fillAlpha;

            var r = color[0] * alpha;
            var g = color[1] * alpha;
            var b = color[2] * alpha;

            var verts = webGLData.points;
            var indices = webGLData.indices;

            var vertPos = verts.length / 6;

            // start
            verts.push(x, y);
            verts.push(r, g, b, alpha);

            verts.push(x + width, y);
            verts.push(r, g, b, alpha);

            verts.push(x , y + height);
            verts.push(r, g, b, alpha);

            verts.push(x + width, y + height);
            verts.push(r, g, b, alpha);

            // insert 2 dead triangles..
            indices.push(vertPos, vertPos, vertPos + 1, vertPos + 2, vertPos + 3, vertPos + 3);
        }

        if (graphicsData.lineWidth)
        {
            var tempPoints = graphicsData.points;

            graphicsData.points = [
                x, y,
                x + width, y,
                x + width, y + height,
                x, y + height,
                x, y
            ];

            Phaser.Renderer.WebGL.GameObjects.Graphics.buildLine(graphicsData, webGLData);

            graphicsData.points = tempPoints;
        }
    },

    buildRoundedRectangle: function (graphicsData, webGLData)
    {
        var rrectData = graphicsData.shape;
        var x = rrectData.x;
        var y = rrectData.y;
        var width = rrectData.width;
        var height = rrectData.height;

        var radius = rrectData.radius;

        var recPoints = [];

        recPoints.push(x, y + radius);

        recPoints = recPoints.concat(Phaser.Renderer.WebGL.GameObjects.Graphics.quadraticBezierCurve(x, y + height - radius, x, y + height, x + radius, y + height));
        recPoints = recPoints.concat(Phaser.Renderer.WebGL.GameObjects.Graphics.quadraticBezierCurve(x + width - radius, y + height, x + width, y + height, x + width, y + height - radius));
        recPoints = recPoints.concat(Phaser.Renderer.WebGL.GameObjects.Graphics.quadraticBezierCurve(x + width, y + radius, x + width, y, x + width - radius, y));
        recPoints = recPoints.concat(Phaser.Renderer.WebGL.GameObjects.Graphics.quadraticBezierCurve(x + radius, y, x, y, x, y + radius));

        if (graphicsData.fill)
        {
            var color = Phaser.Color.hexToRGBArray(graphicsData.fillColor);
            var alpha = graphicsData.fillAlpha;

            var r = color[0] * alpha;
            var g = color[1] * alpha;
            var b = color[2] * alpha;

            var verts = webGLData.points;
            var indices = webGLData.indices;

            var vecPos = verts.length / 6;

            var triangles = Phaser.EarCut.Triangulate(recPoints, null, 2);

            var i = 0;

            for (i = 0; i < triangles.length; i += 3)
            {
                indices.push(triangles[i] + vecPos);
                indices.push(triangles[i] + vecPos);
                indices.push(triangles[i + 1] + vecPos);
                indices.push(triangles[i + 2] + vecPos);
                indices.push(triangles[i + 2] + vecPos);
            }

            for (i = 0; i < recPoints.length; i++)
            {
                verts.push(recPoints[i], recPoints[++i], r, g, b, alpha);
            }
        }

        if (graphicsData.lineWidth)
        {
            var tempPoints = graphicsData.points;

            graphicsData.points = recPoints;

            Phaser.Renderer.WebGL.GameObjects.Graphics.buildLine(graphicsData, webGLData);

            graphicsData.points = tempPoints;
        }
    },

    quadraticBezierCurve: function (fromX, fromY, cpX, cpY, toX, toY)
    {
        var xa;
        var ya;
        var xb;
        var yb;
        var x;
        var y;
        var n = 20;
        var points = [];

        function getPt (n1, n2, perc)
        {
            var diff = n2 - n1;

            return n1 + (diff * perc);
        };

        var j = 0;

        for (var i = 0; i <= n; i++)
        {
            j = i / n;

            // The Green Line
            xa = getPt(fromX, cpX, j);
            ya = getPt(fromY, cpY, j);
            xb = getPt(cpX, toX, j);
            yb = getPt(cpY, toY, j);

            // The Black Dot
            x = getPt(xa, xb, j);
            y = getPt(ya, yb, j);

            points.push(x, y);
        }

        return points;
    },

    buildCircle: function (graphicsData, webGLData)
    {
        //  Need to convert points to a nice regular data
        var circleData = graphicsData.shape;
        var x = circleData.x;
        var y = circleData.y;
        var width;
        var height;

        if (graphicsData.type === Phaser.CIRCLE)
        {
            width = circleData.radius;
            height = circleData.radius;
        }
        else
        {
            width = circleData.width;
            height = circleData.height;
        }

        var totalSegs = Phaser.Renderer.WebGL.GameObjects.Graphics.circleSegments;
        var seg = (Math.PI * 2) / totalSegs;

        var i = 0;

        if (graphicsData.fill)
        {
            var color = Phaser.Color.hexToRGBArray(graphicsData.fillColor);
            var alpha = graphicsData.fillAlpha;

            var r = color[0] * alpha;
            var g = color[1] * alpha;
            var b = color[2] * alpha;

            var verts = webGLData.points;
            var indices = webGLData.indices;

            var vecPos = verts.length / 6;

            indices.push(vecPos);

            for (i = 0; i < totalSegs + 1 ; i++)
            {
                verts.push(x, y, r, g, b, alpha);

                verts.push(
                    x + Math.sin(seg * i) * width,
                    y + Math.cos(seg * i) * height,
                    r, g, b, alpha
                );

                indices.push(vecPos++, vecPos++);
            }

            indices.push(vecPos - 1);
        }

        if (graphicsData.lineWidth)
        {
            var tempPoints = graphicsData.points;

            graphicsData.points = [];

            for (i = 0; i < totalSegs + 1; i++)
            {
                graphicsData.points.push(
                    x + Math.sin(seg * i) * width,
                    y + Math.cos(seg * i) * height
                );
            }

            Phaser.Renderer.WebGL.GameObjects.Graphics.buildLine(graphicsData, webGLData);

            graphicsData.points = tempPoints;
        }
    },

    buildLine: function (graphicsData, webGLData)
    {
        var i = 0;
        var points = graphicsData.points;

        if (points.length === 0)
        {
            return;
        }

        //  If the line width is an odd number add 0.5 to align to a whole pixel
        if (graphicsData.lineWidth % 2)
        {
            for (i = 0; i < points.length; i++)
            {
                points[i] += 0.5;
            }
        }

        //  Get first and last point. Figure out the middle!
        var firstPoint = new Phaser.Point(points[0], points[1]);
        var lastPoint = new Phaser.Point(points[points.length - 2], points[points.length - 1]);

        //  If the first point is the last point we're gonna have issues :)
        if (firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y)
        {
            //  Need to clone as we are going to slightly modify the shape
            points = points.slice();

            points.pop();
            points.pop();

            lastPoint = new Phaser.Point(points[points.length - 2], points[points.length - 1]);

            var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) * 0.5;
            var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) * 0.5;

            points.unshift(midPointX, midPointY);
            points.push(midPointX, midPointY);
        }

        var verts = webGLData.points;
        var indices = webGLData.indices;
        var length = points.length / 2;
        var indexCount = points.length;
        var indexStart = verts.length / 6;

        //  Draw the Line
        var width = graphicsData.lineWidth / 2;

        //  Sort the color
        var color = Phaser.Color.hexToRGBArray(graphicsData.lineColor);
        var alpha = graphicsData.lineAlpha;
        var r = color[0] * alpha;
        var g = color[1] * alpha;
        var b = color[2] * alpha;

        var px, py, p1x, p1y, p2x, p2y, p3x, p3y;
        var perpx, perpy, perp2x, perp2y, perp3x, perp3y;
        var a1, b1, c1, a2, b2, c2;
        var denom, pdist, dist;

        p1x = points[0];
        p1y = points[1];

        p2x = points[2];
        p2y = points[3];

        perpx = -(p1y - p2y);
        perpy = p1x - p2x;

        dist = Math.sqrt(perpx * perpx + perpy * perpy);

        perpx /= dist;
        perpy /= dist;
        perpx *= width;
        perpy *= width;

        //  Start
        verts.push(p1x - perpx , p1y - perpy, r, g, b, alpha);

        verts.push(p1x + perpx , p1y + perpy, r, g, b, alpha);

        for (i = 1; i < length - 1; i++)
        {
            p1x = points[(i - 1) * 2];
            p1y = points[(i - 1) * 2 + 1];

            p2x = points[(i) * 2];
            p2y = points[(i) * 2 + 1];

            p3x = points[(i + 1) * 2];
            p3y = points[(i + 1) * 2 + 1];

            perpx = -(p1y - p2y);
            perpy = p1x - p2x;

            dist = Math.sqrt(perpx * perpx + perpy * perpy);

            perpx /= dist;
            perpy /= dist;
            perpx *= width;
            perpy *= width;

            perp2x = -(p2y - p3y);
            perp2y = p2x - p3x;

            dist = Math.sqrt(perp2x * perp2x + perp2y * perp2y);

            perp2x /= dist;
            perp2y /= dist;
            perp2x *= width;
            perp2y *= width;

            a1 = (-perpy + p1y) - (-perpy + p2y);
            b1 = (-perpx + p2x) - (-perpx + p1x);
            c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y);
            a2 = (-perp2y + p3y) - (-perp2y + p2y);
            b2 = (-perp2x + p2x) - (-perp2x + p3x);
            c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y);

            denom = a1 * b2 - a2 * b1;

            if (Math.abs(denom) < 0.1)
            {
                denom += 10.1;
                verts.push(p2x - perpx, p2y - perpy, r, g, b, alpha);
                verts.push(p2x + perpx, p2y + perpy, r, g, b, alpha);
                continue;
            }

            px = (b1 * c2 - b2 * c1) / denom;
            py = (a2 * c1 - a1 * c2) / denom;

            pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y);

            if (pdist > 140 * 140)
            {
                perp3x = perpx - perp2x;
                perp3y = perpy - perp2y;

                dist = Math.sqrt(perp3x * perp3x + perp3y * perp3y);

                perp3x /= dist;
                perp3y /= dist;
                perp3x *= width;
                perp3y *= width;

                verts.push(p2x - perp3x, p2y -perp3y, r, g, b, alpha);
                verts.push(p2x + perp3x, p2y +perp3y, r, g, b, alpha);
                verts.push(p2x - perp3x, p2y -perp3y, r, g, b, alpha);

                indexCount++;
            }
            else
            {
                verts.push(px, py, r, g, b, alpha);
                verts.push(p2x - (px-p2x), p2y - (py - p2y), r, g, b, alpha);
            }
        }

        p1x = points[(length - 2) * 2];
        p1y = points[(length - 2) * 2 + 1];

        p2x = points[(length - 1) * 2];
        p2y = points[(length - 1) * 2 + 1];

        perpx = -(p1y - p2y);
        perpy = p1x - p2x;

        dist = Math.sqrt(perpx * perpx + perpy * perpy);

        perpx /= dist;
        perpy /= dist;
        perpx *= width;
        perpy *= width;

        verts.push(p2x - perpx , p2y - perpy, r, g, b, alpha);
        verts.push(p2x + perpx , p2y + perpy, r, g, b, alpha);

        indices.push(indexStart);

        for (i = 0; i < indexCount; i++)
        {
            indices.push(indexStart++);
        }

        indices.push(indexStart - 1);
    },

    buildComplexPoly: function(graphicsData, webGLData)
    {
        var points = graphicsData.points.slice();

        if (points.length < 6)
        {
            return;
        }

        //  Get first and last point.. figure out the middle!
        var indices = webGLData.indices;
        webGLData.points = points;
        webGLData.alpha = graphicsData.fillAlpha;
        webGLData.color = Phaser.Color.hexToRGBArray(graphicsData.fillColor);

        //  Calculate the bounds
        var minX = Infinity;
        var maxX = -Infinity;

        var minY = Infinity;
        var maxY = -Infinity;

        var x;
        var y;

        //  Get the size
        for (var i = 0; i < points.length; i += 2)
        {
            x = points[i];
            y = points[i + 1];

            minX = (x < minX) ? x : minX;
            maxX = (x > maxX) ? x : maxX;

            minY = (y < minY) ? y : minY;
            maxY = (y > maxY) ? y : maxY;
        }

        //  Add a quad to the end because there is no point making another buffer!
        points.push(
            minX, minY,
            maxX, minY,
            maxX, maxY,
            minX, maxY
        );

        //  TODO: Is this even needed?
        var length = points.length / 2;

        for (i = 0; i < length; i++)
        {
            indices.push(i);
        }
    },

    buildPoly: function (graphicsData, webGLData)
    {
        var points = graphicsData.points;

        if (points.length < 6)
        {
            return;
        }

        //  Get first and last point.. figure out the middle!
        var verts = webGLData.points;
        var indices = webGLData.indices;

        var length = points.length / 2;

        //  Sort the color
        var color = Phaser.Color.hexToRGBArray(graphicsData.fillColor);
        var alpha = graphicsData.fillAlpha;
        var r = color[0] * alpha;
        var g = color[1] * alpha;
        var b = color[2] * alpha;

        var triangles = Phaser.EarCut.Triangulate(points, null, 2);

        if (!triangles)
        {
            return false;
        }

        var vertPos = verts.length / 6;

        var i = 0;

        for (i = 0; i < triangles.length; i += 3)
        {
            indices.push(triangles[i] + vertPos);
            indices.push(triangles[i] + vertPos);
            indices.push(triangles[i + 1] + vertPos);
            indices.push(triangles[i + 2] +vertPos);
            indices.push(triangles[i + 2] + vertPos);
        }

        for (i = 0; i < length; i++)
        {
            verts.push(
                points[i * 2],
                points[i * 2 + 1],
                r, g, b, alpha
            );
        }

        return true;
    }

};


var key = GetValue(item, 'key', defaultTextureKey);


module.exports = {

    Pad: require('./Pad'),
    Format: require('./Format'),
    Reverse: require('./Reverse')

};


/**
 * Compares the `x`, `y` and `radius` properties of the two given Circles.
 * Returns `true` if they all match, otherwise returns `false`.
 *
 * @function Phaser.Geom.Circle.Equals
 * @since 3.0.0
 *
 * @param {Phaser.Geom.Circle} circle - The first Circle to compare.
 * @param {Phaser.Geom.Circle} toCompare - The second Circle to compare.
 *
 * @return {boolean} `true` if the two Circles equal each other, otherwise `false`.
 */
var Equals = function (circle, toCompare)
{
    return (
        circle.x === toCompare.x &&
        circle.y === toCompare.y &&
        circle.radius === toCompare.radius
    );
};

module.exports = Equals;


var Acceleration = {

    setAcceleration: function (x, y)
    {
        this.body.acceleration.set(x, y);

        return this;
    },

    setAccelerationX: function (value)
    {
        this.body.acceleration.x = value;

        return this;
    },

    setAccelerationY: function (value)
    {
        this.body.acceleration.y = value;

        return this;
    }

};

module.exports = Acceleration;


keep keep keep keep keep replace replace replace keep

L.Map.include(!L.DomUtil.TRANSITION ? {} : {
	_zoomToIfCenterInView: function(center, zoom, centerOffset) {

		if (this._animatingZoom) { return true; }
		if (!this.options.zoomAnimation) { return false; }

		var zoomDelta = zoom - this._zoom,
			scale = Math.pow(2, zoomDelta),
			offset = centerOffset.divideBy(1 - 1/scale);

		//if offset does not exceed half of the view
		if (!this._offsetIsWithinView(offset, 1)) { return false; }

		this._mapPane.className += ' leaflet-zoom-anim';

		var centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(2)),
			origin = centerPoint.add(offset);

		this._prepareTileBg();

		this._runAnimation(center, zoom, scale, origin);

		return true;
	},

	_runAnimation: function(center, zoom, scale, origin) {
		this._animatingZoom = true;

		this._animateToCenter = center;
		this._animateToZoom = zoom;

		var transform = L.DomUtil.TRANSFORM;

		//dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem
		if (L.Browser.gecko || window.opera) {
			this._tileBg.style[transform] += ' translate(0,0)';
		}

		var scaleStr;

		// Android doesn't like translate/scale chains, transformOrigin + scale works better but
		// it breaks touch zoom which Anroid doesn't support anyway, so that's a really ugly hack
		// TODO work around this prettier
		if (L.Browser.android) {
			this._tileBg.style[transform + 'Origin'] = origin.x + 'px ' + origin.y + 'px';
			scaleStr = 'scale(' + scale + ')';
		} else {
			scaleStr = L.DomUtil.getScaleString(scale, origin);
		}

		L.Util.falseFn(this._tileBg.offsetWidth); //hack to make sure transform is updated before running animation

		var options = {};
		options[transform] = this._tileBg.style[transform] + ' ' + scaleStr;
		this._tileBg.transition.run(options);
	},

	_prepareTileBg: function() {
		if (!this._tileBg) {
			this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane);
			this._tileBg.style.zIndex = 1;
		}

		var tilePane = this._tilePane,
			tileBg = this._tileBg;

		// prepare the background pane to become the main tile pane
		//tileBg.innerHTML = '';
		tileBg.style[L.DomUtil.TRANSFORM] = '';
		tileBg.style.visibility = 'hidden';

		// tells tile layers to reinitialize their containers
		tileBg.empty = true;
		tilePane.empty = false;

		this._tilePane = this._panes.tilePane = tileBg;
		this._tileBg = tilePane;

		if (!this._tileBg.transition) {
			this._tileBg.transition = new L.Transition(this._tileBg, {duration: 0.3, easing: 'cubic-bezier(0.25,0.1,0.25,0.75)'});
			this._tileBg.transition.on('end', this._onZoomTransitionEnd, this);
		}

		this._removeExcessiveBgTiles();
	},

	// stops loading all tiles in the background layer and removes tiles that not in the current bbox
	_removeExcessiveBgTiles: function() {
		if (!this._container.getBoundingClientRect) return;

		var mapRect = this._container.getBoundingClientRect(),
			tiles = [].slice.call(this._tileBg.getElementsByTagName('img'));

		for (var i = 0, len = tiles.length; i < len; i++) {
			var tileRect = tiles[i].getBoundingClientRect();

			if (!tiles[i].complete ||
				(tileRect.right <= mapRect.left) ||
				(tileRect.left >= mapRect.right) ||
				(tileRect.top >= mapRect.bottom) ||
				(tileRect.bottom <= mapRect.top)) {

				tiles[i].src = '';
				tiles[i].parentNode.removeChild(tiles[i]);
			}
		}
	},

	_onZoomTransitionEnd: function() {
		this._restoreTileFront();

		L.Util.falseFn(this._tileBg.offsetWidth);
		this._resetView(this._animateToCenter, this._animateToZoom, true);

		//TODO clear tileBg on map layersload

		this._mapPane.className = this._mapPane.className.replace(' leaflet-zoom-anim', ''); //TODO toggleClass util
		this._animatingZoom = false;
	},

	_restoreTileFront: function() {
		this._tilePane.innerHTML = '';
		this._tilePane.style.visibility = '';
		this._tilePane.style.zIndex = 2;
		this._tileBg.style.zIndex = 1;
	},

	_clearTileBg: function() {
		if (!this._animatingZoom && !this.touchZoom._zooming) {
			this._tileBg.innerHTML = '';
		}
	}
});


../dist/leaflet.js: Makefile
	java -jar ../lib/closure-compiler/compiler.jar \
	--js ../src/Leaflet.js \
	--js ../src/core/Util.js \
	--js ../src/core/Class.js \
	--js ../src/core/Events.js \
	--js ../src/core/Browser.js \
	--js ../src/geometry/Point.js \
	--js ../src/geometry/Bounds.js \
	--js ../src/geometry/Transformation.js \
	--js ../src/geometry/LineUtil.js \
	--js ../src/geometry/PolyUtil.js \
	--js ../src/dom/DomEvent.js \
	--js ../src/dom/DomEvent.DoubleTap.js \
	--js ../src/dom/DomUtil.js \
	--js ../src/dom/Draggable.js \
	--js ../src/dom/transition/Transition.js \
	--js ../src/dom/transition/Transition.Native.js \
	--js ../src/dom/transition/Transition.Timer.js \
	--js ../src/geo/LatLng.js \
	--js ../src/geo/LatLngBounds.js \
	--js ../src/geo/projection/Projection.js \
	--js ../src/geo/projection/Projection.SphericalMercator.js \
	--js ../src/geo/projection/Projection.LonLat.js \
	--js ../src/geo/projection/Projection.Mercator.js \
	--js ../src/geo/crs/CRS.js \
	--js ../src/geo/crs/CRS.EPSG3857.js \
	--js ../src/geo/crs/CRS.EPSG4326.js \
	--js ../src/geo/crs/CRS.EPSG3395.js \
	--js ../src/layer/LayerGroup.js \
	--js ../src/layer/FeatureGroup.js \
	--js ../src/layer/tile/TileLayer.js \
	--js ../src/layer/tile/TileLayer.WMS.js \
	--js ../src/layer/tile/TileLayer.Canvas.js \
	--js ../src/layer/ImageOverlay.js \
	--js ../src/layer/Popup.js \
	--js ../src/layer/marker/Icon.js \
	--js ../src/layer/marker/Marker.js \
	--js ../src/layer/marker/Marker.Popup.js \
	--js ../src/layer/vector/Path.js \
	--js ../src/layer/vector/Path.VML.js \
	--js ../src/layer/vector/Path.Popup.js \
	--js ../src/layer/vector/Polyline.js \
	--js ../src/layer/vector/Polygon.js \
	--js ../src/layer/vector/MultiPoly.js \
	--js ../src/layer/vector/Circle.js \
	--js ../src/layer/vector/CircleMarker.js \
	--js ../src/layer/GeoJSON.js \
	--js ../src/handler/Handler.js \
	--js ../src/handler/MapDrag.js \
	--js ../src/handler/TouchZoom.js \
	--js ../src/handler/ScrollWheelZoom.js \
	--js ../src/handler/DoubleClickZoom.js \
	--js ../src/handler/ShiftDragZoom.js \
	--js ../src/handler/MarkerDrag.js \
	--js ../src/control/Control.js \
	--js ../src/control/Control.Zoom.js \
	--js ../src/control/Control.Attribution.js \
	--js ../src/map/Map.js \
	--js ../src/map/ext/Map.Geolocation.js \
	--js ../src/map/ext/Map.Popup.js \
	--js ../src/map/ext/Map.PanAnimation.js \
	--js ../src/map/ext/Map.ZoomAnimation.js \
	--js ../src/map/ext/Map.Control.js \
	--js_output_file ../dist/leaflet.js


/*
 * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
 */

L.LatLngBounds = L.Class.extend({
	initialize: function(southWest, northEast) {	// (LatLng, LatLng) or (LatLng[])
		if (!southWest) return;
		var latlngs = (southWest instanceof Array ? southWest : [southWest, northEast]);
		for (var i = 0, len = latlngs.length; i < len; i++) {
			this.extend(latlngs[i]);
		}
	},

	// extend the bounds to contain the given point
	extend: function(/*LatLng*/ latlng) {
		if (!this._southWest && !this._northEast) {
			this._southWest = new L.LatLng(latlng.lat, latlng.lng);
			this._northEast = new L.LatLng(latlng.lat, latlng.lng);
		} else {
			this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
			this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
			this._northEast.lat = Math.max(latlng.lat, this._northEast.lat);
			this._northEast.lng = Math.max(latlng.lng, this._northEast.lng);
		}
	},

	getCenter: function() /*-> LatLng*/ {
		return new L.LatLng(
				(this._southWest.lat + this._northEast.lat) / 2,
				(this._southWest.lng + this._northEast.lng) / 2);
	},

	getSouthWest: function() { return this._southWest; },

	getNorthEast: function() { return this._northEast; },

	getNorthWest: function() {
		return new L.LatLng(this._northEast.lat, this._southWest.lng);
	},

	getSouthEast: function() {
		return new L.LatLng(this._southWest.lat, this._northEast.lng);
	},

	contains: function(/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ {
		var sw = this._southWest,
			ne = this._northEast,
			sw2, ne2;

		if (obj instanceof L.LatLngBounds) {
			sw2 = obj.getSouthWest();
			ne2 = obj.getNorthEast();
		} else {
			sw2 = ne2 = obj;
		}

		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
				(sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
	}
});

//TODO International date line?


<!DOCTYPE html>

	<title>Leaflet debug page</title>

	<link rel="stylesheet" href="../../dist/leaflet.css" />

	<link rel="stylesheet" href="../css/screen.css" />

	<button id="populate">Populate with 10 markers</button>


this._tileBg.transition = new L.Transition(this._tileBg, {duration: 0.3, easing: 'cubic-bezier(0.25,0.1,0.25,0.75)'});


Leaflet Changelog
=================

## 0.2 (master)

### Highlights

 * Added **WMS** layer (`TileLayer.WMS`).
 * Added different **projections** support, having `EPSG:3857`, `EPSG:4326` and `EPSG:3395` out of the box (through `crs` option in `Map`). Thanks to [@Miroff](https://github.com/Miroff) & [@Komzpa](https://github.com/Komzpa) for great advice and explanation regarding this.
 * Added **GeoJSON** layer.
 * Added **MultiPolyline** and **MultiPolygon** layers. [#77](https://github.com/CloudMade/Leaflet/issues/77)

### Improvements

 * Added `TileLayer.Canvas` for easy creation of canvas-based tile layers.
 * Changed `Circle` to be zoom-dependent (with radius in meters); circle of a permanent size is now called `CircleMarker`.
 * Added `mouseover` and `mouseout` events to map, markers and paths; added map `mousemove` event.
 * Added `setLatLngs`, `spliceLatLngs`, `addLatLng`, `getLatLngs` methods to polylines and polygons.
 * Added `setLatLng` and `setRadius` methods to `Circle` and `CircleMarker`.
 * Improved `LatLngBounds contains` method to accept `LatLng` in addition to `LatLngBounds`, the same for `Bounds contains` and `Point`
 * Improved `LatLngBounds` & `Bounds` to allow their instantiation without arguments (by [@snc](https://github.com/snc)).
 * Added TMS tile numbering support through `TileLayer` `scheme: 'tms'` option (by [@tmcw](https://github.com/tmcw)).
 * Added `opacity` option and `setOpacity` method to `TileLayer`.
 * Added `setLatLng` and `setIcon` methods to `Marker`.
 * Added `title` option to `Marker`.
 * Added `maxZoom` argument to `map.locateAndSetView` method.
 * Improved geolocation error handling: better error messages, explicit timeout, set world view on locateAndSetView failure. [#61](https://github.com/CloudMade/Leaflet/issues/61)
 * Added ability to pass Geolocation options to map `locate` and `locateAndSetView` methods (by [@JasonSanford](https://github.com/JasonSanford)).
 * Improved `Popup` to accept HTML elements in addition to strings as its content.
 * Added `Makefile` for building `leaflet.js` on non-Windows machines (by [@tmcw](https://github.com/tmcw)).
 * Improved `debug/leaflet-include.js` script to allow using it outside of `debug` folder (by [@antonj](https://github.com/antonj)).
 * Improved `L` definition to be compatible with CommonJS. [#122](https://github.com/CloudMade/Leaflet/issues/122)

### Bug fixes

#### General bugfixes

 * Fixed a bug where zooming is broken if the map contains a polygon and you zoom to an area where it's not visible. [#47](https://github.com/CloudMade/Leaflet/issues/47)
 * Fixed a bug where closed polylines would not appear on the map.
 * Fixed a bug where marker that was added, removed and then added again would not appear on the map. [#66](https://github.com/CloudMade/Leaflet/issues/66)
 * Fixed a bug where tile layer that was added, removed and then added again would not appear on the map.
 * Fixed a bug where some tiles would not load when panning across the date line. [#97](https://github.com/CloudMade/Leaflet/issues/97)
 * Fixed a bug where map div with `position: absolute` is reset to `relative`. [#100](https://github.com/CloudMade/Leaflet/issues/100)
 * Fixed a bug that caused an error when trying to add a marker without shadow in its icon.
 * Fixed a bug where popup content would not update on `setContent` call. [#94](https://github.com/CloudMade/Leaflet/issues/94)
 * Fixed a bug where double click zoom wouldn't work if popup is opened on map click
 * Fixed a bug with click propagation on popup close button. [#99](https://github.com/CloudMade/Leaflet/issues/99)
 * Fixed inability to remove ImageOverlay layer.

#### Desktop browsers bugfixes

 * Fixed a bug where paths would not appear in IE8.
 * Fixed incorrect zoom animation & popup styling in Opera 11.11.
 * Fixed popup fade animation in Firefox and Opera.
 * Fixed a bug where map isn't displayed in Firefox when there's an `img { max-width: 100% }` rule.

#### Mobile browsers bugfixes

 * Disabled zoom animation on Android by default because it's buggy on some devices (will be enabled back when it's stable enough). [#32](https://github.com/CloudMade/Leaflet/issues/32)
 * Fixed a bug where map would occasionally break while multi-touch-zooming on iOS. [#32](https://github.com/CloudMade/Leaflet/issues/32)
 * Fixed potentional memory leak on WebKit when removing tiles, thanks to [@Scalar4eg](https://github.com/Scalar4eg). [#107](https://github.com/CloudMade/Leaflet/issues/107)

## 0.1 (2011-05-13)

 * Initial Leaflet release.


L.CRS.EPSG3857 = L.Util.extend({}, L.CRS, {
	code: 'EPSG:3857',

	projection: L.Projection.SphericalMercator,
	transformation: new L.Transformation(0.5/Math.PI, 0.5, -0.5/Math.PI, 0.5),

	project: function(/*LatLng*/ latlng)/*-> Point*/ {
		var projectedPoint = this.projection.project(latlng),
			earthRadius = 6378137;
		return projectedPoint.multiplyBy(earthRadius);
	}
});

L.CRS.EPSG900913 = L.Util.extend({}, L.CRS.EPSG3857, {
	code: 'EPSG:900913'
});


/*
 * L.Point represents a point with x and y coordinates.
 */

L.Point = function(/*Number*/ x, /*Number*/ y, /*Boolean*/ round) {
	this.x = (round ? Math.round(x) : x);
	this.y = (round ? Math.round(y) : y);
};

L.Point.prototype = {
	add: function(point) {
		return this.clone()._add(point);
	},

	_add: function(point) {
		this.x += point.x;
		this.y += point.y;
		return this;
	},

	subtract: function(point) {
		return this.clone()._subtract(point);
	},

	// destructive subtract (faster)
	_subtract: function(point) {
		this.x -= point.x;
		this.y -= point.y;
		return this;
	},

	divideBy: function(num, round) {
		return new L.Point(this.x/num, this.y/num, round);
	},

	multiplyBy: function(num) {
		return new L.Point(this.x * num, this.y * num);
	},

	distanceTo: function(point) {
		var x = point.x - this.x,
			y = point.y - this.y;
		return Math.sqrt(x*x + y*y);
	},

	round: function() {
		return this.clone()._round();
	},

	// destructive round
	_round: function() {
		this.x = Math.round(this.x);
		this.y = Math.round(this.y);
		return this;
	},

	clone: function() {
		return new L.Point(this.x, this.y);
	},

	toString: function() {
		return 'Point(' +
				L.Util.formatNum(this.x) + ', ' +
				L.Util.formatNum(this.y) + ')';
	}
};


// stops loading all tiles in the background layer and removes tiles that not in the current bbox

var mapRect = this._container.getBoundingClientRect(),

for (var i = 0, len = tiles.length; i < len; i++) {

keep keep replace replace replace replace replace replace keep keep

var tiles = [].slice.call(this._tileBg.getElementsByTagName('img'));


/*
 * Popup extension to L.Path (polylines, polygons, circles), adding bindPopup method.
 */

L.Path.include({
	bindPopup: function(content, options) {
		if (!this._popup || this._popup.options !== options) {
			this._popup = new L.Popup(options);
		}
		this._popup.setContent(content);

		if (!this._openPopupAdded) {
			this.on('click', this._openPopup, this);
			this._openPopupAdded = true;
		}

		return this;
	},

	_openPopup: function(e) {
		this._popup.setLatLng(e.latlng);
		this._map.openPopup(this._popup);
	}
});