├── .eslintrc.json
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── contrib
├── cakejs.png
├── di.md
├── dist.js
├── loop.md
└── namespaces.png
├── examples
├── browser
│ └── index.html
├── counter
│ ├── .babelrc
│ ├── app
│ │ ├── index.html
│ │ └── index.js
│ ├── package.json
│ └── webpack.config.js
├── nesting
│ ├── .babelrc
│ ├── app
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── record.js
│ │ └── store.js
│ ├── package.json
│ └── webpack.config.js
├── routes
│ ├── .babelrc
│ ├── app
│ │ ├── index.html
│ │ └── index.js
│ ├── package.json
│ └── webpack.config.js
├── select
│ ├── .babelrc
│ ├── app
│ │ ├── index.html
│ │ └── index.js
│ ├── package.json
│ └── webpack.config.js
└── todo
│ ├── .babelrc
│ ├── app
│ ├── index.css
│ ├── index.html
│ └── index.js
│ ├── package.json
│ └── webpack.config.js
├── index.js
├── lib
├── bvd
│ ├── examples
│ │ ├── countdown
│ │ │ ├── .babelrc
│ │ │ ├── app
│ │ │ │ ├── index.html
│ │ │ │ └── index.js
│ │ │ ├── package.json
│ │ │ └── webpack.config.js
│ │ └── misc
│ │ │ └── inspect.gif
│ ├── index.js
│ ├── lib
│ │ ├── diff.js
│ │ ├── h.js
│ │ └── patch.js
│ └── tests
│ │ ├── fixtures
│ │ ├── basic.js
│ │ ├── nested.js
│ │ ├── simple.js
│ │ ├── svg.js
│ │ └── textnodes.js
│ │ └── mocha-tests
│ │ ├── attributes.test.js
│ │ ├── diff.test.js
│ │ ├── events.test.js
│ │ ├── h.test.js
│ │ ├── patch.test.js
│ │ ├── render.test.js
│ │ ├── svg.test.js
│ │ └── unrefobjects.test.js
├── cake.js
├── caramel.js
├── container.js
├── cream.js
├── dom.js
├── index.js
├── mixer.js
├── recipes
│ ├── fn.js
│ └── rafcaf.js
└── zefir.js
├── package.json
└── tests
├── cake.test.js
├── caramel.test.js
├── container.test.js
├── cream-observers.test.js
├── cream.test.js
├── create.test.js
├── mixer.test.js
└── zefir.test.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha" : true
5 | },
6 | "extends": "eslint:recommended",
7 | "globals" : {
8 | "describe" : true,
9 | "it" : true,
10 | "window" : true,
11 | "document" : true
12 | },
13 | "rules": {
14 | "indent": [
15 | "error",
16 | 2, { "SwitchCase" : 0 }
17 | ],
18 | "linebreak-style": [
19 | "error",
20 | "unix"
21 | ],
22 | "quotes": [
23 | "error",
24 | "single"
25 | ],
26 | "semi": [
27 | "error",
28 | "always"
29 | ]
30 | }
31 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | package-lock.json
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # nyc test coverage
19 | .nyc_output
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 | build
30 | dist
31 |
32 | # Dependency directories
33 | node_modules
34 | jspm_packages
35 |
36 | # Optional npm cache directory
37 | .npm
38 |
39 | # Optional REPL history
40 | .node_repl_history
41 | node_modules
42 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6"
4 | install:
5 | - npm i
6 | script:
7 | - npm run cov
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Svetlana Linuxenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://notabug.org/hofuku/cakejs2/raw/master/contrib/cakejs.png)
2 |
3 | `CakeJS2` is a lightweight front-end framework which borrows most awesome features from others.
4 |
5 | ### New features
6 |
7 | * Virtual-dom merged into a source tree
8 | * No dependencies
9 | * SVG support (partial)
10 |
11 | ### Features
12 |
13 | * All in one
14 | * [Dependency management](./contrib/di.md)
15 | * [Live rendering](./contrib/loop.md) (+ [virtual dom](https://github.com/linuxenko/basic-virtual-dom))
16 | * Good [performance](https://15lyfromsaturn.github.io/js-repaint-perfs/cakejs/index.html)
17 | * JSX support
18 | * Small error stack trace (?)
19 | * Small size and codebase ([about 23kb](https://unpkg.com/cakejs2@latest/dist/cake.min.js))
20 | * ES5 support (Yeah!)
21 | * Extremely easy to learn
22 |
23 | **For example: a candle counter recipe:**
24 |
25 | ```js
26 | create().route('/', 'counter');
27 |
28 | Cream.extend({
29 | _namespace : 'counter',
30 |
31 | candles : 0,
32 |
33 | increment : function() {
34 | this.set('candles', this.candles + 1);
35 | },
36 |
37 | render : function() {
38 | return h('button', { onClick : this.increment }, 'Candles on the Cake: ' + this.candles);
39 | }
40 | });
41 | ```
42 |
43 | Hyperscript is an requirement:
44 |
45 | ```
46 | /** @jsx h */
47 | ```
48 |
49 | **More examples**
50 |
51 | Live demos:
52 |
53 | * [todomvc](http://codepen.io/linuxenko/pen/jVRwLL)
54 | * [js-repaint-perf](https://15lyfromsaturn.github.io/js-repaint-perfs/cakejs/index.html)
55 |
56 | Check out [examples](./examples) folder.
57 |
58 | **Installation**
59 |
60 | To install latest version, the repository archive could be used:
61 |
62 | ```
63 | https://notabug.org/hofuku/cakejs2/archive/master.zip
64 | ```
65 |
66 | CDN
67 |
68 | None yet
69 |
70 | Deprecated:
71 | ```
72 | https://unpkg.com/cakejs2@latest/dist/cake.min.js
73 | ```
74 |
75 | **API**
76 |
77 | * `h`
78 | * [next](/contrib/loop.md)
79 | * [register](/contrib/di.md)
80 | * [unregister](/contrib/di.md)
81 | * [inject](/contrib/di.md)
82 | * `create`
83 | * `Cream`
84 |
85 | `create` options:
86 |
87 | ```js
88 | create({
89 | element : document.body // by default
90 | elementClass : cake
91 | elementId : cake
92 | createRoot : false // do not create root node, use render's
93 |
94 | ```
95 |
96 | `route`:
97 |
98 | ```js
99 | create().route(
100 | '/posts/:id/post', // URL pattern, also available "*" pattern
101 | 'home' // Namespace of the component
102 | );
103 |
104 | ```
105 |
106 | **Namespaces**
107 |
108 | [](http://i.imgur.com/USVdVuM.gifv)
109 |
110 | *Cream* is a base component of a cake.
111 |
112 | Functions:
113 |
114 | * `init`
115 | * `willTransition`
116 | * `didTransition`
117 | * `render`
118 |
119 | Options:
120 |
121 | * `_namespsace` - object's namespace
122 | * `_after` - DI after
123 |
124 | Zefir:
125 |
126 | * `props` - routing options ( `/:id/` for an instance became `props.id` )
127 | * `params` - params eg `?iam=param` bacame `params.iam`
128 |
129 | Sugar:
130 |
131 | * `observes`
132 | * `property` - computed property
133 |
134 | `observes` creates observer function
135 |
136 | ```js
137 | dataWatcher : function() { .... }.observes('posts', /^store/)
138 | ```
139 |
140 | #### History
141 |
142 | * [2022] Forked to became a general development version for now. [notabug](https://notabug.org/hofuku/cakejs2)
143 | * [2016] `Cakejs2` is a second generation of the `cakejs` framework [origin](https://githubg.com/linuxenko/cakejs2)
144 | * [2012] First version of `cakejs` were made and [published](https://github.com/linuxenko/cakejs2/tree/outdated-v1) in 2014th.
145 |
146 | #### License
147 |
148 | MIT License
149 |
150 | Copyright (c) 2016 Svetlana Linuxenko
151 |
152 |
--------------------------------------------------------------------------------
/contrib/cakejs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linuxenko/cakejs2/3d2323a3f7b3737932b95bcb031df1bc966bd1f8/contrib/cakejs.png
--------------------------------------------------------------------------------
/contrib/di.md:
--------------------------------------------------------------------------------
1 | **Dependency Management**
2 |
3 | Every registered object has its own `_container` and namespace.
4 | How to place `Cream` into `container`:
5 |
6 | * By specifying `_namespace` property inside of an object.
7 | * Using `register` function, for example: `register('routes.home', homeCream)`.
8 |
9 | To manage dependencies priority, the `_after` option could be used:
10 |
11 | Using an appropriate argument list for `register()` function:
12 |
13 | ```js
14 | var Home = Cream.extend({});
15 |
16 | register('routes.home', Home, 'mystore');
17 |
18 | ```
19 |
20 | or just explicitly specifying using the `_after` keyword inside of a `Cream`:
21 |
22 | ```js
23 | Cream.extend({
24 | _namespace : 'routes.home',
25 | _after : 'mystore'
26 | });
27 |
28 | ```
29 |
30 | **Dependency Injection**
31 |
32 | Any registered object or its property can be easily injected using `inject` function:
33 |
34 | ```js
35 | Cream.extend({
36 | store : inject('mystore.records')
37 | });
38 |
39 | ```
40 |
41 |
--------------------------------------------------------------------------------
/contrib/dist.js:
--------------------------------------------------------------------------------
1 | window.cake = require('../');
2 |
--------------------------------------------------------------------------------
/contrib/loop.md:
--------------------------------------------------------------------------------
1 | **Run loop**
2 |
3 | `CakeJS2` renders changes using `run loop` iterations in case namesapaces or
4 | properties were changed.
5 |
6 | `Run loop` is a `mixer` component of the `cake`'s internals, it uses brower's implementations
7 | e.g `requestAnimationFrame` or implements its own using plain old `settimeout` function.
8 |
9 | **Explicit actions**
10 |
11 | Any calculations, anything that application do, is hidden from the render loop,
12 | until you call `set`. Only then it will rerender `DOM` when next iteration comes..
13 |
--------------------------------------------------------------------------------
/contrib/namespaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linuxenko/cakejs2/3d2323a3f7b3737932b95bcb031df1bc966bd1f8/contrib/namespaces.png
--------------------------------------------------------------------------------
/examples/browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/examples/counter/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | 'presets' : ['es2015', 'react']
3 | }
4 |
--------------------------------------------------------------------------------
/examples/counter/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/counter/app/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx h */
2 | import './index.html';
3 | import {h, Cream, create, _container } from '../../../';
4 |
5 | create({
6 | element : document.body,
7 | elementId : 'application',
8 | elementClass : 'cake-application'
9 | })
10 | .route('/', 'counter');
11 |
12 | Cream.extend({
13 | _namespace : 'counter',
14 |
15 | clicked : 0,
16 |
17 | increment() {
18 | this.set('clicked', this.get('clicked') + 1);
19 | },
20 |
21 | render() {
22 | return (
23 |
24 | );
25 | }
26 | });
27 |
--------------------------------------------------------------------------------
/examples/counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "author": "Svetlana Linuxenko (http://www.linuxenko.pro)",
4 | "license": "MIT",
5 | "devDependencies": {
6 | "babel-core": "^6.18.2",
7 | "babel-loader": "^6.2.8",
8 | "babel-preset-es2015": "^6.18.0",
9 | "babel-preset-react": "^6.16.0",
10 | "file-loader": "^0.9.0",
11 | "webpack": "^1.13.3"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/counter/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname */
2 |
3 | var path = require('path');
4 |
5 | var webpack = require('webpack');
6 | var dir_js = path.resolve(__dirname, 'app');
7 | var dir_build = path.resolve(__dirname, 'build');
8 |
9 | module.exports = {
10 | entry: {
11 | app : path.resolve(dir_js, 'index.js')
12 | },
13 | devtool: 'source-map',
14 | output: {
15 | path: dir_build,
16 | filename: 'bundle.js'
17 | },
18 | resolveLoader: {
19 | fallback: [path.join(__dirname, 'node_modules')]
20 | },
21 | resolve: {
22 | modulesDirectories: ['node_modules', '../../../', dir_js],
23 | fallback: [path.join(__dirname, 'node_modules')]
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | loader: 'babel-loader',
32 | test: /\.js$/,
33 | exclude: /node_modules/,
34 | presets : ['es2015', 'react']
35 | },
36 | {
37 | test : /\.html$/,
38 | loader : 'file?name=[name].html'
39 | }
40 | ]
41 | },
42 | plugins: [
43 | new webpack.NoErrorsPlugin()
44 |
45 | ],
46 | stats: {
47 | colors: true
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/examples/nesting/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | 'presets' : ['es2015', 'react']
3 | }
4 |
--------------------------------------------------------------------------------
/examples/nesting/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/nesting/app/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx h */
2 | import './index.html';
3 | import {h, Cream, create, inject, _container } from '../../../';
4 | import Store from './store';
5 | import Record from './record';
6 |
7 | var c =create({
8 | element : document.body,
9 | elementId : 'application',
10 | elementClass : 'cake-application'
11 | })
12 | .route('/', 'records.index')
13 |
14 | Cream.extend({
15 | _namespace: 'records.index',
16 |
17 | render: function() {
18 | return (
19 |
20 |
List of Records
21 | {Store.store.map(function(r, i) {
22 | return (
23 |
24 | {r.content}
25 |
26 | );
27 | })
28 | }
29 |
30 | );
31 | }
32 | });
33 |
34 |
--------------------------------------------------------------------------------
/examples/nesting/app/record.js:
--------------------------------------------------------------------------------
1 | /** @jsx h */
2 | import {h, Cream} from '../../../';
3 |
4 | export default Cream.extend({
5 | render: function() {
6 | return (
7 |
8 |
{this.props.title}
9 |
{this.props.children}
10 |
11 | );
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/examples/nesting/app/store.js:
--------------------------------------------------------------------------------
1 | import {Cream} from '../../../';
2 |
3 | export default Cream.extend({
4 | byId: function(id) {
5 | return this.store[id];
6 | },
7 |
8 | store: [
9 | { title: 'First record', content: 'First record content' },
10 | { title: 'Second record', content: 'Second record content' },
11 | { title: 'Third record', content: 'Third record content' }
12 | ]
13 | });
14 |
--------------------------------------------------------------------------------
/examples/nesting/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "author": "Svetlana Linuxenko (http://www.linuxenko.pro)",
4 | "license": "MIT",
5 | "devDependencies": {
6 | "babel-core": "^6.18.2",
7 | "babel-loader": "^6.2.8",
8 | "babel-preset-es2015": "^6.18.0",
9 | "babel-preset-react": "^6.16.0",
10 | "file-loader": "^0.9.0",
11 | "webpack": "^1.13.3"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/nesting/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname */
2 |
3 | var path = require('path');
4 |
5 | var webpack = require('webpack');
6 | var dir_js = path.resolve(__dirname, 'app');
7 | var dir_build = path.resolve(__dirname, 'build');
8 |
9 | module.exports = {
10 | entry: {
11 | app : path.resolve(dir_js, 'index.js')
12 | },
13 | devtool: 'source-map',
14 | output: {
15 | path: dir_build,
16 | filename: 'bundle.js'
17 | },
18 | resolveLoader: {
19 | fallback: [path.join(__dirname, 'node_modules')]
20 | },
21 | resolve: {
22 | modulesDirectories: ['node_modules', '../../../', dir_js],
23 | fallback: [path.join(__dirname, 'node_modules')]
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | loader: 'babel-loader',
32 | test: /\.js$/,
33 | exclude: /node_modules/,
34 | presets : ['es2015', 'react']
35 | },
36 | {
37 | test : /\.html$/,
38 | loader : 'file?name=[name].html'
39 | }
40 | ]
41 | },
42 | plugins: [
43 | new webpack.NoErrorsPlugin()
44 |
45 | ],
46 | stats: {
47 | colors: true
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/examples/routes/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | 'presets' : ['es2015', 'react']
3 | }
4 |
--------------------------------------------------------------------------------
/examples/routes/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/routes/app/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx h */
2 | import './index.html';
3 | import {h, Cream, create, inject, _container } from '../../../';
4 |
5 | var c =create({
6 | element : document.body,
7 | elementId : 'application',
8 | elementClass : 'cake-application'
9 | })
10 | .route('/', 'records.index')
11 | .route('/:id', 'records.record');
12 |
13 | Cream.extend({
14 | _namespace : 'records.data',
15 | store : [
16 | { title : 'First record', content : 'First record content' },
17 | { title : 'Second record', content : 'Second record content' }
18 | ]
19 | });
20 |
21 | function appWrapper(children) {
22 | return (
23 |
Routes example
24 | { children }
25 | );
26 | }
27 |
28 | Cream.extend({
29 | _namespace : 'records.record',
30 |
31 | store : inject('records.data.store'),
32 |
33 | record : function() {
34 | return this.get('store.' + this.get('props.id'));
35 | }.property(),
36 |
37 | component() {
38 | return (
39 |
{ this.get('record').title }
40 |
{ this.get('record').content }
41 |
back
42 |
);
43 | },
44 |
45 | render() {
46 | return appWrapper(this.component());
47 | }
48 |
49 | });
50 |
51 | Cream.extend({
52 | _namespace : 'records.index',
53 |
54 | store : inject('records.data.store'),
55 |
56 | records : function() {
57 | return this.get('store').map(function(record, i) {
58 | record.idx = i;
59 |
60 | return record;
61 | });
62 | }.property(),
63 |
64 | component() {
65 | return (
66 |
67 | { this.get('records').map(function(record) {
68 | return (
69 |
70 |
71 | { record.title }
72 | |
73 |
74 | view
75 | |
76 |
77 | );
78 | })
79 | }
80 |
81 | );
82 | },
83 | render() {
84 | return appWrapper(this.component());
85 | }
86 | });
87 |
--------------------------------------------------------------------------------
/examples/routes/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "author": "Svetlana Linuxenko (http://www.linuxenko.pro)",
4 | "license": "MIT",
5 | "devDependencies": {
6 | "babel-core": "^6.18.2",
7 | "babel-loader": "^6.2.8",
8 | "babel-preset-es2015": "^6.18.0",
9 | "babel-preset-react": "^6.16.0",
10 | "file-loader": "^0.9.0",
11 | "webpack": "^1.13.3"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/routes/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname */
2 |
3 | var path = require('path');
4 |
5 | var webpack = require('webpack');
6 | var dir_js = path.resolve(__dirname, 'app');
7 | var dir_build = path.resolve(__dirname, 'build');
8 |
9 | module.exports = {
10 | entry: {
11 | app : path.resolve(dir_js, 'index.js')
12 | },
13 | devtool: 'source-map',
14 | output: {
15 | path: dir_build,
16 | filename: 'bundle.js'
17 | },
18 | resolveLoader: {
19 | fallback: [path.join(__dirname, 'node_modules')]
20 | },
21 | resolve: {
22 | modulesDirectories: ['node_modules', '../../../', dir_js],
23 | fallback: [path.join(__dirname, 'node_modules')]
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | loader: 'babel-loader',
32 | test: /\.js$/,
33 | exclude: /node_modules/,
34 | presets : ['es2015', 'react']
35 | },
36 | {
37 | test : /\.html$/,
38 | loader : 'file?name=[name].html'
39 | }
40 | ]
41 | },
42 | plugins: [
43 | new webpack.NoErrorsPlugin()
44 |
45 | ],
46 | stats: {
47 | colors: true
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/examples/select/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | 'presets' : ['es2015', 'react']
3 | }
4 |
--------------------------------------------------------------------------------
/examples/select/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/select/app/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx h */
2 | import './index.html';
3 | import { h, Cream, create, _container } from '../../../';
4 |
5 | create({
6 | element: document.body,
7 | elementId: 'application',
8 | elementClass: 'cake-application'
9 | })
10 | .route('/', 'counter');
11 |
12 | Cream.extend({
13 | _namespace: 'counter',
14 |
15 | selected: 'None',
16 | data: [{
17 | id: 1,
18 | text: "Bella"
19 | }, {
20 | id: 2,
21 | text: "Kitty"
22 | }, {
23 | id: 3,
24 | text: "Loki"
25 | }, {
26 | id: 4,
27 | text: "Milo"
28 | }, {
29 | id: 5,
30 | text: "Missy"
31 | }],
32 |
33 | changeSelect(event) {
34 | this.set('selected', this.get('data').filter(i => i.id == event.target.value)[0].text);
35 | },
36 |
37 | render() {
38 | return (
39 |
40 | {this.selected}
41 |
42 |
53 |
54 | );
55 | }
56 | });
57 |
--------------------------------------------------------------------------------
/examples/select/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "author": "Svetlana Linuxenko (http://www.linuxenko.pro)",
4 | "license": "MIT",
5 | "devDependencies": {
6 | "babel-core": "^6.18.2",
7 | "babel-loader": "^6.2.8",
8 | "babel-preset-es2015": "^6.18.0",
9 | "babel-preset-react": "^6.16.0",
10 | "file-loader": "^0.9.0",
11 | "webpack": "^1.13.3"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/select/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname */
2 |
3 | var path = require('path');
4 |
5 | var webpack = require('webpack');
6 | var dir_js = path.resolve(__dirname, 'app');
7 | var dir_build = path.resolve(__dirname, 'build');
8 |
9 | module.exports = {
10 | entry: {
11 | app : path.resolve(dir_js, 'index.js')
12 | },
13 | devtool: 'source-map',
14 | output: {
15 | path: dir_build,
16 | filename: 'bundle.js'
17 | },
18 | resolveLoader: {
19 | fallback: [path.join(__dirname, 'node_modules')]
20 | },
21 | resolve: {
22 | modulesDirectories: ['node_modules', '../../../', dir_js],
23 | fallback: [path.join(__dirname, 'node_modules')]
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | loader: 'babel-loader',
32 | test: /\.js$/,
33 | exclude: /node_modules/,
34 | presets : ['es2015', 'react']
35 | },
36 | {
37 | test : /\.html$/,
38 | loader : 'file?name=[name].html'
39 | }
40 | ]
41 | },
42 | plugins: [
43 | new webpack.NoErrorsPlugin()
44 |
45 | ],
46 | stats: {
47 | colors: true
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/examples/todo/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | 'presets' : ['es2015', 'react']
3 | }
4 |
--------------------------------------------------------------------------------
/examples/todo/app/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * sindresorhus todomvc-css
4 | */
5 |
6 | html,
7 | body {
8 | margin: 0;
9 | padding: 0;
10 | }
11 |
12 | button {
13 | margin: 0;
14 | padding: 0;
15 | border: 0;
16 | background: none;
17 | font-size: 100%;
18 | vertical-align: baseline;
19 | font-family: inherit;
20 | font-weight: inherit;
21 | color: inherit;
22 | -webkit-appearance: none;
23 | appearance: none;
24 | -webkit-font-smoothing: antialiased;
25 | -moz-osx-font-smoothing: grayscale;
26 | }
27 |
28 | body {
29 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
30 | line-height: 1.4em;
31 | background: #f5f5f5;
32 | color: #4d4d4d;
33 | min-width: 230px;
34 | max-width: 550px;
35 | margin: 0 auto;
36 | -webkit-font-smoothing: antialiased;
37 | -moz-osx-font-smoothing: grayscale;
38 | font-weight: 300;
39 | }
40 |
41 | :focus {
42 | outline: 0;
43 | }
44 |
45 | .hidden {
46 | display: none;
47 | }
48 |
49 | .todoapp {
50 | background: #fff;
51 | margin: 130px 0 40px 0;
52 | position: relative;
53 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
54 | 0 25px 50px 0 rgba(0, 0, 0, 0.1);
55 | }
56 |
57 | .todoapp input::-webkit-input-placeholder {
58 | font-style: italic;
59 | font-weight: 300;
60 | color: #e6e6e6;
61 | }
62 |
63 | .todoapp input::-moz-placeholder {
64 | font-style: italic;
65 | font-weight: 300;
66 | color: #e6e6e6;
67 | }
68 |
69 | .todoapp input::input-placeholder {
70 | font-style: italic;
71 | font-weight: 300;
72 | color: #e6e6e6;
73 | }
74 |
75 | .todoapp h1 {
76 | position: absolute;
77 | top: -155px;
78 | width: 100%;
79 | font-size: 100px;
80 | font-weight: 100;
81 | text-align: center;
82 | color: rgba(175, 47, 47, 0.15);
83 | -webkit-text-rendering: optimizeLegibility;
84 | -moz-text-rendering: optimizeLegibility;
85 | text-rendering: optimizeLegibility;
86 | }
87 |
88 | .new-todo,
89 | .edit {
90 | position: relative;
91 | margin: 0;
92 | width: 100%;
93 | font-size: 24px;
94 | font-family: inherit;
95 | font-weight: inherit;
96 | line-height: 1.4em;
97 | border: 0;
98 | color: inherit;
99 | padding: 6px;
100 | border: 1px solid #999;
101 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
102 | box-sizing: border-box;
103 | -webkit-font-smoothing: antialiased;
104 | -moz-osx-font-smoothing: grayscale;
105 | }
106 |
107 | .new-todo {
108 | padding: 16px 16px 16px 60px;
109 | border: none;
110 | background: rgba(0, 0, 0, 0.003);
111 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
112 | }
113 |
114 | .main {
115 | position: relative;
116 | z-index: 2;
117 | border-top: 1px solid #e6e6e6;
118 | }
119 |
120 | label[for='toggle-all'] {
121 | display: none;
122 | }
123 |
124 | .toggle-all {
125 | position: absolute;
126 | top: -55px;
127 | left: -12px;
128 | width: 60px;
129 | height: 34px;
130 | text-align: center;
131 | border: none; /* Mobile Safari */
132 | }
133 |
134 | .toggle-all:before {
135 | content: '❯';
136 | font-size: 22px;
137 | color: #e6e6e6;
138 | padding: 10px 27px 10px 27px;
139 | }
140 |
141 | .toggle-all:checked:before {
142 | color: #737373;
143 | }
144 |
145 | .todo-list {
146 | margin: 0;
147 | padding: 0;
148 | list-style: none;
149 | }
150 |
151 | .todo-list li {
152 | position: relative;
153 | font-size: 24px;
154 | border-bottom: 1px solid #ededed;
155 | }
156 |
157 | .todo-list li:last-child {
158 | border-bottom: none;
159 | }
160 |
161 | .todo-list li.editing {
162 | border-bottom: none;
163 | padding: 0;
164 | }
165 |
166 | .todo-list li.editing .edit {
167 | display: block;
168 | width: 506px;
169 | padding: 12px 16px;
170 | margin: 0 0 0 43px;
171 | }
172 |
173 | .todo-list li.editing .view {
174 | display: none;
175 | }
176 |
177 | .todo-list li .toggle {
178 | text-align: center;
179 | width: 40px;
180 | /* auto, since non-WebKit browsers doesn't support input styling */
181 | height: auto;
182 | position: absolute;
183 | top: 0;
184 | bottom: 0;
185 | margin: auto 0;
186 | border: none; /* Mobile Safari */
187 | -webkit-appearance: none;
188 | appearance: none;
189 | }
190 |
191 | .todo-list li .toggle:after {
192 | content: url('data:image/svg+xml;utf8,');
193 | }
194 |
195 | .todo-list li .toggle:checked:after {
196 | content: url('data:image/svg+xml;utf8,');
197 | }
198 |
199 | .todo-list li label {
200 | word-break: break-all;
201 | padding: 15px 60px 15px 15px;
202 | margin-left: 45px;
203 | display: block;
204 | line-height: 1.2;
205 | transition: color 0.4s;
206 | }
207 |
208 | .todo-list li.completed label {
209 | color: #d9d9d9;
210 | text-decoration: line-through;
211 | }
212 |
213 | .todo-list li .destroy {
214 | display: none;
215 | position: absolute;
216 | top: 0;
217 | right: 10px;
218 | bottom: 0;
219 | width: 40px;
220 | height: 40px;
221 | margin: auto 0;
222 | font-size: 30px;
223 | color: #cc9a9a;
224 | margin-bottom: 11px;
225 | transition: color 0.2s ease-out;
226 | }
227 |
228 | .todo-list li .destroy:hover {
229 | color: #af5b5e;
230 | }
231 |
232 | .todo-list li .destroy:after {
233 | content: '×';
234 | }
235 |
236 | .todo-list li:hover .destroy {
237 | display: block;
238 | }
239 |
240 | .todo-list li .edit {
241 | display: none;
242 | }
243 |
244 | .todo-list li.editing:last-child {
245 | margin-bottom: -1px;
246 | }
247 |
248 | .footer {
249 | color: #777;
250 | padding: 10px 15px;
251 | height: 20px;
252 | text-align: center;
253 | border-top: 1px solid #e6e6e6;
254 | }
255 |
256 | .footer:before {
257 | content: '';
258 | position: absolute;
259 | right: 0;
260 | bottom: 0;
261 | left: 0;
262 | height: 50px;
263 | overflow: hidden;
264 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
265 | 0 8px 0 -3px #f6f6f6,
266 | 0 9px 1px -3px rgba(0, 0, 0, 0.2),
267 | 0 16px 0 -6px #f6f6f6,
268 | 0 17px 2px -6px rgba(0, 0, 0, 0.2);
269 | }
270 |
271 | .todo-count {
272 | float: left;
273 | text-align: left;
274 | }
275 |
276 | .todo-count strong {
277 | font-weight: 300;
278 | }
279 |
280 | .filters {
281 | margin: 0;
282 | padding: 0;
283 | list-style: none;
284 | position: absolute;
285 | right: 0;
286 | left: 0;
287 | }
288 |
289 | .filters li {
290 | display: inline;
291 | }
292 |
293 | .filters li a {
294 | color: inherit;
295 | margin: 3px;
296 | padding: 3px 7px;
297 | text-decoration: none;
298 | border: 1px solid transparent;
299 | border-radius: 3px;
300 | }
301 |
302 | .filters li a:hover {
303 | border-color: rgba(175, 47, 47, 0.1);
304 | }
305 |
306 | .filters li a.selected {
307 | border-color: rgba(175, 47, 47, 0.2);
308 | }
309 |
310 | .clear-completed,
311 | html .clear-completed:active {
312 | float: right;
313 | position: relative;
314 | line-height: 20px;
315 | text-decoration: none;
316 | cursor: pointer;
317 | }
318 |
319 | .clear-completed:hover {
320 | text-decoration: underline;
321 | }
322 |
323 | .info {
324 | margin: 65px auto 0;
325 | color: #bfbfbf;
326 | font-size: 10px;
327 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
328 | text-align: center;
329 | }
330 |
331 | .info p {
332 | line-height: 1;
333 | }
334 |
335 | .info a {
336 | color: inherit;
337 | text-decoration: none;
338 | font-weight: 400;
339 | }
340 |
341 | .info a:hover {
342 | text-decoration: underline;
343 | }
344 |
345 | /*
346 | Hack to remove background from Mobile Safari.
347 | Can't use it globally since it destroys checkboxes in Firefox
348 | */
349 | @media screen and (-webkit-min-device-pixel-ratio:0) {
350 | .toggle-all,
351 | .todo-list li .toggle {
352 | background: none;
353 | }
354 |
355 | .todo-list li .toggle {
356 | height: 40px;
357 | }
358 |
359 | .toggle-all {
360 | -webkit-transform: rotate(90deg);
361 | transform: rotate(90deg);
362 | -webkit-appearance: none;
363 | appearance: none;
364 | }
365 | }
366 |
367 | @media (max-width: 430px) {
368 | .footer {
369 | height: 50px;
370 | }
371 |
372 | .filters {
373 | bottom: 10px;
374 | }
375 | }
--------------------------------------------------------------------------------
/examples/todo/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | CakeJS • TodoMVC
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/todo/app/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx h */
2 | import './index.html';
3 | import './index.css';
4 | import {h, Cream, create, _container } from '../../../';
5 |
6 | create({
7 | element : document.body,
8 | createRoot : false
9 | })
10 | .route('/', 'todomvc')
11 | .route('/:filter', 'todomvc');
12 |
13 | Cream.extend({
14 | _namespace : 'todomvc',
15 |
16 | todosStore : [
17 | {
18 | title : 'Taste Javascript',
19 | complete : true,
20 | },
21 | {
22 | title : 'Buy a unicorn',
23 | complete : false
24 | }
25 | ],
26 |
27 | completeAll : function(e) {
28 | this.get('todosStore').map((t, i) =>
29 | this.set('todosStore.'+ i +'.complete', e.target.checked));
30 | },
31 |
32 | clearCompleted : function() {
33 | this.set('todosStore', this.get('todosStore').filter( todo => todo.complete === false ));
34 | },
35 |
36 | itemsLeft : function() {
37 | return this.get('todosStore').filter( t => t.complete === false ).length;
38 | }.property(),
39 |
40 | completeTodo : function(idx) {
41 | this.set('todosStore.' + idx + '.complete',
42 | !this.get('todosStore.' + idx + '.complete'));
43 | },
44 |
45 | removeTodo : function(idx) {
46 | this.splice('todosStore', idx, 1);
47 | },
48 |
49 | insertTodo : function(e) {
50 | if (e.keyCode !== 13 || e.target.value.length < 1) {
51 | return;
52 | }
53 |
54 | this.unshift('todosStore', { title : e.target.value, complete : false });
55 | e.target.value = '';
56 | },
57 |
58 | untoggleEditings : function(e) {
59 | if (e && e.target.tagName !== 'INPUT') {
60 | this.set('todosStore', this.get('todosStore').map( t => { t.editing = false; return t; }));
61 | }
62 | },
63 |
64 | toggleEditing : function(idx) {
65 | this.untoggleEditings();
66 | this.set('todosStore.' + idx + '.editing', !this.get('todosStore.' + idx + '.editing'));
67 | },
68 |
69 | editTodo : function(idx, e) {
70 | if (e.keyCode !== 13 || e.target.value.length < 1) {
71 | return;
72 | }
73 |
74 | this.set('todosStore.' + idx + '.title', e.target.value);
75 | this.set('todosStore.' + idx + '.editing', false);
76 | },
77 |
78 | todos : function() {
79 | return this.get('todosStore').map((t, i) => {
80 | t.idx = i;
81 | t.classes = [];
82 | if (t.complete) t.classes.push('completed');
83 | if (t.editing) t.classes.push('editing');
84 | return t;
85 | }).filter(todo => {
86 | switch(this.get('props.filter')) {
87 | case 'active' : return todo.complete === false;
88 | case 'completed' : return todo.complete === true;
89 | default: return todo;
90 | }
91 | });
92 | }.property(),
93 |
94 | render() {
95 | return (
96 |
97 |
101 |
120 |
135 |
136 | );
137 | }
138 | });
139 |
--------------------------------------------------------------------------------
/examples/todo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "author": "Svetlana Linuxenko (http://www.linuxenko.pro)",
4 | "license": "MIT",
5 | "devDependencies": {
6 | "babel-core": "^6.18.2",
7 | "babel-loader": "^6.2.8",
8 | "babel-preset-es2015": "^6.18.0",
9 | "babel-preset-react": "^6.16.0",
10 | "file-loader": "^0.9.0",
11 | "webpack": "^1.13.3"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/todo/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname */
2 |
3 | var path = require('path');
4 |
5 | var webpack = require('webpack');
6 | var dir_js = path.resolve(__dirname, 'app');
7 | var dir_build = path.resolve(__dirname, 'build');
8 |
9 | module.exports = {
10 | entry: {
11 | app : path.resolve(dir_js, 'index.js')
12 | },
13 | devtool: 'source-map',
14 | output: {
15 | path: dir_build,
16 | filename: 'bundle.js'
17 | },
18 | resolveLoader: {
19 | fallback: [path.join(__dirname, 'node_modules')]
20 | },
21 | resolve: {
22 | modulesDirectories: ['node_modules', '../../../', dir_js],
23 | fallback: [path.join(__dirname, 'node_modules')]
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | loader: 'babel-loader',
32 | test: /\.js$/,
33 | exclude: /node_modules/,
34 | presets : ['es2015', 'react']
35 | },
36 | {
37 | test : /\.html$/,
38 | loader : 'file?name=[name].html'
39 | },
40 | {
41 | test : /\.css$/,
42 | loader : 'file?name=[name].css'
43 | }
44 | ]
45 | },
46 | plugins: [
47 | new webpack.NoErrorsPlugin()
48 |
49 | ],
50 | stats: {
51 | colors: true
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/');
2 |
--------------------------------------------------------------------------------
/lib/bvd/examples/countdown/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | 'presets' : ['es2015', 'react']
3 | }
4 |
--------------------------------------------------------------------------------
/lib/bvd/examples/countdown/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/bvd/examples/countdown/app/index.js:
--------------------------------------------------------------------------------
1 | import './index.html';
2 |
3 | /* coundown gist from https://gist.github.com/loremipson/8834955 */
4 | var date = new Date(),
5 | month = date.getMonth(),
6 | day = date.getDate(),
7 | weekDay = date.getDay();
8 |
9 | var hours = {
10 | start: new Date(date.getFullYear(), month, day),
11 | end: new Date(date.getFullYear(), month, day)
12 | };
13 |
14 | // weekDay var [0 = sun, 1 = mon, 2 = tues ... 5 = fri 6 = sat]
15 |
16 | // If it's Monday - Friday
17 | if(weekDay >= 1 && weekDay <= 5){
18 |
19 | // Start at 7am, end at 8pm
20 | hours.start.setHours(7);
21 | hours.end.setHours(20);
22 |
23 | // If it's Saturday
24 | } else if(weekDay == 6){
25 |
26 | // Start at 8am, end at 8pm
27 | hours.start.setHours(8);
28 | hours.end.setHours(20);
29 |
30 | // If it's Sunday
31 | } else {
32 |
33 | // Start at 9am, end at 6pm
34 | hours.start.setHours(9);
35 | hours.end.setHours(18);
36 | }
37 |
38 | function countDown(){
39 | var date = new Date(),
40 | countHours = ('0' + (hours.end.getHours() - date.getHours())).substr(-2),
41 | countMinutes = ('0' + (59 - date.getMinutes())).substr(-2),
42 | countSeconds = ('0' + (59 - date.getSeconds())).substr(-2);
43 |
44 | return { h : countHours, m : countMinutes, s : countSeconds };
45 | }
46 |
47 |
48 | /** @jsx h */
49 |
50 | import {h, patch, diff} from '../../../';
51 |
52 | var initialDom = (
53 |
56 | );
57 |
58 | document.getElementById('application')
59 | .appendChild(initialDom.render());
60 |
61 | setInterval(function() {
62 | var cd = countDown();
63 | var countDownDom = (
64 |
65 |
Day Countdown
66 |
67 | {cd.h} :
68 | {cd.m} :
69 | {cd.s}
70 |
71 |
72 | );
73 |
74 | var diffs = diff(initialDom, countDownDom);
75 | patch(initialDom, diffs);
76 |
77 | }, 1000);
--------------------------------------------------------------------------------
/lib/bvd/examples/countdown/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "author": "Svetlana Linuxenko (http://www.linuxenko.pro)",
4 | "license": "MIT",
5 | "devDependencies": {
6 | "babel-core": "^6.18.2",
7 | "babel-loader": "^6.2.8",
8 | "babel-preset-es2015": "^6.18.0",
9 | "babel-preset-react": "^6.16.0",
10 | "file-loader": "^0.9.0",
11 | "webpack": "^1.13.3"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/bvd/examples/countdown/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* global __dirname */
2 |
3 | var path = require('path');
4 |
5 | var webpack = require('webpack');
6 | var dir_js = path.resolve(__dirname, 'app');
7 | var dir_build = path.resolve(__dirname, 'build');
8 |
9 | module.exports = {
10 | entry: {
11 | app : path.resolve(dir_js, 'index.js')
12 | },
13 | devtool: 'source-map',
14 | output: {
15 | path: dir_build,
16 | filename: 'bundle.js'
17 | },
18 | resolveLoader: {
19 | fallback: [path.join(__dirname, 'node_modules')]
20 | },
21 | resolve: {
22 | modulesDirectories: ['node_modules', '../../../lib', dir_js],
23 | fallback: [path.join(__dirname, 'node_modules')]
24 | },
25 | devServer: {
26 | contentBase: dir_build,
27 | },
28 | module: {
29 | loaders: [
30 | {
31 | loader: 'babel-loader',
32 | test: /\.js$/,
33 | exclude: /node_modules/,
34 | presets : ['es2015', 'react']
35 | },
36 | {
37 | test : /\.html$/,
38 | loader : 'file?name=[name].html'
39 | },
40 | {
41 | test : /\.cur$/,
42 | loader : 'file?name=[name].cur'
43 | }
44 | ]
45 | },
46 | plugins: [
47 | new webpack.NoErrorsPlugin()
48 |
49 | ],
50 | stats: {
51 | colors: true
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/lib/bvd/examples/misc/inspect.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linuxenko/cakejs2/3d2323a3f7b3737932b95bcb031df1bc966bd1f8/lib/bvd/examples/misc/inspect.gif
--------------------------------------------------------------------------------
/lib/bvd/index.js:
--------------------------------------------------------------------------------
1 | exports.h = require('./lib/h').h;
2 | exports.diff = require('./lib/diff').diff;
3 | exports.patch = require('./lib/patch').patch;
4 |
5 | exports.PATCH_CREATE = require('./lib/diff').PATCH_CREATE;
6 | exports.PATCH_REMOVE = require('./lib/diff').PATCH_REMOVE;
7 | exports.PATCH_REPLACE = require('./lib/diff').PATCH_REPLACE;
8 | exports.PATCH_REORDER = require('./lib/diff').PATCH_REORDER;
9 | exports.PATCH_PROPS = require('./lib/diff').PATCH_PROPS;
10 |
--------------------------------------------------------------------------------
/lib/bvd/lib/diff.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff
3 | */
4 |
5 | var PATCH_CREATE = 0;
6 | var PATCH_REMOVE = 1;
7 | var PATCH_REPLACE = 2;
8 | var PATCH_REORDER = 3;
9 | var PATCH_PROPS = 4;
10 |
11 | /**
12 | * Diff two virtual dom trees
13 | *
14 | * @name diff
15 | * @function
16 | * @access public
17 | * @param {Object} oldNode virtual tree to compare
18 | * @param {Object} newNode virtual tree to compare with
19 | */
20 | var diff = function (oldNode, newNode) {
21 | if (typeof oldNode === 'undefined' || typeof newNode === 'undefined') {
22 | throw new Error('cannot diff undefined nodes');
23 | }
24 |
25 | if (!_isNodeSame(oldNode, newNode)) {
26 | throw new Error('unable create diff replace for root node');
27 | }
28 |
29 | return _diffTree(oldNode, newNode, []);
30 | };
31 |
32 | /**
33 | * Tree walker function
34 | *
35 | * @name _diffTree
36 | * @function
37 | * @access private
38 | * @param {} a
39 | * @param {} b
40 | * @param {} patches
41 | */
42 | var _diffTree = function (a, b, patches) {
43 | _diffProps(a, b, patches);
44 |
45 | if (b.tag === 'text') {
46 | if (b.children !== a.children) {
47 | patches.push({ t: PATCH_REPLACE, node: a, with: b });
48 | }
49 | return;
50 | }
51 |
52 | if (Array.isArray(b.children)) {
53 | _diffChild(a.children, b.children, a, patches);
54 | } else if (Array.isArray(a.children)) {
55 | for (var i = 0; i < a.children.length; i++) {
56 | patches.push({ t: PATCH_REMOVE, from: i, node: _nodeId(a), item: _nodeId(a.children[i]) });
57 | }
58 | }
59 |
60 | return patches;
61 | };
62 |
63 | /**
64 | * Tree children diffings
65 | *
66 | * @name _diffChild
67 | * @function
68 | * @access private
69 | * @param {} a
70 | * @param {} b
71 | * @param {} pn
72 | * @param {} patches
73 | */
74 | var _diffChild = function (a, b, pn, patches) {
75 | var reorderMap = [];
76 | var i;
77 | var j;
78 | var found;
79 |
80 | for (i = 0; i < b.length; i++) {
81 | found = false;
82 |
83 | if (!a) {
84 | if (!pn.children) {
85 | pn.children = [];
86 | }
87 |
88 | if (b[i].tag === 'text') {
89 | patches.push({ t: PATCH_CREATE, to: i, node: _nodeId(pn), item: _nodeId(b[i]) });
90 | } else {
91 | patches.push({ t: PATCH_CREATE, to: i, node: _nodeId(pn), item: _nodeId(b[i].clone()) });
92 | }
93 | continue;
94 | }
95 |
96 | for (j = 0; j < a.length; j++) {
97 | if (_isNodeSame(a[j], b[i]) && reorderMap.indexOf(a[j]) === -1) {
98 | if (j !== i) {
99 | patches.push({ t: PATCH_REORDER, from: j, to: i, node: _nodeId(pn), item: _nodeId(a[j]) });
100 | }
101 | reorderMap.push(a[j]);
102 |
103 | _diffTree(a[j], b[i], patches);
104 | found = true;
105 | break;
106 | }
107 | }
108 |
109 | if (found === false) {
110 | reorderMap.push(null);
111 | patches.push({ t: PATCH_CREATE, to: i, node: _nodeId(pn), item: b[i].tag === 'text' ? _nodeId(b[i]) : _nodeId(b[i].clone()) });
112 | }
113 | }
114 |
115 | if (!a) return;
116 |
117 | for (i = 0; i < a.length; i++) {
118 | if (reorderMap.indexOf(a[i]) === -1) {
119 | patches.push({ t: PATCH_REMOVE, from: i, node: _nodeId(pn), item: _nodeId(a[i]) });
120 | }
121 | }
122 | };
123 |
124 | /**
125 | * Props diffings
126 | *
127 | * @name _diffProps
128 | * @function
129 | * @access private
130 | * @param {} a
131 | * @param {} b
132 | * @param {} patches
133 | * @param {} type
134 | */
135 | var _diffProps = function (a, b, patches) {
136 | if (!a || !b || !a.props && !b.props) {
137 | return;
138 | }
139 |
140 | var toChange = [];
141 | var toRemove = [];
142 | var battrs = Object.keys(b.props);
143 | var aattrs = Object.keys(a.props);
144 | var aattrsLen = aattrs.filter(function(attr) {
145 | return (attr !== 'ref' && !(attr.match(/^on/)));
146 | }).length;
147 | var i;
148 |
149 | if (a.el && a.el.attributes.length !== aattrsLen) {
150 | for (i = 0; i < a.el.attributes.length; i++) {
151 | var attr = a.el.attributes[i];
152 | var name = attr.name;
153 |
154 | if (name === 'class') {
155 | name = 'className';
156 | }
157 |
158 | if (!(name in aattrs)) {
159 | a.props[name] = attr.value;
160 | }
161 |
162 | if (attr.value !== a.props[name]) {
163 | a.props[name] = attr.value;
164 | }
165 | }
166 | aattrs = Object.keys(a.props);
167 | }
168 |
169 | for (i = 0; i < battrs.length || i < aattrs.length; i++) {
170 | if (i < battrs.length) {
171 | if (!(battrs[i] in a.props) || b.props[battrs[i]] !== a.props[battrs[i]]) {
172 | toChange.push({ name: battrs[i], value: b.props[battrs[i]] });
173 | }
174 | }
175 |
176 | if (i < aattrs.length) {
177 | if (!(aattrs[i] in b.props)) {
178 | toRemove.push({ name: aattrs[i] });
179 | }
180 | }
181 | }
182 |
183 | if (toRemove.length > 0) {
184 | patches.push({ t: PATCH_PROPS, remove: toRemove, node: _nodeId(a) });
185 | }
186 |
187 | if (toChange.length > 0) {
188 | patches.push({ t: PATCH_PROPS, change: toChange, node: _nodeId(a) });
189 | }
190 | };
191 |
192 | /**
193 | * Node identifier
194 | *
195 | * @name _nodeId
196 | * @function
197 | * @access private
198 | * @param {} node
199 | */
200 | var _nodeId = function (node) {
201 | return node;
202 | };
203 |
204 | /**
205 | * Nodes comparison
206 | *
207 | * @name _isNodeSame
208 | * @function
209 | * @access private
210 | * @param {} a
211 | * @param {} b
212 | */
213 | var _isNodeSame = function (a, b) {
214 | return a.tag === b.tag;
215 | };
216 |
217 | exports.PATCH_CREATE = PATCH_CREATE;
218 | exports.PATCH_REMOVE = PATCH_REMOVE;
219 | exports.PATCH_REPLACE = PATCH_REPLACE;
220 | exports.PATCH_REORDER = PATCH_REORDER;
221 | exports.PATCH_PROPS = PATCH_PROPS;
222 | exports.diff = diff;
223 |
--------------------------------------------------------------------------------
/lib/bvd/lib/h.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Element
3 | */
4 |
5 | /**
6 | * General tree
7 | *
8 | * /** @jsx h * /
9 | *
10 | * @name h
11 | * @function
12 | * @access public
13 | */
14 |
15 | var xmlns = "http://www.w3.org/2000/svg";
16 |
17 | var H = function (argv) {
18 | if (!(this instanceof H)) {
19 | if (!(argv instanceof H)) {
20 | if (typeof argv === 'function') {
21 | return argv.apply(argv, [].slice.call(arguments, 1, arguments.length));
22 | }
23 |
24 | if (typeof argv === 'object' && typeof argv.render === 'function') {
25 | if ('props' in argv) {
26 | for (var i in arguments[1]) {
27 | argv.props[i] = arguments[1][i];
28 | }
29 | } else {
30 | argv.props = arguments[1] || [];
31 | }
32 | argv.props.children = [].slice.call(arguments, 2, arguments.length);
33 | return argv.render(argv.props);
34 | }
35 | }
36 | return new H(arguments);
37 | }
38 |
39 | if (argv[0] instanceof H) {
40 | return argv[0];
41 | }
42 |
43 | this.tag = argv[0].toLowerCase();
44 | this.props = argv[1] || {};
45 |
46 | if (this.tag === 'text') {
47 | this.tag = 'svgtext';
48 | }
49 |
50 | if (argv[2] === null || argv[2] === undefined) {
51 | return;
52 | }
53 |
54 | if (argv.length > 2) {
55 | if (typeof argv[2] !== 'object' && argv.length === 3) {
56 | this.children = [_createTextNode(argv[2])];
57 | } else if (Array.isArray(argv[2])) {
58 | this.children = argv[2];
59 | } else {
60 | this.children = [].concat.apply([], [].slice.call(argv, 2, argv.length))
61 | .filter(function (n) {
62 | return n !== null && n !== undefined && n !== false;
63 | })
64 | .map(function (n) {
65 | if (!(n instanceof H)) {
66 | return _createTextNode(n);
67 | } else {
68 | return n;
69 | }
70 | });
71 | }
72 | }
73 | };
74 |
75 | /**
76 | * Tree renderer
77 | *
78 | * @name render
79 | * @function
80 | * @access public
81 | * @param {Boolean} fasle - do not save DOM into tree
82 | */
83 | H.prototype.render = function (node, parent) {
84 | node = node || this;
85 |
86 | node.el = createElement(node.tag ? node : this, parent);
87 |
88 | var children = node.children;
89 |
90 | if (typeof children === 'object') {
91 | for (var i = 0; i < children.length; i++) {
92 | node.el.appendChild(this.render(children[i], node.el));
93 | }
94 | }
95 |
96 | return node.el;
97 | };
98 |
99 | H.prototype.setAttribute = function(name, value) {
100 | try {
101 | if (this.el instanceof window.SVGElement) {
102 | this.el.setAttributeNS(null, name, value);
103 | } else {
104 | this.el.setAttribute(name, value);
105 | }
106 | } catch(e) {
107 | this.el.setAttribute(name, value);
108 | }
109 | };
110 |
111 | H.prototype.setProp = function (name, value) {
112 | if (typeof this.el !== 'undefined') {
113 | if (name === 'className') {
114 | this.setAttribute('class', value);
115 | } else if (name === 'style' && typeof value !== 'string') {
116 | this.setAttribute('style', _stylePropToString(value));
117 | } else if (name.match(/^on/)) {
118 | this.addEvent(name, value);
119 | } else if (name === 'ref') {
120 | if (typeof value === 'function') {
121 | value(this.el);
122 | }
123 | } else if (typeof value === 'boolean' || value === 'true') {
124 | this.setAttribute(name, value);
125 | this.el[name] = Boolean(value);
126 | } else {
127 | this.setAttribute(name, value);
128 | }
129 | }
130 |
131 | this.props[name] = value;
132 | };
133 |
134 | H.prototype.setProps = function (props) {
135 | var propNames = Object.keys(props);
136 |
137 | for (var i = 0; i < propNames.length; i++) {
138 | var prop = propNames[i];
139 | this.setProp(prop, props[prop]);
140 | }
141 | };
142 |
143 | H.prototype.rmProp = function (name) {
144 | if (typeof this.el !== 'undefined') {
145 | if (name === 'className') {
146 | this.el.removeAttribute('class');
147 | } else if (name.match(/^on/)) {
148 | this.removeEvent(name);
149 | } else if (name === 'ref') {
150 | /* Nothing to do */
151 | } else if (typeof value === 'boolean') {
152 | this.el.removeAttribute(name);
153 | delete this.el[name];
154 | } else {
155 | this.el.removeAttribute(name);
156 | }
157 | }
158 |
159 | delete this.props[name];
160 | };
161 |
162 | H.prototype.addEvent = function (name, listener) {
163 | name = name.slice(2).toLowerCase();
164 |
165 | this.listeners = this.listeners || {};
166 |
167 | if (name in this.listeners) {
168 | this.removeEvent(name);
169 | }
170 |
171 | this.listeners[name] = listener;
172 | this.el.addEventListener(name, listener);
173 | };
174 |
175 | H.prototype.removeEvent = function (name) {
176 | name = name.replace(/^on/, '').toLowerCase();
177 | if (name in this.listeners) {
178 | this.el.removeEventListener(name, this.listeners[name]);
179 | delete this.listeners[name];
180 | }
181 | };
182 |
183 | H.prototype.clone = function () {
184 | var node = {
185 | tag: String(this.tag),
186 | props: _cloneProps(this.props)
187 | };
188 |
189 | if (typeof this.children !== 'undefined') {
190 | node.children = this.tag === 'text'
191 | ? String(this.children)
192 | : this.children.map(function (child) {
193 | return child.tag === 'text' ? _createTextNode(child.children) : child.clone();
194 | });
195 | }
196 |
197 | return H(node.tag, node.props, node.children);
198 | };
199 |
200 | var _cloneProps = function (props, keepRefs) {
201 | if (typeof keepRefs === 'undefined') {
202 | keepRefs = true;
203 | }
204 |
205 | var attrs = Object.keys(props);
206 | var i;
207 | var name;
208 | var cloned = {};
209 |
210 | for (i = 0; i < attrs.length; i++) {
211 | name = attrs[i];
212 |
213 | if (typeof props[name] === 'string') {
214 | cloned[name] = String(props[name]);
215 | } else if (typeof props[name] === 'function' && keepRefs === true) {
216 | cloned[name] = props[name];
217 | } else if (typeof props[name] === 'boolean') {
218 | cloned[name] = Boolean(props[name]);
219 | } else if (typeof props[name] === 'object') {
220 | cloned[name] = _cloneProps(props[name]);
221 | }
222 | }
223 |
224 | return cloned;
225 | };
226 |
227 | var _stylePropToString = function (props) {
228 | var out = '';
229 | var attrs = Object.keys(props);
230 |
231 | for (var i = 0; i < attrs.length; i++) {
232 | out += attrs[i].replace(/([A-Z])/g, '-$1').toLowerCase();
233 | out += ':';
234 | out += props[attrs[i]];
235 | out += ';';
236 | }
237 |
238 | return out;
239 | };
240 |
241 | var _createTextNode = function (text) {
242 | return {
243 | tag: 'text',
244 | children: String(text)
245 | };
246 | };
247 |
248 | var createElement = function (node, parent) {
249 | // node.el = node.tag === 'text'
250 | // ? document.createTextNode(node.children)
251 | // : document.createElement(node.tag);
252 |
253 |
254 | switch (node.tag) {
255 | case 'text':
256 | node.el = document.createTextNode(node.children);
257 | break;
258 | case 'svgtext':
259 | node.el = document.createElementNS(xmlns, 'text');
260 | break;
261 | case 'lineargradient':
262 | node.el = document.createElementNS(xmlns, 'linearGradient');
263 | break;
264 | case 'radialgradient':
265 | node.el = document.createElementNS(xmlns, 'radialGradient');
266 | break;
267 | case 'fegaussianblur':
268 | node.el = document.createElementNS(xmlns, 'feGaussianBlur');
269 | break;
270 | case 'feoffset':
271 | node.el = document.createElementNS(xmlns, 'feOffset');
272 | break;
273 | case 'feblend':
274 | node.el = document.createElementNS(xmlns, 'feBlend');
275 | break;
276 | case 'svg':
277 | case 'g':
278 | case 'circle':
279 | case 'ellipse':
280 | case 'line':
281 | case 'path':
282 | case 'polygon':
283 | case 'polyline':
284 | case 'rect':
285 | case 'defs':
286 | case 'stop':
287 | case 'filter':
288 | node.el = document.createElementNS(xmlns, node.tag);
289 | break;
290 | default:
291 | node.el = document.createElement(node.tag);
292 | break;
293 | }
294 |
295 | if (typeof node.props !== 'undefined') {
296 | node.setProps(node.props);
297 | }
298 |
299 | if (typeof parent !== 'undefined') {
300 | parent.appendChild(node.el);
301 | }
302 |
303 | return node.el;
304 | };
305 |
306 | exports.h = H;
307 | exports.createElement = createElement;
308 |
--------------------------------------------------------------------------------
/lib/bvd/lib/patch.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Patch
3 | */
4 |
5 | var PATCH_CREATE = require('./diff').PATCH_CREATE;
6 | var PATCH_REMOVE = require('./diff').PATCH_REMOVE;
7 | var PATCH_REPLACE = require('./diff').PATCH_REPLACE;
8 | var PATCH_REORDER = require('./diff').PATCH_REORDER;
9 | var PATCH_PROPS = require('./diff').PATCH_PROPS;
10 |
11 | var createElement = require('./h').createElement;
12 |
13 | /**
14 | * Patch DOM and virtual tree
15 | *
16 | * @name patch
17 | * @function
18 | * @access public
19 | * @param {Object} tree Tree to patch
20 | * @param {Array} patches Array of patches
21 | */
22 | var patch = function(tree, patches) {
23 | var render = true;
24 |
25 | if (typeof tree.el === 'undefined') {
26 | render = false;
27 | }
28 |
29 | for (var i = 0; i < patches.length; i++) {
30 | var p = patches[i];
31 |
32 | switch(p.t) {
33 | case PATCH_REORDER:
34 | _patchReorder(p, render);
35 | break;
36 | case PATCH_CREATE:
37 | _patchCreate(p, render);
38 | break;
39 | case PATCH_REMOVE:
40 | _patchRemove(p, render);
41 | break;
42 | case PATCH_REPLACE:
43 | _patchReplace(p, render);
44 | break;
45 | case PATCH_PROPS:
46 | _patchProps(p, render);
47 | break;
48 | }
49 | }
50 | };
51 |
52 | /**
53 | * Replace existen node content
54 | *
55 | * @name patchReplace
56 | * @function
57 | * @access private
58 | */
59 | var _patchReplace = function(p, render) {
60 | p.node.children = String(p.with.children);
61 |
62 | if (render === true) {
63 | p.node.el.nodeValue = String(p.with.children);
64 | }
65 | };
66 |
67 | /**
68 | * Reorder existen node
69 | *
70 | * @name patchReorder
71 | * @function
72 | * @access private
73 | */
74 | var _patchReorder = function(p, render) {
75 | if (render === true) {
76 | p.node.el.insertBefore(p.item.el, p.node.el.childNodes[p.to]);
77 | }
78 |
79 | p.node.children.splice(p.to, 0,
80 | p.node.children.splice(p.node.children.indexOf(p.item), 1)[0]);
81 | };
82 |
83 | /**
84 | * Create new tree node
85 | *
86 | * @name patchCreate
87 | * @function
88 | * @access private
89 | */
90 | var _patchCreate = function(p, render) {
91 | var element;
92 |
93 | if (render === true) {
94 | element = p.item.tag === 'text' ?
95 | createElement(p.item) : p.item.render();
96 | }
97 |
98 | if (p.node.children.length - 1 < p.to) {
99 | p.node.children.push(p.item);
100 |
101 | if (render === true) {
102 | p.node.el.appendChild(element);
103 | }
104 | } else {
105 | p.node.children.splice(p.to, 0, p.item);
106 |
107 | if (render === true) {
108 | p.node.el.insertBefore(element, p.node.el.childNodes[p.to]);
109 | }
110 | }
111 | };
112 |
113 | /**
114 | * Remove tree node
115 | *
116 | * @name patchRemove
117 | * @function
118 | * @access private
119 | */
120 | var _patchRemove = function(p, render) {
121 | if (render === true) {
122 | p.node.el.removeChild(p.item.el);
123 | }
124 |
125 | for (var i = 0; i < p.node.children.length; i++) {
126 | if (p.node.children[i] === p.item) {
127 | p.node.children.splice(i, 1);
128 | }
129 | }
130 | };
131 |
132 | /**
133 | * Replace props
134 | *
135 | * @name _patchProps
136 | * @function
137 | * @access private
138 | */
139 | var _patchProps = function(p) {
140 | var i;
141 |
142 | if ('remove' in p) {
143 | for (i = 0; i < p.remove.length; i++) {
144 | p.node.rmProp(p.remove[i].name);
145 | }
146 | return;
147 | }
148 |
149 | if ('change' in p) {
150 | for (i = 0; i < p.change.length; i++) {
151 | p.node.setProp(p.change[i].name, p.change[i].value);
152 | }
153 | return;
154 | }
155 | };
156 |
157 | exports.patch = patch;
158 |
--------------------------------------------------------------------------------
/lib/bvd/tests/fixtures/basic.js:
--------------------------------------------------------------------------------
1 | var h = require('../../').h;
2 |
3 | exports.tree1 = h(
4 | 'div',
5 | { id: 'application', className: 'main-app' },
6 | h(
7 | 'em',
8 | { className: 'em' },
9 | 'Item 1'
10 | ),
11 | h(
12 | 'div',
13 | null,
14 | 'ffirett'
15 | ),
16 | h(
17 | 'div',
18 | { className: '2to-remove' },
19 | '2removable div'
20 | ),
21 | h(
22 | 'span',
23 | { className: 'menu-item' },
24 | 'Item 1'
25 | ),
26 | h(
27 | 'ul',
28 | null,
29 | h(
30 | 'li',
31 | null,
32 | h(
33 | 'span',
34 | { className: 'menu-item' },
35 | 'Item 1'
36 | ),
37 | h(
38 | 'p',
39 | { className: 'redundant-item' },
40 | 'new text Item 2'
41 | )
42 | ),
43 | h(
44 | 'li',
45 | null,
46 | h(
47 | 'div',
48 | { className: 'changed-menu-item' },
49 | 'new text Item 2'
50 | ),
51 | h(
52 | 'span',
53 | { className: 'menu-item' },
54 | 'Item 2'
55 | )
56 | )
57 | )
58 | );
59 |
60 | exports.tree2 = h(
61 | 'div',
62 | { id: 'app', className: 'changed-class' },
63 | h(
64 | 'span',
65 | { className: 'menu-item' },
66 | 'Item 1'
67 | ),
68 | h(
69 | 'strong',
70 | null,
71 | 'sttrong'
72 | ),
73 | h(
74 | 'ul',
75 | { className : 'test test' },
76 | h(
77 | 'li',
78 | null,
79 | h(
80 | 'span',
81 | { className : 'llll', id : 'kkkkk' },
82 | 'Item changed text 1'
83 | )
84 | ),
85 | h(
86 | 'li',
87 | null,
88 | h(
89 | 'span',
90 | { className: 'changed-menu-item' },
91 | 'new text Item 2'
92 | ),
93 | h(
94 | 'div',
95 | { className: 'changed-menu-item' },
96 | 'new text Item 2'
97 | )
98 | ),
99 | h(
100 | 'li',
101 | null,
102 | h(
103 | 'span',
104 | { className: 'menu-item' },
105 | 'Item 3'
106 | )
107 | )
108 | ),
109 | h(
110 | 'div',
111 | { className: 'to-remove' },
112 | 'removable div'
113 | )
114 | );
115 |
--------------------------------------------------------------------------------
/lib/bvd/tests/fixtures/nested.js:
--------------------------------------------------------------------------------
1 | var h = require('../../').h;
2 |
3 | exports.a = h(
4 | 'div',
5 | { id: 'app', className: 'changed-class' },
6 | h(
7 | 'ul',
8 | null,
9 | h(
10 | 'li',
11 | null,
12 | h(
13 | 'span',
14 | { className: 'menu-item' },
15 | 'Item 1'
16 | )
17 | ),
18 | h(
19 | 'li',
20 | null,
21 | h(
22 | 'span',
23 | { className: 'changed-menu-item' },
24 | 'Item 2'
25 | )
26 | ),
27 | h(
28 | 'li',
29 | null,
30 | h(
31 | 'span',
32 | { className: 'menu-item' },
33 | 'Item 3'
34 | )
35 | )
36 | )
37 | );
38 |
39 | exports.b = h(
40 | 'div',
41 | { id: 'app', className: 'changed-class' },
42 | h(
43 | 'ul',
44 | null,
45 | h(
46 | 'li',
47 | null,
48 | h(
49 | 'span',
50 | { className: 'menu-item' },
51 | 'Item 1'
52 | )
53 | ),
54 | h(
55 | 'li',
56 | null,
57 | h(
58 | 'span',
59 | { className: 'changed-menu-item' },
60 | 'Item 2'
61 | )
62 | ),
63 | h(
64 | 'li',
65 | null,
66 | h(
67 | 'span',
68 | { className: 'menu-item' },
69 | 'Item 3 added text'
70 | )
71 | )
72 | )
73 | );
74 |
75 |
76 | exports.a1 = h(
77 | 'div',
78 | { id: 'app', className: 'changed-class' },
79 | h(
80 | 'ul',
81 | null,
82 | h(
83 | 'li',
84 | null,
85 | h(
86 | 'span',
87 | { className: 'menu-item' },
88 | 'Item 1'
89 | )
90 | ),
91 | h(
92 | 'li',
93 | null,
94 | h(
95 | 'span',
96 | { className: 'changed-menu-item' },
97 | 'Item 2'
98 | )
99 | ),
100 | h(
101 | 'li',
102 | null,
103 | h(
104 | 'span',
105 | { className: 'menu-item' },
106 | 'Item 3'
107 | )
108 | )
109 | )
110 | );
111 |
112 | exports.b1 = h(
113 | 'div',
114 | { id: 'app', className: 'changed-class' },
115 | h(
116 | 'ul',
117 | null,
118 | h(
119 | 'li',
120 | null,
121 | h(
122 | 'span',
123 | { className: 'menu-item' },
124 | 'Item 1'
125 | )
126 | ),
127 | h(
128 | 'li',
129 | null,
130 | h(
131 | 'span',
132 | { className: 'changed-menu-item' },
133 | 'Item 2'
134 | )
135 | ),
136 | h(
137 | 'li',
138 | null,
139 | h(
140 | 'span',
141 | { className: 'menu-item' },
142 | 'Item 3 added text'
143 | )
144 | )
145 | )
146 | );
147 |
148 |
--------------------------------------------------------------------------------
/lib/bvd/tests/fixtures/simple.js:
--------------------------------------------------------------------------------
1 | var h = require('../../').h;
2 |
3 | exports.a = h(
4 | 'div',
5 | { id: 'application', className: 'test-class test-class2' },
6 | h(
7 | 'div',
8 | null,
9 | 'text'
10 | )
11 | );
12 |
13 | exports.a1 = h(
14 | 'div',
15 | { id: 'application', className: 'test-class test-class2' },
16 | 'text'
17 | );
18 |
19 | exports.a2 = h(
20 | 'div',
21 | { id: 'application', className: 'no-class' },
22 | h(
23 | 'div',
24 | null,
25 | 'changed'
26 | )
27 | );
28 |
29 | exports.b = h(
30 | 'div',
31 | { id: 'application', className: 'test-class test-class2' },
32 | 'text'
33 | );
34 |
35 | exports.c = h(
36 | 'div',
37 | { id: 'application', className: 'test-class test-class2' },
38 | h(
39 | 'div',
40 | null,
41 | 'changed'
42 | )
43 | );
44 |
45 | exports.d = h(
46 | 'div',
47 | null,
48 | h(
49 | 'span',
50 | { id : 'text-node', className : 'text-node' },
51 | 'node-text'
52 | )
53 | );
54 |
55 | exports.e = h(
56 | 'div',
57 | { 'data-name' : 'to remove', className : 'test-class' },
58 | h(
59 | 'div',
60 | null,
61 | 'changed'
62 | )
63 | );
64 |
65 | exports.f = h(
66 | 'div',
67 | { id: 'app', className: 'changed-class' },
68 | h(
69 | 'span',
70 | { className: 'changed-menu-item' },
71 | 'Reorder me'
72 | ),
73 | h(
74 | 'ul',
75 | null,
76 | h(
77 | 'li',
78 | null,
79 | h(
80 | 'span',
81 | { className: 'menu-item' },
82 | 'Item 1'
83 | )
84 | ),
85 | h(
86 | 'li',
87 | null,
88 | h(
89 | 'span',
90 | { className: 'changed-menu-item' },
91 | 'Item 2'
92 | )
93 | ),
94 | h(
95 | 'li',
96 | null,
97 | h(
98 | 'span',
99 | { className: 'menu-item' },
100 | 'Item 3'
101 | )
102 | )
103 | )
104 | );
105 |
106 | exports.f1 = h(
107 | 'div',
108 | { id: 'app', className: 'changed-class' },
109 | h(
110 | 'ul',
111 | null,
112 | h(
113 | 'li',
114 | null,
115 | h(
116 | 'span',
117 | { className: 'menu-item' },
118 | 'Item 1'
119 | )
120 | ),
121 | h(
122 | 'li',
123 | null,
124 | h(
125 | 'span',
126 | { className: 'changed-menu-item' },
127 | 'Item 2'
128 | )
129 | ),
130 | h(
131 | 'li',
132 | null,
133 | h(
134 | 'span',
135 | { className: 'menu-item' },
136 | 'Item 3'
137 | )
138 | )
139 | ),
140 | h(
141 | 'span',
142 | { className: 'changed-menu-item' },
143 | 'Reorder me'
144 | )
145 | );
146 |
--------------------------------------------------------------------------------
/lib/bvd/tests/fixtures/svg.js:
--------------------------------------------------------------------------------
1 | var h = require('../../').h;
2 |
3 | exports.a =
4 | h('svg', { id: 'app'},
5 | h(
6 | 'g',
7 | null,
8 | h('rect', { x: '40', y: '80', fill: 'blue' }),
9 | h('rect', { x: '40', y: '80', fill: 'red' }),
10 | h('rect', { x: '40', y: '80', fill: 'green' })
11 | )
12 | );
13 |
14 | exports.a =
15 | h('svg', { id: 'app'},
16 | h(
17 | 'g',
18 | null,
19 | h('rect', { x: '40', y: '80', fill: 'blue' }),
20 | h('text', { x: '40', y: '80' }, 'text node')
21 | )
22 | );
23 |
24 |
25 |
--------------------------------------------------------------------------------
/lib/bvd/tests/fixtures/textnodes.js:
--------------------------------------------------------------------------------
1 | var h = require('../../').h;
2 | exports.a = h(
3 | 'div',
4 | null,
5 | h(
6 | 'div',
7 | null,
8 | h(
9 | 'h3',
10 | null,
11 | 'Counter'
12 | )
13 | )
14 | );
15 |
16 | exports.b = h(
17 | 'div',
18 | null,
19 | h(
20 | 'div',
21 | null,
22 | h(
23 | 'h3',
24 | null,
25 | 'Day Countdown'
26 | )
27 | ),
28 | h(
29 | 'div',
30 | { className: 'clock' },
31 | h(
32 | 'strong',
33 | null,
34 | '1'
35 | ),
36 | ' :',
37 | h(
38 | 'strong',
39 | null,
40 | '2'
41 | ),
42 | ' :',
43 | h(
44 | 'strong',
45 | null,
46 | '3'
47 | )
48 | )
49 | );
50 |
51 |
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/attributes.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 |
3 | var jsdom = require('mocha-jsdom');
4 |
5 | var h = require('../../').h;
6 | var diff = require('../../').diff;
7 | var patch = require('../../').patch;
8 |
9 | describe('Test attributes', function() {
10 | jsdom();
11 |
12 | it('should setup style attribute', function() {
13 | var a = h('div', { style: { backgroundColor: '#fff', left: '20px' } },
14 | h('div', { style: 'right:20px;' })
15 | );
16 |
17 | var dom = a.render();
18 |
19 | expect(a.props['style']).to.be.exists;
20 | expect(a.props['style']).to.be.deep.equal({ backgroundColor: '#fff', left: '20px' });
21 | expect(dom.getAttribute('style')).to.be.a('string');
22 | expect(dom.getAttribute('style')).to.be.equal('background-color:#fff;left:20px;');
23 | expect(a.children[0].props['style']).to.be.equal('right:20px;');
24 | });
25 |
26 | it('should diff and patch style attribute', function() {
27 | var a = h('div', { style: { left: '21px' }}, '');
28 | var b = h('div', { style: { backgroundColor: '#fff', left: '20px' } },
29 | h('div', { style: 'right:20px;' })
30 | );
31 |
32 | var dom = a.render();
33 | patch(a, diff(a, b));
34 |
35 | expect(a.props['style']).to.be.exists;
36 | expect(a.props['style']).to.be.deep.equal({ backgroundColor: '#fff', left: '20px' });
37 | expect(dom.getAttribute('style')).to.be.a('string');
38 | expect(dom.getAttribute('style')).to.be.equal('background-color:#fff;left:20px;');
39 | expect(a.children[0].props['style']).to.be.equal('right:20px;');
40 | });
41 |
42 | it('should patch attrs modified via dom node', function () {
43 | var a = h('div', null, '');
44 | var b = h('div', null, '');
45 |
46 | var dom = a.render();
47 |
48 | dom.classList.add('active');
49 | patch(a, diff(a, b));
50 |
51 | expect(dom.attributes.length).to.be.equal(0);
52 | expect(dom.props).to.be.an('undefined');
53 | });
54 |
55 | it('should patch attrs created via dom node', function () {
56 | var a = h('div', { onClick: function () {}}, '');
57 | var b = h('div', { ref: function () {}, onClick: function () {}} , '');
58 |
59 | var dom = a.render();
60 |
61 | dom.classList.add('active');
62 |
63 | expect(dom.attributes.length).to.be.equal(1);
64 | patch(a, diff(a, b));
65 |
66 | expect(dom.attributes.length).to.be.equal(0);
67 | expect(Object.keys(a.props).length).to.be.equal(2);
68 | });
69 |
70 | it('should patch attrs created via dom node #2', function () {
71 | var a = h('div', { onClick: function () {}}, '');
72 | var b = h('div', { onClick: function () {}, tabindex: 0} , '');
73 |
74 | var dom = a.render();
75 |
76 | dom.classList.add('active');
77 |
78 | expect(dom.attributes.length).to.be.equal(1);
79 | patch(a, diff(a, b));
80 |
81 | expect(dom.attributes.length).to.be.equal(1);
82 | expect(dom.attributes[0].name).to.be.equal('tabindex');
83 | expect(Object.keys(a.props).length).to.be.equal(2);
84 | });
85 |
86 | it('should patch listeners #1', function () {
87 | var listener1 = function() { return 1; };
88 | var a = h('div', { onClick: listener1}, '');
89 | var b = h('div', { onClick: function () { return 2; }, tabindex: 0} , '');
90 |
91 | a.render();
92 |
93 | expect(a.props.onClick()).to.be.equal(1);
94 | patch(a, diff(a, b));
95 | expect(a.props.onClick()).to.be.equal(2);
96 | });
97 |
98 | it('should patch replace listeners #2', function () {
99 | var fail = false;
100 | var listener1 = function() { fail = true; };
101 | var a = h('div', { onClick: listener1}, '');
102 | var b = h('div', { tabindex: 0} , '');
103 |
104 | var dom = a.render();
105 | patch(a, diff(a, b));
106 |
107 | dom.click();
108 | expect(fail).to.be.false;
109 | });
110 | });
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/diff.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 |
3 | var jsdom = require('mocha-jsdom');
4 |
5 | var diff = require('../../').diff;
6 | var h = require('../../').h;
7 |
8 | var a = require('../fixtures/simple').a;
9 | var b = require('../fixtures/simple').b;
10 | var c = require('../fixtures/simple').c;
11 | var e = require('../fixtures/simple').e;
12 |
13 | var na = require('../fixtures/nested').a;
14 | var nb = require('../fixtures/nested').b;
15 |
16 | var PATCH_REPLACE = require('../../').PATCH_REPLACE;
17 |
18 | describe('Test diff()', function() {
19 | jsdom();
20 |
21 | it('should create trees', function() {
22 | expect(a).to.be.an('object');
23 | expect(b).to.be.an('object');
24 | });
25 |
26 | it('should deal with empty arguments', function() {
27 | expect(function() { diff(a); }).to.throw();
28 | expect(function() { diff(undefined, a); }).to.throw();
29 | });
30 |
31 | it('should diff simple tree', function() {
32 | var diffs;
33 |
34 | a.render();
35 |
36 | expect(function() {
37 | diffs = diff(a, c);
38 | }).not.throw();
39 |
40 | expect(diffs).to.be.an('array');
41 | expect(diffs.length).to.be.equal(1);
42 | expect(diffs[0].t).to.be.equal(PATCH_REPLACE);
43 | });
44 |
45 | it('should diff nonexisten node', function() {
46 | var dom = b.render();
47 | var diffs;
48 |
49 | expect(dom.children).not.be.exists;
50 |
51 | diffs = diff(b, a);
52 |
53 | expect(diffs.length).to.be.equal(2);
54 | });
55 |
56 | it('should diff props', function() {
57 | e.render();
58 | var diffs = diff(e, c);
59 |
60 |
61 | expect(diffs[0].t).to.be.equal(4);
62 | expect(diffs[1].t).to.be.equal(4);
63 | expect(diffs[1].change.length).to.be.equal(2);
64 | });
65 |
66 | it('should diff complex tree', function() {
67 | na.render();
68 | expect(na.children[0].children.length).to.be.equal(3);
69 | var diffs = diff(na, nb);
70 |
71 | expect(diffs).to.be.an('array');
72 | expect(diffs.length).to.be.equal(1);
73 | expect(diffs[0].t).to.be.equal(2);
74 | });
75 |
76 | it('should not diff root node', function() {
77 | var src = h('span', null, 'hello world');
78 | var dst = h('strong', null, 'bye bye');
79 |
80 | src.render();
81 | expect(function() { diff(src, dst); }).to.throw();
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/events.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var jsdom = require('mocha-jsdom');
3 |
4 | var h = require('../../').h;
5 | var diff = require('../../').diff;
6 | var patch = require('../../').patch;
7 |
8 | describe('Test events', function() {
9 | jsdom();
10 |
11 | it('should handle very simple click event', function(done) {
12 | var tree = h('div', { onClick : function() { done(); } }, '');
13 |
14 | expect(tree.props.onClick).to.be.a('function');
15 | tree.render();
16 | tree.el.click();
17 | });
18 |
19 | it('should repatch event handlers', function(done) {
20 | var counter = 0;
21 | var a = h('div', { onClick : function() { counter++; } }, '');
22 | var b = h('div', { onClick : function() {
23 | expect(counter).to.be.equal(1);
24 | done();
25 | } }, '');
26 |
27 | a.render();
28 | a.el.click();
29 |
30 | expect(counter).to.be.equal(1);
31 | expect(Object.keys(a.listeners).length).to.be.equal(1);
32 |
33 | patch(a, diff(a, b));
34 | expect(Object.keys(a.listeners).length).to.be.equal(1);
35 | a.el.click();
36 | });
37 |
38 | it('should work with refs', function() {
39 | var elRef;
40 | var a = h('div', { ref : function(ref) { elRef = ref; } }, '');
41 |
42 | expect(elRef).to.be.an('undefined');
43 | a.render();
44 | expect(elRef).to.be.equal(a.el);
45 | });
46 |
47 | it('should replace refs by patch', function() {
48 | var elRef1;
49 | var elRef2;
50 | var a = h('div', { ref : function(ref) { elRef1 = ref; } }, '');
51 | var b = h('div', { ref : function(ref) { elRef2 = ref; } }, '');
52 |
53 | a.render();
54 | var diffs = diff(a, b);
55 | patch(a, diffs);
56 |
57 | expect(elRef1).to.be.equal(a.el);
58 | expect(elRef2).to.be.equal(a.el);
59 | });
60 | });
61 |
62 |
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/h.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var sinon = require('sinon');
3 | var jsdom = require('mocha-jsdom');
4 |
5 | var a = require('../fixtures/simple').a;
6 | var d = require('../fixtures/simple').d;
7 |
8 | var h = require('../../').h;
9 |
10 | describe('Test h()', function() {
11 | jsdom();
12 | it('should create object tree', function() {
13 | expect(a).to.be.exists;
14 | expect(a).to.be.an('object');
15 | expect(a.tag).to.be.equal('div');
16 | expect(a.children).to.be.an('array');
17 | });
18 |
19 | it('should create object with props', function() {
20 | expect(a.props.className).to.be.a('string');
21 | expect(a.props.className).to.be.equal('test-class test-class2');
22 | });
23 |
24 | it('should create object with childs', function() {
25 | expect(a.children[0].props).to.be.an('object');
26 | expect(a.children[0].tag).to.be.a('string');
27 | expect(a.children[0].tag).to.be.equal('div');
28 | expect(a.children[0].children[0].tag).to.be.a('string');
29 | expect(a.children[0].children[0].tag).to.be.equal('text');
30 | expect(a.children[0].children[0].children).to.be.a('string');
31 | });
32 |
33 | it('should create childrem props', function() {
34 | expect(d.children[0].tag).to.be.equal('span');
35 | expect(d.children[0].children[0].children).to.be.equal('node-text');
36 | expect(d.children[0].props.className).to.be.equal('text-node');
37 | expect(d.children[0].props.id).to.be.equal('text-node');
38 | });
39 |
40 | it('should create more compex trees', function() {
41 | var nested;
42 |
43 | expect(function() {
44 | nested = require('../fixtures/nested').a;
45 | }).not.to.throw();
46 |
47 | expect(nested.tag).to.be.equal('div');
48 | expect(nested.children).to.be.an('array');
49 | });
50 |
51 | it('should create empty text nodes', function() {
52 | var empty = h('div', null, '');
53 |
54 | expect(empty.tag).to.be.equal('div');
55 | expect(empty.children[0].tag).to.be.equal('text');
56 | expect(empty.children[0].children).to.be.equal('');
57 | expect(empty.children[0].children).to.be.a('string');
58 | });
59 |
60 | it('should create nested node text', function() {
61 | var node;
62 |
63 | expect(function() {
64 | node = h('div', null, h('strong', null, ''), 'L', h('strong', null, 'test'));
65 | }).not.throw();
66 |
67 | expect(node.children.length).to.be.equal(3);
68 | expect(node.children[1].tag).to.be.equal('text');
69 | expect(node.children[1].children).to.be.equal('L');
70 | });
71 |
72 | it('should create nested with text first tree', function() {
73 | var node;
74 |
75 | expect(function() {
76 | node = h('div', null, '', h('strong', null, 'rrr'), h('strong', null, 'test'));
77 | }).not.throw();
78 |
79 | expect(node.children.length).to.be.equal(3);
80 | expect(node.children[0].tag).to.be.equal('text');
81 | expect(node.children[0].children).to.be.equal('');
82 | });
83 |
84 | it('should handle react like nesting', function() {
85 | var Test = h('div', null, 'text');
86 | var Result = h('div', null, h(Test, null));
87 |
88 | expect(Result.children[0].tag).to.be.equal('div');
89 | expect(Result.children[0].children[0].children).to.be.equal('text');
90 | });
91 |
92 | it('should render h with falsy childs', function() {
93 | expect(function() {
94 | h('div', null, null).render();
95 | h('div', null, 0).render();
96 | h('div', null, '').render();
97 | h('div', null, undefined).render();
98 | }).not.throw();
99 |
100 | var p = h('div', null, null);
101 | expect(p.children).to.be.an('undefined');
102 | p = h('div', null, undefined);
103 | expect(p.children).to.be.an('undefined');
104 | p = h('div', null, '');
105 | expect(p.children[0].children).to.be.equal('');
106 | p = h('div', null, 0);
107 | expect(p.children[0].children).to.be.equal('0');
108 | });
109 |
110 | it('should render h with nested mixed types', function() {
111 | var p = h('button', null, 'Clicked ', 1, null);
112 | expect(p.children.length).to.be.equal(2);
113 | expect(function() { p.render(); }).not.throw();
114 | });
115 |
116 | it('should create boolean props', function() {
117 | var p = h('div', null, h('input', { type: 'checkbox', checked: 'true' })).render();
118 | expect(p.children[0].getAttribute('checked')).to.be.equal('true');
119 | var checkbox = p.childNodes[0];
120 | expect(checkbox.checked).to.be.true;
121 | });
122 |
123 | it('should create from arrays of childs', function() {
124 | var p = h('div', null ,
125 | h('span', null,'child1'),
126 | 'child2',
127 | h('span', null, 'child3'),
128 | [ h('div', null, '1'), h('div', null, '2') ]
129 | );
130 |
131 | expect(p.children[3].tag).to.be.equal('div');
132 | });
133 |
134 | it('should handle nesting functions', function() {
135 | var rendered = sinon.spy();
136 | var Nested = {
137 | render: function() {
138 | rendered();
139 | return h('div', null, this.props.name, this.props.children);
140 | }
141 | };
142 |
143 | var cl = h('div', null, h(Nested, {name: 'hello'}, 'world'));
144 |
145 | expect(rendered.calledOnce).to.be.true;
146 | expect(cl.children[0].children.length).to.be.equal(2);
147 | expect(cl.children[0].children[0].children).to.be.equal('hello');
148 | expect(cl.children[0].children[1].children).to.be.equal('world');
149 | });
150 |
151 | it('should only update props', function() {
152 | var Nested = {
153 | props: { ownProp: 'keepme' },
154 | render: function() {
155 | return h('div', null, this.props.name, this.props.children, this.props.ownProp);
156 | }
157 | };
158 |
159 | var cl = h('div', null, h(Nested, {name: 'hello'}, 'world'));
160 |
161 | expect(cl.children[0].children.length).to.be.equal(3);
162 | expect(cl.children[0].children[0].children).to.be.equal('hello');
163 | expect(cl.children[0].children[1].children).to.be.equal('world');
164 | expect(cl.children[0].children[2].children).to.be.equal('keepme');
165 | });
166 | });
167 |
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/patch.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 |
3 | var jsdom = require('mocha-jsdom');
4 |
5 | var diff = require('../../').diff;
6 | var patch = require('../../').patch;
7 | var h = require('../../').h;
8 |
9 | var a = require('../fixtures/simple').a;
10 | var b = require('../fixtures/simple').b;
11 |
12 | var a1 = require('../fixtures/simple').a1;
13 | var a2 = require('../fixtures/simple').a2;
14 |
15 | var f = require('../fixtures/simple').f;
16 | var f1 = require('../fixtures/simple').f1;
17 |
18 | var tree1 = require('../fixtures/basic').tree1;
19 | var tree2 = require('../fixtures/basic').tree2;
20 |
21 | describe('Test patch()', function() {
22 | jsdom();
23 |
24 | it('should patch simple nodes', function() {
25 | var dom = b.render();
26 | var diffs;
27 |
28 | expect(dom.childNodes[0]).to.be.instanceof(window.Text);
29 | diffs = diff(b, a);
30 |
31 | expect(function() {
32 | patch(b, diffs);
33 | }).not.throw();
34 |
35 | expect(dom.childNodes[0]).to.be.instanceof(window.HTMLDivElement);
36 | expect(dom.childNodes[0].childNodes[0]).to.be.instanceof(window.Text);
37 | expect(dom.childNodes[0].childNodes[0].textContent)
38 | .to.be.equal('text');
39 | });
40 |
41 | it('should patch virtual tree', function() {
42 | a1.render();
43 | var diffs;
44 |
45 | expect(a1.children).to.be.an('array');
46 | expect(a1.children[0].children).to.be.a('string');
47 | expect(a1.children[0].tag).to.be.equal('text');
48 |
49 | diffs = diff(a1, a2);
50 | patch(a1, diffs);
51 |
52 | expect(a1.children).to.be.an('array');
53 | expect(a1.children[0].children).to.be.an('array');
54 | expect(a1.children[0].tag).to.be.equal('div');
55 | });
56 |
57 | it('should patch props', function() {
58 | expect(a1.props.className).to.be.equal(a2.props.className);
59 | });
60 |
61 | it('should reorder nested tree', function() {
62 | f.render();
63 |
64 | expect(f.children[0].tag).to.be.equal('span');
65 | expect(f.children[1].tag).to.be.equal('ul');
66 |
67 | var diffs = diff(f, f1);
68 | patch(f, diffs);
69 |
70 | expect(f.children[1].tag).to.be.equal('span');
71 | expect(f.children[0].tag).to.be.equal('ul');
72 | });
73 |
74 | it('should reorder more complex tree', function() {
75 | tree1.render();
76 |
77 | expect(tree1.children[0].tag).to.be.equal('em');
78 | expect(tree1.children[1].tag).to.be.equal('div');
79 | expect(tree1.children[2].tag).to.be.equal('div');
80 | expect(tree1.children[3].tag).to.be.equal('span');
81 | expect(tree1.children[4].tag).to.be.equal('ul');
82 |
83 | patch(tree1, diff(tree1, tree2));
84 |
85 | expect(tree1.children[0].tag).to.be.equal('span');
86 | expect(tree1.children[1].tag).to.be.equal('strong');
87 | expect(tree1.children[2].tag).to.be.equal('ul');
88 | expect(tree1.children[3].tag).to.be.equal('div');
89 | expect(tree1.children[4]).to.be.a('undefined');
90 |
91 | /* tested reordering */
92 | expect(tree1.children[2].children[1].children[0].tag).to.be.equal('span');
93 | expect(tree1.children[2].children[1].children[1].tag).to.be.equal('div');
94 | expect(tree1.children[2].children[1].children[2]).to.be.a('undefined');
95 |
96 | });
97 |
98 | it('should complex tree inners be equal', function() {
99 | expect(tree1.el.innerHTML).to.be.equal(tree2.render().innerHTML);
100 | });
101 |
102 | it('should replace root node props and text', function() {
103 | var src = h('span', null, 'hello world');
104 | var dst = h('span', { c : 'e', d : 'z'}, 'bye bye');
105 |
106 | src.render();
107 |
108 | expect(src.el.attributes.length).to.be.equal(0);
109 |
110 | patch(src, diff(src, dst));
111 |
112 | expect(src.children[0].children).to.be.equal('bye bye');
113 | expect(src.el.attributes['c'].value).to.be.equal('e');
114 | expect(src.el.attributes['d'].value).to.be.equal('z');
115 | });
116 |
117 | it('should remove some props', function() {
118 | var src = h('span', { c : 'e', d : 'z'}, 'hello world');
119 | var dst = h('span', null, 'bye bye');
120 |
121 | src.render();
122 | expect(src.el.attributes.length).to.be.equal(2);
123 | patch(src, diff(src, dst));
124 | expect(src.el.attributes.length).to.be.equal(0);
125 | });
126 |
127 | it('should patch boolean props', function() {
128 | var src = h('span', { c : true, d : 'z'}, 'hello world');
129 | var dst = h('span', { b : true, c : false}, 'bye bye');
130 |
131 | src.render();
132 | expect(src.el.attributes.length).to.be.equal(2);
133 | patch(src, diff(src, dst));
134 | expect(src.el.attributes.length).to.be.equal(2);
135 | expect(src.el.getAttribute('b')).to.be.equal('true');
136 | expect(src.el.getAttribute('c')).to.be.equal('false');
137 | });
138 |
139 | it('should patch empty node', function() {
140 | var t = h('div', null, '');
141 | var p = h('div', { a : 'b' }, h('span', null, ''));
142 |
143 | t.render();
144 | patch(t, diff(t, p));
145 | p.render();
146 |
147 | expect(t.children.length).to.be.equal(1);
148 | expect(t.children[0].tag).to.be.equal('span');
149 | expect(t.props.a).to.be.equal('b');
150 | expect(t.el.getAttribute('a')).to.be.equal('b');
151 | });
152 |
153 | it('should patch complex text nodes sequence', function() {
154 | var src = require('../fixtures/textnodes').a;
155 | var dst = require('../fixtures/textnodes').b;
156 |
157 | src.render();
158 | expect(src.children.length).to.be.equal(1);
159 | patch(src, diff(src, dst));
160 | expect(src.children.length).to.be.equal(2);
161 | expect(src.children[1].children.length).to.be.equal(5);
162 | });
163 |
164 | it('should be able patch before render()', function() {
165 | var t = h('div', null, '');
166 | var p = h('div', { a : 'b' }, h('span', null, 'text'));
167 |
168 | expect(function() { patch(t, diff(t, p)); }).not.throw();
169 |
170 | expect(t.el).to.be.an('undefined');
171 | expect(t.children[0].el).to.be.an('undefined');
172 | expect(t.children[0].children[0].el).to.be.an('undefined');
173 | expect(t.children[0].children[0].children).to.be.equal('text');
174 |
175 | t.render();
176 |
177 | expect(t.el).to.be.instanceof(window.HTMLDivElement);
178 | expect(t.children[0].el).to.be.instanceof(window.HTMLSpanElement);
179 | expect(t.children[0].children[0].el).to.be.instanceof(window.Text);
180 | expect(t.children[0].children[0].children).to.be.equal('text');
181 | });
182 | });
183 |
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/render.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 |
3 | var jsdom = require('mocha-jsdom');
4 |
5 | var a = require('../fixtures/simple').a;
6 | var b = require('../fixtures/simple').b;
7 | var c = require('../fixtures/simple').c;
8 |
9 | var h = require('../../').h;
10 |
11 | describe('Test render()', function() {
12 | jsdom();
13 |
14 | it('should render simple nodes', function() {
15 | expect(a).to.be.an('object');
16 | expect(b).to.be.an('object');
17 | expect(c).to.be.an('object');
18 |
19 | expect(function() {
20 | a.render();
21 | b.render();
22 | c.render();
23 | }).not.throw();
24 | });
25 |
26 | it('shoud render DOM nodes', function() {
27 | var dom = a.render();
28 | expect(dom).to.be.instanceof(window.HTMLDivElement);
29 | dom = b.render();
30 | expect(dom).to.be.instanceof(window.HTMLDivElement);
31 | });
32 |
33 | it('should render text nodes', function() {
34 | var dom = a.render();
35 | expect(dom.childNodes[0].childNodes[0]).to.be.instanceof(window.Text);
36 | expect(dom.childNodes[0].childNodes[0].textContent).to.be.equal('text');
37 | });
38 |
39 | it('should render more complex nodes', function() {
40 | var dom;
41 |
42 | expect(function() {
43 | dom = require('../fixtures/nested').a.render();
44 | }).not.throw();
45 |
46 | expect(dom).to.be.instanceof(window.HTMLDivElement);
47 |
48 | expect(dom.childNodes[0]).to.be.instanceof(window.HTMLUListElement);
49 | expect(dom.childNodes[0].childNodes[0])
50 | .to.be.instanceof(window.HTMLLIElement);
51 | expect(dom.childNodes[0].childNodes[1])
52 | .to.be.instanceof(window.HTMLLIElement);
53 | expect(dom.childNodes[0].childNodes[2])
54 | .to.be.instanceof(window.HTMLLIElement);
55 | });
56 |
57 | it('should render empty text node', function() {
58 | var tree = h('div', { prop1 : 'propval' }, '');
59 |
60 | expect(function() { tree.render(); }).not.throw();
61 | });
62 |
63 | it('should set props', function() {
64 | var tree = h('div', { prop1 : 'propval' }, '');
65 | expect(tree.props.prop1).to.be.equal('propval');
66 | tree.render();
67 | expect(tree.el.getAttribute('prop1')).to.be.equal('propval');
68 | });
69 | });
70 |
--------------------------------------------------------------------------------
/lib/bvd/tests/mocha-tests/svg.test.js:
--------------------------------------------------------------------------------
1 | var expect = require('chai').expect;
2 | var sinon = require('sinon');
3 | var jsdom = require('mocha-jsdom');
4 |
5 | var a = require('../fixtures/svg').a;
6 | var d = require('../fixtures/svg').d;
7 | var h = require('../../').h;
8 |
9 | describe('Test