├── .editorconfig
├── .gitignore
├── .jshintignore
├── .travis.yml
├── .zuul.yml
├── CONTRIBUTION.md
├── LICENCE
├── README.md
├── ROADMAP.md
├── bin
├── browserify-editor.js
├── build.js
├── dist.js
├── example-server.js
├── example-tasks.js
└── modules-docs.js
├── dist
└── mercury.js
├── docs
├── faq.md
├── hooks.md
├── introduction.md
├── life-cycles.md
├── mercury-component.md
├── modules
│ ├── README.md
│ ├── dom-delegator.md
│ ├── geval.md
│ ├── main-loop.md
│ ├── observ-array.md
│ ├── observ-struct.md
│ ├── observ-varhash.md
│ ├── observ.md
│ ├── value-event.md
│ ├── vdom-thunk.md
│ ├── vdom.md
│ ├── virtual-dom.md
│ ├── virtual-hyperscript.md
│ └── vtree.md
├── thunks.md
└── widgets.md
├── examples
├── 2048
│ ├── render.js
│ └── style.css
├── async-state.js
├── bmi-counter.js
├── canvas.js
├── count.js
├── examples-styles.css
├── geometry
│ ├── browser.js
│ ├── lib
│ │ └── drag-handler.js
│ └── shapes.js
├── hot-reload
│ ├── README.md
│ ├── browser.js
│ ├── index.html
│ ├── package.json
│ └── render.js
├── index.html
├── lib
│ ├── focus-hook.js
│ ├── reset-hook.js
│ ├── router
│ │ ├── anchor.js
│ │ ├── index.js
│ │ └── view.js
│ ├── safe-hook.js
│ └── weakmap-event.js
├── login-form
│ ├── browser.js
│ ├── login-component-render.js
│ ├── login-component.js
│ └── styles.js
├── markdown
│ ├── app.js
│ ├── browser.js
│ ├── component
│ │ ├── inlineMdEditor.js
│ │ ├── mdRender.js
│ │ ├── sideBySideMdEditor.js
│ │ └── textarea.js
│ └── style.css
├── number-input
│ ├── browser.js
│ └── number-component.js
├── real-dom.js
├── server-rendering
│ ├── browser.js
│ ├── render.js
│ ├── server.js
│ └── test.js
├── shared-state.js
├── todomvc
│ ├── bg.png
│ ├── browser.js
│ ├── lib
│ │ └── raf-listen.js
│ ├── style.css
│ ├── todo-app.js
│ └── todo-item.js
└── unidirectional
│ ├── README.md
│ ├── backbone
│ ├── browser.js
│ └── observ-backbone.js
│ ├── immutable
│ └── browser.js
│ └── jsx
│ ├── browser.js
│ ├── package.json
│ └── render.jsx
├── index.js
├── package.json
├── svg.js
├── test
├── bmi-counter.js
├── count.js
├── index.js
├── lib
│ ├── embed-component.js
│ └── load-hook.js
├── package.json
├── shared-state.js
├── ssr.js
├── synthetic-events.js
├── time-travel.js
└── transforms
│ └── intercept-mercury-app.js
└── time-travel.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.js]
2 | indent_style = space
3 | indent_size = 4
4 | insert_final_newline = true
5 | max_line_length = 80
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .monitor
3 | .*.swp
4 | .nodemonignore
5 | releases
6 | *.log
7 | *.err
8 | fleet.json
9 | public/browserify
10 | bin/*.json
11 | .bin
12 | build
13 | compile
14 | .lock-wscript
15 | coverage
16 | node_modules
17 | disc.html
18 | TODO.md
19 | *.iml
20 | .idea
21 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | concepts/
4 | coverage/
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.12'
5 | - iojs
6 | script: npm run travis-test
7 | env:
8 | global:
9 | - secure: Eaf4v0L7GrNeQU2MtrFSVBuD0QrvGaqHRxwsrKU1Og+7x21cXmmQdbtWG2efWgxuutPT4UYPA1tStLc3rh20c6vdiMjvDrqcDvzT82ge6N8KHh8kUZ9jBLVOs6gubQyhd7qkMT6adFqCgUenQy+oYLU3WvYe4kg/Qh+gAdGtKuo=
10 | - secure: GE44G8Ru8cbULLX/B/eMVM5TuRqn2LSMDszOC402PfEE7HlQEv73mMHHNQbCq2roz/cOSNHlUvUbIY9JEhFofkR0/vaEhFVjAa3kMspoBACL2M/R1hZjOo44P3jm6dpKDTbfTjSfqO6KpwSFx87V1RUPLMmR3jb73ZqY2ICgHAU=
11 |
--------------------------------------------------------------------------------
/.zuul.yml:
--------------------------------------------------------------------------------
1 | ui: tape
2 | concurrency: 1
3 | browsers:
4 | - name: chrome
5 | version: latest
6 | - name: ie
7 | version: 9..latest
8 | - name: firefox
9 | version: 25..latest
10 | - name: opera
11 | version: 11..latest
12 | - name: safari
13 | version: 6..latest
14 |
--------------------------------------------------------------------------------
/CONTRIBUTION.md:
--------------------------------------------------------------------------------
1 | # This is an OPEN Open Source Project
2 |
3 | ## What?
4 |
5 | Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
6 |
7 | ## Rules
8 |
9 | There are a few basic ground-rules for contributors:
10 |
11 | - No `--force` pushes or modifying the Git history in any way.
12 | - Non-master branches ought to be used for ongoing work.
13 | - External API changes and significant modifications ought to be subject to an internal pull-request to solicit feedback from other contributors.
14 | - Internal pull-requests to solicit feedback are encouraged for any other non-trivial contribution but left to the discretion of the contributor.
15 | - For significant changes wait a full 24 hours before merging so that active contributors who are distributed throughout the world have a chance to weigh in.
16 | - Contributors should attempt to adhere to the prevailing code-style.
17 |
18 | ## Releases
19 |
20 | Declaring formal releases requires peer review.
21 |
22 | - A reviewer of a pull request should recommend a new version number (patch, minor or major).
23 | - Once your change is merged feel free to bump the version as recommended by the reviewer.
24 | - A new version number should not be cut without peer review unless done by the project maintainer.
25 |
26 | ## Want to contribute?
27 |
28 | Even though collaborators may contribute as they see fit, if you are not sure what to do, here's a suggested process:
29 |
30 | ### Cutting a new version
31 |
32 | - Get your branch merged on master
33 | - Run `npm version major` or `npm version minor` or `npm version patch`
34 | - `git push origin master --tags`
35 | - If you are a project owner, then `npm publish`
36 |
37 | ### If you want to have a bug fixed or a feature added:
38 |
39 | - Check open issues for what you want.
40 | - If there is an open issue, comment on it, otherwise open an issue describing your bug or feature with use cases.
41 | - Discussion happens on the issue about how to solve your problem.
42 | - You or a core contributor opens a pull request solving the issue with tests and documentation.
43 | - The pull requests gets reviewed and then merged.
44 | - A new release version get's cut.
45 | - (Disclaimer: Your feature might get rejected.)
46 |
47 | ### Changes to this arrangement
48 |
49 | This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
50 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Raynos.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mercury
2 |
3 | # Deprecated: Not actively being worked on.
4 |
5 | Instead [`tonic`](https://github.com/optoolco/tonic) and https://tonic.technology/ are actively being worked on.
6 |
7 | `mercury` has some interesting ideas but they are not very practical at scale.
8 | `tonic` is a lightweigth component system on top of web components that leverages
9 | the browsers HTML parser for the heavy lifting instead of `virtual-dom`.
10 |
11 | It comes with a set of [`components`](https://github.com/optoolco/components) which help with building
12 | apps more quickly by having some re-usable components out of the box.
13 |
14 | ## Description
15 |
16 | A truly modular frontend framework
17 |
18 | To understand what I mean by truly modular just [read the source](https://github.com/Raynos/mercury/blob/master/index.js)
19 |
20 |
21 | ## Examples
22 |
23 | ### Hello world
24 |
25 | ```js
26 | 'use strict';
27 |
28 | var document = require('global/document');
29 | var hg = require('mercury');
30 | var h = require('mercury').h;
31 |
32 | function App() {
33 | return hg.state({
34 | value: hg.value(0),
35 | channels: {
36 | clicks: incrementCounter
37 | }
38 | });
39 | }
40 |
41 | function incrementCounter(state) {
42 | state.value.set(state.value() + 1);
43 | }
44 |
45 | App.render = function render(state) {
46 | return h('div.counter', [
47 | 'The state ', h('code', 'clickCount'),
48 | ' has value: ' + state.value + '.', h('input.button', {
49 | type: 'button',
50 | value: 'Click me!',
51 | 'ev-click': hg.send(state.channels.clicks)
52 | })
53 | ]);
54 | };
55 |
56 | hg.app(document.body, App(), App.render);
57 | ```
58 |
59 | ### Basic Examples
60 |
61 | - [count](examples/count.js)
62 | - [shared-state](examples/shared-state.js)
63 | - [bmi-counter](examples/bmi-counter.js)
64 | - [canvas](examples/canvas.js)
65 | - [async-state](examples/async-state.js)
66 | - [real-dom](examples/real-dom.js)
67 |
68 | ### Intermediate Examples
69 |
70 | - [TodoMVC](examples/todomvc)
71 | - [markdown editor](examples/markdown)
72 | - [number-input](examples/number-input)
73 | - [serverside rendering](examples/server-rendering)
74 | - [login form](examples/login-form)
75 | - [geometry](examples/geometry)
76 | - [2048 (wip)](https://github.com/Raynos/mercury/tree/2048-wip/examples/2048)
77 | - [github issues (wip)](https://github.com/Raynos/mercury/tree/github-issues/examples/github-issues-viewer)
78 | - [hot reloading with browserify or webpack](examples/hot-reload)
79 |
80 | ### Unidirectional examples
81 |
82 | The following examples demonstrate how you can mix & match
83 | mercury with other frameworks. This is possible because mercury
84 | is fundamentally modular.
85 |
86 | **Disclaimer:** The following are neither "good" nor "bad" ideas.
87 | Your milage may vary on using these ideas
88 |
89 | - [Backbone + Mercury](examples/unidirectional/backbone)
90 | - [Immutable + Mercury](examples/unidirectional/immutable)
91 | - [JSX + Mercury](examples/unidirectional/jsx)
92 |
93 | ## Motivation
94 |
95 | ### Mercury vs React
96 |
97 | `mercury` is similar to react, however it's larger in scope,
98 | it is better compared against [`om`][om] or
99 | [`quiescent`][quiescent]
100 |
101 | - mercury leverages [`virtual-dom`][virtual-dom] which uses
102 | an immutable vdom structure
103 | - mercury comes with [`observ-struct`][observ-struct] which uses
104 | immutable data for your state atom
105 | - mercury is truly modular, you can trivially swap out
106 | subsets of it for other modules
107 | - mercury source code itself is maintainable, the modules it
108 | uses are all small, well tested and well documented.
109 | You should not be afraid to use mercury in production
110 | as it's easy to maintain & fix.
111 | - mercury encourages zero dom manipulation in your application code. As far as your application is concerned
112 | elements do not exist. This means you don't need to reference DOM elements when rendering or when handling
113 | events
114 | - mercury is compact, it's 11kb min.gzip.js, that's smaller than backbone.
115 | - mercury strongly encourages FRP techniques and discourages local mutable state.
116 | - mercury is highly performant, it's faster than React / Om / ember+htmlbars in multiple benchmarks
117 | [TodoMVC benchmark](http://matt-esch.github.io/mercury-perf/)\
118 | [animation benchmark](http://jsfiddle.net/sVPQL/11/)
119 | [TodoMVC benchmark source](https://github.com/matt-esch/mercury-perf)
120 | - mercury comes with FP features like time-travel / easy undo out of the box.
121 | - mercury is lean, it's an weekend's read at 2.5kloc. (virtual-dom is 1.1kloc, an evening's read.)
122 | compared to react which is almost 20kloc (a month's read)
123 |
124 | ## Modules
125 |
126 | `mercury` is a small glue layer that composes a set of modules
127 | that solves a subset of the frontend problem.
128 |
129 | If `mercury` is not ideal for your needs, you should check out
130 | the individual modules and see if you can re-use something.
131 |
132 | Alternatively if the default set of modules in `mercury` doesn't
133 | work for you, you can just require other modules. It's possible
134 | to for example, swap out [`vtree`][vtree] with
135 | [`react`][react] or swap out [`observ-struct`][observ-struct]
136 | with [`backbone`][backbone]
137 |
138 | See [the modules README](docs/modules/README.md) for more
139 | information.
140 |
141 | ## Documentation
142 |
143 | See the [documentation folder](docs)
144 |
145 | ### FAQ
146 |
147 | See the [FAQ document](docs/faq.md)
148 |
149 | ### API
150 |
151 | WIP. In lieu of documentation please see examples :(
152 |
153 | ## Installation
154 |
155 | `npm install mercury`
156 |
157 | ## Development
158 |
159 | If you want to develop on `mercury` you can clone the code
160 |
161 | ```sh
162 | git clone git@github.com:Raynos/mercury
163 | cd mercury
164 | npm install
165 | npm test
166 | ```
167 |
168 | ### npm run tasks
169 |
170 | - `npm test` runs the tests
171 | - `npm run jshint` will run jshint on the code
172 | - `npm run disc` will open discify (if globally installed)
173 | - `npm run build` will build the html assets for gh-pages
174 | - `npm run examples` will start a HTTP server that shows examples
175 | - `npm run dist` will create a distributed version of mercury
176 | - `npm run modules-docs` will (re)generate docs of mercury modules
177 |
178 | ## Inspirations
179 |
180 | A lot of the philosophy and design of `mercury` is inspired by
181 | the following:
182 |
183 | - [`react`][react] for documenting and explaining the concept
184 | of a virtual DOM and its diffing algorithm
185 | - [`om`][om] for explaining the concept and benefits of
186 | immutable state and time travel.
187 | - [`elm`][elm] for explaining the concept of FRP and having a
188 | reference implementation of FRP in JavaScript. I wrote a
189 | pre-cursor to `mercury` that was literally a
190 | re-implementation of [`elm`][elm] in javascript
191 | ([`graphics`][graphics])
192 | - [`reflex`][reflex] for demonstrating the techniques used to
193 | implement dynamic inputs.
194 |
195 | ## Contributors
196 |
197 | - Raynos
198 | - Matt-Esch
199 | - neonstalwart
200 | - parshap
201 | - nrw
202 | - kumavis
203 |
204 | ## MIT Licenced
205 |
206 | [1]: https://secure.travis-ci.org/Raynos/mercury.svg
207 | [2]: https://travis-ci.org/Raynos/mercury
208 | [3]: https://badge.fury.io/js/mercury.svg
209 | [4]: https://badge.fury.io/js/mercury
210 | [5]: http://img.shields.io/coveralls/Raynos/mercury.svg
211 | [6]: https://coveralls.io/r/Raynos/mercury
212 | [7]: https://gemnasium.com/Raynos/mercury.png
213 | [8]: https://gemnasium.com/Raynos/mercury
214 | [9]: https://david-dm.org/Raynos/mercury.svg
215 | [10]: https://david-dm.org/Raynos/mercury
216 | [11]: https://img.shields.io/badge/GITTER-join%20chat-green.svg
217 | [12]: https://gitter.im/Raynos/mercury
218 | [13]: https://badge-size.herokuapp.com/Raynos/mercury/master/dist/mercury.js
219 | [14]: https://badge-size.herokuapp.com/Raynos/mercury/master/dist/mercury.js
220 |
221 | [graphics]: https://github.com/Raynos/graphics
222 | [elm]: https://github.com/elm-lang/Elm
223 | [react]: https://github.com/facebook/react
224 | [om]: https://github.com/swannodette/om
225 | [reflex]: https://github.com/Gozala/reflex
226 | [backbone]: https://github.com/jashkenas/backbone
227 | [quiescent]: https://github.com/levand/quiescent
228 | [virtual-dom]: https://github.com/Matt-Esch/virtual-dom
229 | [vtree]: https://github.com/Matt-Esch/virtual-dom/tree/master/vtree
230 | [vdom]: https://github.com/Matt-Esch/virtual-dom/tree/master/vdom
231 | [vdom-create-element]: https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/create-element.js
232 | [vdom-patch]: https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/patch.js
233 | [min-document]: https://github.com/Raynos/min-document
234 | [virtual-hyperscript]: https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript
235 | [main-loop]: https://github.com/Raynos/main-loop
236 | [vdom-thunk]: https://github.com/Raynos/vdom-thunk
237 | [observ]: https://github.com/Raynos/observ
238 | [observ-computed]: https://github.com/Raynos/observ/blob/master/computed.js
239 | [observ-struct]: https://github.com/Raynos/observ-struct
240 | [observ-array]: https://github.com/Raynos/observ-array
241 | [geval]: https://github.com/Raynos/geval
242 | [dom-delegator]: https://github.com/Raynos/dom-delegator
243 | [value-event]: https://github.com/Raynos/value-event
244 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # Initial
2 |
3 | ## Goal
4 |
5 | Write web applications in simple and expressive way using FP techniques
6 |
7 | ## Feature summary
8 |
9 | - Guide / tutorial
10 | - Documentation
11 | - Motivation & Philosophy
12 | - Benchmarks
13 | - Website / logo thing ?
14 |
15 | - VDOM thunks
16 | - 2048 + github issues examples in
17 | - lazy observables
18 | - test debt
19 |
20 | ## Detailed summary
21 |
22 | ### Guide / tutorial
23 |
24 | Write a guide either similar to Om / React / Mithril or write
25 | a tutorial walking through authoring one of the exmaples
26 |
27 | ### Documentation
28 |
29 | We should write high quality docs for mercury AND all it's direct
30 | dependencies.
31 |
32 | ### Motivation & Philosophy
33 |
34 | This is hand wavey
35 |
36 | ### Benchmarks
37 |
38 | Mostly done, just need to mention them
39 |
40 | ### Websites / logo
41 |
42 | Would be nice to have a proper gh-pages or proper mercuryjs.com
43 |
44 | ### VDOM thunks
45 |
46 | Implement thunks in virtual-dom & update vdom-thunk to use thunks
47 | instead of widgets
48 |
49 | ### 2048 & github issues
50 |
51 | Implement these examples, 2048 is already a branch
52 |
53 | ### lazy observables
54 |
55 | Implement lazy data structures for performance
56 |
57 | ### test debt
58 |
59 | Deal with the fact that both mercury & some of its dependencies
60 | dont have any tests
61 |
--------------------------------------------------------------------------------
/bin/browserify-editor.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var browserify = require('browserify');
4 | var fs = require('fs');
5 | var path = require('path');
6 | var cuid = require('cuid');
7 | var process = require('process');
8 |
9 | function addLink(href) {
10 | return '' +
11 | 'var link = document.createElement("link")\n' +
12 | 'link.rel = "stylesheet"\n' +
13 | 'link.href = "' + href + '"\n' +
14 | 'document.head.appendChild(link)\n';
15 | }
16 |
17 | function floatElement(name, float) {
18 | return '' +
19 | name + '.style.float = "' + float + '"\n' +
20 | name + '.style.padding = 0\n' +
21 | name + '.style.margin = 0\n' +
22 | name + '.style.width = "50%"\n';
23 | }
24 |
25 | function main(fileName) {
26 | var src = fs.readFileSync(path.resolve(fileName), 'utf8');
27 |
28 | var code = '' +
29 | 'var container = document.createElement("div")\n' +
30 | floatElement('document.body', 'left') +
31 | floatElement('container', 'right') +
32 | 'var createEditor = require("javascript-editor")\n' +
33 | 'window.addEventListener("load", function () {\n' +
34 | ' var editor = createEditor({\n' +
35 | ' container: container,\n' +
36 | ' value: ' + JSON.stringify(src) + ',\n' +
37 | ' readOnly: true\n' +
38 | ' })\n' +
39 | ' container.childNodes[0].style.fontSize = "12px"\n' +
40 | ' editor.editor.refresh()\n' +
41 | '})\n' +
42 | 'document.documentElement.appendChild(container)\n' +
43 | addLink('https://cdn.rawgit.com/maxogden/javascript-editor/' +
44 | '1835d09bdfe83e5121befe2ff660bc336a520d9a/css/codemirror.css') +
45 | addLink('https://cdn.rawgit.com/maxogden/javascript-editor/' +
46 | '1835d09bdfe83e5121befe2ff660bc336a520d9a/css/theme.css');
47 |
48 | var loc = path.join(__dirname, cuid() + '.js');
49 | fs.writeFileSync(loc, code);
50 |
51 | var bundle = browserify();
52 | bundle.add(path.resolve(fileName));
53 | bundle.add(loc);
54 | var stream = bundle.bundle();
55 | stream.on('end', function onEnd() {
56 | fs.unlinkSync(loc);
57 | });
58 | return stream;
59 | }
60 |
61 | module.exports = main;
62 |
63 | if (require.main === module) {
64 | var file = process.argv[2];
65 | main(file).pipe(process.stdout);
66 | }
67 |
--------------------------------------------------------------------------------
/bin/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var parallel = require('run-parallel');
4 | var fs = require('fs');
5 | var path = require('path');
6 | var process = require('process');
7 | var logger = require('console');
8 | var examplesTasks = require('./example-tasks.js');
9 |
10 | function main() {
11 | var tasks = examplesTasks.map(function writeTask(task) {
12 | return function thunk(cb) {
13 | logger.log('reading', task.src);
14 |
15 | task.createStream()
16 | .pipe(fs.createWriteStream(task.dest))
17 | .on('finish', function onFinish() {
18 | logger.log('writing', task.dest);
19 | cb();
20 | });
21 | };
22 | });
23 |
24 | parallel(tasks, function onTasks(err) {
25 | if (err) {
26 | throw err;
27 | }
28 |
29 | var html = '
\n';
30 | examplesTasks.forEach(function buildHtml(task) {
31 | var dest = path.relative(process.cwd(), task.dest);
32 | html += '
'; // make a list item element
52 | }
53 | list += '';
54 |
55 | return list;
56 | };
57 |
58 | // Routes handlers
59 | router.set('/', function index(req, res) {
60 | var filename = path.dirname(__dirname) + '/examples/index.html';
61 | var buf = fs.readFileSync(filename, 'utf8');
62 |
63 | res.setHeader('Content-Type', 'text/html');
64 |
65 | buf = buf.replace('{{examples}}', buildList(tasks));
66 | res.end(buf);
67 | });
68 | router.set('/:name', handler);
69 | router.set('/:name/*', handler);
70 | router.set('/mercury/*', st({
71 | path: path.dirname(__dirname),
72 | url: '/mercury',
73 | cache: false
74 | }));
75 |
76 | // Server implementation
77 | var server = http.createServer(function handler(req, res) {
78 | router(req, res, {}, onError);
79 |
80 | function onError(err) {
81 | if (err) {
82 | // use your own custom error serialization.
83 | res.statusCode = err.statusCode || 500;
84 | res.end(err.message);
85 | }
86 | }
87 | });
88 | server.listen(8080);
89 | logger.log('listening on port 8080');
90 |
--------------------------------------------------------------------------------
/bin/example-tasks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var indexhtmlify = require('indexhtmlify');
5 | var browserify = require('browserify');
6 |
7 | var browserifyEditor = require('./browserify-editor');
8 |
9 | var examplesDir = path.join(__dirname, '..', 'examples');
10 | var examplesTasks = [
11 | browserifyTask('geometry'),
12 | browserifyTask('todomvc'),
13 | browserifyTask('markdown'),
14 | browserifyTask('number-input'),
15 | browserifyTask(path.join(
16 | 'unidirectional',
17 | 'backbone'
18 | )),
19 | browserifyTask(path.join(
20 | 'unidirectional',
21 | 'jsx'
22 | )),
23 | browserifyTask(path.join(
24 | 'unidirectional',
25 | 'immutable'
26 | )),
27 | browserifyTask('login-form'),
28 | browserifyEditorTask('bmi-counter'),
29 | browserifyEditorTask('shared-state'),
30 | browserifyEditorTask('count'),
31 | browserifyEditorTask('canvas'),
32 | browserifyEditorTask('async-state'),
33 | browserifyEditorTask('real-dom')
34 | ];
35 |
36 | module.exports = examplesTasks;
37 |
38 | function browserifyTask(folder) {
39 | var task = {
40 | src: path.join(examplesDir, folder, 'browser.js'),
41 | dest: path.join(examplesDir, folder, 'index.html'),
42 | type: 'browserify',
43 | name: folder,
44 | createStream: createStream
45 | };
46 |
47 | return task;
48 |
49 | function createStream() {
50 | var stream = browserifyBundle(task.src);
51 | var result = stream.pipe(indexhtmlify({}));
52 |
53 | stream.on('error', function onError(err) {
54 | result.emit('error', err);
55 | });
56 |
57 | return result;
58 | }
59 | }
60 |
61 | function browserifyEditorTask(file) {
62 | var task = {
63 | src: path.join(examplesDir, file + '.js'),
64 | dest: path.join(examplesDir, file + '.html'),
65 | type: 'browserify-editor',
66 | name: file,
67 | createStream: createStream
68 | };
69 |
70 | return task;
71 |
72 | function createStream() {
73 | return browserifyEditor(task.src)
74 | .pipe(indexhtmlify({}));
75 | }
76 | }
77 |
78 | function browserifyBundle(source) {
79 | var b = browserify();
80 | b.add(source);
81 | return b.bundle();
82 | }
83 |
--------------------------------------------------------------------------------
/bin/modules-docs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var fs = require('fs');
5 |
6 | var docsPath = path.join(__dirname, '..', 'docs', 'modules');
7 | var modulesPath = path.join(__dirname, '..', 'node_modules');
8 | var moduleReadmes = {
9 | 'geval': ['geval', 'README.md', 'Raynos/geval'],
10 | 'dom-delegator': ['dom-delegator', 'README.md', 'Raynos/dom-delegator'],
11 | 'value-event': ['value-event', 'README.md', 'Raynos/value-event'],
12 | 'observ-array': ['observ-array', 'README.md', 'Raynos/observ-array'],
13 | 'observ-varhash': ['observ-varhash', 'Readme.md', 'nrw/observ-varhash'],
14 | 'observ-struct': ['observ-struct', 'README.md', 'Raynos/observ-struct'],
15 | 'observ': ['observ', 'README.md', 'Raynos/observ'],
16 | 'virtual-dom': ['virtual-dom', 'README.md', 'Matt-Esch/virtual-dom'],
17 | 'vtree': ['virtual-dom', path.join('vtree', 'README.md'),
18 | 'Matt-Esch/virtual-dom'],
19 | 'vdom': ['virtual-dom', path.join('vdom', 'README.md'),
20 | 'Matt-Esch/virtual-dom'],
21 | 'virtual-hyperscript': ['virtual-dom',
22 | path.join('virtual-hyperscript', 'README.md'),
23 | 'Matt-Esch/virtual-dom'],
24 | 'vdom-thunk': ['vdom-thunk', 'README.md', 'Raynos/vdom-thunk'],
25 | 'main-loop': ['main-loop', 'README.md', 'Raynos/main-loop']
26 | };
27 |
28 | Object.keys(moduleReadmes).forEach(saveModuleDoc);
29 |
30 | function saveModuleDoc(moduleName) {
31 | var moduleData = moduleReadmes[moduleName];
32 | var moduleReadmePath = path.join(modulesPath, moduleData[0],
33 | moduleData[1]);
34 | var moduleUrl = 'https://github.com/' + moduleData[2];
35 | var modulePackagePath = path.join(modulesPath, moduleData[0],
36 | 'package.json');
37 | var modulePackageInfo = JSON.parse(fs.readFileSync(modulePackagePath,
38 | 'utf8'));
39 | var moduleVersion = modulePackageInfo.version;
40 |
41 | var moduleDocPath = path.join(docsPath, moduleName + '.md');
42 | var docFile = fs.createWriteStream(moduleDocPath);
43 |
44 | docFile.write('Auto generated from [' + moduleData[0] + '](' + moduleUrl +
45 | ') (version ' + moduleVersion + ').\n\n',
46 | function writeModuleDocFile() {
47 | var moduleReadmeFile = fs.createReadStream(moduleReadmePath);
48 | moduleReadmeFile.pipe(docFile);
49 | }
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/docs/hooks.md:
--------------------------------------------------------------------------------
1 | # Hooks
2 |
3 | A vnode consists mainly of `{ tagName, properties, children }`. When you diff two vnodes you are applying a diff on the tagName, on the properties and the children.
4 |
5 | The properties normally consist of keys and values, where the values are strings or nested objects (e.g. style, attributes).
6 |
7 | Other than strings and nested objects you can also place hooks on the properties object.
8 |
9 | A hook is a `{ hook: function (domElement, propertyName) {} }` and it's used like:
10 |
11 | ```js
12 | h('div', {
13 | 'arbitrary-keyname': Object.create({
14 | hook: function (elem, propname) {
15 | assert(elem.tagName === 'div');
16 | assert(propname === 'arbitrary-keyname');
17 | }
18 | }
19 | })
20 | ```
21 |
22 | When you place a hook in a virtual node vtree/diff will create a patch object saying the properties have changed. Because hooks are instances of prototypes they will always be a new instance on every `diff` call so the hook value in the properties object will be different between `prev` and `current`.
23 |
24 | The hook only gets executed in `vdom/create-element` and `vdom/patch`. Hooks get executed in key order of the properties object. Hooks also get executed together with property patches being applied. This means you probably want all your hook keys to be at the bottom of your properties object.
25 |
26 | So whenever vdom is patching the properties on a DOM element it will also invoke hooks synchronously. The hook gets called with the DOM element and the property name.
27 |
28 | The use case for hooks is to set properties on DOM nodes that cannot be set through the DOM property interface, for example focus has no property based declarative interface so we have a focus hook that calls the `.focus()` method.
29 |
30 | The other use case for hooks is to manage stateful properties. Hooks are stateful and will always get called even if nothing else has changed. If a vnode has hooks we will call them, this means that even if there are no differences or the thunk has not changed we will still return a set of patches that is "invoke these hooks".
31 |
32 | Because of the fact hooks get called on every diff/patch cycle we can use hook to manage stateful properties. For example if you say `h('input', { value: ValueHook('foo') })` we will set the value of the input to `'foo'` on every render phase because we know that the user can change it away from `'foo'` and thus the DOM and the virtual DOM get out of sync which is not something we want.
33 |
--------------------------------------------------------------------------------
/docs/introduction.md:
--------------------------------------------------------------------------------
1 | ## What is mercury.
2 |
3 | > Below is a comment stolen from a mercury vs cycle ( https://github.com/staltz/cycle/issues/49#issuecomment-68612757 ) comparison.
4 |
5 | Mercury is very component oriented at the root of it. Generally you write applications as a series of components that
6 | return a state (the component data at any given point) and provides a render function. The render function knows how the
7 | component should be rendered including the dom structure, attributes/values and events/event handlers (known as channels in Mercury).
8 |
9 | The following is a quick example that makes a div that can be clicked which will create a new one exactly the same except it
10 | has a different `data-foo-id` attribute value. It is created in the image of a Cycle.js example you can see here ( https://github.com/staltz/cycle/blob/master/examples/simple/simple.js ).
11 |
12 |
13 | ```js
14 | var hg = require('mercury');
15 | var h = require('mercury').h;
16 |
17 | function Foo(initialState) {
18 | return hg.state({
19 | bars: hg.array(initialState.bars, createBar),
20 | channels: {
21 | addBar: Foo.addBar
22 | }
23 | });
24 |
25 | function createBar(x) {
26 | return hg.struct({
27 | id: hg.value(x.id),
28 | bar: hg.value(x.bar)
29 | })
30 | }
31 | }
32 |
33 | Foo.addBar = function addBar(state) {
34 | state.bars.push({
35 | id: 2,
36 | bar: Math.round(Math.random() * 1000)
37 | });
38 | };
39 |
40 | Foo.render = function render(state) {
41 | return h('div', state.bars.map(renderBar))
42 |
43 | function renderBar(bar) {
44 | return h('div', {
45 | 'attributes': {
46 | 'data-foo-id': bar.id
47 | },
48 | 'style': {
49 | 'margin': '10px',
50 | 'background': '#ececec',
51 | 'padding': '5px',
52 | 'cursor': 'pointer',
53 | 'display': 'inline-block'
54 | },
55 | 'ev-click': hg.send(state.channels.addBar)
56 | });
57 | }
58 | };
59 |
60 | function main() {
61 | hg.app(document.body, Foo({
62 | bars: [{ id: 2, bar: 135 }]
63 | }), Foo.render);
64 | }
65 |
66 | main();
67 | ```
68 |
69 | Some of the core ideas are:
70 |
71 | - your entire view is a single complex vtree
72 | - your entire view state is a single complex immutable object.
73 | - your rendering function is pure, it takes just the view state (disclaimer: hooks & widgets are not pure).
74 | - you declare all user input as channels up front in your view state.
75 | - the view state supports cursors, you can nest components in components.
76 |
77 | Mercury is unidirectional because:
78 |
79 | - A DOM event triggers a value to be send to a channel
80 | - The listener for the channel updates the view state
81 | - An update to the view state triggers a re-render
82 | - A new vtree is created
83 | - diff() and patch() update the DOM.
84 |
85 | See more about life cycles here ( https://github.com/Raynos/mercury/blob/master/docs/life-cycles.md ).
86 |
87 | Mercury is also a hybrid of functional and imperative, the rendering logic is functional but the updating logic has an imperative updating interface (backed by a stream of immutable objects under the hood).
88 |
89 | This gives you an FRP style application written in a way that still feels like its javascript
90 |
--------------------------------------------------------------------------------
/docs/life-cycles.md:
--------------------------------------------------------------------------------
1 | # The life cycles of a mercury app
2 |
3 | A well structure mercury app is reasonably deterministic about
4 | when certain code runs.
5 |
6 | There are a few main "phases"
7 |
8 | - Reacting to browser events from the event loop
9 | - Running application update logic
10 | - Running the rendering cycle in `requestAnimationFrame`
11 |
12 | ## Reacting to the event loop
13 |
14 | The only time JavaScript get's executed is when the event loop
15 | tells your code that a new thing happened.
16 |
17 | This includes the initial evaluation of your `browser.js`
18 | source code.
19 |
20 | On first evaluation you setup your state, render your view
21 | and insert it into the DOM.
22 |
23 | ### Reacting to DOM events
24 |
25 | All your handling of DOM events (i.e. `click`, `change`, ...)
26 | should go through `dom-delegator`.
27 |
28 | `dom-delegator` intercepts all events and sees if you have
29 | registered an event handler using the `ev-{{name}}` syntax
30 | in `h()` or have registered a global listener using
31 | `delegator.addGlobalEventListener()`.
32 |
33 | If it finds one it will invoke it.
34 |
35 | If you suspect there might be an issue or bug with an event not
36 | getting fired you should go into
37 | `node_modules/mercury/node_modules/dom-delegator` and edit
38 | your copy of dom-delegator to add print statements.
39 |
40 | One common issue might be that your event is not in the whitelist
41 | ( https://github.com/Raynos/dom-delegator/blob/master/index.js#L10-L16 )
42 | you should call `delegator.listenTo(eventName)` to make sure
43 | the delegator registers a global event handler for it.
44 |
45 | ### Reacting to other entries from the event loop
46 |
47 | There are many other ways the event loop can notify you that
48 | something has changed.
49 |
50 | It's highly recommended that you take all other effects &
51 | entries to the event loop and wrap them in a `geval` interface.
52 |
53 | For example:
54 |
55 | - https://github.com/Raynos/mercury/blob/master/examples/todomvc/input.js#L15
56 | - https://github.com/Raynos/mercury/blob/github-issues/examples/github-issues-viewer/input.js#L11-L12
57 |
58 | The benefit is that once you've done this, you can now trace or
59 | debug `geval` to check all new events entering the event loop.
60 |
61 | The other benefit is isolating your actual core application logic
62 | that does not concern itself with effects from the code with
63 | side effects.
64 |
65 | If you put all the IO in one bucket and create a "seperate"
66 | part of your app that is just "on a geval event, run business
67 | logic and update state" then that second part is really simple
68 | to reason about and unit test.
69 |
70 | ## Running application logic
71 |
72 | ### `geval` event listeners
73 |
74 | Eventually a event from the event loop will trigger into a new
75 | discrete value being emitted on a `geval` Event instance.
76 |
77 | This discrete value should be a application specific value, you
78 | shouldn't have any dom events or raw xhr objects being emitted
79 | through geval but instead have already converted them into
80 | something specific to your application.
81 |
82 | ### Application logic
83 |
84 | At this point the listener to the `geval` Event is generally one
85 | of your applications `update` functions that has access to
86 | either the top level state or one of the nested states.
87 |
88 | This is your core application / business logic and you do some
89 | computation and update the state into a new state.
90 |
91 | ## The request animation frame rendering loop
92 |
93 | ### Scheduling a `requestAnimationFrame`
94 |
95 | Any time you do `state.set()` or `state.some.key.set()` the
96 | `main-loop` module will check if we have scheduled a rerender
97 | yet and if not will schedule a render on the next frame.
98 |
99 | If you suspect a `requestAnimationFrame` is not scheduled then
100 | add logging or tracing to `main-loop`.
101 |
102 | ### Entering a `requestAnimationFrame`
103 |
104 | The browser will enter the `main-loop` rendering phase on the
105 | next animation frame.
106 |
107 | #### Rendering the view to create a new virtual tree.
108 |
109 | `main-loop` will call the top level `render` function that you
110 | passed to `mercury.app(state, render)` and create a new virtual
111 | tree.
112 |
113 | One of the tricks that makes this fast is the fact that
114 | `mercury.partial` is used to avoid evaluating subtrees in
115 | `render` unless needed
116 |
117 | If you suspect this doesn't happen just add debug statements to
118 | your top level render function.
119 |
120 | #### Calling diff on the trees
121 |
122 | The next step is the diff phase, here `vtree` will diff the
123 | new and previous tree. At this point any `partial`'s that have
124 | actually changed will be evaluated and their respective
125 | rendering functions will be called.
126 |
127 | If you suspect anything is wrong here it's recommended you add
128 | a print to `main-loop` to inspect the `patches` returned by
129 | the `diff()` function and see if your expected changes are
130 | included.
131 |
132 | #### Calling patch on the DOM
133 |
134 | The final step is calling `patch()` on the actual DOM with your
135 | patches from `diff`.
136 |
137 | This is the only place in the life cycle of a mercury app where
138 | actual DOM manipulation happens. it happens at the end of
139 | any raf we have scheduled. Here all patches are applied and
140 | all hooks get called
141 |
142 | If you suspect something is wrong here it's recommended that you
143 | use DOM mutation observers with a helper like listenMutation
144 | ( https://github.com/Raynos/jsonml-stringify/blob/master/examples/lib/listen-mutation.js )
145 | to inspect the actual mutations applied to the DOM and see if
146 | they are what you might expect.
147 |
148 |
--------------------------------------------------------------------------------
/docs/mercury-component.md:
--------------------------------------------------------------------------------
1 | A component in mercury is two things
2 |
3 | - a "constructor" that takes some initialization arguments and
4 | returns state
5 | - a rendering function.
6 |
7 | The `state` that is returned from a component is a
8 | black box & a lens. You embed it into your "top level" state
9 | atom at some key.
10 |
11 | For example:
12 |
13 | ```js
14 | var appState = mercury.struct({
15 | loginState: LoginComponent(...)
16 | })
17 | ```
18 |
19 | You then update your "top level" rendering logic to call the
20 | components rendering function with the state that you
21 | "embedded" at some key.
22 |
23 | For example:
24 |
25 | ```js
26 | function appRender(state) {
27 | return h('div', [
28 | h('.header', [
29 | h('.my-logo'),
30 | LoginComponent.render(state.loginState)
31 | ])
32 | ])
33 | }
34 | ```
35 |
36 | A component also has events, this is similar to how you
37 | pass channels into a component in om except it's slightly
38 | less coupled.
39 |
40 | The events a component jas might be something like
41 | "login button pressed", i.e. a component is saying
42 | "I have a login button but have no idea how to handle login,
43 | please listen to my event and mutate the correct state somewhere".
44 |
45 | For example:
46 |
47 | ```js
48 | var loginComp = LoginComponent(...)
49 |
50 | var appState = mercury.struct({
51 | loginState: loginComp
52 | })
53 |
54 | LoginComponent.onLogin(loginComp, function (user) {
55 | /* do something with user. probably ajax, maybe redirect */
56 | })
57 | ```
58 |
59 | Note that if you want to mutate the black box `state` of a
60 | component you must not mutate it directly, a component
61 | should expose a set of functions like `resetField(state)`
62 | exactly like how evan has demonstrated.
63 |
64 | For example:
65 |
66 | ```js
67 | LoginComponent.renderLogOutForm(loginComp)
68 | ```
69 |
70 | The advantage of this technique is that you need to know nothing
71 | about how a component updates itself nor do you need to know
72 | anything about any events a component might have.
73 |
74 | Instead the component will internally broadcast any events
75 | or do the correct mutation of it's own state.
76 | Since the state it returns is a lens it will "mutate" the top
77 | level state that the caller of the component has embedded the
78 | component into thus triggering a redraw.
79 |
80 | Note: this article is written based on my reply to the Elm
81 | discuss thread about modularity, to see other techniques used
82 | by Elm & om, read more at https://groups.google.com/forum/#!msg/elm-discuss/sv7DVJ47QkE/VhLr9_V-5E4J
83 |
--------------------------------------------------------------------------------
/docs/modules/README.md:
--------------------------------------------------------------------------------
1 | ### Input, State, Render and Output
2 |
3 | There are three pieces to mercury, Input (Controller),
4 | State (Model) and Render (View).
5 |
6 | In a normal mercury app you define your top level Input which
7 | is a finite list of events.
8 |
9 | You then define your top level state "atom". Generally you want
10 | a large fat state object for your entire application. We then
11 | wire all the events in Input up to some updating logic, i.e.
12 | every time an event occurs in Input we do some logic and then
13 | update the State.
14 |
15 | Finally we define our Rendering logic as a single function
16 | that takes the entire state of our application and returns a
17 | virtual DOM representation of our UI. Every time the state
18 | changes we just call render and then update the DOM.
19 |
20 | You may also need Output for your application, if we want to
21 | have some other side effect other then updating the UI, like
22 | sending a HTTP POST or writing to a websocket or persisting
23 | to indexedDB. Then we generally listen to changes in the state
24 | and have our side effect. Note that Render is just a specific
25 | subset of the Output of your application.
26 |
27 | ### Rendering modules (The view layer)
28 |
29 | For the view layer mercury uses a set of modules that come
30 | together and make it easy to work with a Virtual DOM.
31 |
32 | In `mercury` the view is just a function that takes your
33 | application state and returns a virtual DOM representation.
34 | This makes writing your view really easy, you just write it
35 | top to bottom.
36 |
37 | `mercury` then uses the following modules to make it really
38 | performant to use the virtual DOM to update the real DOM.
39 |
40 | #### [`vtree`][vtree]
41 |
42 | [`vtree`][vtree] is the module that contains the data
43 | structures for the virtual DOM. These are the primitive
44 | objects and values that the rendering functions in a
45 | mercury app will return.
46 |
47 | [`vtree`][vtree] also contains the diffing algorithm used in
48 | `mercury`. Mercury uses a diffing algorithm on a virtual DOM
49 | to compute a minimal set of `VPatch` records that it can apply to the DOM.
50 |
51 | #### [`vdom`][vdom]
52 |
53 | [`vdom`][vdom] is the module that contains the `create` and
54 | `patch` algorithm for turning the `vtree` data structures
55 | into real DOM objects.
56 |
57 | [`vdom/create-element`][vdom-create-element] is used to turn a
58 | virtual DOM into a real DOM. this is used for the initial
59 | rendering of your app.
60 |
61 | [`vdom/patch`][vdom-patch] is used to apply a series of `VPatch`
62 | records to a real DOM element.
63 |
64 | You can also use [`vdom`][vdom] and
65 | [`min-document`][min-document] together on the server to
66 | generate HTML strings. [`min-document`][min-document] is a
67 | minimal fake DOM for use on the server, you can pass
68 | [`vdom`][vdom] any `document` you want. In this case
69 | [`min-document`][min-document] contains the logic to convert
70 | its fake DOM into a HTML string.
71 |
72 | #### [`virtual-hyperscript`][virtual-hyperscript]
73 |
74 | [`virtual-hyperscript`][virtual-hyperscript] is a module that
75 | makes it easier to create `VTree` nodes. It basically exports
76 | a `h()` function that creates a DSL similar to `jade`
77 | (just more brackets ;)).
78 |
79 | [`virtual-hyperscript`][virtual-hyperscript] allows you to write
80 | your views in an expressive manner.
81 |
82 | #### [`vdom-thunk`][vdom-thunk]
83 |
84 | [`vdom-thunk`][vdom-thunk] is a module that increases the
85 | performance of building applications with a virtual DOM
86 | system. One of the important parts of using a virtual DOM
87 | and functional programming in general is to make extensive
88 | use of caching.
89 |
90 | You can use [`vdom-thunk`][vdom-thunk] to effectively memoize a
91 | function that returns a virtual DOM node. This means if you
92 | call it twice with the same arguments it will not re-evaluate
93 | the function.
94 |
95 | This basically means you only have to render that which has
96 | changed instead of rendering the entire virtual tree of your
97 | application.
98 |
99 | It should be noted that [`vdom-thunk`][vdom-thunk] assumes
100 | arguments are immutable and thus does an O(1) `===` check
101 | to see whether the arguments has changed. This will only
102 | work if your state is immutable. Thankfully,
103 | [`observ-struct`][observ-struct] is immutable
104 |
105 | #### [`main-loop`][main-loop]
106 |
107 | [`main-loop`][main-loop] is another optimization module for a
108 | virtual DOM system. Normally you would re-create the virtual
109 | tree every time your state changes. This is not optimum,
110 | with [`main-loop`][main-loop] you will only update your
111 | virtual tree at most once per request animation frame.
112 |
113 | [`main-loop`][main-loop] basically gives you batching of your
114 | virtual DOM changes, which means if you change your model
115 | multiple times it will be rendered once asynchronously on
116 | the next request animation frame.
117 |
118 | ### State modules (The model layer)
119 |
120 | In `mercury` we use immutable data structure primitives to
121 | represent our model. Using immutable data structures allows
122 | you to use the [`vdom-thunk`][vdom-thunk] optimization.
123 |
124 | `mercury` uses an observable state representation so that you
125 | can be notified of any changes.
126 |
127 | Generally applications built with mercury will have a single
128 | top level state "atom". i.e. there is one large state object
129 | for your application and child components do not have local or
130 | hidden state. However we can directly embed the state of a
131 | child component in our top level state "atom" to achieve
132 | composition.
133 |
134 | #### [`observ`][observ]
135 |
136 | [`observ`][observ] is the data structure used for observable
137 | data. It allows you to create a value for which you can
138 | listen for changes.
139 |
140 | [`observ`][observ] also comes with higher order functions
141 | like [`observ/computed`][observ-computed] that can be used to
142 | create new dependent observables. Generally these computed
143 | observables cannot be directly mutated but instead change
144 | when they data they rely on changes.
145 |
146 | [`observ`][observ] is basically an implementation of the
147 | `Signal` type that is normally used in FRP.
148 |
149 | #### [`observ-struct`][observ-struct]
150 |
151 | [`observ-struct`][observ-struct] is an observable that contains an
152 | object with a fixed number of keys. Generally the key-value
153 | pairs in [`observ-struct`][observ-struct] are themselves
154 | observables. You can change the value of any key in an
155 | [`observ-struct`][observ-struct] and the top level object
156 | will also change to be a new object with that key changed.
157 |
158 | [`observ-struct`][observ-struct] uses shallow extension to ensure
159 | that every time the struct changes you get a fresh immutable
160 | object.
161 |
162 | #### [`observ-array`][observ-array]
163 |
164 | [`observ-array`][observ-array] is an observable that contains
165 | an array of observables. It's generally recommended that this a
166 | heterogeneous array. You can change the value of any item in
167 | the array and the top level array will also change to be a
168 | new array.
169 |
170 | [`observ-array`][observ-array] uses shallow extension to ensure
171 | that every time the array changes (an item changes or an
172 | item is added or removed) you get a fresh immutable array.
173 |
174 | [`observ-array`][observ-array] has the benefit of being able
175 | to add or remove items from the array, where as
176 | [`observ-struct`][observ-struct] has a fixed number
177 | of keys and you cannot add more keys to an
178 | [`observ-struct`][observ-struct]
179 |
180 | ### Input modules (The controller layer)
181 |
182 | In `mercury` we model all the inputs to our application
183 | explicitly. We define an input object that contains a bunch of
184 | [`geval`][geval] Event instances.
185 |
186 | Somewhere else in our application we listen to the Input and
187 | run some logic and update our state when an event happens.
188 |
189 | #### [`geval`][geval]
190 |
191 | [`geval`][geval] is our data structure for Events. it gives us
192 | a way of listening to events and a way of publishing them.
193 |
194 | Most of the time you will either create a computed Event that
195 | emits events based on some raw source, like winddow scroll
196 | events or a websocket. Or you can create a mutable Event which
197 | you pass to the UI renderer so it can emit events for dynamically
198 | created UI components.
199 |
200 | [`geval`][geval] is basically an implementation of the
201 | `Event` type that is normally used in FRP.
202 |
203 | #### [`dom-delegator`][dom-delegator]
204 |
205 | [`dom-delegator`][dom-delegator] is an event delegator that
206 | allows you to seperate your event listeners from your
207 | event emitters. It sets up global event listeners and
208 | allow you to embed event handlers on your virtual DOM
209 | elements without having to manage adding or removing
210 | actual event listeners.
211 |
212 | #### [`value-event`][value-event]
213 |
214 | [`value-event`][value-event] allows you to create event
215 | handlers that you can embed in a virtual DOM.
216 | These event handlers work with both the native DOM and
217 | [`dom-delegator`][dom-delegator].
218 |
219 | [`value-event`][value-event] contains a set of higher order
220 | functions that allows you to write to a value to a
221 | [`geval`][geval] Event when some user interaction occurs.
222 |
223 | Using the higher order functions defined in
224 | [`value-event`][value-event] (change, submit, etc. )
225 | allows you to not have to write any DOM event handling
226 | code in your application. [`value-event`][value-event]
227 | takes care of all the reading from the DOM.
228 |
229 |
230 |
231 |
232 | [graphics]: https://github.com/Raynos/graphics
233 | [elm]: https://github.com/elm-lang/Elm
234 | [react]: https://github.com/facebook/react
235 | [om]: https://github.com/swannodette/om
236 | [reflex]: https://github.com/Gozala/reflex
237 | [backbone]: https://github.com/jashkenas/backbone
238 | [quiescent]: https://github.com/levand/quiescent
239 | [virtual-dom]: https://github.com/Matt-Esch/virtual-dom
240 | [vtree]: https://github.com/Matt-Esch/virtual-dom/tree/master/vtree
241 | [vdom]: https://github.com/Matt-Esch/virtual-dom/tree/master/vdom
242 | [vdom-create-element]: https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/create-element.js
243 | [vdom-patch]: https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/patch.js
244 | [min-document]: https://github.com/Raynos/min-document
245 | [virtual-hyperscript]: https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript
246 | [main-loop]: https://github.com/Raynos/main-loop
247 | [vdom-thunk]: https://github.com/Raynos/vdom-thunk
248 | [observ]: https://github.com/Raynos/observ
249 | [observ-computed]: https://github.com/Raynos/observ/blob/master/computed.js
250 | [observ-struct]: https://github.com/Raynos/observ-struct
251 | [observ-array]: https://github.com/Raynos/observ-array
252 | [geval]: https://github.com/Raynos/geval
253 | [dom-delegator]: https://github.com/Raynos/dom-delegator
254 | [value-event]: https://github.com/Raynos/value-event
255 |
256 |
--------------------------------------------------------------------------------
/docs/modules/dom-delegator.md:
--------------------------------------------------------------------------------
1 | Auto generated from [dom-delegator](https://github.com/Raynos/dom-delegator) package (version 13.1.0).
2 |
3 | # dom-delegator
4 |
5 |
12 |
13 |
14 |
15 | Decorate elements with delegated events
16 |
17 | `dom-delegator` allows you to attach an `EventHandler` to
18 | a dom element.
19 |
20 | When event of the correct type occurs `dom-delegator` will
21 | invoke your `EventHandler`
22 |
23 | This allows you to seperate your event listeners from your
24 | event writers. Sprinkle your event writers in the template
25 | in one part of your codebase. Attach listeners to the event
26 | sources in some other part of the code base.
27 |
28 | This decouples the event definition in the DOM from your event
29 | listeners in your application code.
30 |
31 | Also see [`html-delegator`](https://github.com/Raynos/html-delegator)
32 | for the same idea using html `data-` attributes.
33 |
34 | ## Example
35 |
36 | ```html
37 |
38 |
bar
39 |
baz
40 |
41 | ```
42 |
43 | ```js
44 | var document = require("global/document")
45 | var Delegator = require("dom-delegator")
46 | var EventEmitter = require("events").EventEmitter
47 |
48 | var del = Delegator()
49 | var emitter = EventEmitter()
50 | emitter.on('textClicked', function (value) {
51 | // either 'bar' or 'bar' depends on which
52 | // `
` was clicked
53 | console.log("doSomething", value.type)
54 | })
55 |
56 | var elem = document.querySelector(".foo")
57 |
58 | // add individual elems. (in a different file?)
59 | del.addEventListener(elem.querySelector(".bar"), "click", function (ev) {
60 | emmitter.emit('textClicked', { type: 'bar' })
61 | })
62 | del.addEventListener(elem.querySelector(".baz"), "click", function (ev) {
63 | emitter.emit('textClicked', { type: 'baz' })
64 | })
65 | ```
66 |
67 | ## Example (global listeners)
68 |
69 | Sometimes you don't want to add events bound to an element but
70 | instead listen to them globally.
71 |
72 | ```js
73 | var Delegator = require("dom-delegator")
74 |
75 | var d = Delegator()
76 | d.addGlobalEventListener("keydown", function (ev) {
77 | // hit for every global key press
78 | // can implement keyboard shortcuts
79 |
80 |
81 | })
82 |
83 | d.addEventListener(document.documentElement, "keydown", function (ev) {
84 | // hit for every keydown that is not captured
85 | // by an element listener lower in the tree
86 |
87 | // by default dom-delegator does not bubble events up
88 | // to other listeners on parent nodes
89 |
90 | // you can use global event listeners to intercept everything
91 | // even if there are listeners lower in the tree
92 | })
93 | ```
94 |
95 | ## Installation
96 |
97 | `npm install dom-delegator`
98 |
99 | ## Contributors
100 |
101 | - Raynos
102 |
103 | ## MIT Licenced
104 |
105 | [1]: https://secure.travis-ci.org/Raynos/dom-delegator.png
106 | [2]: https://travis-ci.org/Raynos/dom-delegator
107 | [3]: https://badge.fury.io/js/dom-delegator.png
108 | [4]: https://badge.fury.io/js/dom-delegator
109 | [5]: https://coveralls.io/repos/Raynos/dom-delegator/badge.png
110 | [6]: https://coveralls.io/r/Raynos/dom-delegator
111 | [7]: https://gemnasium.com/Raynos/dom-delegator.png
112 | [8]: https://gemnasium.com/Raynos/dom-delegator
113 | [9]: https://david-dm.org/Raynos/dom-delegator.png
114 | [10]: https://david-dm.org/Raynos/dom-delegator
115 | [11]: https://ci.testling.com/Raynos/dom-delegator.png
116 | [12]: https://ci.testling.com/Raynos/dom-delegator
117 |
--------------------------------------------------------------------------------
/docs/modules/geval.md:
--------------------------------------------------------------------------------
1 | Auto generated from [geval](https://github.com/Raynos/geval) package (version 2.1.1).
2 |
3 | # geval
4 |
5 | [![build status][1]][2]
6 | [![NPM version][3]][4]
7 | [![Coverage Status][5]][6]
8 | [![Davis Dependency status][9]][10]
9 |
10 | [![browser support][11]][12]
11 |
12 | An implementation of an event
13 |
14 | ## Example
15 |
16 | ```js
17 | var Event = require("geval")
18 | var document = require("global/document")
19 |
20 | var clicks = Event(function (broadcast) {
21 | document.addEventListener("click", function (ev) {
22 | broadcast(ev)
23 | })
24 | })
25 |
26 | var removeListener = clicks(function listener(ev) {
27 | console.log('click happened', ev)
28 | })
29 |
30 | // later you can call `removeListener()` to stop listening to events
31 | ```
32 |
33 | ## What about [`dominictarr/observable`](https://github.com/dominictarr/observable) ?
34 |
35 | Both `geval` and `observable` having a similar interface.
36 |
37 | - `thing(function (ev) { ... })` listens for new values.
38 |
39 | The main difference is that `geval` is an `Event`. For discrete
40 | events it doesn't make sense to call `thing()` to get the
41 | current state. Events do not have a notion of current state.
42 |
43 | So the `"click"` event doesn't have a `.get()` method because
44 | clicks do not have a notion of current state that makes sense
45 |
46 | However you should not make an `Event` of the windows current
47 | width & height. You should make an `observable` instead which
48 | internally listens on the `"resize"` event and sets the correct
49 | new width & height.
50 |
51 | ## Motivation
52 |
53 | EventEmitter's are complex. They are multiplexed events by default
54 |
55 | `Event` is the simpler version of an `EventEmitter`
56 |
57 | The main differences are:
58 |
59 | - just one event.
60 | - no implicit string based events
61 | - forces explicit interfaces with named properties that are
62 | `Event`'s
63 | - no inheritance, you don't have to inherit from `Event` like
64 | you have to inherit from `EventEmitter`.
65 | - `Event` interface only has public listening functionality,
66 | this gives a clear seperation between broadcast and listen
67 |
68 | Instead of something like
69 |
70 | ```js
71 | var EventEmitter = require('events').EventEmitter
72 |
73 | var stream = new EventEmitter()
74 |
75 | stream.on('data', onData)
76 | stream.on('end', onEnd)
77 | stream.on('close', onClose)
78 | ```
79 |
80 | you can do:
81 |
82 | ```js
83 | var Event = require('geval')
84 |
85 | var stream = {
86 | ondata: Event(function () { ... }),
87 | onend: Event(function () { ... }),
88 | onclose: Event(function () { ... })
89 | }
90 |
91 | stream.ondata(onData)
92 | stream.onend(onEnd)
93 | stream.onclose(onClose)
94 | ```
95 |
96 | Here the benefits are:
97 |
98 | - `stream` is an object of your shape and choice, you can call
99 | the properties whatever you want. the `[[Prototype]]` can
100 | be whatever you want.
101 | - `stream` has three well named properties that can be inspected
102 | statically or at run time which means the consumer knows
103 | exactly what type of events are available.
104 | - A consumer of `stream` could pass the `ondata` event to
105 | another object or module without also passing all other
106 | events along.
107 | - the `ondata` event is a concrete value. This allows for
108 | calling higher order functions on the value and enables
109 | various types of reactive programming techniques.
110 | - there are no special `"error"` semantics. There is no magic
111 | integration with `domain` or `"uncaughtException"`.
112 | - there is no public `emit()` function on the `stream` interface
113 | It's impossible for the consumer to emit events that it
114 | should not be emitting, you know that all events that
115 | come out of `ondata` are coming from the actual `stream`
116 | implementation.
117 |
118 | ## Docs
119 |
120 | ### `var removeListener = ev(function listener(value) {})`
121 |
122 | ```js
123 | var Event = require("geval")
124 |
125 | var ev = Event(...)
126 |
127 | var removeListener = ev(function listener(value) {
128 | /* do something with the event value */
129 | })
130 |
131 | // call `removeListener()` when you are done with the `ev`.
132 | ```
133 |
134 | A concrete `ev` is a function which you can pass a `listener`
135 | to. The `listener` you pass to `ev` will be called with
136 | an `value` each time an event occurs.
137 |
138 | When calling `ev` with a `listener` it will return a
139 | `removeListener` function. You can call `removeListener` to
140 | remove your `listener` function from the event. After you call
141 | it your listener function will not be called with any future
142 | values coming from the event.
143 |
144 | ### `var ev = Event(function broadcaster(broadcast) {})`
145 |
146 | ```js
147 | var Event = require("geval")
148 |
149 | var ev = Event(function broadcaster(broadcast) {
150 | /* call broadcast with a value */
151 | })
152 | ```
153 |
154 |
155 | `Event` takes a broadcasting function and returns an `event`
156 | function.
157 |
158 | The `broadcasting` function takes one argument, the `broadcast`
159 | function. The broadcaster can call `broadcast` each time it
160 | wants to make an event occur. Each time you call `broadcast`
161 | with a `value`, all listeners that are registered with `ev`
162 | will be invoked with the `value`
163 |
164 | ## Installation
165 |
166 | `npm install geval`
167 |
168 | ## Contributors
169 |
170 | - Raynos
171 |
172 | ## MIT Licenced
173 |
174 | [1]: https://secure.travis-ci.org/Raynos/geval.png
175 | [2]: https://travis-ci.org/Raynos/geval
176 | [3]: https://badge.fury.io/js/geval.png
177 | [4]: https://badge.fury.io/js/geval
178 | [5]: https://coveralls.io/repos/Raynos/geval/badge.png
179 | [6]: https://coveralls.io/r/Raynos/geval
180 | [7]: https://gemnasium.com/Raynos/geval.png
181 | [8]: https://gemnasium.com/Raynos/geval
182 | [9]: https://david-dm.org/Raynos/geval.png
183 | [10]: https://david-dm.org/Raynos/geval
184 | [11]: https://ci.testling.com/Raynos/geval.png
185 | [12]: https://ci.testling.com/Raynos/geval
186 |
--------------------------------------------------------------------------------
/docs/modules/main-loop.md:
--------------------------------------------------------------------------------
1 | Auto generated from [main-loop](https://github.com/Raynos/main-loop) package (version 3.1.0).
2 |
3 | # main-loop
4 |
5 |
12 |
13 |
14 |
15 | A rendering loop for diffable UIs
16 |
17 | ## Example
18 |
19 | ```js
20 | var mainLoop = require("main-loop")
21 | var h = require("virtual-dom/h")
22 |
23 | var initState = { fruits: ["apple", "banana"], name: "Steve" }
24 |
25 | function render(state) {
26 | return h("div", [
27 | h("div", [
28 | h("span", "hello "),
29 | h("span.name", state.name)
30 | ]),
31 | h("ul", state.fruits.map(renderFruit))
32 | ])
33 |
34 | function renderFruit(fruitName) {
35 | return h("li", [
36 | h("span", fruitName)
37 | ])
38 | }
39 | }
40 |
41 | // set up a loop
42 | var loop = mainLoop(initState, render, {
43 | create: require("virtual-dom/create-element"),
44 | diff: require("virtual-dom/diff"),
45 | patch: require("virtual-dom/patch")
46 | })
47 | document.body.appendChild(loop.target)
48 |
49 | // update the loop with the new application state
50 | loop.update({
51 | fruits: ["apple", "banana", "cherry"],
52 | name: "Steve"
53 | })
54 | loop.update({
55 | fruits: ["apple", "banana", "cherry"],
56 | name: "Stevie"
57 | })
58 | ```
59 |
60 | ## Installation
61 |
62 | `npm install main-loop`
63 |
64 | ## Contributors
65 |
66 | - Raynos
67 |
68 | ## MIT Licenced
69 |
70 | [1]: https://secure.travis-ci.org/Raynos/main-loop.png
71 | [2]: https://travis-ci.org/Raynos/main-loop
72 | [3]: https://badge.fury.io/js/main-loop.png
73 | [4]: https://badge.fury.io/js/main-loop
74 | [5]: https://coveralls.io/repos/Raynos/main-loop/badge.png
75 | [6]: https://coveralls.io/r/Raynos/main-loop
76 | [7]: https://gemnasium.com/Raynos/main-loop.png
77 | [8]: https://gemnasium.com/Raynos/main-loop
78 | [9]: https://david-dm.org/Raynos/main-loop.png
79 | [10]: https://david-dm.org/Raynos/main-loop
80 | [11]: https://ci.testling.com/Raynos/main-loop.png
81 | [12]: https://ci.testling.com/Raynos/main-loop
82 |
--------------------------------------------------------------------------------
/docs/modules/observ-array.md:
--------------------------------------------------------------------------------
1 | Auto generated from [observ-array](https://github.com/Raynos/observ-array) package (version 3.1.0).
2 |
3 | # observ-array
4 |
5 |
12 |
13 |
14 |
15 | An array containing observable values
16 |
17 | ## Example
18 |
19 | An `ObservArray` is an observable version of an array, every
20 | mutation of the array or mutation of an observable element in
21 | the array will cause the `ObservArray` to emit a new changed
22 | plain javascript array.
23 |
24 | ```js
25 | var ObservArray = require("observ-array")
26 | var ObservStruct = require("observ-struct")
27 | var Observ = require("observ")
28 | var uuid = require("uuid")
29 |
30 | function createTodo(title) {
31 | return ObservStruct({
32 | id: uuid(),
33 | title: Observ(title || ""),
34 | completed: Observ(false)
35 | })
36 | }
37 |
38 | var state = ObservStruct({
39 | todos: ObservArray([
40 | createTodo("some todo"),
41 | createTodo("some other todo")
42 | ])
43 | })
44 |
45 | state(function (currState) {
46 | // currState.todos is a plain javascript todo
47 | // currState.todos[0] is a plain javascript value
48 | currState.todos.forEach(function (todo, index) {
49 | console.log("todo", todo.title, index)
50 | })
51 | })
52 |
53 | state.todos.get(0).title.set("some new title")
54 | state.todos.push(createTodo("another todo"))
55 | ```
56 |
57 | ### Transactions
58 |
59 | Batch changes together with transactions.
60 |
61 | ```js
62 | var array = ObservArray([ Observ("foo"), Observ("bar") ])
63 |
64 | var removeListener = array(handleChange)
65 |
66 | array.transaction(function(rawList) {
67 | rawList.push(Observ("foobar"))
68 | rawList.splice(1, 1, Observ("baz"), Observ("bazbar"))
69 | rawList.unshift(Observ("foobaz"))
70 | rawList[6] = Observ("foobarbaz")
71 | })
72 |
73 | function handleChange(value) {
74 | // this will only be called once
75 | // changes are batched into a single diff
76 | value._diff //= [ [1,1,"baz","bazbar","foobar", , "foobarbaz"],
77 | // [0,0,"foobaz"] ]
78 | }
79 | ```
80 |
81 | ## Installation
82 |
83 | `npm install observ-array`
84 |
85 | ## Contributors
86 |
87 | - Raynos
88 | - [Matt McKegg][13]
89 |
90 | ## MIT Licenced
91 |
92 | [1]: https://secure.travis-ci.org/Raynos/observ-array.png
93 | [2]: https://travis-ci.org/Raynos/observ-array
94 | [3]: https://badge.fury.io/js/observ-array.png
95 | [4]: https://badge.fury.io/js/observ-array
96 | [5]: https://coveralls.io/repos/Raynos/observ-array/badge.png
97 | [6]: https://coveralls.io/r/Raynos/observ-array
98 | [7]: https://gemnasium.com/Raynos/observ-array.png
99 | [8]: https://gemnasium.com/Raynos/observ-array
100 | [9]: https://david-dm.org/Raynos/observ-array.png
101 | [10]: https://david-dm.org/Raynos/observ-array
102 | [11]: https://ci.testling.com/Raynos/observ-array.png
103 | [12]: https://ci.testling.com/Raynos/observ-array
104 | [13]: https://github.com/mmckegg
105 |
--------------------------------------------------------------------------------
/docs/modules/observ-struct.md:
--------------------------------------------------------------------------------
1 | Auto generated from [observ-struct](https://github.com/Raynos/observ-struct) package (version 5.0.1).
2 |
3 | # observ-struct
4 |
5 |
12 |
13 |
14 |
15 | An object with observable key value pairs
16 |
17 | ## Example
18 |
19 | An observable will emit a new immutable value whenever one of
20 | its keys changes.
21 |
22 | Nested keys will still be the same value if they were not changed
23 | in that particular `.set()` call.
24 |
25 | ```js
26 | var ObservStruct = require("observ-struct")
27 | var Observ = require("observ")
28 | var assert = require("assert")
29 |
30 | var state = ObservStruct({
31 | fruits: ObservStruct({
32 | apples: Observ(3),
33 | oranges: Observ(5)
34 | }),
35 | customers: Observ(5)
36 | })
37 |
38 | state(function (current) {
39 | console.log("apples", current.fruit.apples)
40 | console.log("customers", current.customers)
41 | })
42 |
43 | state.fruits(function (current) {
44 | console.log("apples", current.apples)
45 | })
46 |
47 | var initialState = state()
48 | assert.equal(initialState.fruits.bananas, 5)
49 | assert.equal(initialState.customers, 5)
50 |
51 | state.fruits.oranges.set(6)
52 | state.customers.set(5)
53 | state.fruits.apples.set(4)
54 | ```
55 |
56 | ## Docs
57 |
58 | ### `var obj = ObservStruct(opts)`
59 |
60 | `ObservStruct()` takes an object literal of string keys to either
61 | normal values or observable values.
62 |
63 | It returns an `Observ` instance `obj`. The value of `obj` is
64 | a plain javascript object where the value for each key is either
65 | the normal value passed in or the value of the observable for
66 | that key.
67 |
68 | Whenever one of the observables on a `key` changes the `obj` will
69 | emit a new object that's a shallow copy with that `key` set to
70 | the value of the appropiate observable on that `key`.
71 |
72 | ## Installation
73 |
74 | `npm install observ-struct`
75 |
76 | ## Contributors
77 |
78 | - Raynos
79 |
80 | ## MIT Licenced
81 |
82 | [1]: https://secure.travis-ci.org/Raynos/observ-struct.png
83 | [2]: https://travis-ci.org/Raynos/observ-struct
84 | [3]: https://badge.fury.io/js/observ-struct.png
85 | [4]: https://badge.fury.io/js/observ-struct
86 | [5]: https://coveralls.io/repos/Raynos/observ-struct/badge.png
87 | [6]: https://coveralls.io/r/Raynos/observ-struct
88 | [7]: https://gemnasium.com/Raynos/observ-struct.png
89 | [8]: https://gemnasium.com/Raynos/observ-struct
90 | [9]: https://david-dm.org/Raynos/observ-struct.png
91 | [10]: https://david-dm.org/Raynos/observ-struct
92 | [11]: https://ci.testling.com/Raynos/observ-struct.png
93 | [12]: https://ci.testling.com/Raynos/observ-struct
94 |
--------------------------------------------------------------------------------
/docs/modules/observ-varhash.md:
--------------------------------------------------------------------------------
1 | Auto generated from [observ-varhash](https://github.com/nrw/observ-varhash) package (version 1.0.4).
2 |
3 | # observ-varhash [![build status][1]][2]
4 |
5 | An object with observable key value pairs that can be added and removed
6 |
7 | ## Example
8 |
9 | An `ObservVarhash` is a version of `observ-struct` that allows
10 | adding and removing keys. Mutation of an observable element in
11 | the hash will cause the `ObservVarhash` to emit a new changed
12 | plain javascript object.
13 |
14 | ```js
15 | var ObservVarhash = require("observ-varhash")
16 | var Observ = require("observ")
17 |
18 | var people = ObservVarhash({jack: 'Jack'}, function create (obj, key) {
19 | return Observ(obj)
20 | })
21 |
22 | people.put('diane', 'Diane')
23 |
24 | console.log(people())
25 | // plain javascript object {jack: 'Jack', diane: 'Diane'}
26 | ```
27 |
28 | ## Installation
29 |
30 | `npm install observ-varhash`
31 |
32 | ## Contributors
33 |
34 | - Nicholas Westlake
35 |
36 | API based on [`observ-struct`](https://github.com/Raynos/observ-struct)
37 |
38 | ## MIT Licenced
39 |
40 | [1]: https://secure.travis-ci.org/nrw/observ-varhash.png
41 | [2]: https://travis-ci.org/nrw/observ-varhash
42 |
--------------------------------------------------------------------------------
/docs/modules/observ.md:
--------------------------------------------------------------------------------
1 | Auto generated from [observ](https://github.com/Raynos/observ) package (version 0.2.0).
2 |
3 | # observ
4 |
5 | [![build status][1]][2] [![NPM version][3]][4] [![Davis Dependency status][9]][10]
6 |
7 | [![browser support][11]][12]
8 |
9 | [![NPM][13]][14]
10 |
11 | A observable value representation
12 |
13 | ## Example
14 |
15 | ```js
16 | var Observable = require("observ")
17 |
18 | var v = Observable("initial value")
19 | v(function onchange(newValue) {
20 | assert.equal(newValue, "new value")
21 | })
22 | v.set("new value")
23 |
24 | var curr = v()
25 | assert.equal(curr, "new value")
26 | ```
27 |
28 |
29 | ## What about `dominictarr/observable` ?
30 |
31 | Both `observ` & `observable` have the same interface of
32 |
33 | - `thing()` gets the value
34 | - `thing.set(...)` sets the value
35 | - `thing(function (value) { ... })` listens to the value.
36 |
37 | The way `observ` and `observable` differ is in listening.
38 |
39 | - `observ` will ONLY call the listener if `.set()` is invoked.
40 | - `observable` calls the listener IMMEDIATELY and calls it whenever
41 | `.set()` is invoked
42 |
43 | `observ` can be used in a similar fashion to `observable` by using
44 | `var watch = require("observ/watch")`. You can then just
45 | `watch(thing, function (value) { ... })` and it will call the
46 | listener immediately
47 |
48 | Both `observ` & `observable` have a computed method with the same
49 | interface.
50 |
51 | - `require("observable").compute`
52 | - `require("observ/computed")`
53 |
54 | ## Example computed
55 |
56 | ```js
57 | var Observable = require("observ")
58 | var computed = require("observ/computed")
59 |
60 | var one = Observable(1)
61 | var two = Observable(2)
62 |
63 | var together = computed([one, two], function (a, b) {
64 | return a + b
65 | })
66 |
67 | assert.equal(together(), 3)
68 | two.set(5)
69 | assert.equal(together(), 7)
70 | ```
71 |
72 | ## Docs
73 |
74 | ```ocaml
75 | type Observable := {
76 | () => A &
77 | (Function) => void,
78 | set: (A) => void
79 | }
80 |
81 | observ := (A) => Observable
82 | ```
83 |
84 |
85 | ## Installation
86 |
87 | `npm install observ`
88 |
89 | ## Contributors
90 |
91 | - Raynos
92 |
93 | ## MIT Licenced
94 |
95 | [1]: https://secure.travis-ci.org/Raynos/observ.png
96 | [2]: https://travis-ci.org/Raynos/observ
97 | [3]: https://badge.fury.io/js/observ.png
98 | [4]: https://badge.fury.io/js/observ
99 | [5]: https://coveralls.io/repos/Raynos/observ/badge.png
100 | [6]: https://coveralls.io/r/Raynos/observ
101 | [7]: https://gemnasium.com/Raynos/observ.png
102 | [8]: https://gemnasium.com/Raynos/observ
103 | [9]: https://david-dm.org/Raynos/observ.png
104 | [10]: https://david-dm.org/Raynos/observ
105 | [11]: https://ci.testling.com/Raynos/observ.png
106 | [12]: https://ci.testling.com/Raynos/observ
107 | [13]: http://nodei.co/npm/observ.png
108 | [14]: http://nodei.co/npm/observ
109 |
--------------------------------------------------------------------------------
/docs/modules/value-event.md:
--------------------------------------------------------------------------------
1 | Auto generated from [value-event](https://github.com/Raynos/value-event) package (version 5.0.0).
2 |
3 | # value-event
4 |
5 |
12 |
13 |
14 |
15 | Create DOM event handlers that write to listeners
16 |
17 | ## Example (event)
18 |
19 | ```html
20 |