Fast 3kB alternative to React, with the same ES2015 API.

**All the power of Virtual DOM components, without the overhead:**

- Familiar React API & patterns: [ES6 Class] and [Functional Components]
- Extensive React compatibility via a simple [preact-compat] alias
- Everything you need: JSX, VDOM, React DevTools, HMR, SSR..
- A highly optimized diff algorithm and seamless Server Side Rendering
- Transparent asynchronous rendering with a pluggable scheduler

### 💁 More information at the [Preact Website ➞](https://preactjs.com)

---

- [Demos](#demos)
- [Libraries & Add-ons](#libraries--add-ons)
- [Getting Started](#getting-started)
	- [Import what you need](#import-what-you-need)
	- [Rendering JSX](#rendering-jsx)
	- [Components](#components)
	- [Props & State](#props--state)
- [Linked State](#linked-state)
- [Examples](#examples)
- [Extensions](#extensions)
- [Developer Tools](#developer-tools)
- [Backers](#backers)
- [Sponsors](#sponsors)
- [License](#license)

# Preact

[![npm](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact)
[![CDNJS](https://img.shields.io/cdnjs/v/preact.svg)](https://cdnjs.com/libraries/preact)
[![Preact Slack Community](https://preact-slack.now.sh/badge.svg)](https://preact-slack.now.sh)
[![OpenCollective Backers](https://opencollective.com/preact/backers/badge.svg)](#backers)
[![OpenCollective Sponsors](https://opencollective.com/preact/sponsors/badge.svg)](#sponsors)
[![travis](https://travis-ci.org/developit/preact.svg?branch=master)](https://travis-ci.org/developit/preact)
[![coveralls](https://img.shields.io/coveralls/developit/preact/master.svg)](https://coveralls.io/github/developit/preact)

Preact supports modern browsers and IE9+:

[![Browsers](https://saucelabs.com/browser-matrix/preact.svg)](https://saucelabs.com/u/preact)

---

## Demos

- [**ESBench**](http://esbench.com) is built using Preact.
- [**Nectarine.rocks**](http://nectarine.rocks) _([Github Project](https://github.com/developit/nectarine))_ :peach:
- [**Documentation Viewer**](https://documentation-viewer.firebaseapp.com) _([Github Project](https://github.com/developit/documentation-viewer))_
- [**TodoMVC**](https://preact-todomvc.surge.sh) _([Github Project](https://github.com/developit/preact-todomvc))_
- [**Hacker News Minimal**](https://developit.github.io/hn_minimal/) _([Github Project](https://github.com/developit/hn_minimal))_
- [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([Github Project](https://github.com/developit/preact-boilerplate))_ :zap:
- [**Preact Offline Starter**](https://preact-starter.now.sh) _([Github Project](https://github.com/lukeed/preact-starter))_ :100:
- [**Preact PWA**](https://preact-pwa.appspot.com/) _([Github Project](https://github.com/ezekielchentnik/preact-pwa))_ :hamburger:
- [**Preact Mobx Starter**](https://awaw00.github.io/preact-mobx-starter/) _([Github Project](https://github.com/awaw00/preact-mobx-starter))_ :sunny:
- [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star:
- [**Flickr Browser**](http://codepen.io/developit/full/VvMZwK/) (@ CodePen)
- [**Animating Text**](http://codepen.io/developit/full/LpNOdm/) (@ CodePen)
- [**60FPS Rainbow Spiral**](http://codepen.io/developit/full/xGoagz/) (@ CodePen)
- [**Simple Clock**](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/) (@ JSFiddle)
- [**3D + ThreeJS**](http://codepen.io/developit/pen/PPMNjd?editors=0010) (@ CodePen)
- [**Stock Ticker**](http://codepen.io/developit/pen/wMYoBb?editors=0010) (@ CodePen)
- [**Create your Own!**](https://jsfiddle.net/developit/rs6zrh5f/embedded/result/) (@ JSFiddle)
- [**Preact Coffeescript**](https://github.com/crisward/preact-coffee)
- [**GuriVR**](https://gurivr.com) _([Github Project](https://github.com/opennewslabs/guri-vr))_
- [**V2EX Preact**](https://github.com/yanni4night/v2ex-preact)
- [**BigWebQuiz**](https://bigwebquiz.com/) _([Github Project](https://github.com/jakearchibald/big-web-quiz))_
- [**Color Picker**](https://colors.now.sh) _([Github Project](https://github.com/lukeed/colors-app))_ :art:

## Libraries & Add-ons

- :raised_hands: [**preact-compat**](https://git.io/preact-compat): use any React library with Preact *([full example](http://git.io/preact-compat-example))*
- :page_facing_up: [**preact-render-to-string**](https://git.io/preact-render-to-string): Universal rendering.
- :earth_americas: [**preact-router**](https://git.io/preact-router): URL routing for your components
- :bookmark_tabs: [**preact-markup**](https://git.io/preact-markup): Render HTML & Custom Elements as JSX & Components
- :satellite: [**preact-portal**](https://git.io/preact-portal): Render Preact components into (a) SPACE :milky_way:
- :pencil: [**preact-richtextarea**](https://git.io/preact-richtextarea): Simple HTML editor component
- :bookmark: [**preact-token-input**](https://github.com/developit/preact-token-input): Text field that tokenizes input, for things like tags
- :card_index: [**preact-virtual-list**](https://github.com/developit/preact-virtual-list): Easily render lists with millions of rows ([demo](https://jsfiddle.net/developit/qqan9pdo/))
- :repeat: [**preact-cycle**](https://git.io/preact-cycle): Functional-reactive paradigm for Preact
- :triangular_ruler: [**preact-layout**](https://download.github.io/preact-layout/): Small and simple layout library
- :thought_balloon: [**preact-socrates**](https://github.com/matthewmueller/preact-socrates): Preact plugin for [Socrates](http://github.com/matthewmueller/socrates)
- :rowboat: [**preact-flyd**](https://github.com/xialvjun/preact-flyd): Use [flyd](https://github.com/paldepind/flyd) FRP streams in Preact + JSX
- :speech_balloon: [**preact-i18nline**](https://github.com/download/preact-i18nline): Integrates the ecosystem around [i18n-js](https://github.com/everydayhero/i18n-js) with Preact via [i18nline](https://github.com/download/i18nline).
- :metal: [**preact-mui**](https://git.io/v1aVO): The MUI CSS Preact library.
- :white_square_button: [**preact-mdl**](https://git.io/preact-mdl): Use [MDL](https://getmdl.io) as Preact components
- :rocket: [**preact-photon**](https://git.io/preact-photon): build beautiful desktop UI with [photon](http://photonkit.com)
- :microscope: [**preact-jsx-chai**](https://git.io/preact-jsx-chai): JSX assertion testing _(no DOM, right in Node)_
- :tophat: [**preact-classless-component**](https://github.com/ld0rman/preact-classless-component): create preact components without the class keyword
- :hammer: [**preact-hyperscript**](https://github.com/queckezz/preact-hyperscript): Hyperscript-like syntax for creating elements
- :white_check_mark: [**shallow-compare**](https://github.com/tkh44/shallow-compare): simplified `shouldComponentUpdate` helper.
- :shaved_ice: [**preact-codemod**](https://github.com/vutran/preact-codemod): Transform your React code to Preact.
- :construction_worker: [**preact-helmet**](https://github.com/download/preact-helmet): A document head manager for Preact
- :necktie: [**preact-delegate**](https://github.com/NekR/preact-delegate): Delegate DOM events

---

## Getting Started

> 💁 You [don't _have_ to use ES2015 to use Preact](https://github.com/developit/preact-without-babel)... but you should.

The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc.  If you don't, start with [preact-boilerplate] or a [CodePen Template](http://codepen.io/developit/pen/pgaROe?editors=0010).

### Import what you need

The `preact` module provides both named and default exports, so you can either import everything under a namespace of your choosing, or just what you need as locals:

##### Named:

```js
import { h, render, Component } from 'preact';

// Tell Babel to transform JSX into h() calls:
/** @jsx h */
```

##### Default:

```js
import preact from 'preact';

// Tell Babel to transform JSX into preact.h() calls:
/** @jsx preact.h */
```

> Named imports work well for highly structured applications, whereas the default import is quick and never needs to be updated when using different parts of the library.
>
> Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`:
>
> **For Babel 5 and prior:**
>
> ```json
> { "jsxPragma": "h" }
> ```
>
> **For Babel 6:**
>
> ```json
> {
>   "plugins": [
>     ["transform-react-jsx", { "pragma":"h" }]
>   ]
> }
> ```

### Rendering JSX

Out of the box, Preact provides an `h()` function that turns your JSX into Virtual DOM elements _([here's how](http://jasonformat.com/wtf-is-jsx))_. It also provides a `render()` function that creates a DOM tree from that Virtual DOM.

To render some JSX, just import those two functions and use them like so:

```js
import { h, render } from 'preact';

render((
	<div id="foo">
		<span>Hello, world!</span>
		<button onClick={ e => alert("hi!") }>Click Me</button>
	</div>
), document.body);
```

This should seem pretty straightforward if you've used hyperscript or one of its many friends. If you're not, the short of it is that the h function import gets used in the final, transpiled code as a drop in replacement for React.createElement, and so needs to be imported even if you don't explicitly use it in the code you write. Also note that if you're the kind of person who likes writing your React code in "pure JavaScript" (you know who you are) you will need to use h(...) wherever you would otherwise use React.createElement.

Rendering hyperscript with a virtual DOM is pointless, though. We want to render components and have them updated when data changes - that's where the power of virtual DOM diffing shines. :star2:

### Components

Preact exports a generic `Component` class, which can be extended to build encapsulated, self-updating pieces of a User Interface.  Components support all of the standard React [lifecycle methods], like `shouldComponentUpdate()` and `componentWillReceiveProps()`.  Providing specific implementations of these methods is the preferred mechanism for controlling _when_ and _how_ components update.

Components also have a `render()` method, but unlike React this method is passed `(props, state)` as arguments. This provides an ergonomic means to destructure `props` and `state` into local variables to be referenced from JSX.

Let's take a look at a very simple `Clock` component, which shows the current time.

```js
import { h, render, Component } from 'preact';

class Clock extends Component {
	render() {
		let time = new Date().toLocaleTimeString();
		return <span>{ time }</span>;
	}
}

// render an instance of Clock into <body>:
render(<Clock />, document.body);
```

That's great. Running this produces the following HTML DOM structure:

```html
<span>10:28:57 PM</span>
```

In order to have the clock's time update every second, we need to know when `<Clock>` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component:

| Lifecycle method            | When it gets called                              |
|-----------------------------|--------------------------------------------------|
| `componentWillMount`        | before the component gets mounted to the DOM     |
| `componentDidMount`         | after the component gets mounted to the DOM      |
| `componentWillUnmount`      | prior to removal from the DOM                    |
| `componentDidUnmount`       | after removal from the DOM                       |
| `componentWillReceiveProps` | before new props get accepted                    |
| `shouldComponentUpdate`     | before `render()`. Return `false` to skip render |
| `componentWillUpdate`       | before `render()`                                |
| `componentDidUpdate`        | after `render()`                                 |

So, we want to have a 1-second timer start once the Component gets added to the DOM, and stop if it is removed. We'll create the timer and store a reference to it in `componentDidMount`, and stop the timer in `componentWillUnmount`. On each timer tick, we'll update the component's `state` object with a new time value. Doing this will automatically re-render the component.

```js
import { h, render, Component } from 'preact';

class Clock extends Component {
	constructor() {
		super();
		// set initial time:
		this.state = {
			time: Date.now()
		};
	}

	componentDidMount() {
		// update time every second
		this.timer = setInterval(() => {
			this.setState({ time: Date.now() });
		}, 1000);
	}

	componentWillUnmount() {
		// stop when not renderable
		clearInterval(this.timer);
	}

	render(props, state) {
		let time = new Date(state.time).toLocaleTimeString();
		return <span>{ time }</span>;
	}
}

// render an instance of Clock into <body>:
render(<Clock />, document.body);
```

Now we have [a ticking clock](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/)!

### Props & State

The concept (and nomenclature) for `props` and `state` is the same as in React. `props` are passed to a component by defining attributes in JSX, `state` is internal state. Changing either triggers a re-render, though by default Preact re-renders Components asynchronously for `state` changes and synchronously for `props` changes.  You can tell Preact to render `prop` changes asynchronously by setting `options.syncComponentUpdates` to `false`.

---

## Linked State

One area Preact takes a little further than React is in optimizing state changes. A common pattern in ES2015 React code is to use Arrow functions within a `render()` method in order to update state in response to events.  Creating functions enclosed in a scope on every render is inefficient and forces the garbage collector to do more work than is necessary.

One solution to this is to bind component methods declaratively.
Here is an example using [decko](http://git.io/decko):

```js
class Foo extends Component {
	@bind
	updateText(e) {
		this.setState({ text: e.target.value });
	}
	render({ }, { text }) {
		return <input value={text} onInput={this.updateText} />;
	}
}
```

While this achieves much better runtime performance, it's still a lot of unnecessary code to wire up state to UI.

Fortunately there is a solution, in the form of `linkState()`. Calling `component.linkState('text')` returns a function that accepts an Event and uses it's associated value to update the given property in your component's state. Calls to linkState() with the same state property are cached, so there is no performance penalty.  Here is the previous example rewritten using _Linked State_:

```js
class Foo extends Component {
	render({ }, { text }) {
		return <input value={text} onInput={this.linkState('text')} />;
	}
}
```

Simple and effective. It handles linking state from any input type, or an optional second parameter can be used to explicitly provide a keypath to the new state value.

## Examples

Here is a somewhat verbose Preact `<Link>` component:

```js
class Link extends Component {
	render(props, state) {
		return <a href={ props.href }>{ props.children }</a>;
	}
}
```

Since this is ES6/ES2015, we can further simplify:

```js
class Link extends Component {
	render({ href, children }) {
		return <a {...{ href, children }} />;
	}
}

// or, for wide-open props support:
class Link extends Component {
	render(props) {
		return <a {...props} />;
	}
}

// or, as a stateless functional component:
const Link = ({ children, ...props }) => (
	<a {...props}>{ children }</a>
);
```

## Extensions

It is likely that some projects based on Preact would wish to extend Component with great new functionality.

Perhaps automatic connection to stores for a Flux-like architecture, or mixed-in context bindings to make it feel more like `React.createClass()`.  Just use ES2015 inheritance:

```js
class BoundComponent extends Component {
	constructor(props) {
		super(props);
		this.bind();
	}
	bind() {
		this.binds = {};
		for (let i in this) {
			this.binds[i] = this[i].bind(this);
		}
	}
}

// example usage
class Link extends BoundComponent {
	click() {
		open(this.href);
	}
	render() {
		let { click } = this.binds;
		return <span onclick={ click }>{ children }</span>;
	}
}
```

The possibilities are pretty endless here. You could even add support for rudimentary mixins:

```js
class MixedComponent extends Component {
	constructor() {
		super();
		(this.mixins || []).forEach( m => Object.assign(this, m) );
	}
}
```

## Developer Tools

You can inspect and modify the state of your Preact UI components at runtime using the
[React Developer Tools](https://github.com/facebook/react-devtools) browser extension.

1. Install the [React Developer Tools](https://github.com/facebook/react-devtools) extension
2. Import the "preact/devtools" module in your app
3. Reload and go to the 'React' tab in the browser's development tools

```js
import { h, Component, render } from 'preact';

// Enable devtools. You can reduce the size of your app by only including this
// module in development builds. eg. In Webpack, wrap this with an `if (module.hot) {...}`
// check.
require('preact/devtools');
```

## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/preact#backer)]

## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/preact#sponsor)]

## License

MIT

[![Preact](http://i.imgur.com/YqCHvEW.gif)](https://preactjs.com)

[preact-compat]: https://github.com/developit/preact-compat
[ES6 Class]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
[Functional Components]: https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components
[hyperscript]: https://github.com/dominictarr/hyperscript
[preact-boilerplate]: https://github.com/developit/preact-boilerplate
[lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html


keep keep keep replace keep keep keep keep

expect(Bar).to.have.been.calledWithMatch({ b:'b', ref:sinon.match.falsy }, { });

import { h, render, Component } from '../../src/preact';
/** @jsx h */

// gives call count and argument errors names (otherwise sinon just uses "spy"):
let spy = (name, ...args) => {
	let spy = sinon.spy(...args);
	spy.displayName = `spy('${name}')`;
	return spy;
};

describe('refs', () => {
	let scratch;

	before( () => {
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);
	});

	beforeEach( () => {
		scratch.innerHTML = '';
	});

	after( () => {
		scratch.parentNode.removeChild(scratch);
		scratch = null;
	});

	it('should invoke refs in render()', () => {
		let ref = spy('ref');
		render(
, scratch);
		expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild);
	});

	it('should invoke refs in Component.render()', () => {
		let outer = spy('outer'),
			inner = spy('inner');
		class Foo extends Component {
			render() {
				return (

				);
			}
		}
		render(<Foo />, scratch);

		expect(outer).to.have.been.calledWith(scratch.firstChild);
		expect(inner).to.have.been.calledWith(scratch.firstChild.firstChild);
	});

	it('should pass components to ref functions', () => {
		let ref = spy('ref'),
			instance;
		class Foo extends Component {
			constructor() {
				super();
				instance = this;
			}
			render() {
				return
;
			}
		}
		render(<Foo ref={ref} />, scratch);

		expect(ref).to.have.been.calledOnce.and.calledWith(instance);
	});

	it('should pass rendered DOM from functional components to ref functions', () => {
		let ref = spy('ref');

		const Foo = () =>
;

		let root = render(<Foo ref={ref} />, scratch);
		expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild);

		ref.reset();
		render(<Foo ref={ref} />, scratch, root);
		expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild);

		ref.reset();
		render(, scratch, root);
		expect(ref).to.have.been.calledOnce.and.calledWith(null);
	});

	it('should pass children to ref functions', () => {
		let outer = spy('outer'),
			inner = spy('inner'),
			rerender, inst;
		class Outer extends Component {
			constructor() {
				super();
				rerender = () => this.forceUpdate();
			}
			render() {
				return (

						<Inner ref={outer} />

				);
			}
		}
		class Inner extends Component {
			constructor() {
				super();
				inst = this;
			}
			render() {
				return ;
			}
		}

		let root = render(<Outer />, scratch);

		expect(outer).to.have.been.calledOnce.and.calledWith(inst);
		expect(inner).to.have.been.calledOnce.and.calledWith(inst.base);

		outer.reset();
		inner.reset();

		rerender();

		expect(outer).to.have.been.calledOnce.and.calledWith(inst);
		expect(inner).to.have.been.calledOnce.and.calledWith(inst.base);

		outer.reset();
		inner.reset();

		render(
, scratch, root);

		expect(outer).to.have.been.calledOnce.and.calledWith(null);
		expect(inner).to.have.been.calledOnce.and.calledWith(null);
	});

	it('should pass high-order children to ref functions', () => {
		let outer = spy('outer'),
			inner = spy('inner'),
			innermost = spy('innermost'),
			outerInst,
			innerInst;
		class Outer extends Component {
			constructor() {
				super();
				outerInst = this;
			}
			render() {
				return <Inner ref={inner} />;
			}
		}
		class Inner extends Component {
			constructor() {
				super();
				innerInst = this;
			}
			render() {
				return ;
			}
		}

		let root = render(<Outer ref={outer} />, scratch);

		expect(outer, 'outer initial').to.have.been.calledOnce.and.calledWith(outerInst);
		expect(inner, 'inner initial').to.have.been.calledOnce.and.calledWith(innerInst);
		expect(innermost, 'innerMost initial').to.have.been.calledOnce.and.calledWith(innerInst.base);

		outer.reset();
		inner.reset();
		innermost.reset();
		root = render(<Outer ref={outer} />, scratch, root);

		expect(outer, 'outer update').to.have.been.calledOnce.and.calledWith(outerInst);
		expect(inner, 'inner update').to.have.been.calledOnce.and.calledWith(innerInst);
		expect(innermost, 'innerMost update').to.have.been.calledOnce.and.calledWith(innerInst.base);

		outer.reset();
		inner.reset();
		innermost.reset();
		render(
, scratch, root);

		expect(outer, 'outer unmount').to.have.been.calledOnce.and.calledWith(null);
		expect(inner, 'inner unmount').to.have.been.calledOnce.and.calledWith(null);
		expect(innermost, 'innerMost unmount').to.have.been.calledOnce.and.calledWith(null);
	});

	it('should not pass ref into component as a prop', () => {
		let foo = spy('foo'),
			bar = spy('bar');

		class Foo extends Component {
			render(){ return
; }
		}
		const Bar = spy('Bar', () =>
);

		sinon.spy(Foo.prototype, 'render');

		render((

				<Foo ref={foo} a="a" />
				<Bar ref={bar} b="b" />

		), scratch);

		expect(Foo.prototype.render).to.have.been.calledWithMatch({ ref:sinon.match.falsy, a:'a' }, { }, { });
		expect(Bar).to.have.been.calledWithMatch({ b:'b', ref:bar }, { });
	});

	// Test for #232
	it('should only null refs after unmount', () => {
		let root, outer, inner;

		class TestUnmount extends Component {
			componentWillUnmount() {
				expect(this).to.have.property('outer', outer);
				expect(this).to.have.property('inner', inner);
			}

			componentDidUnmount() {
				expect(this).to.have.property('outer', null);
				expect(this).to.have.property('inner', null);
			}

			render() {
				return (

 this.outer=c }>

 this.inner=c } />

				);
			}
		}

		sinon.spy(TestUnmount.prototype, 'componentWillUnmount');
		sinon.spy(TestUnmount.prototype, 'componentDidUnmount');

		root = render(
<TestUnmount />
, scratch, root);
		outer = scratch.querySelector('#outer');
		inner = scratch.querySelector('#inner');

		expect(TestUnmount.prototype.componentWillUnmount).not.to.have.been.called;
		expect(TestUnmount.prototype.componentDidUnmount).not.to.have.been.called;

		render(
, scratch, root);

		expect(TestUnmount.prototype.componentWillUnmount).to.have.been.calledOnce;
		expect(TestUnmount.prototype.componentDidUnmount).to.have.been.calledOnce;
	});

	it('should null and re-invoke refs when swapping component root element type', () => {
		let inst;

		class App extends Component {
			render() {
				return
<Child />
;
			}
		}

		class Child extends Component {
			constructor(props, context) {
				super(props, context);
				this.state = { show:false };
				inst = this;
			}
			handleMount(){}
			render(_, { show }) {
				if (!show) return

;
				return some test content;
			}
		}
		sinon.spy(Child.prototype, 'handleMount');

		render(<App />, scratch);
		expect(inst.handleMount).to.have.been.calledOnce.and.calledWith(scratch.querySelector('#div'));
		inst.handleMount.reset();

		inst.setState({ show:true });
		inst.forceUpdate();
		expect(inst.handleMount).to.have.been.calledTwice;
		expect(inst.handleMount.firstCall).to.have.been.calledWith(null);
		expect(inst.handleMount.secondCall).to.have.been.calledWith(scratch.querySelector('#span'));
		inst.handleMount.reset();

		inst.setState({ show:false });
		inst.forceUpdate();
		expect(inst.handleMount).to.have.been.calledTwice;
		expect(inst.handleMount.firstCall).to.have.been.calledWith(null);
		expect(inst.handleMount.secondCall).to.have.been.calledWith(scratch.querySelector('#div'));
	});

	it('should add refs to components representing DOM nodes with no attributes if they have been pre-rendered', () => {
		// Simulate pre-render
		let parent = document.createElement('div');
		let child = document.createElement('div');
		parent.appendChild(child);
		scratch.appendChild(parent); // scratch contains:

		let ref = spy('ref');

		function Wrapper() {
			return

;
		}

		render(
<Wrapper ref={ref} />
, scratch, scratch.firstChild);
		expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild.firstChild);
	});
});


// ES2015 APIs used by developer tools integration
import 'core-js/es6/map';
import 'core-js/fn/array/fill';
import 'core-js/fn/array/from';
import 'core-js/fn/object/assign';


import { h, render } from '../../src/preact';
/** @jsx h */

// hacky normalization of attribute order across browsers.
function sortAttributes(html) {
	return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
		let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
		if (~after.indexOf('/')) after = '></'+pre+'>';
		return '<' + pre + list.join('') + after;
	});
}

describe('svg', () => {
	let scratch;

	before( () => {
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);
	});

	beforeEach( () => {
		scratch.innerHTML = '';
	});

	after( () => {
		scratch.parentNode.removeChild(scratch);
		scratch = null;
	});

	it('should render SVG to string', () => {
		render((
			<svg viewBox="0 0 360 360">
				<path stroke="white" fill="black" d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" />
			</svg>
		), scratch);

		let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', ''));
		expect(html).to.equal(sortAttributes(`
			<svg viewBox="0 0 360 360">
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white"></path>
			</svg>
		`.replace(/[\n\t]+/g,'')));
	});

	it('should render SVG to DOM', () => {
		const Demo = () => (
			<svg viewBox="0 0 360 360">
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white" />
			</svg>
		);
		render(<Demo />, scratch);

		let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', ''));
		expect(html).to.equal(sortAttributes('<svg viewBox="0 0 360 360"><path stroke="white" fill="black" d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z"></path></svg>'));
	});

	it('should render with the correct namespace URI', () => {
		render(<svg />, scratch);

		let namespace = scratch.querySelector('svg').namespaceURI;

		expect(namespace).to.equal("http://www.w3.org/2000/svg");
	});

	it('should use attributes for className', () => {
		const Demo = ({ c }) => (
			<svg viewBox="0 0 360 360" {...(c ? {class:'foo_'+c} : {})}>
				<path class={c && ('bar_'+c)} stroke="white" fill="black" d="M347.1 357.9L183.3 256.5 13 357.9V1.7h334.1v356.2zM58.5 47.2v231.4l124.8-74.1 118.3 72.8V47.2H58.5z" />
			</svg>
		);
		let root = render(<Demo c="1" />, scratch, root);
		sinon.spy(root, 'removeAttribute');
		root = render(<Demo />, scratch, root);
		expect(root.removeAttribute).to.have.been.calledOnce.and.calledWith('class');
		root.removeAttribute.restore();

		root = render(
, scratch, root);
		root = render(<Demo />, scratch, root);
		sinon.spy(root, 'setAttribute');
		root = render(<Demo c="2" />, scratch, root);
		expect(root.setAttribute).to.have.been.calledOnce.and.calledWith('class', 'foo_2');
		root.setAttribute.restore();
		root = render(<Demo c="3" />, scratch, root);
		root = render(<Demo />, scratch, root);
	});

	it('should still support class attribute', () => {
		render((
			<svg viewBox="0 0 1 1" class="foo bar" />
		), scratch);

		expect(scratch.innerHTML).to.contain(` class="foo bar"`);
	});

	it('should serialize class', () => {
		render((
			<svg viewBox="0 0 1 1" class={{ foo: true, bar: false, other: 'hello' }} />
		), scratch);

		expect(scratch.innerHTML).to.contain(` class="foo other"`);
	});

	it('should switch back to HTML for <foreignObject>', () => {
		render((
			<svg>
				<g>
					<foreignObject>
						test
					</foreignObject>
				</g>
			</svg>
		), scratch);

		expect(scratch.getElementsByTagName('a'))
			.to.have.property('0')
			.that.is.a('HTMLAnchorElement');
	});
});


The MIT License (MIT)

Copyright (c) 2017 Jason Miller

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


scratch.appendChild(parent); // scratch contains:

keep keep keep keep replace replace keep keep

import { h, Component, render } from '../../src/preact';
import { initDevTools } from '../../devtools/devtools';
import { unmountComponent } from '../../src/vdom/component';

class StatefulComponent extends Component {
	constructor(props) {
		super(props);

		this.state = {count: 0};
	}

	render() {
		return h('span', {}, String(this.state.count));
	}
}

function FunctionalComponent() {
	return h('span', {class: 'functional'}, 'Functional');
}

function Label({label}) {
	return label;
}

class MultiChild extends Component {
	constructor(props) {
		super(props);
		this.state = {count: props.initialCount};
	}

	render() {
		return h('div', {}, Array(this.state.count).fill('child'));
	}
}

let describe_ = describe;
if (!('name' in Function.prototype)) {
	// Skip these tests under Internet Explorer
	describe_ = describe.skip;
}

describe_('React Developer Tools integration', () => {
	let cleanup;
	let container;
	let renderer;

	// Maps of DOM node to React*Component-like objects.
	// For composite components, there will be two instances for each node, one
	// for the composite component (instanceMap) and one for the root child DOM
	// component rendered by that component (domInstanceMap)
	let instanceMap = new Map();
	let domInstanceMap = new Map();

	beforeEach(() => {
		container = document.createElement('div');
		document.body.appendChild(container);

		const onMount = instance => {
			if (instance._renderedChildren) {
				domInstanceMap.set(instance.node, instance);
			} else {
				instanceMap.set(instance.node, instance);
			}
		};

		const onUnmount = instance => {
			instanceMap.delete(instance.node);
			domInstanceMap.delete(instance.node);
		};

		global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
			inject: sinon.spy(_renderer => {
				renderer = _renderer;
				renderer.Mount._renderNewRootComponent = sinon.stub();
				renderer.Reconciler.mountComponent = sinon.spy(onMount);
				renderer.Reconciler.unmountComponent = sinon.spy(onUnmount);
				renderer.Reconciler.receiveComponent = sinon.stub();
			})
		};
		cleanup = initDevTools();
	});

	afterEach(() => {
		container.remove();
		cleanup();
	});

	it('registers preact as a renderer with the React DevTools hook', () => {
		expect(global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject).to.be.called;
	});

	// Basic component addition/update/removal tests
	it('notifies dev tools about new components', () => {
		render(h(StatefulComponent), container);
		expect(renderer.Reconciler.mountComponent).to.be.called;
	});

	it('notifies dev tools about component updates', () => {
		const node = render(h(StatefulComponent), container);
		node._component.forceUpdate();
		expect(renderer.Reconciler.receiveComponent).to.be.called;
	});

	it('notifies dev tools when components are removed', () => {
		const node = render(h(StatefulComponent), container);
		unmountComponent(node._component, true);
		expect(renderer.Reconciler.unmountComponent).to.be.called;
	});

	// Test properties of DOM components exposed to devtools via
	// ReactDOMComponent-like instances
	it('exposes the tag name of DOM components', () => {
		const node = render(h(StatefulComponent), container);
		const domInstance = domInstanceMap.get(node);
		expect(domInstance._currentElement.type).to.equal('span');
	});

	it('exposes DOM component props', () => {
		const node = render(h(FunctionalComponent), container);
		const domInstance = domInstanceMap.get(node);
		expect(domInstance._currentElement.props.class).to.equal('functional');
	});

	it('exposes text component contents', () => {
		const node = render(h(Label, {label: 'Text content'}), container);
		const textInstance = domInstanceMap.get(node);
		expect(textInstance._stringText).to.equal('Text content');
	});

	// Test properties of composite components exposed to devtools via
	// ReactCompositeComponent-like instances
	it('exposes the name of composite component classes', () => {
		const node = render(h(StatefulComponent), container);
		expect(instanceMap.get(node).getName()).to.equal('StatefulComponent');
	});

	it('exposes composite component props', () => {
		const node = render(h(Label, {label: 'Text content'}), container);
		const instance = instanceMap.get(node);
		expect(instance._currentElement.props.label).to.equal('Text content');
	});

	it('exposes composite component state', () => {
		const node = render(h(StatefulComponent), container);

		node._component.setState({count: 42});
		node._component.forceUpdate();

		expect(instanceMap.get(node).state).to.deep.equal({count: 42});
	});

	// Test setting state via devtools
	it('updates component when setting state from devtools', () => {
		const node = render(h(StatefulComponent), container);

		instanceMap.get(node).setState({count: 10});
		instanceMap.get(node).forceUpdate();

		expect(node.textContent).to.equal('10');
	});

	// Test that the original instance is exposed via `_instance` so it can
	// be accessed conveniently via `$r` in devtools

	// Functional component handling tests
	it('wraps functional components with stateful ones', () => {
		const vnode = h(FunctionalComponent);
		expect(vnode.nodeName.prototype).to.have.property('render');
	});

	it('exposes the name of functional components', () => {
		const node = render(h(FunctionalComponent), container);
		const instance = instanceMap.get(node);
		expect(instance.getName()).to.equal('FunctionalComponent');
	});

	it('exposes a fallback name if the component has no useful name', () => {
		const node = render(h(() => h('div')), container);
		const instance = instanceMap.get(node);
		expect(instance.getName()).to.equal('(Function.name missing)');
	});

	// Test handling of DOM children
	it('notifies dev tools about DOM children', () => {
		const node = render(h(StatefulComponent), container);
		const domInstance = domInstanceMap.get(node);
		expect(renderer.Reconciler.mountComponent).to.have.been.calledWith(domInstance);
	});

	it('notifies dev tools when a component update adds DOM children', () => {
		const node = render(h(MultiChild, {initialCount: 2}), container);

		node._component.setState({count: 4});
		node._component.forceUpdate();

		expect(renderer.Reconciler.mountComponent).to.have.been.called.twice;
	});

	it('notifies dev tools when a component update modifies DOM children', () => {
		const node = render(h(StatefulComponent), container);

		instanceMap.get(node).setState({count: 10});
		instanceMap.get(node).forceUpdate();

		const textInstance = domInstanceMap.get(node.childNodes[0]);
		expect(textInstance._stringText).to.equal('10');
	});

	it('notifies dev tools when a component update removes DOM children', () => {
		const node = render(h(MultiChild, {initialCount: 1}), container);

		node._component.setState({count: 0});
		node._component.forceUpdate();

		expect(renderer.Reconciler.unmountComponent).to.be.called;
	});

	// Root component info
	it('exposes root components on the _instancesByReactRootID map', () => {
		render(h(StatefulComponent), container);
		expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1);
	});

	it('notifies dev tools when new root components are mounted', () => {
		render(h(StatefulComponent), container);
		expect(renderer.Mount._renderNewRootComponent).to.be.called;
	});

	it('removes root components when they are unmounted', () => {
		const node = render(h(StatefulComponent), container);
		unmountComponent(node._component, true);
		expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(0);
	});

	it('counts root components correctly when a root renders a composite child', () => {
		function Child() {
			return h('main');
		}
		function Parent() {
			return h(Child);
		}

		render(h(Parent), container);

		expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1);
	});

	it('counts root components correctly when a native element has a composite child', () => {
		function Link() {
			return h('a');
		}
		function Root() {
			return h('div', {}, h(Link));
		}

		render(h(Root), container);

		expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1);
	});
});


import { h, Component, render } from '../../src/preact';
/** @jsx h */

describe('keys', () => {
	let scratch;

	before( () => {
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);
	});

	beforeEach( () => {
		scratch.innerHTML = '';
	});

	after( () => {
		scratch.parentNode.removeChild(scratch);
		scratch = null;
	});

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

1

a

		), scratch);

		render((

2

b

		), scratch, root);

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

2

b

');
	});

	it('should set VNode#key property', () => {
		expect(
).to.have.property('key').that.is.empty;
		expect(
).to.have.property('key').that.is.empty;
		expect(
).to.have.property('key', '1');
	});

	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

;
			}
		}

		let root;

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

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

indicator

indicator

indicator

');
	});
});


import { h } from '../../src/preact';
import { VNode } from '../../src/vnode';
import { expect } from 'chai';

/*eslint-env browser, mocha */

/** @jsx h */

const buildVNode = (nodeName, attributes, children=[]) => ({
	nodeName,
	children,
	attributes,
	key: attributes && attributes.key
});

describe('h(jsx)', () => {
	it('should return a VNode', () => {
		let r;
		expect( () => r = h('foo') ).not.to.throw();
		expect(r).to.be.an('object');
		expect(r).to.be.an.instanceof(VNode);
		expect(r).to.have.property('nodeName', 'foo');
		expect(r).to.have.property('attributes', undefined);
		expect(r).to.have.property('children').that.eql([]);
	});

	it('should perserve raw attributes', () => {
		let attrs = { foo:'bar', baz:10, func:()=>{} },
			r = h('foo', attrs);
		expect(r).to.be.an('object')
			.with.property('attributes')
			.that.deep.equals(attrs);
	});

	it('should support element children', () => {
		let r = h(
			'foo',
			null,
			h('bar'),
			h('baz')
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				buildVNode('bar'),
				buildVNode('baz')
			]);
	});

	it('should support multiple element children, given as arg list', () => {
		let r = h(
			'foo',
			null,
			h('bar'),
			h('baz', null, h('test'))
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				buildVNode('bar'),
				buildVNode('baz', undefined, [
					buildVNode('test')
				])
			]);
	});

	it('should handle multiple element children, given as an array', () => {
		let r = h(
			'foo',
			null,
			[
				h('bar'),
				h('baz', null, h('test'))
			]
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				buildVNode('bar'),
				buildVNode('baz', undefined, [
					buildVNode('test')
				])
			]);
	});

	it('should handle multiple children, flattening one layer as needed', () => {
		let r = h(
			'foo',
			null,
			h('bar'),
			[
				h('baz', null, h('test'))
			]
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				buildVNode('bar'),
				buildVNode('baz', undefined, [
					buildVNode('test')
				])
			]);
	});

	it('should support nested children', () => {
		const m = x => h(x);
		expect(
			h('foo', null, m('a'), [m('b'), m('c')], m('d'))
		).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));

		expect(
			h('foo', null, [m('a'), [m('b'), m('c')], m('d')])
		).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));

		expect(
			h('foo', { children: [m('a'), [m('b'), m('c')], m('d')] })
		).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));

		expect(
			h('foo', { children: [[m('a'), [m('b'), m('c')], m('d')]] })
		).to.have.property('children').that.eql(['a', 'b', 'c', 'd'].map(m));

		expect(
			h('foo', { children: m('a') })
		).to.have.property('children').that.eql([m('a')]);

		expect(
			h('foo', { children: 'a' })
		).to.have.property('children').that.eql(['a']);
	});

	it('should support text children', () => {
		let r = h(
			'foo',
			null,
			'textstuff'
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.with.length(1)
			.with.property('0')
			.that.equals('textstuff');
	});

	it('should merge adjacent text children', () => {
		let r = h(
			'foo',
			null,
			'one',
			'two',
			h('bar'),
			'three',
			h('baz'),
			h('baz'),
			'four',
			null,
			'five',
			'six'
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				'onetwo',
				buildVNode('bar'),
				'three',
				buildVNode('baz'),
				buildVNode('baz'),
				'fourfivesix'
			]);
	});

	it('should merge nested adjacent text children', () => {
		let r = h(
			'foo',
			null,
			'one',
			['two', null, 'three'],
			null,
			['four', null, 'five', null],
			'six',
			null
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				'onetwothreefourfivesix'
			]);
	});
	it('should not merge children that are boolean values', () => {
		let r = h(
			'foo',
			null,
			'one',
			true,
			'two',
			false,
			'three'
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals(['onetwothree']);
	});
});


import nodeResolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import memory from 'rollup-plugin-memory';

export default {
	exports: 'named',
	useStrict: false,
	plugins: [
		memory({
			path: 'src/preact',
			contents: "export * from './preact';"
		}),
		nodeResolve({
			main: true
		}),
		babel({
			sourceMap: true,
			loose: 'all',
			blacklist: ['es6.tailCall'],
			exclude: 'node_modules/**'
		})
	]
};


render(
<Wrapper ref={ref} />
, scratch, scratch.firstChild);

render(
<Wrapper ref={ c => ref(c.base) } />
, scratch, scratch.firstChild);


import { NON_DIMENSION_PROPS, NON_BUBBLING_EVENTS } from '../constants';
import options from '../options';
import { toLowerCase, isString, isFunction, hashToClassName } from '../util';

/** Removes a given DOM Node from its parent. */
export function removeNode(node) {
	let p = node.parentNode;
	if (p) p.removeChild(node);
}

/** Set a named attribute on the given Node, with special behavior for some names and event handlers.
 *	If `value` is `null`, the attribute/handler will be removed.
 *	@param {Element} node	An element to mutate
 *	@param {string} name	The name/key to set, such as an event or attribute name
 *	@param {any} old	The last value that was set for this name/node pair
 *	@param {any} value	An attribute value, such as a function to be used as an event handler
 *	@param {Boolean} isSvg	Are we currently diffing inside an svg?
 *	@private
 */
export function setAccessor(node, name, old, value, isSvg) {

	if (name==='className') name = 'class';

	if (name==='class' && value && typeof value==='object') {
		value = hashToClassName(value);
	}

	if (name==='key') {
		// ignore
	}
	else if (name==='class' && !isSvg) {
		node.className = value || '';
	}
	else if (name==='style') {
		if (!value || isString(value) || isString(old)) {
			node.style.cssText = value || '';
		}
		if (value && typeof value==='object') {
			if (!isString(old)) {
				for (let i in old) if (!(i in value)) node.style[i] = '';
			}
			for (let i in value) {
				node.style[i] = typeof value[i]==='number' && !NON_DIMENSION_PROPS[i] ? (value[i]+'px') : value[i];
			}
		}
	}
	else if (name==='dangerouslySetInnerHTML') {
		if (value) node.innerHTML = value.__html || '';
	}
	else if (name[0]=='o' && name[1]=='n') {
		let l = node._listeners || (node._listeners = {});
		name = toLowerCase(name.substring(2));
		// @TODO: this might be worth it later, un-breaks focus/blur bubbling in IE9:
		// if (node.attachEvent) name = name=='focus'?'focusin':name=='blur'?'focusout':name;
		if (value) {
			if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
		}
		else if (l[name]) {
			node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
		}
		l[name] = value;
	}
	else if (name!=='list' && name!=='type' && !isSvg && name in node) {
		setProperty(node, name, value==null ? '' : value);
		if (value==null || value===false) node.removeAttribute(name);
	}
	else {
		let ns = isSvg && name.match(/^xlink\:?(.+)/);
		if (value==null || value===false) {
			if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]));
			else node.removeAttribute(name);
		}
		else if (typeof value!=='object' && !isFunction(value)) {
			if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value);
			else node.setAttribute(name, value);
		}
	}
}

/** Attempt to set a DOM property to the given value.
 *	IE & FF throw for certain property-value combinations.
 */
function setProperty(node, name, value) {
	try {
		node[name] = value;
	} catch (e) { }
}

/** Proxy an event to hooked event handlers
 *	@private
 */
function eventProxy(e) {
	return this._listeners[e.type](options.event && options.event(e) || e);
}


/* eslint no-console:0 */

// parent node types that we don't want to remove pointless initializations from (because it breaks hoisting)
const BLOCKED = ['ForStatement', 'WhileStatement']; // 'IfStatement', 'SwitchStatement'

/** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */
export default (file, api) => {
	let { jscodeshift } = api,
		found = 0;

	let code = jscodeshift(file.source)
		.find(jscodeshift.VariableDeclaration)
		.forEach(handleDeclaration);

	function handleDeclaration(decl) {
		let p = decl,
			remove = true;

		while ((p = p.parentPath)) {
			if (~BLOCKED.indexOf(p.value.type)) {
				remove = false;
				break;
			}
		}

		decl.value.declarations.filter(isPointless).forEach( node => {
			if (remove===false) {
				console.log(`> Skipping removal of undefined init for "${node.id.name}": within ${p.value.type}`);
			}
			else {
				removeNodeInitialization(node);
			}
		});
	}

	function removeNodeInitialization(node) {
		node.init = null;
		found++;
	}

	function isPointless(node) {
		let { init } = node;
		if (init) {
			if (init.type==='UnaryExpression' && init.operator==='void' && init.argument.value==0) {
				return true;
			}
			if (init.type==='Identifier' && init.name==='undefined') {
				return true;
			}
		}
		return false;
	}

	return found ? code.toSource({ quote: 'single' }) : null;
};


if (!params.get("skipUpdateCheck") && !brackets.inBrowser) {

// as the check will skip if the last check was not -24h ago

* Setup event handlers prior to dispatching AppInit.HTML_READY

keep keep keep keep replace keep keep keep keep keep

/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, PathUtils, window, Mustache */

/**
 *  Utilities functions for displaying update notifications
 *
 */
define(function (require, exports, module) {
    "use strict";

    var Dialogs              = require("widgets/Dialogs"),
        DefaultDialogs       = require("widgets/DefaultDialogs"),
        PreferencesManager   = require("preferences/PreferencesManager"),
        AppInit              = require("utils/AppInit"),
        Global               = require("utils/Global"),
        NativeApp            = require("utils/NativeApp"),
        StringUtils          = require("utils/StringUtils"),
        Strings              = require("strings"),
        UpdateDialogTemplate = require("text!htmlContent/update-dialog.html"),
        UpdateListTemplate   = require("text!htmlContent/update-list.html");

    var defaultPrefs = {lastNotifiedBuildNumber: 0};

    // Extract current build number from package.json version field 0.0.0-0
    var _buildNumber = Number(/-([0-9]+)/.exec(brackets.metadata.version)[1]);

    // PreferenceStorage
    var _prefs = PreferencesManager.getPreferenceStorage(module, defaultPrefs);
    //TODO: Remove preferences migration code
    PreferencesManager.handleClientIdChange(_prefs, module.id);

    // This is the last version we notified the user about. If checkForUpdate()
    // is called with "false", only show the update notification dialog if there
    // is an update newer than this one. This value is saved in preferences.
    var _lastNotifiedBuildNumber = _prefs.getValue("lastNotifiedBuildNumber");

    // Last time the versionInfoURL was fetched
    var _lastInfoURLFetchTime = _prefs.getValue("lastInfoURLFetchTime");

    // URL to load version info from. By default this is loaded no more than once a day. If
    // you force an update check it is always loaded.

    // URL to fetch the version information.
    var _versionInfoURL;

    // Information on all posted builds of Brackets. This is an Array, where each element is
    // an Object with the following fields:
    //
    //  {Number} buildNumber Number of the build
    //  {String} versionString String representation of the build number (ie "Sprint 14")
    //  {String} dateString Date of the build
    //  {String} releaseNotesURL URL of the release notes for this build
    //  {String} downloadURL URL to download this build
    //  {Array} newFeatures Array of new features in this build. Each entry has two fields:
    //      {String} name Name of the feature
    //      {String} description Description of the feature
    //
    // This array must be reverse sorted by buildNumber (newest build info first)

    /**
     * @private
     * Flag that indicates if we've added a click handler to the update notification icon.
     */
    var _addedClickHandler = false;

    /**
     * Get a data structure that has information for all builds of Brackets.
     *
     * If force is true, the information is always fetched from _versionInfoURL.
     * If force is false, we try to use cached information. If more than
     * 24 hours have passed since the last fetch, or if cached data can't be found,
     * the data is fetched again.
     *
     * If new data is fetched and dontCache is false, the data is saved in preferences
     * for quick fetching later.
     */
    function _getUpdateInformation(force, dontCache) {
        var result = new $.Deferred();
        var fetchData = false;
        var data;

        // If force is true, always fetch
        if (force) {
            fetchData = true;
        }

        // If we don't have data saved in prefs, fetch
        data = _prefs.getValue("updateInfo");
        if (!data) {
            fetchData = true;
        }

        // If more than 24 hours have passed since our last fetch, fetch again
        if ((new Date()).getTime() > _lastInfoURLFetchTime + (1000 * 60 * 60 * 24)) {
            fetchData = true;
        }

        if (fetchData) {
            $.ajax(_versionInfoURL, {
                dataType: "text",
                cache: false,
                complete: function (jqXHR, status) {
                    if (status === "success") {
                        try {
                            data = JSON.parse(jqXHR.responseText);
                            if (!dontCache) {
                                _lastInfoURLFetchTime = (new Date()).getTime();
                                _prefs.setValue("lastInfoURLFetchTime", _lastInfoURLFetchTime);
                                _prefs.setValue("updateInfo", data);
                            }
                            result.resolve(data);
                        } catch (e) {
                            console.log("Error parsing version information");
                            console.log(e);
                            result.reject();
                        }
                    }
                },
                error: function (jqXHR, status, error) {
                    // When loading data for unit tests, the error handler is
                    // called but the responseText is valid. Try to use it here,
                    // but *don't* save the results in prefs.

                    if (!jqXHR.responseText) {
                        // Text is NULL or empty string, reject().
                        result.reject();
                        return;
                    }

                    try {
                        data = JSON.parse(jqXHR.responseText);
                        result.resolve(data);
                    } catch (e) {
                        result.reject();
                    }
                }
            });
        } else {
            result.resolve(data);
        }

        return result.promise();
    }

    /**
     * Return a new array of version information that is newer than "buildNumber".
     * Returns null if there is no new version information.
     */
    function _stripOldVersionInfo(versionInfo, buildNumber) {
        // Do a simple linear search. Since we are going in reverse-chronological order, we
        // should get through the search quickly.
        var lastIndex = 0;
        var len = versionInfo.length;

        while (lastIndex < len) {
            if (versionInfo[lastIndex].buildNumber <= buildNumber) {
                break;
            }
            lastIndex++;
        }

        if (lastIndex > 0) {
            return versionInfo.slice(0, lastIndex);
        }

        // No new version info
        return null;
    }

    /**
     * Show a dialog that shows the update
     */
    function _showUpdateNotificationDialog(updates) {
        Dialogs.showModalDialogUsingTemplate(Mustache.render(UpdateDialogTemplate, Strings))
            .done(function (id) {
                if (id === Dialogs.DIALOG_BTN_DOWNLOAD) {
                    // The first entry in the updates array has the latest download link
                    NativeApp.openURLInDefaultBrowser(updates[0].downloadURL);
                }
            });

        // Populate the update data
        var $dlg        = $(".update-dialog.instance"),
            $updateList = $dlg.find(".update-info");

        updates.Strings = Strings;
        $updateList.html(Mustache.render(UpdateListTemplate, updates));

        AppInit.appReady(function () {
            $dlg.find("button").focus();
        });
    }

    /**
     * Check for updates. If "force" is true, update notification dialogs are always displayed
     * (if an update is available). If "force" is false, the update notification is only
     * displayed for newly available updates.
     *
     * If an update is available, show the "update available" notification icon in the title bar.
     *
     * @param {boolean} force If true, always show the notification dialog.
     * @param {Object} _testValues This should only be used for testing purposes. See comments for details.
     * @return {$.Promise} jQuery Promise object that is resolved or rejected after the update check is complete.
     */
    function checkForUpdate(force, _testValues) {
        // The second param, if non-null, is an Object containing value overrides. Values
        // in the object temporarily override the local values. This should *only* be used for testing.
        // If any overrides are set, permanent changes are not made (including showing
        // the update notification icon and saving prefs).
        var oldValues;
        var usingOverrides = false; // true if any of the values are overridden.
        var result = new $.Deferred();

        if (_testValues) {
            oldValues = {};

            if (_testValues.hasOwnProperty("_buildNumber")) {
                oldValues._buildNumber = _buildNumber;
                _buildNumber = _testValues._buildNumber;
                usingOverrides = true;
            }

            if (_testValues.hasOwnProperty("_lastNotifiedBuildNumber")) {
                oldValues._lastNotifiedBuildNumber = _lastNotifiedBuildNumber;
                _lastNotifiedBuildNumber = _testValues._lastNotifiedBuildNumber;
                usingOverrides = true;
            }

            if (_testValues.hasOwnProperty("_versionInfoURL")) {
                oldValues._versionInfoURL = _versionInfoURL;
                _versionInfoURL = _testValues._versionInfoURL;
                usingOverrides = true;
            }
        }

        _getUpdateInformation(force || usingOverrides, usingOverrides)
            .done(function (versionInfo) {
                // Get all available updates
                var allUpdates = _stripOldVersionInfo(versionInfo, _buildNumber);

                // When running directly from GitHub source (as opposed to
                // an installed build), _buildNumber is 0. In this case, if the
                // test is not forced, don't show the update notification icon or
                // dialog.
                if (_buildNumber === 0 && !force) {
                    result.resolve();
                    return;
                }

                if (allUpdates) {
                    // Always show the "update available" icon if any updates are available
                    var $updateNotification = $("#update-notification");

                    $updateNotification.css("display", "block");
                    if (!_addedClickHandler) {
                        _addedClickHandler = true;
                        $updateNotification.on("click", function () {
                            checkForUpdate(true);
                        });
                    }

                    // Only show the update dialog if force = true, or if the user hasn't been
                    // alerted of this update
                    if (force || allUpdates[0].buildNumber >  _lastNotifiedBuildNumber) {
                        _showUpdateNotificationDialog(allUpdates);

                        // Update prefs with the last notified build number
                        _lastNotifiedBuildNumber = allUpdates[0].buildNumber;
                        // Don't save prefs is we have overridden values
                        if (!usingOverrides) {
                            _prefs.setValue("lastNotifiedBuildNumber", _lastNotifiedBuildNumber);
                        }
                    }
                } else if (force) {
                    // No updates are available. If force == true, let the user know.
                    Dialogs.showModalDialog(
                        DefaultDialogs.DIALOG_ID_ERROR,
                        Strings.NO_UPDATE_TITLE,
                        Strings.NO_UPDATE_MESSAGE
                    );
                }

                if (oldValues) {
                    if (oldValues.hasOwnProperty("_buildNumber")) {
                        _buildNumber = oldValues._buildNumber;
                    }
                    if (oldValues.hasOwnProperty("_lastNotifiedBuildNumber")) {
                        _lastNotifiedBuildNumber = oldValues._lastNotifiedBuildNumber;
                    }
                    if (oldValues.hasOwnProperty("_versionInfoURL")) {
                        _versionInfoURL = oldValues._versionInfoURL;
                    }
                }
                result.resolve();
            })
            .fail(function () {
                // Error fetching the update data. If this is a forced check, alert the user
                if (force) {
                    Dialogs.showModalDialog(
                        DefaultDialogs.DIALOG_ID_ERROR,
                        Strings.ERROR_FETCHING_UPDATE_INFO_TITLE,
                        Strings.ERROR_FETCHING_UPDATE_INFO_MSG
                    );
                }
                result.reject();
            });

        return result.promise();
    }

    // Append locale to version info URL
    _versionInfoURL = brackets.config.update_info_url + brackets.getLocale() + ".json";

    // Define public API
    exports.checkForUpdate = checkForUpdate;
});


/* ===========================================================
 * bootstrap-tooltip.js v2.3.1
 * http://twitter.github.com/bootstrap/javascript.html#tooltips
 * Inspired by the original jQuery.tipsy by Jason Frame
 * ===========================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */

!function ($) {

  "use strict"; // jshint ;_;

 /* TOOLTIP PUBLIC CLASS DEFINITION
  * =============================== */

  var Tooltip = function (element, options) {
    this.init('tooltip', element, options)
  }

  Tooltip.prototype = {

    constructor: Tooltip

  , init: function (type, element, options) {
      var eventIn
        , eventOut
        , triggers
        , trigger
        , i

      this.type = type
      this.$element = $(element)
      this.options = this.getOptions(options)
      this.enabled = true

      triggers = this.options.trigger.split(' ')

      for (i = triggers.length; i--;) {
        trigger = triggers[i]
        if (trigger == 'click') {
          this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
        } else if (trigger != 'manual') {
          eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
          eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
          this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
          this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
        }
      }

      this.options.selector ?
        (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
        this.fixTitle()
    }

  , getOptions: function (options) {
      options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)

      if (options.delay && typeof options.delay == 'number') {
        options.delay = {
          show: options.delay
        , hide: options.delay
        }
      }

      return options
    }

  , enter: function (e) {
      var defaults = $.fn[this.type].defaults
        , options = {}
        , self

      this._options && $.each(this._options, function (key, value) {
        if (defaults[key] != value) options[key] = value
      }, this)

      self = $(e.currentTarget)[this.type](options).data(this.type)

      if (!self.options.delay || !self.options.delay.show) return self.show()

      clearTimeout(this.timeout)
      self.hoverState = 'in'
      this.timeout = setTimeout(function() {
        if (self.hoverState == 'in') self.show()
      }, self.options.delay.show)
    }

  , leave: function (e) {
      var self = $(e.currentTarget)[this.type](this._options).data(this.type)

      if (this.timeout) clearTimeout(this.timeout)
      if (!self.options.delay || !self.options.delay.hide) return self.hide()

      self.hoverState = 'out'
      this.timeout = setTimeout(function() {
        if (self.hoverState == 'out') self.hide()
      }, self.options.delay.hide)
    }

  , show: function () {
      var $tip
        , pos
        , actualWidth
        , actualHeight
        , placement
        , tp
        , e = $.Event('show')

      if (this.hasContent() && this.enabled) {
        this.$element.trigger(e)
        if (e.isDefaultPrevented()) return
        $tip = this.tip()
        this.setContent()

        if (this.options.animation) {
          $tip.addClass('fade')
        }

        placement = typeof this.options.placement == 'function' ?
          this.options.placement.call(this, $tip[0], this.$element[0]) :
          this.options.placement

        $tip
          .detach()
          .css({ top: 0, left: 0, display: 'block' })

        this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)

        pos = this.getPosition()

        actualWidth = $tip[0].offsetWidth
        actualHeight = $tip[0].offsetHeight

        switch (placement) {
          case 'bottom':
            tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
            break
          case 'top':
            tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
            break
          case 'left':
            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
            break
          case 'right':
            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
            break
        }

        this.applyPlacement(tp, placement)
        this.$element.trigger('shown')
      }
    }

  , applyPlacement: function(offset, placement){
      var $tip = this.tip()
        , width = $tip[0].offsetWidth
        , height = $tip[0].offsetHeight
        , actualWidth
        , actualHeight
        , delta
        , replace

      $tip
        .offset(offset)
        .addClass(placement)
        .addClass('in')

      actualWidth = $tip[0].offsetWidth
      actualHeight = $tip[0].offsetHeight

      if (placement == 'top' && actualHeight != height) {
        offset.top = offset.top + height - actualHeight
        replace = true
      }

      if (placement == 'bottom' || placement == 'top') {
        delta = 0

        if (offset.left < 0){
          delta = offset.left * -2
          offset.left = 0
          $tip.offset(offset)
          actualWidth = $tip[0].offsetWidth
          actualHeight = $tip[0].offsetHeight
        }

        this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
      } else {
        this.replaceArrow(actualHeight - height, actualHeight, 'top')
      }

      if (replace) $tip.offset(offset)
    }

  , replaceArrow: function(delta, dimension, position){
      this
        .arrow()
        .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
    }

  , setContent: function () {
      var $tip = this.tip()
        , title = this.getTitle()

      $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
      $tip.removeClass('fade in top bottom left right')
    }

  , hide: function () {
      var that = this
        , $tip = this.tip()
        , e = $.Event('hide')

      this.$element.trigger(e)
      if (e.isDefaultPrevented()) return

      $tip.removeClass('in')

      function removeWithAnimation() {
        var timeout = setTimeout(function () {
          $tip.off($.support.transition.end).detach()
        }, 500)

        $tip.one($.support.transition.end, function () {
          clearTimeout(timeout)
          $tip.detach()
        })
      }

      $.support.transition && this.$tip.hasClass('fade') ?
        removeWithAnimation() :
        $tip.detach()

      this.$element.trigger('hidden')

      return this
    }

  , fixTitle: function () {
      var $e = this.$element
      if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
        $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
      }
    }

  , hasContent: function () {
      return this.getTitle()
    }

  , getPosition: function () {
      var el = this.$element[0]
      return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
        width: el.offsetWidth
      , height: el.offsetHeight
      }, this.$element.offset())
    }

  , getTitle: function () {
      var title
        , $e = this.$element
        , o = this.options

      title = $e.attr('data-original-title')
        || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)

      return title
    }

  , tip: function () {
      return this.$tip = this.$tip || $(this.options.template)
    }

  , arrow: function(){
      return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
    }

  , validate: function () {
      if (!this.$element[0].parentNode) {
        this.hide()
        this.$element = null
        this.options = null
      }
    }

  , enable: function () {
      this.enabled = true
    }

  , disable: function () {
      this.enabled = false
    }

  , toggleEnabled: function () {
      this.enabled = !this.enabled
    }

  , toggle: function (e) {
      var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
      self.tip().hasClass('in') ? self.hide() : self.show()
    }

  , destroy: function () {
      this.hide().$element.off('.' + this.type).removeData(this.type)
    }

  }

 /* TOOLTIP PLUGIN DEFINITION
  * ========================= */

  var old = $.fn.tooltip

  $.fn.tooltip = function ( option ) {
    return this.each(function () {
      var $this = $(this)
        , data = $this.data('tooltip')
        , options = typeof option == 'object' && option
      if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.tooltip.Constructor = Tooltip

  $.fn.tooltip.defaults = {
    animation: true
  , placement: 'top'
  , selector: false
  , template: '

'
  , trigger: 'hover focus'
  , title: ''
  , delay: 0
  , html: false
  , container: false
  }

 /* TOOLTIP NO CONFLICT
  * =================== */

  $.fn.tooltip.noConflict = function () {
    $.fn.tooltip = old
    return this
  }

}(window.jQuery);


(The MIT License)

Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


var crypto = require('crypto')
  , fs = require('../lib')
  , path = require('path')
  , testutil = require('testutil')
  , mkdir = require('mkdirp');

var DIR = '';

  buildDir = function() { //shit function that should be deleted
    var baseDir, buf, bytesWritten, ex, subDir;
    buf = new Buffer(5);
    bytesWritten = 0;
    while (bytesWritten < buf.length) {
      buf[bytesWritten] = Math.floor(Math.random() * 256);
      bytesWritten += 1;
    }
    ex = Date.now();
    baseDir = path.join(DIR, "TEST_fs-extra_rmrf-" + ex);
    fs.mkdirSync(baseDir);
    fs.writeFileSync(path.join(baseDir, Math.random() + ''), buf);
    fs.writeFileSync(path.join(baseDir, Math.random() + ''), buf);
    subDir = path.join(DIR, Math.random() + '');
    fs.mkdirSync(subDir);
    fs.writeFileSync(path.join(subDir, Math.random() + ''));
    return baseDir;
  };

describe('fs-extra', function() {
  beforeEach(function() {
    DIR = testutil.createTestDir('fs-extra');
  })

  afterEach(function(done) {
    if (fs.existsSync(DIR)) {
      fs.remove(DIR, done);
    } else {
      done();
    }
  })

  describe('+ removeSync()', function() {
    it('should delete directories and files synchronously', function() {
      T (fs.existsSync(DIR));
      fs.removeSync(DIR);
      F (fs.existsSync(DIR));
    })

    it('should delete an empty directory synchronously', function() {
      T (fs.existsSync(DIR));
      fs.removeSync(DIR);
      F (fs.existsSync(DIR));
    })

    it('should delete a file synchronously', function() {
      var file = testutil.createFileWithData(path.join(DIR, 'file'), 4);
      T (fs.existsSync(file));
      fs.removeSync(file);
    })
  })

  describe('+ remove()', function() {
    it('should delete an empty directory', function(done) {
      T (fs.existsSync(DIR));
      fs.remove(DIR, function(err) {
        T (err === null);
        F (fs.existsSync(DIR));
        done();
      })
    })

    it('should delete a directory full of directories and files', function(done) {
      var dir = buildDir();
      T(fs.existsSync(DIR));
      return fs.remove(DIR, function(err) {
        T(err === null);
        F(fs.existsSync(DIR));
        return done();
      });
    });
    it('should delete a file', function(done) {
      var file = testutil.createFileWithData(path.join(DIR, 'file'), 4);

      T (fs.existsSync(file));
      fs.remove(file, function(err) {
        T (err === null);
        F (fs.existsSync(file));
        done();
      });
    })

    it('should delete without a callback', function(done) {
      var file = testutil.createFileWithData(path.join(DIR, 'file'), 4);
      T (fs.existsSync(file));
      var existsChecker = setInterval(function() {
        fs.exists(file, function(itDoes) {
          if (!itDoes) {
            clearInterval(existsChecker);
            done();
          }
        });
      }, 25);
      fs.remove(file);
    })
  })

  describe('+ delete()', function() {
    it('should delete an empty directory', function(done) {
      T (fs.existsSync(DIR));
      fs["delete"](DIR, function(err) {
        T (err === null);
        F (fs.existsSync(DIR));
        done();
      })
    })
  })

  describe('+ deleteSync()', function() {
    it('should delete directories and files synchronously', function() {
      T (fs.existsSync(DIR));
      fs.deleteSync(DIR);
      F (fs.existsSync(DIR));
    })
  })

})


var $dlg        = $(".update-dialog.instance"),

keep keep keep keep keep replace replace replace replace keep keep keep


language: node_js
node_js:
  - 0.6
  - 0.8
  - 0.9


<!DOCTYPE html>

            Test&

            Test! &

            Test´

            Test ;  &

        link

This & that & the other

            &#


_versionInfoURL = brackets.config.update_info_url + brackets.getLocale() + ".json";

// Check for updates on App Ready
AppInit.appReady(function () {
checkForUpdate();
});


var mkdirp = require('../');
var path = require('path');
var fs = require('fs');
var test = require('tap').test;

test('woo', function (t) {
    t.plan(2);
    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);

    var file = '/tmp/' + [x,y,z].join('/');

    mkdirp(file, 0755, function (err) {
        if (err) t.fail(err);
        else path.exists(file, function (ex) {
            if (!ex) t.fail('file not created')
            else fs.stat(file, function (err, stat) {
                if (err) t.fail(err)
                else {
                    t.equal(stat.mode & 0777, 0755);
                    t.ok(stat.isDirectory(), 'target not a directory');
                    t.end();
                }
            })
        })
    });
});


# cookie-signature

  Sign and unsign cookies.

## Example

```js
var cookie = require('cookie-signature');

var val = cookie.sign('hello', 'tobiiscool');
val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI');

var val = cookie.sign('hello', 'tobiiscool');
cookie.unsign(val, 'tobiiscool').should.equal('hello');
cookie.unsign(val, 'luna').should.be.false;
```

## License

(The MIT License)

Copyright (c) 2012 LearnBoost <tj@learnboost.com>

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


SHELL := /bin/bash

test:
	@./test/run.js

.PHONY: test


var NativeFileSystem    = require("NativeFileSystem").NativeFileSystem

,   PreferencesManager  = require("PreferencesManager");

keep keep keep keep replace keep keep keep

,   CommandManager      = require("CommandManager")

,   Commands            = require("Commands");

/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */
define(function(require, exports, module) {
    require("thirdparty/path-utils/path-utils.min");

    // Load dependent modules
    var CommandManager      = require("CommandManager")
    ,   Commands            = require("Commands")
    ,   NativeFileSystem    = require("NativeFileSystem").NativeFileSystem
    ,   ProjectManager      = require("ProjectManager")
    ,   DocumentManager     = require("DocumentManager")
    ,   EditorManager       = require("EditorManager")
    ,   EditorUtils         = require("EditorUtils")
    ,   Strings             = require("strings");
    ;

    /**
     * Handlers for commands related to file handling (opening, saving, etc.)
     */

    /** @type {jQueryObject} */
    var _title;
    /** @type {string} */
    var _currentFilePath;  // TODO: eliminate this and just use getCurrentDocument().file.fullPath
    /** @type {string} */
    var _currentTitlePath;

    function init(title) {
        _title = title;

        // Register global commands
        CommandManager.register(Commands.FILE_OPEN, handleFileOpen);
        CommandManager.register(Commands.FILE_ADD_TO_WORKING_SET, handleFileAddToWorkingSet);
        // TODO: For now, hook up File > New to the "new in project" handler. Eventually
        // File > New should open a new blank tab, and handleFileNewInProject should
        // be called from a "+" button in the project
        CommandManager.register(Commands.FILE_NEW, handleFileNewInProject);
        CommandManager.register(Commands.FILE_SAVE, handleFileSave);
        CommandManager.register(Commands.FILE_CLOSE, handleFileClose);
        CommandManager.register(Commands.FILE_CLOSE_ALL, handleFileCloseAll);
        CommandManager.register(Commands.FILE_QUIT, handleFileQuit);

        $(DocumentManager).on("dirtyFlagChange", handleDirtyChange);
        $(DocumentManager).on("currentDocumentChange", handleCurrentDocumentChange);
    };

    function handleCurrentDocumentChange(event) {
        var newDocument = DocumentManager.getCurrentDocument();

        if (newDocument != null) {
            var fullPath = newDocument.file.fullPath;

            _currentFilePath = _currentTitlePath = fullPath;

            // In the main toolbar, show the project-relative path (if the file is inside the current project)
            // or the full absolute path (if it's not in the project).
            _currentTitlePath = ProjectManager.makeProjectRelativeIfPossible(fullPath);

        } else {
            _currentFilePath = _currentTitlePath = null;
        }

        // Update title text & "dirty dot" display
        updateTitle();
    }

    function handleDirtyChange(event, changedDoc) {
        if (changedDoc.file.fullPath == _currentFilePath) {
            updateTitle();
        }
    }

    function updateTitle() {
        var currentDoc = DocumentManager.getCurrentDocument();
        if (currentDoc) {
            _title.text( _currentTitlePath + (currentDoc.isDirty ? " \u2022" : "") );
        } else {
            _title.text("");
        }
    }

    function handleFileAddToWorkingSet(fullPath){
        handleFileOpen(fullPath).done(function(doc) {
            DocumentManager.addToWorkingSet(doc);
            // jstree dblclick handling seems to steal focus from editor, so set focus again
            EditorManager.focusEditor();
        });
    }

    function handleFileOpen(fullPath) {
        var result = doOpenWithOptionalPath(fullPath);
        result.always(function() {
            EditorManager.focusEditor();
        });
        return result;
    }

    /**
     * @private
     * Creates a document and displays an editor for the specified file path.
     * If no path is specified, a file prompt is provided for input.
     * @return {Deferred} a jQuery Deferred that will be resolved with a new
     *  document for the specified file path, or rejected if the file can not be read.
     */
    function doOpenWithOptionalPath(fullPath) {
        var result;
        if (!fullPath) {
            // Prompt the user with a dialog
            // TODO: we're relying on this to not be asynchronous--is that safe?
            NativeFileSystem.showOpenDialog(false, false, Strings.OPEN_FILE, ProjectManager.getProjectRoot().fullPath,
                ["htm", "html", "js", "css"], function(files) {
                    if (files.length > 0) {
                        result = doOpen(files[0]);
                        return;
                    }
                });
        }
        else {
            result = doOpen(fullPath);
        }
        if (!result)
            result = (new $.Deferred()).reject();
        return result;
    }

    /**
     * @private
     * Creates a document and displays an editor for the specified file path.
     * @return {Deferred} a jQuery Deferred that will be resolved with a new
     *  document for the specified file path, or rejected if the file can not be read.
     */
    function doOpen(fullPath) {
        var result = new $.Deferred();
        if (!fullPath) {
            console.log("doOpen() called without fullPath");
            return result.reject();
        }

        // TODO: we should implement something like NativeFileSystem.resolveNativeFileSystemURL() (similar
        // to what's in the standard file API) to get a FileEntry, rather than manually constructing it
        var fileEntry = new NativeFileSystem.FileEntry(fullPath);

        var doc = DocumentManager.getDocumentForFile(fileEntry);
        if (doc != null) {
            // File already open - don't need to load it, just switch to it in the UI
            DocumentManager.showInEditor(doc);
            result.resolve(doc);

        } else {
            var docResult = EditorManager.createDocumentAndEditor(fileEntry);

            docResult.done(function(doc) {
                DocumentManager.showInEditor(doc);
                result.resolve(doc);
            });

            docResult.fail(function(error) {
                EditorUtils.showFileOpenError(error.code, fullPath);
                result.reject();
            });
        }

        return result;
    }

    function handleFileNewInProject() {
        // Determine the directory to put the new file
        // If a file is currently selected, put it next to it.
        // If a directory is currently selected, put it in it.
        // If nothing is selected, put it at the root of the project
        var baseDir,
            selected = ProjectManager.getSelectedItem() || ProjectManager.getProjectRoot();

        baseDir = selected.fullPath;
        if (selected.isFile)
            baseDir = baseDir.substr(0, baseDir.lastIndexOf("/"));

        // Create the new node. The createNewItem function does all the heavy work
        // of validating file name, creating the new file and selecting.
        var deferred = _getUntitledFileSuggestion(baseDir, "Untitled", ".js");
        var createWithSuggestedName = function ( suggestedName ) {
            ProjectManager.createNewItem(baseDir, suggestedName, false).pipe( deferred.resolve, deferred.reject, deferred.notify );
        };

        deferred.done( createWithSuggestedName );
        deferred.fail( function createWithDefault() { createWithSuggestedName( "Untitled.js" ); } );
        return deferred;
    }

    function handleFileSave() {
        return doSave( DocumentManager.getCurrentDocument() );
    }

    function doSave(docToSave) {
        var result = new $.Deferred();

        if (docToSave && docToSave.isDirty) {
            var fileEntry = docToSave.file;

            //setup our resolve and reject handlers
            result.done( function fileSaved() {
                docToSave.markClean();
            });

            result.fail( function fileError(error) {
                showSaveFileError(error.code, fileEntry.fullPath);
            });

            fileEntry.createWriter(
                function(writer) {
                    writer.onwriteend = function() {
                        result.resolve();
                    }
                    writer.onerror = function(error) {
                        result.reject(error);
                    }

                    // TODO (jasonsj): Blob instead of string
                    writer.write( docToSave.getText() );
                },
                function(error) {
                    result.reject(error);
                }
            );
        }
        else {
            result.resolve();
        }
        result.always(function() {
            EditorManager.focusEditor();
        });
        return result;
    }

    /**
     * Saves all unsaved documents. Returns a Promise that will be resolved once ALL the save
     * operations have been completed. If any ONE save operation fails, an error dialog is immediately
     * shown and the promise fails.
     * TODO: But subsequent save operations continue in the background, and if more fail the error
     * dialogs will stack up on top of the old one.
     *
     * @return {$.Promise}
     */
    function saveAll() {
        var saveResults = [];

        DocumentManager.getWorkingSet().forEach(function(doc) {
            saveResults.push( doSave(doc) );
        });

        // Aggregate all the file-save Deferreds into one master
        // (p.s., it would be nice if $.when() accepted an array instead of varargs, but oh well...)
        var overallResult = $.when.apply($, saveResults);

        return overallResult;
    }

    /**
     * Closes the specified document. Prompts user about saving file if document is dirty.
     *
     * @param {?Document} doc  Document to close; assumes the current document if null.
     * @param {boolean} promptOnly  If true, only displays the relevant confirmation UI and does NOT
     *          actually close the document. This is useful when chaining file-close together with
     *          other user prompts that may be cancelable.
     * @return {$.Deferred}
     */
    function handleFileClose( doc, promptOnly ) {

        // utility function for handleFileClose: closes document & removes from working set
        function doClose(doc) {
            if (!promptOnly) {
                // This selects a different document if the working set has any other options
                DocumentManager.closeDocument(doc);

                EditorManager.focusEditor();
            }
        }

        var result = new $.Deferred();

        // Default to current document if doc is null
        if (!doc)
            doc =  DocumentManager.getCurrentDocument();
        // No-op if called when nothing is open; TODO: should command be grayed out instead?
        if (!doc)
            return;

        if (doc.isDirty) {
            var filename = PathUtils.parseUrl(doc.file.fullPath).filename;

            brackets.showModalDialog(
                  brackets.DIALOG_ID_SAVE_CLOSE
                , Strings.SAVE_CLOSE_TITLE
                , Strings.format(Strings.SAVE_CLOSE_MESSAGE, filename )
            ).done(function(id) {
                if (id === brackets.DIALOG_BTN_CANCEL) {
                    result.reject();
                }
                else if (id === brackets.DIALOG_BTN_OK) {
                    doSave(doc)
                        .done(function() {
                            doClose(doc);
                            result.resolve();
                        })
                        .fail(function() {
                            result.reject();
                        });
                }
                else {
                    // This is the "Don't Save" case--we can just go ahead and close the file.
                    doClose(doc);
                    result.resolve();
                }
            });
            result.always(function() {
                EditorManager.focusEditor();
            });
        }
        else {
            // Doc is not dirty, just close
            doClose(doc);
            EditorManager.focusEditor();
            result.resolve();
        }
        return result;
    }

    /**
     * Closes all open documents; equivalent to calling handleFileClose() for each document, except
     * that unsaved changes are confirmed once, in bulk.
     * @param {boolean} promptOnly  If true, only displays the relevant confirmation UI and does NOT
     *          actually close any documents. This is useful when chaining close-all together with
     *          other user prompts that may be cancelable.
     * @return {$.Deferred}
     */
    function handleFileCloseAll(promptOnly) {
        var result = new $.Deferred();

        var unsavedDocs = DocumentManager.getWorkingSet().filter( function(doc) {
            return doc.isDirty;
        });

        if (unsavedDocs.length == 0) {
            // No unsaved changes, so we can proceed without a prompt
            result.resolve();

        } else if (unsavedDocs.length == 1) {
            // Only one unsaved file: show the usual single-file-close confirmation UI
            handleFileClose( unsavedDocs[0], promptOnly ).done( function() {
                // still need to close any other, non-unsaved documents
                result.resolve();
            }).fail( function() {
                result.reject();
            });

        } else {
            // Multiple unsaved files: show a single bulk prompt listing all files
            var message = Strings.SAVE_CLOSE_MULTI_MESSAGE;

            message += "
";
            unsavedDocs.forEach(function(doc) {
                message += "
" + ProjectManager.makeProjectRelativeIfPossible(doc.file.fullPath) + "
";
            });
            message += "
";

            brackets.showModalDialog(
                  brackets.DIALOG_ID_SAVE_CLOSE
                , Strings.SAVE_CLOSE_TITLE
                , message
            ).done(function(id) {
                if (id === brackets.DIALOG_BTN_CANCEL) {
                    result.reject();
                }
                else if (id === brackets.DIALOG_BTN_OK) {
                    // Save all unsaved files, then if that succeeds, close all
                    saveAll().done( function() {
                        result.resolve();
                    }).fail( function() {
                        result.reject();
                    });
                }
                else {
                    // "Don't Save" case--we can just go ahead and close all  files.
                    result.resolve();
                }
            });
        }

        // If all the unsaved-changes confirmations pan out above, then go ahead & close all editors
        // NOTE: this still happens before any done() handlers added by our caller, because jQ
        // guarantees that handlers run in the order they are added.
        result.done(function() {
            if (!promptOnly)
                DocumentManager.closeAll();
        });

        return result;
    }

    /** Confirms any unsaved changes, then exits Brackets */
    function handleFileQuit() {
        handleFileCloseAll(false)
        .done(function() {
            window.close();  // TODO: call a native API to quit the whole app
        });
        // if fail, don't exit: user canceled (or asked us to save changes first, but we failed to do so)
    }

    /**
     * @private
     * Ensures the suggested file name doesn't already exit.
     * @param {string} dir  The directory to use
     * @param {string} baseFileName  The base to start with, "-n" will get appened to make unique
     * @param {string} fileExt  The file extension
     */
    function _getUntitledFileSuggestion( dir, baseFileName, fileExt ) {
        var result = new $.Deferred();
        var suggestedName = baseFileName + fileExt;
        var dirEntry = new NativeFileSystem.DirectoryEntry(dir);

        result.progress( function attemptNewName( suggestedName, nextIndexToUse ) {
            if( nextIndexToUse > 99 ) {
                //we've tried this enough
                result.reject();
                return;
            }

            //check this name
            dirEntry.getFile( suggestedName
                            , {}
                            , function successCallback(entry){
                                //file exists, notify to the next progress
                                result.notify(baseFileName + "-" + nextIndexToUse + fileExt , nextIndexToUse + 1);
                                }
                             , function errorCallback(error) {
                                //most likely error is FNF, user is better equiped to handle the rest
                                result.resolve(suggestedName);
                                }
                            );
        });

        //kick it off
        result.notify(baseFileName + fileExt , 1);

        return result;
    }

    function showSaveFileError(code, path) {
        return brackets.showModalDialog(
              brackets.DIALOG_ID_ERROR
            , Strings.ERROR_SAVING_FILE_TITLE
            , Strings.format(
                    Strings.ERROR_SAVING_FILE
                  , path
                  , EditorUtils.getFileErrorString(code))
        );
    }

    // Define public API
    exports.init = init;
});


/* Brackets Variables */

/* All paddings, gutters, etc. should be multiples of this */
@base-padding: 10px;