├── .babelrc
├── .gitattributes
├── .gitignore
├── .idea
└── scopes
│ └── scope_settings.xml
├── LICENSE
├── README.md
├── bower_components
├── chai
│ ├── .bower.json
│ ├── CONTRIBUTING.md
│ ├── History.md
│ ├── README.md
│ ├── ReleaseNotes.md
│ ├── bower.json
│ ├── chai.js
│ ├── component.json
│ ├── karma.conf.js
│ ├── karma.sauce.js
│ ├── package.json
│ └── sauce.browsers.js
└── mocha
│ ├── .bower.json
│ ├── .editorconfig
│ ├── .mailmap
│ ├── CONTRIBUTING.md
│ ├── HISTORY.md
│ ├── LICENSE
│ ├── README.md
│ ├── bower.json
│ ├── media
│ └── logo.svg
│ ├── mocha.css
│ └── mocha.js
├── demo
├── app.css
├── demo-choose-web-framework.coffee
├── demo-controls.coffee
├── demo-counter.coffee
├── demo-debug.coffee
├── demo-each.coffee
├── demo-function-lead-item.coffee
├── demo-show-hide.coffee
├── demo-sum.coffee
├── demo-text-model.coffee
├── index-dist.html
├── index.coffee
├── index.html
├── learn.json
├── todomvc-dist.html
├── todomvc.coffee
├── todomvc.css
├── todomvc.html
└── util.coffee
├── dist
├── demo.js
├── domcom.js
├── domcom.min.js
├── test.js
└── todomvc.js
├── doc
├── concepts and principles.md
├── Chinese
│ ├── API参考.md
│ ├── 介绍和辅导教程.md
│ ├── 从React到Domcom.md
│ ├── 包目录结构.md
│ ├── 常见问题.md
│ ├── 概念和原理.md
│ └── 速查表.md
├── From-react-to-domcom.md
├── api-reference.md
├── cheat-sheet.md
├── faq.md
├── introduction and tutorials.md
└── package-directory.md
├── gulpfile.js
├── history.md
├── package-lock.json
├── package.json
├── scripts
├── tasks
│ ├── build-tasks.coffee
│ ├── clean.coffee
│ ├── coffee.coffee
│ ├── remove.coffee
│ ├── rename.coffee
│ ├── typescript.coffee
│ └── webpack.coffee
└── webpack-config.coffee
├── src
├── Component.coffee
├── Emitter.coffee
├── dc-directive.coffee
├── dc-error.coffee
├── dc-util.coffee
├── index.coffee
└── react-proxy.coffee
├── static
├── bootstrap-3.3.4-dist
│ ├── css
│ │ ├── bootstrap-theme.css
│ │ ├── bootstrap-theme.css.map
│ │ ├── bootstrap-theme.min.css
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ └── bootstrap.min.css
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ └── js
│ │ ├── bootstrap.js
│ │ ├── bootstrap.min.js
│ │ └── npm.js
└── js
│ └── sinon-1.15.4.js
├── test
├── draft.html
├── helper.coffee
├── index.coffee
├── mocha-runner-dist.html
├── mocha-runner.html
├── mocha-test.css
├── react-vue-components
│ ├── HelloReact.coffee
│ └── HelloVue.coffee
├── test-antd.coffee
├── test-directive.coffee
├── test-event.coffee
├── test-material-ui.coffee
├── test-new-dc.coffee
└── test-react-proxy.coffee
└── todo.md
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "presets": ["es2015", "react"],
4 | "plugins": []
5 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | bower_components
4 |
5 | dev
6 | app
7 | build
8 |
9 | public
10 |
11 | .idea
12 | .tmp
13 | temp
14 | temp.*
15 | .sass-cache
16 | .DS_Store
17 | .sass-cache
18 | db
19 |
20 | .idea/**/*.xml
21 | webpack.config.js
22 | *-.js
23 | *-.coffee
24 | *.log
25 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2017 曹星明(Caoxingming, simeon.chaos@gmail.com)
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Domcom
2 | the web framework to provide dom component
3 |
4 | ## document
5 |
6 | See [doc/](https://github.com/taijiweb/domcom/tree/master/doc) for the document. Both English and Chinese document are provided.
7 |
8 | #### 中文文档: **请看 [doc/Chinese/](https://github.com/taijiweb/domcom/tree/master/doc/Chinese) 文件夹。**
9 |
10 | ## download and install
11 |
12 | npm install --save domcom
13 |
14 | useDomcom in page by script tag,add React andReactDom at first
15 |
16 |
17 |
18 |
19 | or
20 |
21 |
22 |
23 |
24 | and then add script tag for Domcom itself:
25 |
26 |
27 |
28 |
29 | or use the cdn provided by unpg or jsdelivr:
30 |
31 |
32 | https://unpkg.com/domcom/dist/domcom.js
33 | https://unpkg.com/domcom/dist/domcom.min.js
34 | https://cdn.jsdelivr.net/npm/domcom/dist/domcom.js
35 | https://cdn.jsdelivr.net/npm/domcom/dist/domcom.min.js
36 |
37 | ## Features
38 | * simple API:
39 |
40 | component = dc({data, view});
41 | component.mount(parentNode);
42 | component.update()
43 |
44 | * use plain array tree as view language, long live js, byebye JSX
45 |
46 | * MVC pattern( data/view/Component), byebye flux/redux
47 | data is the model, Component is just the controller
48 |
49 | * render to dom by react( maybe add other proxy, e.g. Vue, in the future)
50 |
51 | ## Samples
52 | There is [some samples](https://github.com/taijiweb/domcom/tree/master/demo), and a [todoMVC implementation](https://github.com/taijiweb/domcom/tree/master/demo/coffee/todomvc.coffee).
53 |
54 | The code below give a taste of domcom:
55 |
56 | const data = { a: 1, b: 2 };
57 | const view = data => {
58 | let props1 = {
59 | value: data.a,
60 | onChange(event) {
61 | data.a = event.target.value*1
62 | comp.update()
63 | }
64 | };
65 | props2 = {
66 | value: data.b,
67 | onChange(event) {
68 | data.b = event.target.value*1
69 | comp.update();
70 | };
71 | };
72 | return ['div',
73 | ['text', props1],
74 | ['text', props2],
75 | ['p', data.a + data.b]
76 | ];
77 | };
78 | const comp = dc({data, view});
79 | comp.mount('#demo');
80 |
81 |
82 | ## LICENSE
83 | MIT, see [LICENSE](https://github.com/taijiweb/domcom/blob/master/LICENSE)
84 |
--------------------------------------------------------------------------------
/bower_components/chai/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chai",
3 | "version": "2.1.1",
4 | "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.",
5 | "license": "MIT",
6 | "keywords": [
7 | "test",
8 | "assertion",
9 | "assert",
10 | "testing",
11 | "chai"
12 | ],
13 | "main": "chai.js",
14 | "ignore": [
15 | "build",
16 | "components",
17 | "lib",
18 | "node_modules",
19 | "support",
20 | "test",
21 | "index.js",
22 | "Makefile",
23 | ".*"
24 | ],
25 | "dependencies": {},
26 | "devDependencies": {},
27 | "homepage": "https://github.com/chaijs/chai",
28 | "_release": "2.1.1",
29 | "_resolution": {
30 | "type": "version",
31 | "tag": "2.1.1",
32 | "commit": "d7cafca0232756f767275bb00e66930a7823b027"
33 | },
34 | "_source": "git://github.com/chaijs/chai.git",
35 | "_target": "~2.1.1",
36 | "_originalSource": "chai",
37 | "_direct": true
38 | }
--------------------------------------------------------------------------------
/bower_components/chai/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Chai Contribution Guidelines
2 |
3 | We like to encourage you to contribute to the Chai.js repository. This should be as easy as possible for you but there are a few things to consider when contributing. The following guidelines for contribution should be followed if you want to submit a pull request or open an issue.
4 |
5 | Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features.
6 |
7 | #### Table of Contents
8 |
9 | - [TLDR;](#tldr)
10 | - [Contributing](#contributing)
11 | - [Bug Reports](#bugs)
12 | - [Feature Requests](#features)
13 | - [Pull Requests](#pull-requests)
14 | - [Releasing](#releasing)
15 | - [Support](#support)
16 | - [Resources](#resources)
17 | - [Core Contributors](#contributors)
18 |
19 |
20 | ## TLDR;
21 |
22 | - Creating an Issue or Pull Request requires a [GitHub](http://github.com) account.
23 | - Issue reports should be **clear**, **concise** and **reproducible**. Check to see if your issue has already been resolved in the [master]() branch or already reported in Chai's [GitHub Issue Tracker](https://github.com/chaijs/chai/issues).
24 | - Pull Requests must adhere to strict [coding style guidelines](https://github.com/chaijs/chai/wiki/Chai-Coding-Style-Guide).
25 | - In general, avoid submitting PRs for new Assertions without asking core contributors first. More than likely it would be better implemented as a plugin.
26 | - Additional support is available via the [Google Group](http://groups.google.com/group/chaijs) or on irc.freenode.net#chaijs.
27 | - **IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by the project.
28 |
29 |
30 |
31 |
32 | ## Contributing
33 |
34 | The issue tracker is the preferred channel for [bug reports](#bugs),
35 | [feature requests](#features) and [submitting pull
36 | requests](#pull-requests), but please respect the following restrictions:
37 |
38 | * Please **do not** use the issue tracker for personal support requests (use
39 | [Google Group](https://groups.google.com/forum/#!forum/chaijs) or IRC).
40 | * Please **do not** derail or troll issues. Keep the discussion on topic and
41 | respect the opinions of others
42 |
43 |
44 | ### Bug Reports
45 |
46 | A bug is a **demonstrable problem** that is caused by the code in the repository.
47 |
48 | Guidelines for bug reports:
49 |
50 | 1. **Use the GitHub issue search** — check if the issue has already been reported.
51 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or development branch in the repository.
52 | 3. **Isolate the problem** — create a test case to demonstrate your issue. Provide either a repo, gist, or code sample to demonstrate you problem.
53 |
54 | A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. What is your environment? What steps will reproduce the issue? What browser(s) and/or Node.js versions experience the problem? What would you expect to be the outcome? All these details will help people to fix any potential bugs.
55 |
56 | Example:
57 |
58 | > Short and descriptive example bug report title
59 | >
60 | > A summary of the issue and the browser/OS environment in which it occurs. If suitable, include the steps required to reproduce the bug.
61 | >
62 | > 1. This is the first step
63 | > 2. This is the second step
64 | > 3. Further steps, etc.
65 | >
66 | > `` - a link to the reduced test case OR
67 | > ```js
68 | > expect(a).to.equal('a');
69 | > // code sample
70 | > ```
71 | >
72 | > Any other information you want to share that is relevant to the issue being reported. This might include the lines of code that you have identified as causing the bug, and potential solutions (and your opinions on their merits).
73 |
74 |
75 | ### Feature Requests
76 |
77 | Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to *you* to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible.
78 |
79 | Furthermore, since Chai.js has a [robust plugin API](http://chaijs.com/guide/plugins/), we encourage you to publish **new Assertions** as plugins. If your feature is an enhancement to an **existing Assertion**, please propose your changes as an issue prior to opening a pull request. If the core Chai.js contributors feel your plugin would be better suited as a core assertion, they will invite you to open a PR in [chaijs/chai](https://github.com/chaijs/chai).
80 |
81 |
82 | ### Pull Requests
83 |
84 | - PRs for new core-assertions are advised against.
85 | - PRs for core-assertion bug fixes are always welcome.
86 | - PRs for enhancing the interfaces are always welcome.
87 | - PRs that increase test coverage are always welcome.
88 | - PRs are scrutinized for coding-style.
89 |
90 | Good pull requests - patches, improvements, new features - are a fantastic help. They should remain focused in scope and avoid containing unrelated commits.
91 |
92 | **Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project.
93 |
94 | Please adhere to the coding conventions used throughout a project (indentation, accurate comments, etc.) and any other requirements (such as test coverage). Please review the [Chai.js Coding Style Guide](https://github.com/chaijs/chai/wiki/Chai-Coding-Style-Guide).
95 |
96 | Follow this process if you'd like your work considered for inclusion in the project:
97 |
98 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes:
99 |
100 | ```bash
101 | # Clone your fork of the repo into the current directory
102 | git clone https://github.com//
103 | # Navigate to the newly cloned directory
104 | cd
105 | # Assign the original repo to a remote called "upstream"
106 | git remote add upstream https://github.com//
107 | ```
108 |
109 | 2. If you cloned a while ago, get the latest changes from upstream:
110 |
111 | ```bash
112 | git checkout
113 | git pull upstream
114 | ```
115 |
116 | 3. Create a new topic branch (off the main project development branch) to contain your feature, change, or fix:
117 |
118 | ```bash
119 | git checkout -b
120 | ```
121 |
122 | 4. Commit your changes in logical chunks. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public.
123 |
124 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
125 |
126 | ```bash
127 | git pull [--rebase] upstream
128 | ```
129 |
130 | 6. Push your topic branch up to your fork:
131 |
132 | ```bash
133 | git push origin
134 | ```
135 |
136 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description.
137 |
138 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by the project.
139 |
140 |
141 | ## Releasing
142 |
143 | Releases can be prepared by any core-contributor or user whom has push access to
144 | the `chaijs/chai` repository.
145 |
146 | This process requires [git-extras](https://github.com/tj/git-extras) for some steps.
147 |
148 | 1. Ensure all tests pass.
149 | 2. Bump the version tag in-code and for all package managers.
150 | - `lib/chai.js`
151 | - `package.json`
152 | - `component.json`
153 | - `bower.json`
154 | 3. Build the browser version with `make`.
155 | 4. Append commit log to `HISTORY.md` using `git changelog` command.
156 | 5. Write human-friendly `ReleaseNotes.md` based on changelog.
157 | - If breaking changes, write migration tutorial(s) and reasoning.
158 | - Callouts for community contributions (PRs) with links to PR and contributing user.
159 | - Callouts for other fixes made by core contributors with links to issue.
160 | 6. Update `README.md` with an updated contributors list using `git summary` command.
161 | 7. Push a tagged release using `git release x.x.x`.
162 | - All tagged releases are published to NPM.
163 |
164 |
165 | ## Support
166 |
167 |
168 | ### Resources
169 |
170 | For most of the documentation you are going to want to visit [ChaiJS.com](http://chaijs.com).
171 |
172 | - [Getting Started Guide](http://chaijs.com/guide/)
173 | - [API Reference](http://chaijs.com/api/)
174 | - [Plugins](http://chaijs.com/plugins/)
175 |
176 | Alternatively, the [wiki](https://github.com/chaijs/chai/wiki) might be what you are looking for.
177 |
178 | - [Chai Coding Style Guide](https://github.com/chaijs/chai/wiki/Chai-Coding-Style-Guide)
179 | - [Third-party Resources](https://github.com/chaijs/chai/wiki/Third-Party-Resources)
180 |
181 | Or finally, you may find a core-contributor or like-minded developer in any of our support channels.
182 |
183 | - IRC: irc.freenode.org #chaijs
184 | - [Mailing List / Google Group](https://groups.google.com/forum/#!forum/chaijs)
185 |
186 |
187 | ### Core Contributors
188 |
189 | Feel free to reach out to any of the core-contributors with you questions or concerns. We will do our best to respond in a timely manner.
190 |
191 | - Jake Luer
192 | - GH: [@logicalparadox](https://github.com/logicalparadox)
193 | - TW: [@jakeluer](http://twitter.com/jakeluer)
194 | - IRC: logicalparadox
195 | - Veselin Todorov
196 | - GH: [@vesln](https://github.com/vesln/)
197 | - TW: [@vesln](http://twitter.com/vesln)
198 | - IRC: vesln
199 |
--------------------------------------------------------------------------------
/bower_components/chai/README.md:
--------------------------------------------------------------------------------
1 | [](http://chaijs.com)
2 |
3 | Chai is a BDD / TDD assertion library for [node](http://nodejs.org) and the browser that
4 | can be delightfully paired with any javascript testing framework.
5 |
6 | For more information or to download plugins, view the [documentation](http://chaijs.com).
7 |
8 | [](https://travis-ci.org/chaijs/chai)
9 |
10 | [](https://saucelabs.com/u/chaijs)
11 |
12 | ### Plugins
13 |
14 | Chai offers a robust Plugin architecture for extending Chai's assertions and interfaces.
15 |
16 | - Need a plugin? View the [official plugin list](http://chaijs.com/plugins).
17 | - Have a plugin and want it listed? Open a Pull Request at [chaijs/chai-docs:plugin.js](https://github.com/chaijs/chai-docs/blob/master/plugins.js#L1-L12).
18 | - Want to build a plugin? Read the [plugin api documentation](http://chaijs.com/guide/plugins/).
19 |
20 | ### Related Projects
21 |
22 | - [chaijs / assertion-error](https://github.com/chaijs/assertion-error): Custom `Error` constructor thrown upon an assertion failing.
23 | - [chaijs / deep-eql](https://github.com/chaijs/deep-eql): Improved deep equality testing for Node.js and the browser.
24 |
25 | ### Contributors
26 |
27 | project : chai
28 | repo age : 3 years, 3 months
29 | active : 224 days
30 | commits : 859
31 | files : 59
32 | authors :
33 | 554 Jake Luer 64.5%
34 | 79 Veselin Todorov 9.2%
35 | 43 Domenic Denicola 5.0%
36 | 29 Keith Cirkel 3.4%
37 | 14 Joshua Perry 1.6%
38 | 8 Chris Polis 0.9%
39 | 6 Ian Zamojc 0.7%
40 | 6 Ruben Verborgh 0.7%
41 | 5 Juliusz Gonera 0.6%
42 | 5 George Kats 0.6%
43 | 5 Jo Liss 0.6%
44 | 5 Scott Nonnenberg 0.6%
45 | 5 leider 0.6%
46 | 4 charlierudolph 0.5%
47 | 4 Chris Jones 0.5%
48 | 4 Max Edmands 0.5%
49 | 4 David da Silva 0.5%
50 | 4 Veselin 0.5%
51 | 4 josher19 0.5%
52 | 4 John Firebaugh 0.5%
53 | 4 Nick Heiner 0.5%
54 | 3 Andrei Neculau 0.3%
55 | 3 Duncan Beevers 0.3%
56 | 3 Ryunosuke SATO 0.3%
57 | 3 Jake Rosoman 0.3%
58 | 3 Jason Karns 0.3%
59 | 3 Jeff Barczewski 0.3%
60 | 2 Jakub Nešetřil 0.2%
61 | 2 Teddy Cross 0.2%
62 | 2 Roman Masek 0.2%
63 | 2 Gregg Lind 0.2%
64 | 2 Edwin Shao 0.2%
65 | 2 Bartvds 0.2%
66 | 1 toastynerd 0.1%
67 | 1 Anand Patil 0.1%
68 | 1 Benjamin Horsleben 0.1%
69 | 1 Brandon Payton 0.1%
70 | 1 Chasen Le Hara 0.1%
71 | 1 Chris Connelly 0.1%
72 | 1 Chris Thompson 0.1%
73 | 1 Christopher Hiller 0.1%
74 | 1 Chun-Yi 0.1%
75 | 1 DD 0.1%
76 | 1 Danilo Vaz 0.1%
77 | 1 Dido Arellano 0.1%
78 | 1 Jeff Welch 0.1%
79 | 1 Jesse McCarthy 0.1%
80 | 1 Julien Wajsberg 0.1%
81 | 1 Kilian Ciuffolo 0.1%
82 | 1 Luís Cardoso 0.1%
83 | 1 Martin Middel 0.1%
84 | 1 Mathias Schreck 0.1%
85 | 1 Michael Lange 0.1%
86 | 1 Niklas Närhinen 0.1%
87 | 1 Paul Miller 0.1%
88 | 1 Refael Ackermann 0.1%
89 | 1 Sasha Koss 0.1%
90 | 1 Victor Costan 0.1%
91 | 1 Vinay Pulim 0.1%
92 | 1 Virginie BARDALES 0.1%
93 | 1 ericdouglas 0.1%
94 | 1 laconbass 0.1%
95 | 1 mohayonao 0.1%
96 | 1 piecioshka 0.1%
97 | 1 shinnn 0.1%
98 | 1 Adam Hull 0.1%
99 |
100 |
101 | ## License
102 |
103 | (The MIT License)
104 |
105 | Copyright (c) 2011-2015 Jake Luer
106 |
107 | Permission is hereby granted, free of charge, to any person obtaining a copy
108 | of this software and associated documentation files (the "Software"), to deal
109 | in the Software without restriction, including without limitation the rights
110 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
111 | copies of the Software, and to permit persons to whom the Software is
112 | furnished to do so, subject to the following conditions:
113 |
114 | The above copyright notice and this permission notice shall be included in
115 | all copies or substantial portions of the Software.
116 |
117 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
118 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
119 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
120 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
121 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
122 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
123 | THE SOFTWARE.
124 |
--------------------------------------------------------------------------------
/bower_components/chai/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chai",
3 | "version": "2.1.1",
4 | "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.",
5 | "license": "MIT",
6 | "keywords": [
7 | "test",
8 | "assertion",
9 | "assert",
10 | "testing",
11 | "chai"
12 | ],
13 | "main": "chai.js",
14 | "ignore": [
15 | "build",
16 | "components",
17 | "lib",
18 | "node_modules",
19 | "support",
20 | "test",
21 | "index.js",
22 | "Makefile",
23 | ".*"
24 | ],
25 | "dependencies": {},
26 | "devDependencies": {}
27 | }
28 |
--------------------------------------------------------------------------------
/bower_components/chai/component.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chai"
3 | , "repo": "chaijs/chai"
4 | , "version": "2.1.1"
5 | , "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic."
6 | , "license": "MIT"
7 | , "keywords": [
8 | "test"
9 | , "assertion"
10 | , "assert"
11 | , "testing"
12 | , "chai"
13 | ]
14 | , "main": "index.js"
15 | , "scripts": [
16 | "index.js"
17 | , "lib/chai.js"
18 | , "lib/chai/assertion.js"
19 | , "lib/chai/config.js"
20 | , "lib/chai/core/assertions.js"
21 | , "lib/chai/interface/assert.js"
22 | , "lib/chai/interface/expect.js"
23 | , "lib/chai/interface/should.js"
24 | , "lib/chai/utils/addChainableMethod.js"
25 | , "lib/chai/utils/addMethod.js"
26 | , "lib/chai/utils/addProperty.js"
27 | , "lib/chai/utils/flag.js"
28 | , "lib/chai/utils/getActual.js"
29 | , "lib/chai/utils/getEnumerableProperties.js"
30 | , "lib/chai/utils/getMessage.js"
31 | , "lib/chai/utils/getName.js"
32 | , "lib/chai/utils/getPathValue.js"
33 | , "lib/chai/utils/getPathInfo.js"
34 | , "lib/chai/utils/hasProperty.js"
35 | , "lib/chai/utils/getProperties.js"
36 | , "lib/chai/utils/index.js"
37 | , "lib/chai/utils/inspect.js"
38 | , "lib/chai/utils/objDisplay.js"
39 | , "lib/chai/utils/overwriteMethod.js"
40 | , "lib/chai/utils/overwriteProperty.js"
41 | , "lib/chai/utils/overwriteChainableMethod.js"
42 | , "lib/chai/utils/test.js"
43 | , "lib/chai/utils/transferFlags.js"
44 | , "lib/chai/utils/type.js"
45 | ]
46 | , "dependencies": {
47 | "chaijs/assertion-error": "1.0.0"
48 | , "chaijs/deep-eql": "0.1.3"
49 | }
50 | , "development": {}
51 | }
52 |
--------------------------------------------------------------------------------
/bower_components/chai/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 | frameworks: [ 'mocha' ]
4 | , files: [
5 | 'build/build.js'
6 | , 'test/bootstrap/karma.js'
7 | , 'test/*.js'
8 | ]
9 | , reporters: [ 'progress' ]
10 | , colors: true
11 | , logLevel: config.LOG_INFO
12 | , autoWatch: false
13 | , browsers: [ 'PhantomJS' ]
14 | , browserDisconnectTimeout: 10000
15 | , browserDisconnectTolerance: 2
16 | , browserNoActivityTimeout: 20000
17 | , singleRun: true
18 | });
19 |
20 | switch (process.env.CHAI_TEST_ENV) {
21 | case 'sauce':
22 | require('./karma.sauce')(config);
23 | break;
24 | default:
25 | // ...
26 | break;
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/bower_components/chai/karma.sauce.js:
--------------------------------------------------------------------------------
1 | var version = require('./package.json').version;
2 | var ts = new Date().getTime();
3 |
4 | module.exports = function(config) {
5 | var auth;
6 |
7 | try {
8 | auth = require('./test/auth/index');
9 | } catch(ex) {
10 | auth = {};
11 | auth.SAUCE_USERNAME = process.env.SAUCE_USERNAME || null;
12 | auth.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY || null;
13 | }
14 |
15 | if (!auth.SAUCE_USERNAME || !auth.SAUCE_ACCESS_KEY) return;
16 | if (process.env.SKIP_SAUCE) return;
17 |
18 | var branch = process.env.TRAVIS_BRANCH || 'local'
19 | var browserConfig = require('./sauce.browsers');
20 | var browsers = Object.keys(browserConfig);
21 | var tags = [ 'chaijs_' + version, auth.SAUCE_USERNAME + '@' + branch ];
22 | var tunnel = process.env.TRAVIS_JOB_NUMBER || ts;
23 |
24 | if (process.env.TRAVIS_JOB_NUMBER) {
25 | tags.push('travis@' + process.env.TRAVIS_JOB_NUMBER);
26 | }
27 |
28 | config.browsers = config.browsers.concat(browsers);
29 | config.customLaunchers = browserConfig;
30 | config.reporters.push('saucelabs');
31 | config.transports = [ 'xhr-polling' ];
32 |
33 | config.sauceLabs = {
34 | username: auth.SAUCE_USERNAME
35 | , accessKey: auth.SAUCE_ACCESS_KEY
36 | , startConnect: true
37 | , tags: tags
38 | , testName: 'ChaiJS'
39 | , tunnelIdentifier: tunnel
40 | };
41 | };
42 |
--------------------------------------------------------------------------------
/bower_components/chai/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Jake Luer ",
3 | "name": "chai",
4 | "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.",
5 | "keywords": [
6 | "test",
7 | "assertion",
8 | "assert",
9 | "testing",
10 | "chai"
11 | ],
12 | "homepage": "http://chaijs.com",
13 | "license": "MIT",
14 | "contributors": [
15 | "Jake Luer ",
16 | "Domenic Denicola (http://domenicdenicola.com)",
17 | "Veselin Todorov ",
18 | "John Firebaugh "
19 | ],
20 | "version": "2.1.1",
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/chaijs/chai"
24 | },
25 | "bugs": {
26 | "url": "https://github.com/chaijs/chai/issues"
27 | },
28 | "main": "./index",
29 | "scripts": {
30 | "test": "make test"
31 | },
32 | "engines": {
33 | "node": ">= 0.4.0"
34 | },
35 | "dependencies": {
36 | "assertion-error": "1.0.0",
37 | "deep-eql": "0.1.3"
38 | },
39 | "devDependencies": {
40 | "component": "*",
41 | "karma": "0.12.x",
42 | "karma-mocha": "*",
43 | "karma-sauce-launcher": "0.2.x",
44 | "karma-phantomjs-launcher": "0.1.1",
45 | "mocha": "1.21.x",
46 | "istanbul": "0.2.x"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/bower_components/chai/sauce.browsers.js:
--------------------------------------------------------------------------------
1 |
2 | /*!
3 | * Chrome
4 | */
5 |
6 | exports['SL_Chrome'] = {
7 | base: 'SauceLabs'
8 | , browserName: 'chrome'
9 | };
10 |
11 | /*!
12 | * Firefox
13 | */
14 |
15 | /*!
16 | * TODO: Karma doesn't seem to like this, though sauce boots its up
17 | *
18 |
19 | exports['SL_Firefox_23'] = {
20 | base: 'SauceLabs'
21 | , browserName: 'firefox'
22 | , platform: 'Windows XP'
23 | , version: '23'
24 | };
25 |
26 | */
27 |
28 | exports['SL_Firefox_22'] = {
29 | base: 'SauceLabs'
30 | , browserName: 'firefox'
31 | , platform: 'Windows 7'
32 | , version: '22'
33 | };
34 |
35 | /*!
36 | * Opera
37 | */
38 |
39 | exports['SL_Opera_12'] = {
40 | base: 'SauceLabs'
41 | , browserName: 'opera'
42 | , platform: 'Windows 7'
43 | , version: '12'
44 | };
45 |
46 | exports['SL_Opera_11'] = {
47 | base: 'SauceLabs'
48 | , browserName: 'opera'
49 | , platform: 'Windows 7'
50 | , version: '11'
51 | };
52 |
53 | /*!
54 | * Internet Explorer
55 | */
56 |
57 | exports['SL_IE_10'] = {
58 | base: 'SauceLabs'
59 | , browserName: 'internet explorer'
60 | , platform: 'Windows 2012'
61 | , version: '10'
62 | };
63 |
64 | /*!
65 | * Safari
66 | */
67 |
68 | exports['SL_Safari_6'] = {
69 | base: 'SauceLabs'
70 | , browserName: 'safari'
71 | , platform: 'Mac 10.8'
72 | , version: '6'
73 | };
74 |
75 | exports['SL_Safari_5'] = {
76 | base: 'SauceLabs'
77 | , browserName: 'safari'
78 | , platform: 'Mac 10.6'
79 | , version: '5'
80 | };
81 |
82 | /*!
83 | * iPhone
84 | */
85 |
86 | /*!
87 | * TODO: These take forever to boot or shut down. Causes timeout.
88 | *
89 |
90 | exports['SL_iPhone_6'] = {
91 | base: 'SauceLabs'
92 | , browserName: 'iphone'
93 | , platform: 'Mac 10.8'
94 | , version: '6'
95 | };
96 |
97 | exports['SL_iPhone_5-1'] = {
98 | base: 'SauceLabs'
99 | , browserName: 'iphone'
100 | , platform: 'Mac 10.8'
101 | , version: '5.1'
102 | };
103 |
104 | exports['SL_iPhone_5'] = {
105 | base: 'SauceLabs'
106 | , browserName: 'iphone'
107 | , platform: 'Mac 10.6'
108 | , version: '5'
109 | };
110 |
111 | */
112 |
113 | /*!
114 | * Android
115 | */
116 |
117 | /*!
118 | * TODO: fails because of error serialization
119 | *
120 |
121 | exports['SL_Android_4'] = {
122 | base: 'SauceLabs'
123 | , browserName: 'android'
124 | , platform: 'Linux'
125 | , version: '4'
126 | };
127 |
128 | */
129 |
--------------------------------------------------------------------------------
/bower_components/mocha/.bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mocha",
3 | "version": "2.2.1",
4 | "homepage": "http://mocha.github.io/mocha",
5 | "description": "simple, flexible, fun test framework",
6 | "authors": [
7 | "TJ Holowaychuk ",
8 | "Joshua Appelman ",
9 | "Oleg Gaidarenko ",
10 | "Christoffer Hallas ",
11 | "Christopher Hiller ",
12 | "Travis Jeffery ",
13 | "Johnathan Ong ",
14 | "Guillermo Rauch "
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/mochajs/mocha.git"
19 | },
20 | "main": [
21 | "mocha.js",
22 | "mocha.css"
23 | ],
24 | "ignore": [
25 | "bin",
26 | "editors",
27 | "images",
28 | "lib",
29 | "support",
30 | "test",
31 | ".gitignore",
32 | ".npmignore",
33 | ".travis.yml",
34 | "component.json",
35 | "index.js",
36 | "Makefile",
37 | "package.json"
38 | ],
39 | "keywords": [
40 | "mocha",
41 | "test",
42 | "bdd",
43 | "tdd",
44 | "tap"
45 | ],
46 | "license": "MIT",
47 | "_release": "2.2.1",
48 | "_resolution": {
49 | "type": "version",
50 | "tag": "2.2.1",
51 | "commit": "3102c9bc395c3a542b492fb56011a3eba15f3b76"
52 | },
53 | "_source": "git://github.com/mochajs/mocha.git",
54 | "_target": "~2.2.1",
55 | "_originalSource": "mocha",
56 | "_direct": true
57 | }
--------------------------------------------------------------------------------
/bower_components/mocha/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [Makefile]
15 | indent_style = tab
16 |
17 | [*.md]
18 | trim_trailing_whitespace = false
19 |
--------------------------------------------------------------------------------
/bower_components/mocha/.mailmap:
--------------------------------------------------------------------------------
1 | TJ Holowaychuk
--------------------------------------------------------------------------------
/bower_components/mocha/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Mocha
2 |
3 | Hi! We could use your help. Let us help you help us. Or something.
4 |
5 | ## General
6 |
7 | 1. If you are looking for a place to begin, **please send PRs for bugfixes instead of new features**, and/or **look for issues labeled `PR PLEASE`.**
8 |
9 | 2. **Help with documentation and the wiki is always appreciated**.
10 |
11 | 3. Please **be courteous and constructive** when commenting on issues, commits, and pull requests.
12 |
13 | ## Bug Reports & Issues
14 |
15 | 1. When reporting a bug, please **provide steps to reproduce**. If possible, show code.
16 |
17 | 2. Please **show all code in JavaScript**. We don't all read ``. If you do not, you will be asked to.
18 |
19 | 3. Because Mocha works with many third-party libraries and tools, **ensure the bug you are reporting is actually within Mocha**.
20 |
21 | 4. If you report a bug, and it is inactive for a significant amount of time, it may be closed. **Please respond promptly to requests for more information**.
22 |
23 | ## Pull Requests
24 |
25 | 1. Before sending a large PR, it's recommended to **create an issue to propose the change**. Nobody wants to write a book of code and throw it away.
26 |
27 | 2. Because Mocha should be kept as maintainable as possible, its codebase must be kept slim. Historically, *most PRs for new features are not merged*. New features inevitably increase the size of the codebase, and thus reduce maintainability. Only features *deemed essential* are likely to be merged--this is at the discretion of the maintainer(s). If your PR for a feature is not merged, this doesn't necessarily mean your PR was a bad idea, wouldn't be used, or otherwise sucks. It just means **only essential PRs for new features are likely to be merged**.
28 |
29 | 3. Due to the above, before creating a PR for a new feature, **create an issue to propose the feature.**
30 |
31 | 4. Please **respect existing coding conventions**, whatever those may be.
32 |
33 | 5. If your PR has been waiting in limbo for some time, it's very helpful to **rebase against master**, which will make it easier to merge.
34 |
35 | 6. Please **add tests for new code**.
36 |
37 | 7. **Always run `npm test` before sending a PR.** If you break the tests, your PR will not be accepted until they are fixed.
38 |
39 | ## Source Control
40 |
41 | 1. Please **squash your commits** when sending a pull request. If you are unfamiliar with this process, see [this guide](https://help.github.com/articles/about-git-rebase/). If you have already pushed your changesets and are squashing thereafter, this may necessitate the use of a "force push". Please [read the docs](http://git-scm.com/docs/git-push) before you attempt this.
42 |
43 | 2. Please **follow the commit message conventions [outlined here](https://medium.com/code-adventures/git-conventions-a940ee20862d).**
44 |
45 | ## TL;DR
46 |
47 | **Be kind, be diligent, look before you leap into a PR, and follow common community conventions**.
48 |
49 | *- The Mocha Team*
50 |
--------------------------------------------------------------------------------
/bower_components/mocha/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2011-2015 TJ Holowaychuk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/bower_components/mocha/README.md:
--------------------------------------------------------------------------------
1 | [](http://travis-ci.org/mochajs/mocha) [](https://gitter.im/mochajs/mocha?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2 |
3 | [](http://mochajs.org)
4 |
5 | Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://mochajs.org).
6 |
7 | ## Contributors
8 |
9 | ```
10 | project : mocha
11 | repo age : 3 years, 4 months
12 | active : 509 days
13 | commits : 1575
14 | files : 153
15 | authors :
16 | 977 TJ Holowaychuk 62.0%
17 | 132 Travis Jeffery 8.4%
18 | 63 Christopher Hiller 4.0%
19 | 31 Guillermo Rauch 2.0%
20 | 27 Joshua Appelman 1.7%
21 | 13 Attila Domokos 0.8%
22 | 10 John Firebaugh 0.6%
23 | 8 Nathan Rajlich 0.5%
24 | 8 Jo Liss 0.5%
25 | 6 Mike Pennisi 0.4%
26 | 6 Brendan Nee 0.4%
27 | 6 James Carr 0.4%
28 | 5 Aaron Heckmann 0.3%
29 | 5 Raynos 0.3%
30 | 5 Ryunosuke SATO 0.3%
31 | 4 hokaccha 0.3%
32 | 4 Jonathan Ong 0.3%
33 | 4 Joshua Krall 0.3%
34 | 4 Domenic Denicola 0.3%
35 | 4 Forbes Lindesay 0.3%
36 | 4 Xavier Antoviaque 0.3%
37 | 4 David da Silva 0.3%
38 | 3 Ariel Mashraki 0.2%
39 | 3 Ben Bradley 0.2%
40 | 3 Merrick Christensen 0.2%
41 | 3 Andreas Lind Petersen 0.2%
42 | 3 Nathan Bowser 0.2%
43 | 3 Cory Thomas 0.2%
44 | 3 Benjie Gillam 0.2%
45 | 3 Wil Moore III 0.2%
46 | 3 Ben Lindsey 0.2%
47 | 3 Tyson Tate 0.2%
48 | 3 Paul Miller 0.2%
49 | 3 eiji.ienaga 0.2%
50 | 3 Mathieu Desvé 0.2%
51 | 3 Jesse Dailey 0.2%
52 | 3 fool2fish 0.2%
53 | 3 Fredrik Enestad 0.2%
54 | 3 Sindre Sorhus 0.2%
55 | 3 Valentin Agachi 0.2%
56 | 2 jsdevel 0.1%
57 | 2 Arian Stolwijk 0.1%
58 | 2 Juzer Ali 0.1%
59 | 2 David Henderson 0.1%
60 | 2 Justin DuJardin 0.1%
61 | 2 Paul Armstrong 0.1%
62 | 2 Pete Hawkins 0.1%
63 | 2 Jonas Westerlund 0.1%
64 | 2 Quang Van 0.1%
65 | 2 Simon Gaeremynck 0.1%
66 | 2 travis jeffery 0.1%
67 | 2 Dominique Quatravaux 0.1%
68 | 2 Jacob Wejendorp 0.1%
69 | 2 Shawn Krisman 0.1%
70 | 2 FARKAS Máté 0.1%
71 | 2 Konstantin Käfer 0.1%
72 | 2 Timo Tijhof 0.1%
73 | 2 Sean Lang 0.1%
74 | 2 Quanlong He 0.1%
75 | 2 Glen Mailer 0.1%
76 | 2 Alexander Early 0.1%
77 | 2 Ian Storm Taylor 0.1%
78 | 2 Brian Beck 0.1%
79 | 2 Michael Riley 0.1%
80 | 2 Michael Schoonmaker 0.1%
81 | 2 domenic 0.1%
82 | 2 fcrisci 0.1%
83 | 2 Buck Doyle 0.1%
84 | 2 Nathan Alderson 0.1%
85 | 1 Mal Graty 0.1%
86 | 1 Marc Kuo 0.1%
87 | 1 Matija Marohnić 0.1%
88 | 1 Matt Robenolt 0.1%
89 | 1 Matt Smith 0.1%
90 | 1 Matthew Shanley 0.1%
91 | 1 Mattias Tidlund 0.1%
92 | 1 Michael Jackson 0.1%
93 | 1 Michael Olson 0.1%
94 | 1 Michal Charemza 0.1%
95 | 1 Nathan Black 0.1%
96 | 1 Nick Fitzgerald 0.1%
97 | 1 Noshir Patel 0.1%
98 | 1 Panu Horsmalahti 0.1%
99 | 1 Phil Sung 0.1%
100 | 1 R56 0.1%
101 | 1 Refael Ackermann 0.1%
102 | 1 Richard Dingwall 0.1%
103 | 1 Richard Knop 0.1%
104 | 1 Rob Wu 0.1%
105 | 1 Romain Prieto 0.1%
106 | 1 Roman Neuhauser 0.1%
107 | 1 Roman Shtylman 0.1%
108 | 1 Russ Bradberry 0.1%
109 | 1 Russell Munson 0.1%
110 | 1 Rustem Mustafin 0.1%
111 | 1 Salehen Shovon Rahman 0.1%
112 | 1 Sasha Koss 0.1%
113 | 1 Seiya Konno 0.1%
114 | 1 Shaine Hatch 0.1%
115 | 1 Simon Goumaz 0.1%
116 | 1 Standa Opichal 0.1%
117 | 1 Stephen Mathieson 0.1%
118 | 1 Steve Mason 0.1%
119 | 1 Tapiwa Kelvin 0.1%
120 | 1 Teddy Zeenny 0.1%
121 | 1 Tim Ehat 0.1%
122 | 1 Vadim Nikitin 0.1%
123 | 1 Victor Costan 0.1%
124 | 1 Will Langstroth 0.1%
125 | 1 Yanis Wang 0.1%
126 | 1 Yuest Wang 0.1%
127 | 1 Zsolt Takács 0.1%
128 | 1 abrkn 0.1%
129 | 1 airportyh 0.1%
130 | 1 badunk 0.1%
131 | 1 claudyus 0.1%
132 | 1 dasilvacontin 0.1%
133 | 1 fengmk2 0.1%
134 | 1 gaye 0.1%
135 | 1 grasGendarme 0.1%
136 | 1 lakmeer 0.1%
137 | 1 lodr 0.1%
138 | 1 mrShturman 0.1%
139 | 1 nishigori 0.1%
140 | 1 omardelarosa 0.1%
141 | 1 qiuzuhui 0.1%
142 | 1 samuel goldszmidt 0.1%
143 | 1 sebv 0.1%
144 | 1 startswithaj 0.1%
145 | 1 tgautier@yahoo.com 0.1%
146 | 1 traleig1 0.1%
147 | 1 vlad 0.1%
148 | 1 yuitest 0.1%
149 | 1 zhiyelee 0.1%
150 | 1 Adam Crabtree 0.1%
151 | 1 Andreas Brekken 0.1%
152 | 1 Andrew Nesbitt 0.1%
153 | 1 Andrey Popp 0.1%
154 | 1 Arnaud Brousseau 0.1%
155 | 1 Atsuya Takagi 0.1%
156 | 1 Austin Birch 0.1%
157 | 1 Ben Noordhuis 0.1%
158 | 1 Bjørge Næss 0.1%
159 | 1 Brian Lalor 0.1%
160 | 1 Brian M. Carlson 0.1%
161 | 1 Brian Moore 0.1%
162 | 1 Bryan Donovan 0.1%
163 | 1 C. Scott Ananian 0.1%
164 | 1 Casey Foster 0.1%
165 | 1 ChrisWren 0.1%
166 | 1 Connor Dunn 0.1%
167 | 1 Corey Butler 0.1%
168 | 1 Daniel Stockman 0.1%
169 | 1 Dave McKenna 0.1%
170 | 1 Denis Bardadym 0.1%
171 | 1 Devin Weaver 0.1%
172 | 1 Di Wu 0.1%
173 | 1 Diogo Monteiro 0.1%
174 | 1 Dmitry Shirokov 0.1%
175 | 1 Dr. Travis Jeffery 0.1%
176 | 1 Fedor Indutny 0.1%
177 | 1 Florian Margaine 0.1%
178 | 1 Frederico Silva 0.1%
179 | 1 Fredrik Lindin 0.1%
180 | 1 Gareth Aye 0.1%
181 | 1 Gareth Murphy 0.1%
182 | 1 Gavin Mogan 0.1%
183 | 1 Giovanni Bassi 0.1%
184 | 1 Glen Huang 0.1%
185 | 1 Greg Perkins 0.1%
186 | 1 Harish 0.1%
187 | 1 Harry Brundage 0.1%
188 | 1 Herman Junge 0.1%
189 | 1 Ian Young 0.1%
190 | 1 Ivan 0.1%
191 | 1 JP Bochi 0.1%
192 | 1 Jaakko Salonen 0.1%
193 | 1 Jakub Nešetřil 0.1%
194 | 1 James Bowes 0.1%
195 | 1 James Lal 0.1%
196 | 1 Jan Kopriva 0.1%
197 | 1 Jason Barry 0.1%
198 | 1 Javier Aranda 0.1%
199 | 1 Jean Ponchon 0.1%
200 | 1 Jeff Kunkle 0.1%
201 | 1 Jeremy Martin 0.1%
202 | 1 Jimmy Cuadra 0.1%
203 | 1 John Doty 0.1%
204 | 1 Jonathan Creamer 0.1%
205 | 1 Jonathan Park 0.1%
206 | 1 Jussi Virtanen 0.1%
207 | 1 Katie Gengler 0.1%
208 | 1 Kazuhito Hokamura 0.1%
209 | 1 Kent C. Dodds 0.1%
210 | 1 Kevin Conway 0.1%
211 | 1 Kirill Korolyov 0.1%
212 | 1 Koen Punt 0.1%
213 | 1 Laszlo Bacsi 0.1%
214 | 1 Liam Newman 0.1%
215 | 1 Linus Unnebäck 0.1%
216 | 1 László Bácsi 0.1%
217 | 1 Maciej Małecki 0.1%
218 | ```
219 |
220 | ## Links
221 |
222 | - [Google Group](http://groups.google.com/group/mochajs)
223 | - [Wiki](https://github.com/mochajs/mocha/wiki)
224 | - Mocha [Extensions and reporters](https://github.com/mochajs/mocha/wiki)
225 |
--------------------------------------------------------------------------------
/bower_components/mocha/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mocha",
3 | "version": "2.2.1",
4 | "homepage": "http://mocha.github.io/mocha",
5 | "description": "simple, flexible, fun test framework",
6 | "authors": [
7 | "TJ Holowaychuk ",
8 | "Joshua Appelman ",
9 | "Oleg Gaidarenko ",
10 | "Christoffer Hallas ",
11 | "Christopher Hiller ",
12 | "Travis Jeffery ",
13 | "Johnathan Ong ",
14 | "Guillermo Rauch "
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/mochajs/mocha.git"
19 | },
20 | "main": [
21 | "mocha.js",
22 | "mocha.css"
23 | ],
24 | "ignore": [
25 | "bin",
26 | "editors",
27 | "images",
28 | "lib",
29 | "support",
30 | "test",
31 | ".gitignore",
32 | ".npmignore",
33 | ".travis.yml",
34 | "component.json",
35 | "index.js",
36 | "Makefile",
37 | "package.json"
38 | ],
39 | "keywords": [
40 | "mocha",
41 | "test",
42 | "bdd",
43 | "tdd",
44 | "tap"
45 | ],
46 | "license": "MIT"
47 | }
--------------------------------------------------------------------------------
/bower_components/mocha/media/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/bower_components/mocha/mocha.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | body {
4 | margin:0;
5 | }
6 |
7 | #mocha {
8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
9 | margin: 60px 50px;
10 | }
11 |
12 | #mocha ul,
13 | #mocha li {
14 | margin: 0;
15 | padding: 0;
16 | }
17 |
18 | #mocha ul {
19 | list-style: none;
20 | }
21 |
22 | #mocha h1,
23 | #mocha h2 {
24 | margin: 0;
25 | }
26 |
27 | #mocha h1 {
28 | margin-top: 15px;
29 | font-size: 1em;
30 | font-weight: 200;
31 | }
32 |
33 | #mocha h1 a {
34 | text-decoration: none;
35 | color: inherit;
36 | }
37 |
38 | #mocha h1 a:hover {
39 | text-decoration: underline;
40 | }
41 |
42 | #mocha .suite .suite h1 {
43 | margin-top: 0;
44 | font-size: .8em;
45 | }
46 |
47 | #mocha .hidden {
48 | display: none;
49 | }
50 |
51 | #mocha h2 {
52 | font-size: 12px;
53 | font-weight: normal;
54 | cursor: pointer;
55 | }
56 |
57 | #mocha .suite {
58 | margin-left: 15px;
59 | }
60 |
61 | #mocha .test {
62 | margin-left: 15px;
63 | overflow: hidden;
64 | }
65 |
66 | #mocha .test.pending:hover h2::after {
67 | content: '(pending)';
68 | font-family: arial, sans-serif;
69 | }
70 |
71 | #mocha .test.pass.medium .duration {
72 | background: #c09853;
73 | }
74 |
75 | #mocha .test.pass.slow .duration {
76 | background: #b94a48;
77 | }
78 |
79 | #mocha .test.pass::before {
80 | content: '✓';
81 | font-size: 12px;
82 | display: block;
83 | float: left;
84 | margin-right: 5px;
85 | color: #00d6b2;
86 | }
87 |
88 | #mocha .test.pass .duration {
89 | font-size: 9px;
90 | margin-left: 5px;
91 | padding: 2px 5px;
92 | color: #fff;
93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
96 | -webkit-border-radius: 5px;
97 | -moz-border-radius: 5px;
98 | -ms-border-radius: 5px;
99 | -o-border-radius: 5px;
100 | border-radius: 5px;
101 | }
102 |
103 | #mocha .test.pass.fast .duration {
104 | display: none;
105 | }
106 |
107 | #mocha .test.pending {
108 | color: #0b97c4;
109 | }
110 |
111 | #mocha .test.pending::before {
112 | content: '◦';
113 | color: #0b97c4;
114 | }
115 |
116 | #mocha .test.fail {
117 | color: #c00;
118 | }
119 |
120 | #mocha .test.fail pre {
121 | color: black;
122 | }
123 |
124 | #mocha .test.fail::before {
125 | content: '✖';
126 | font-size: 12px;
127 | display: block;
128 | float: left;
129 | margin-right: 5px;
130 | color: #c00;
131 | }
132 |
133 | #mocha .test pre.error {
134 | color: #c00;
135 | max-height: 300px;
136 | overflow: auto;
137 | }
138 |
139 | /**
140 | * (1): approximate for browsers not supporting calc
141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
142 | * ^^ seriously
143 | */
144 | #mocha .test pre {
145 | display: block;
146 | float: left;
147 | clear: left;
148 | font: 12px/1.5 monaco, monospace;
149 | margin: 5px;
150 | padding: 15px;
151 | border: 1px solid #eee;
152 | max-width: 85%; /*(1)*/
153 | max-width: calc(100% - 42px); /*(2)*/
154 | word-wrap: break-word;
155 | border-bottom-color: #ddd;
156 | -webkit-border-radius: 3px;
157 | -webkit-box-shadow: 0 1px 3px #eee;
158 | -moz-border-radius: 3px;
159 | -moz-box-shadow: 0 1px 3px #eee;
160 | border-radius: 3px;
161 | }
162 |
163 | #mocha .test h2 {
164 | position: relative;
165 | }
166 |
167 | #mocha .test a.replay {
168 | position: absolute;
169 | top: 3px;
170 | right: 0;
171 | text-decoration: none;
172 | vertical-align: middle;
173 | display: block;
174 | width: 15px;
175 | height: 15px;
176 | line-height: 15px;
177 | text-align: center;
178 | background: #eee;
179 | font-size: 15px;
180 | -moz-border-radius: 15px;
181 | border-radius: 15px;
182 | -webkit-transition: opacity 200ms;
183 | -moz-transition: opacity 200ms;
184 | transition: opacity 200ms;
185 | opacity: 0.3;
186 | color: #888;
187 | }
188 |
189 | #mocha .test:hover a.replay {
190 | opacity: 1;
191 | }
192 |
193 | #mocha-report.pass .test.fail {
194 | display: none;
195 | }
196 |
197 | #mocha-report.fail .test.pass {
198 | display: none;
199 | }
200 |
201 | #mocha-report.pending .test.pass,
202 | #mocha-report.pending .test.fail {
203 | display: none;
204 | }
205 | #mocha-report.pending .test.pass.pending {
206 | display: block;
207 | }
208 |
209 | #mocha-error {
210 | color: #c00;
211 | font-size: 1.5em;
212 | font-weight: 100;
213 | letter-spacing: 1px;
214 | }
215 |
216 | #mocha-stats {
217 | position: fixed;
218 | top: 15px;
219 | right: 10px;
220 | font-size: 12px;
221 | margin: 0;
222 | color: #888;
223 | z-index: 1;
224 | }
225 |
226 | #mocha-stats .progress {
227 | float: right;
228 | padding-top: 0;
229 | }
230 |
231 | #mocha-stats em {
232 | color: black;
233 | }
234 |
235 | #mocha-stats a {
236 | text-decoration: none;
237 | color: inherit;
238 | }
239 |
240 | #mocha-stats a:hover {
241 | border-bottom: 1px solid #eee;
242 | }
243 |
244 | #mocha-stats li {
245 | display: inline-block;
246 | margin: 0 5px;
247 | list-style: none;
248 | padding-top: 11px;
249 | }
250 |
251 | #mocha-stats canvas {
252 | width: 40px;
253 | height: 40px;
254 | }
255 |
256 | #mocha code .comment { color: #ddd; }
257 | #mocha code .init { color: #2f6fad; }
258 | #mocha code .string { color: #5890ad; }
259 | #mocha code .keyword { color: #8a6343; }
260 | #mocha code .number { color: #2f6fad; }
261 |
262 | @media screen and (max-device-width: 480px) {
263 | #mocha {
264 | margin: 60px 0px;
265 | }
266 |
267 | #mocha #stats {
268 | position: absolute;
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/demo/app.css:
--------------------------------------------------------------------------------
1 | input {
2 | width: auto; }
3 |
4 | input.command-search {
5 | width: 100px; }
6 |
7 | *[disabled]
8 | .disable, span.disable {
9 | color: gray;
10 | font-style: italic; }
11 |
12 | .invalid, input.invalid {
13 | border: 1px solid red; }
14 |
15 | .inline-block {
16 | display: inline-block; }
17 |
18 | img {
19 | display: inline-block; }
20 |
21 | div {
22 | font-size: 20px;
23 | margin: 0 0 0 0;
24 | padding: 0 0 0 0;
25 | border: 0; }
26 |
27 | body {
28 | overflow: hidden; }
29 |
30 | body {
31 | position: absolute;
32 | top: 0px;
33 | left: 0px;
34 | bottom: 0px;
35 | right: 0px; }
36 |
37 | @-webkit-keyframes dcdialog-fadeout {
38 | 0% {
39 | opacity: 1; }
40 | 100% {
41 | opacity: 0; } }
42 |
43 | @keyframes dcdialog-fadeout {
44 | 0% {
45 | opacity: 1; }
46 | 100% {
47 | opacity: 0; } }
48 |
49 | @-webkit-keyframes dcdialog-fadein {
50 | 0% {
51 | opacity: 0; }
52 | 100% {
53 | opacity: 1; } }
54 |
55 | @keyframes dcdialog-fadein {
56 | 0% {
57 | opacity: 0; }
58 | 100% {
59 | opacity: 1; } }
60 |
61 | .dcdialog, .dcdialog *, .dcdialog :after, .dcdialog :before {
62 | -webkit-box-sizing: border-box;
63 | -moz-box-sizing: border-box;
64 | box-sizing: border-box; }
65 |
66 | .dcdialog {
67 | position: fixed;
68 | overflow: auto;
69 | -webkit-overflow-scrolling: touch;
70 | z-index: 10000;
71 | top: 0;
72 | right: 0;
73 | bottom: 0;
74 | left: 0; }
75 |
76 | .dcdialog-overlay {
77 | position: fixed;
78 | background: rgba(0, 0, 0, 0.4);
79 | top: 0;
80 | right: 0;
81 | bottom: 0;
82 | left: 0;
83 | -webkit-backface-visibility: hidden;
84 | -webkit-animation: dcdialog-fadein 0.5s;
85 | animation: dcdialog-fadein 0.5s; }
86 |
87 | .dcdialog.dcdialog-closing .dcdialog-overlay {
88 | -webkit-backface-visibility: hidden;
89 | -webkit-animation: dcdialog-fadeout 0.5s;
90 | animation: dcdialog-fadeout 0.5s; }
91 |
92 | .dcdialog-content {
93 | background: #fff;
94 | -webkit-backface-visibility: hidden;
95 | -webkit-animation: dcdialog-fadein 0.5s;
96 | animation: dcdialog-fadein 0.5s; }
97 |
98 | .dcdialog.dcdialog-closing .dcdialog-content {
99 | -webkit-backface-visibility: hidden;
100 | -webkit-animation: dcdialog-fadeout 0.5s;
101 | animation: dcdialog-fadeout 0.5s; }
102 |
103 | .dcdialog-close:before {
104 | font-family: Helvetica, Arial, sans-serif;
105 | content: '\00D7';
106 | cursor: pointer; }
107 |
108 | body.dcdialog-open {
109 | overflow: hidden; }
110 |
111 | @-webkit-keyframes dcdialog-flyin {
112 | 0% {
113 | opacity: 0;
114 | -webkit-transform: translateY(-40px);
115 | transform: translateY(-40px); }
116 | 100% {
117 | opacity: 1;
118 | -webkit-transform: translateY(0);
119 | transform: translateY(0); } }
120 |
121 | @keyframes dcdialog-flyin {
122 | 0% {
123 | opacity: 0;
124 | -webkit-transform: translateY(-40px);
125 | -ms-transform: translateY(-40px);
126 | transform: translateY(-40px); }
127 | 100% {
128 | opacity: 1;
129 | -webkit-transform: translateY(0);
130 | -ms-transform: translateY(0);
131 | transform: translateY(0); } }
132 |
133 | @-webkit-keyframes dcdialog-flyout {
134 | 0% {
135 | opacity: 1;
136 | -webkit-transform: translateY(0);
137 | transform: translateY(0); }
138 | 100% {
139 | opacity: 0;
140 | -webkit-transform: translateY(-40px);
141 | transform: translateY(-40px); } }
142 |
143 | @keyframes dcdialog-flyout {
144 | 0% {
145 | opacity: 1;
146 | -webkit-transform: translateY(0);
147 | -ms-transform: translateY(0);
148 | transform: translateY(0); }
149 | 100% {
150 | opacity: 0;
151 | -webkit-transform: translateY(-40px);
152 | -ms-transform: translateY(-40px);
153 | transform: translateY(-40px); } }
154 |
155 | .dcdialog.dcdialog-theme-default {
156 | padding-bottom: 160px;
157 | padding-top: 20px; }
158 |
159 | .dcdialog.dcdialog-theme-default.dcdialog-closing .dcdialog-content {
160 | -webkit-animation: dcdialog-flyout 0.5s;
161 | animation: dcdialog-flyout 0.5s; }
162 |
163 | .dcdialog.dcdialog-theme-default .dcdialog-content {
164 | -webkit-animation: dcdialog-flyin 0.5s;
165 | animation: dcdialog-flyin 0.5s;
166 | background: #f0f0f0;
167 | border-radius: 5px;
168 | color: #444;
169 | font-family: 'Helvetica',sans-serif;
170 | font-size: 1.1em;
171 | line-height: 1.5em;
172 | margin: 0 auto;
173 | max-width: 100%;
174 | padding: 1em;
175 | position: relative;
176 | width: 450px; }
177 |
178 | .dcdialog.dcdialog-theme-default .dcdialog-close {
179 | border-radius: 5px;
180 | cursor: pointer;
181 | position: absolute;
182 | right: 0;
183 | top: 0; }
184 |
185 | .dcdialog.dcdialog-theme-default .dcdialog-close:before {
186 | background: transparent;
187 | border-radius: 3px;
188 | color: #bbb;
189 | content: '\00D7';
190 | font-size: 26px;
191 | font-weight: 400;
192 | height: 30px;
193 | line-height: 26px;
194 | position: absolute;
195 | right: 3px;
196 | text-align: center;
197 | top: 3px;
198 | width: 30px; }
199 |
200 | .dcdialog.dcdialog-theme-default .dcdialog-close:hover:before,
201 | .dcdialog.dcdialog-theme-default .dcdialog-close:active:before {
202 | color: #777; }
203 |
204 | .dcdialog.dcdialog-theme-default .dcdialog-message {
205 | margin-bottom: .5em; }
206 |
207 | .dcdialog.dcdialog-theme-default .dcdialog-input {
208 | margin-bottom: 1em; }
209 |
210 | .dcdialog.dcdialog-theme-default .dcdialog-input textarea,
211 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="text"],
212 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="password"],
213 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="email"],
214 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="url"] {
215 | background: #fff;
216 | border: 0;
217 | border-radius: 3px;
218 | font-family: inherit;
219 | font-size: inherit;
220 | font-weight: inherit;
221 | margin: 0 0 0.25em;
222 | min-height: 2.5em;
223 | padding: 0.25em 0.67em;
224 | width: 100%; }
225 |
226 | .dcdialog.dcdialog-theme-default .dcdialog-input textarea:focus,
227 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="text"]:focus,
228 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="password"]:focus,
229 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="email"]:focus,
230 | .dcdialog.dcdialog-theme-default .dcdialog-input input[type="url"]:focus {
231 | -webkit-box-shadow: inset 0 0 0 2px #8dbdf1;
232 | box-shadow: inset 0 0 0 2px #8dbdf1;
233 | outline: none; }
234 |
235 | .dcdialog.dcdialog-theme-default .dcdialog-buttons {
236 | *zoom: 1; }
237 |
238 | .dcdialog.dcdialog-theme-default .dcdialog-buttons:after {
239 | content: '';
240 | display: table;
241 | clear: both; }
242 |
243 | .dcdialog.dcdialog-theme-default .dcdialog-button {
244 | border: 0;
245 | border-radius: 3px;
246 | cursor: pointer;
247 | float: right;
248 | font-family: inherit;
249 | font-size: .8em;
250 | letter-spacing: .1em;
251 | line-height: 1em;
252 | margin: 0 0 0 0.5em;
253 | padding: 0.75em 2em;
254 | text-transform: uppercase; }
255 |
256 | .dcdialog.dcdialog-theme-default .dcdialog-button:focus {
257 | -webkit-animation: dcdialog-pulse 1.1s infinite;
258 | animation: dcdialog-pulse 1.1s infinite;
259 | outline: none; }
260 |
261 | @media (max-width: 568px) {
262 | .dcdialog.dcdialog-theme-default .dcdialog-button:focus {
263 | -webkit-animation: none;
264 | animation: none; } }
265 |
266 | .dcdialog.dcdialog-theme-default .dcdialog-button.dcdialog-button-primary {
267 | background: #3288e6;
268 | color: #fff; }
269 |
270 | .dcdialog.dcdialog-theme-default .dcdialog-button.dcdialog-button-secondary {
271 | background: #e0e0e0;
272 | color: #777; }
273 |
274 | .hidden {
275 | display: none; }
276 |
277 | .splitbarV {
278 | position: absolute;
279 | top: 0px;
280 | left: 200px;
281 | width: 6px;
282 | height: 100%;
283 | z-index: 300;
284 | line-height: 0px;
285 | font-size: 0px;
286 | border-left: solid 1px #9cbdff;
287 | border-right: solid 1px #9cbdff;
288 | background: #cbe1fb url(img/panev.gif) 0% 50%; }
289 |
290 | .splitbuttonV {
291 | position: absolute;
292 | margin-top: -41px;
293 | top: 50%;
294 | left: -11px;
295 | height: 83px;
296 | width: 10px;
297 | background: transparent url(img/panevc.gif) 10px 50%; }
298 |
299 | .splitbuttonV.invert {
300 | position: absolute;
301 | left: 6px;
302 | background: transparent url(img/panevc.gif) 0px 50%; }
303 |
304 | .splitbarH {
305 | position: absolute;
306 | top: 200px;
307 | left: 0px;
308 | width: 100%;
309 | height: 6px;
310 | z-index: 300;
311 | text-align: left;
312 | line-height: 0px;
313 | font-size: 0px;
314 | border-top: solid 1px #9cbdff;
315 | border-bottom: solid 1px #9cbdff;
316 | background: #cbe1fb url(img/paneh.gif) 50% 0%; }
317 |
318 | .splitbuttonH {
319 | position: absolute;
320 | margin-left: -41px;
321 | top: -11px;
322 | left: 50%;
323 | height: 10px !important;
324 | width: 83px;
325 | background: transparent url(img/panehc.gif) 50% -10px; }
326 |
327 | .splitbuttonH.invert {
328 | position: absolute;
329 | top: 6px;
330 | background: transparent url(img/panehc.gif) 50% 0px; }
331 |
332 | .splitbarV.inactive, .splitbarH.inactive, .splitbuttonV.inactive, .splitbuttonH.inactive {
333 | -moz-opacity: .50;
334 | filter: alpha(opacity=50);
335 | opacity: .50; }
336 |
--------------------------------------------------------------------------------
/demo/demo-choose-web-framework.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports = ->
2 | onBlur = (event) ->
3 | comp.otherFramework = event.target.value
4 | comp.update()
5 | comp.textNode.focus()
6 | return
7 |
8 | frameworks = ['Domcom', 'jQuery', 'Angular', 'React', 'Backbone', 'Ember']
9 | view = ->
10 | currentFrameWorks = frameworks.concat([comp.otherFramework || 'other'])
11 | frameworkLiItems = currentFrameWorks.map((item) ->
12 | onClick = ->
13 | comp.choice = item
14 | ['li', {onClick}, "#{item}"])
15 | ['div',
16 | ['label', 'Please choose: '],
17 | ['ol', {}, frameworkLiItems...],
18 | ['div', "You perfer ", comp.choice, "."]
19 | ['label', 'add some others: '],
20 | ['text', {onBlur, value:comp.otherFramework, key:'other-framework', ref:((el) -> comp.textNode = el), keepid:1}]
21 | ]
22 |
23 | comp = dc({view, choice:'Domcom'})
24 |
25 |
26 |
--------------------------------------------------------------------------------
/demo/demo-controls.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports = ->
2 | view = ['div', ['checkbox','a'], ['text', 'a'],['checkbox', 'b'], ['text', 'b']]
3 | return dc({view, a:true, b:true})
--------------------------------------------------------------------------------
/demo/demo-counter.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports = ->
2 | timer = null
3 |
4 | view = ->
5 | comp = this
6 | start = =>
7 | startCounter()
8 | return
9 | stop = =>
10 | stopCounter()
11 | reset = =>
12 | stopCounter()
13 | comp.count = 0
14 |
15 | ['div',
16 | ['p', this.count],
17 | ['p', {onClick: stop, keepid:101}, 'stop'],
18 | ['p', {onClick: reset, keepid:102}, 'reset'],
19 | ['p', {onClick: start, keepid:103}, 'start']
20 | ]
21 | comp = dc({view, count:0})
22 | startCounter = ->
23 | timer = setInterval (-> comp.count++), 300
24 | return
25 | stopCounter = ->
26 | clearInterval timer
27 |
28 | comp.on 'mounted', ->
29 | startCounter()
30 | comp.on 'unmounting', ->
31 | stopCounter()
32 |
33 |
34 | return comp
35 |
--------------------------------------------------------------------------------
/demo/demo-debug.coffee:
--------------------------------------------------------------------------------
1 | exports = {}
2 |
3 | exports.demoEachPush = ->
4 | list = [1, 2]
5 | view = ['div', ['div', this.list.map((item) => ['p', item])], 'some other thing']
6 | comp = dc({view, list})
7 | comp.mount()
8 | lst.push 3
9 | comp.update()
10 |
11 | exports.demoIfEach = ->
12 | view = ->
13 | if this.showing
14 | this.list.map (item) -> ['div', {}, item]
15 | else
16 | null
17 | comp = {view, list:[1, 2]}
18 | comp.mount()
19 | comp.showing = false
20 | comp.showing = true
21 | comp
22 |
23 | exports.demoModelOnMultipleInput = ->
24 | view = ->
25 | ['div', ['text', {'#':[[dc.model,'x']]}], ['text', {'#':[[dc.model,'x']]}]]
26 | comp = dc({view, x:'input some text'})
27 |
28 | export default exports
--------------------------------------------------------------------------------
/demo/demo-each.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports= ->
2 | list = [1, 2, 3, 4, 5, 6]
3 | i = 7
4 | onClick = ->
5 | list.push i++
6 | comp.update()
7 | view = -> ['div', {onClick}, 'click to append: ', this.list.join(' ')]
8 | comp = dc({view, list})
9 |
10 |
--------------------------------------------------------------------------------
/demo/demo-function-lead-item.coffee:
--------------------------------------------------------------------------------
1 | if_ = (test, then_, else_) ->
2 | if test
3 | then_
4 | else
5 | else_
6 |
7 |
8 | export default module.exports = ->
9 | view = -> ['div',
10 | ['text', 'x'],
11 | [if_, !(this.x*1), ['div', 'It is 0 or NaN.'], ['div', 'it is other number']]]
12 | dc({view, x:1})
13 |
--------------------------------------------------------------------------------
/demo/demo-show-hide.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports = ->
2 | view = (data) ->
3 | if this.display
4 | display = 'block'
5 | else
6 | display = 'none'
7 | onClick = () =>
8 | this.display = !this.display
9 |
10 | ['div',
11 | ['div', { onClick}, 'click to show or hide by changing style.display'],
12 | ['p', { style:{display}}, 'this is the controlled content']
13 | ]
14 | comp = dc({view, display:true})
15 | return comp
16 |
17 |
--------------------------------------------------------------------------------
/demo/demo-sum.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports = () ->
2 |
3 | data = { a: 1, b: 2 }
4 | view = (data) ->
5 | props1 =
6 | focusid:1
7 | value: data.a,
8 | onChange:(event) ->
9 | data.a = event.target.value*1
10 | comp.update()
11 |
12 | props2 =
13 | focusid:2
14 | value: data.b,
15 | onChange: (event) ->
16 | data.b = event.target.value*1
17 | comp.update()
18 |
19 | return ['div',{key:0}
20 | ['text', props1],
21 | ['text', props2],
22 | ['p', data.a + data.b]
23 | ]
24 |
25 | comp = dc({data, view})
26 |
--------------------------------------------------------------------------------
/demo/demo-text-model.coffee:
--------------------------------------------------------------------------------
1 | export default module.exports = ->
2 | view = -> ['div', ['text', {$model:'a', key:1, ref:(el)->comp.textInput = el}], ['p', {}, this.a]]
3 | comp = dc({view, a:'hello'})
4 | comp.on 'updated', ->
5 | comp.textInput.focus()
6 | return
7 |
--------------------------------------------------------------------------------
/demo/index-dist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tiiji demo index
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo/index.coffee:
--------------------------------------------------------------------------------
1 | dc.addReactProxy window.React, window.ReactDOM, window.React.Component
2 |
3 | {runDemo, demoMap} = require './util'
4 |
5 | window.onload = ->
6 | # demo = require('./demo-text-model')
7 | # comp = demo()
8 | # comp.mount('#demo')
9 | # return
10 | # comp = demoMap["show hide"]()
11 | # comp = demoMap["counter"]()
12 | # comp = demoMap["dialog"]()
13 | # comp = demoMap["event"]()
14 | # comp = demoMap["controls"]()
15 | # comp = demoMap["if"]()
16 | # comp = demoMap["each1"]()
17 | # comp = demoMap["each2"]()
18 | # comp = demoMap["each3"]()
19 | # comp = demoMap["each4"]()
20 | # comp = demoMap["switch 1 2 3 4"]()
21 | # comp = demoMap["splitter"]()
22 | # comp = demoMap["sum"]()
23 | # comp = demoMap["text model"]()
24 | # comp = demoMap["auto width edit"]()
25 | # comp = demoMap["mount/unmount"]()
26 | # comp = chooseFramework()
27 | # comp.mount()
28 |
29 | # demoEachPush()
30 | # demoIfEach()
31 | # demoModelOnMultipleInput()
32 |
33 | runDemo(demoMap, 'choose web framework', '#demo')
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Domcom demo index
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/demo/todomvc-dist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | domcom.js -- TodoMVC
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/demo/todomvc.coffee:
--------------------------------------------------------------------------------
1 | dc.addReactProxy window.React, window.ReactDOM, window.React.Component
2 |
3 | #######################################################################################################################
4 | # store
5 |
6 | window.fetch = -> JSON.parse localStorage.getItem('todoApp') || '[]'
7 | window.save = (todos) -> localStorage.setItem('todoApp', JSON.stringify(todos))
8 |
9 | #######################################################################################################################
10 | # model
11 | # [ { title: string, completed: boolean } ... ]
12 | window.todos = []
13 | window.todos = fetch()
14 |
15 | #######################################################################################################################
16 | # controller
17 |
18 | viewStatusHash = null
19 | editingTodo = {title:'', completed:false}
20 | originalTodo = null
21 | reverted = null
22 | saving = false
23 |
24 | window.getTodos = ->
25 | if viewStatusHash=='active'
26 | todos.filter (todo) -> todo && !todo.completed
27 | else if viewStatusHash=='completed'
28 | todos.filter (todo) -> todo && todo.completed
29 | else
30 | todos
31 |
32 | remainingCount = -> todos.filter((todo) -> !todo.completed).length
33 | completedCount = -> todos.length - remainingCount()
34 | allChecked = -> !remainingCount()
35 |
36 | pluralize = (test, item) ->
37 | if test>1
38 | item+'s'
39 | else
40 | item
41 |
42 | toggleCompleted = (todo) ->
43 | todo.completed = !todo.completed
44 | save todos
45 | app.update()
46 |
47 | markAll = ->
48 | if allChecked() then completed = false
49 | else completed = true
50 |
51 | valid = true
52 | for todo in todos
53 | if todo.completed!=completed
54 | todo.completed = completed
55 | valid = false
56 |
57 | if !valid
58 | save(todos)
59 | app.update()
60 |
61 | editTodo = (todo) ->
62 | editingTodo = todo
63 | originalTodo = Object.assign {}, todo
64 | app.update()
65 |
66 | removeTodo = (todo) ->
67 | index = todos.indexOf todo
68 | todos.splice index, 1
69 | save todos
70 | app.update()
71 |
72 | clearCompletedTodos = ->
73 | valid = true
74 |
75 | i = todos.length - 1
76 | while i>=0
77 | if todos[i].completed
78 | todos.splice i, 1
79 | valid = false
80 | i--
81 |
82 | if !valid
83 | save todos
84 | app.update()
85 |
86 | #######################################################################################################################
87 | # view
88 |
89 |
90 | todoHeader = ->
91 | onKeyUp = (event) ->
92 | console.log('onBlur todos: ', todos)
93 | keyCode = event.keyCode || event.which
94 | if keyCode==27
95 | editingTodo = {title: '', completed:false}
96 | else if keyCode == 13
97 | editingTodo.title = value = event.target.value
98 | if !value
99 | return
100 | else
101 | todos.push(editingTodo)
102 | editingTodo = {title:'', completed:false}
103 | save todos
104 | app.update()
105 | onBlur = (event) ->
106 | console.log('onBlur todos: ', todos)
107 | debugger
108 | node = event.target
109 | value = node.value
110 | editingTodo.title = value
111 | if !value
112 | return
113 | else
114 | todos.push(editingTodo)
115 | editingTodo = {title:'', completed:false}
116 | save todos
117 | app.update()
118 | return
119 | onChange = (event) ->
120 | node = event.target
121 | value = node.value
122 | editingTodo.title = value
123 | app.update()
124 | return
125 |
126 | ['header#header',{key:1}
127 | ['h1', "todos"]
128 | ['text#new-todo', {value:editingTodo.title, onChange, onBlur, onKeyUp, key:2, focusid:1}]
129 |
130 | ]
131 |
132 |
133 | todoEditArea = ->
134 | todos = getTodos()
135 | todoItems = todos.map (todo, index) ->
136 | onChange = (event) ->
137 | todo.title = event.target.value
138 | app.update()
139 | onBlur = (event) ->
140 | todo.title = event.target.value
141 | save(todos)
142 | editingTodo = {}
143 | app.update();
144 | onFocus = ->
145 | todo = editingTodo
146 | onKeyUp = (event) ->
147 | keyCode = event.keyCode || event.which
148 | if keyCode==27
149 | todos[todos.indexOf(todo)] = editingTodo = originalTodo
150 | else if keyCode == 13
151 | save(todos)
152 | editingTodo = {title:'', completed:false}
153 | app.update()
154 | onToggle = ->
155 | toggleCompleted(todo)
156 | if_ = (x, y, z) ->
157 | if x
158 | y
159 | else
160 | z
161 | ['li', {className: { completed: todo.completed, editing: todo==editingTodo, key:5},onDoubleClick:( -> editTodo(todo) )},
162 | ['div#view',
163 | ['checkbox.toggle', {checked: todo.completed, onChange:onToggle, key:1000+index}],
164 | [if_, todo!=editingTodo,
165 | ['label', todo.title],
166 | ['text.edit', { trim:'false', value:todo.title, onBlur, onChange, onFocus, onKeyUp, focusid:100+index}]]
167 | ['button.destroy##float:right', {onClick:(-> removeTodo(todo))}, 'x']]]
168 |
169 | ['section#main',
170 | ['checkbox.toggle#toggle-all', { key:6, checked: !!allChecked(), onChange: markAll}]
171 | ['label##display:inline-block;', {htmlFor: "toggle-all"}, "Mark all as complete"]
172 | ["ul#todo-list", todoItems...],
173 | ["footer#footer", {$show:!!todos.length},
174 | ["span#todo-count", ['strong',remainingCount()], pluralize(remainingCount(), ' item'), ' left'],
175 | ["ul#filters",
176 | ['li', ['a', {className: {selected: viewStatusHash != 'active' && viewStatusHash != 'completed'}, href: "#/all"}, "All"]]
177 | ['li', ['a', {className: {selected: viewStatusHash == 'active'}, href: "#/active"}, "Active"]]
178 | ['li', ['a', {className: {selected: viewStatusHash == 'completed'}, href: "#/completed"}, "Completed"]]]]
179 |
180 | ['button#clear-completed', {onClick: clearCompletedTodos, $show: !!completedCount()},
181 | "Clear completed: "+completedCount()]]
182 |
183 | todoFooter = ['footer#info', {key:100},
184 | ['p', "Double-click to edit a todo"]
185 | ['p', 'Created by ',
186 | ['a', {href:"http://github.com/taijiweb/domcom"},
187 | 'Caoxingming(Tiijizhenren, simeon.chaos@gmail.com)']]
188 | ['p', "Part of ",
189 | ['a', {href:"http://todomvc.com"},
190 | " TodoMVC"]]]
191 |
192 | view = ->
193 | ['section#todoapp', {key:0},
194 | todoHeader()
195 | todoEditArea()
196 | todoFooter
197 | ]
198 | app = dc({view})
199 |
200 | window.updateHash = ->
201 | locationHash = document.location.hash
202 | if locationHash.indexOf('/') >= 0
203 | viewStatusHash = locationHash.split('/')[1] || ''
204 | else
205 | viewStatusHash = locationHash
206 |
207 | #######################################################################################################################
208 | # run the app
209 |
210 | window.runTodoMvc = ->
211 | updateHash()
212 | app.mount('#todo-app')
213 |
214 | window.addEventListener 'hashchange', ->
215 | updateHash()
216 | app.update()
217 |
--------------------------------------------------------------------------------
/demo/todomvc.css:
--------------------------------------------------------------------------------
1 | .edit {
2 | display: inline-block;
3 | }
4 |
5 | .edit {
6 | width: 90%;
7 | }
--------------------------------------------------------------------------------
/demo/todomvc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | domcom.js -- TodoMVC
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/demo/util.coffee:
--------------------------------------------------------------------------------
1 | # domcom demo
2 |
3 | eachDemo = require './demo-each'
4 | import chooseFramework from './demo-choose-web-framework'
5 |
6 | {demoEachPush, demoIfEach, demoModelOnMultipleInput} = require './demo-debug'
7 |
8 | export default module.exports = exports = {}
9 |
10 | exports.demoMap =
11 | 'choose web framework':chooseFramework,
12 | "show hide": require('./demo-show-hide'),
13 | counter: require('./demo-counter')
14 | controls: require('./demo-controls'),
15 | 'function lead item': require('./demo-function-lead-item'),
16 | sum: require('./demo-sum'),
17 | 'text model': require('./demo-text-model')
18 |
19 | exports.makeDemoComponent = makeDemoComponent = (demoMap, initItem) ->
20 | componentsMap = {}
21 | for key, comp of demoMap
22 | if typeof comp == 'function'
23 | componentsMap[key] = comp()
24 | else
25 | componentsMap[key] = comp
26 | view = ->
27 | ['div',
28 | ['select', {$options:Object.keys(demoMap), $model:'select'}],
29 | ['div', componentsMap[comp.select]]]
30 |
31 | comp = dc({view, select:initItem})
32 |
33 | exports.runDemo = (demoMap, initItem, element) ->
34 | comp = makeDemoComponent(demoMap, initItem)
35 | comp.mount(element)
--------------------------------------------------------------------------------
/doc/ concepts and principles.md:
--------------------------------------------------------------------------------
1 | # Domcom framework basic concepts and principles
2 | The principle of the Domcom framework is very simple, and it is clear in one sentence: the Domcom component itself supports the React element in a js-friendly compact format (nested array form) through its view function, and then uses its React proxy component class ReactProxy in it. This nested array is converted to the actual React element in the render method. that is it. For more details, you can take a look at the following further analysis of the basic concepts involved.
3 |
4 | # basic concept
5 | * **Components**
6 |
7 | TComponent is the core concept of the Domcom skeleton, it is the proxy used by the domcom framework to manage React elements. Each component is constructed from a config object. Only the data and view in the fonfig object have special effects. The most important is the view member (which can be the actual view data or the function that generates the view). Then, through the React component class ReactProxy, a react element is generated. When the React element is mounted, the ReactProxy instance pointer this is provided to the proxy member field of the Domcom component. Domcom uses the instance pointer to manage the Dom update of the React element through the update method. (by calling the component.proxy.setState method) and remove.
8 |
9 | * **ReactProxy**
10 |
11 | Each Domcom component creates a React element via ReactProxy. ReactProxy generates a React element structure based on the view field of the corresponding Domcom component.
12 |
13 | * **High-order virtual Dom**
14 |
15 | React invented the concept and technology of virtual Dom, and Domcom carried forward the concept and technology. If the virtual Dom is a pre-declared Dom hierarchy in the form of some kind of js, in order to manage optimization. If we consider the native Dom as a first-order Dom, React's virtual Dom can be thought of as a second-order Dom, because the api that is used directly to create the React element is cumbersome and inconvenient to read and write. So there is a syntax for jsx that returns to the xhtml format and hybridizes with js. Jsx can also be considered as a third-order high-order virtual Dom. The nested array notation provided by Domcom is just another third-order virtual Dom. This format of virtual Dom is easy to read and write, js friendly, no need for toolchain support, and has many advantages.
16 |
17 | The above has clearly and completely introduced Domcom's concepts and principles. People who have further concerns about technical details can read the source code directly. Domcom's source code is very simple, with a total of only seven files, about 500 lines of code, the more critical code is mainly in [Component.coffee](https://github.com/taijiweb/domcom/blob/master/src/Component. Coffee), [react-proxy.coffee](https://github.com/taijiweb/domcom/blob/master/src/react-proxy.coffee), [dc-util.coffee](https://github. Com/taijiweb/domcom/blob/master/src/dc-util.coffee) Of these three files, there are only a hundred lines of CoffeeScript, which are very concise and intuitive to implement. Welcome to read.
--------------------------------------------------------------------------------
/doc/Chinese/API参考.md:
--------------------------------------------------------------------------------
1 | # Domcom API 参考文档
2 |
3 | ************************************************************************
4 |
5 | ## 使用Domcom
6 | ### 获取Domcom
7 | `npm install --save domcom`
8 | `git clone https://www.github.com/taijiweb/domcom`
9 | 下载:[github版本](https://github.com/taijiweb/domcom/releases)
10 | cdn: 感谢cdn.jsdelivr.net 提供cdn链接(替换x.y.z为实际版本号)
11 |
12 | http://cdn.jsdelivr.net/domcom/x.y.z/domcom.min.js
13 |
14 | ### 在页面中设置Domcom
15 | 先要添加React和ReactDom链接。
16 |
17 |
18 |
19 |
20 | 或
21 |
22 |
23 |
24 |
25 | 根据开发和应用需要从安装或下载的文件夹中选择domcom/dist/下的适当文件,向html页面添加script标签:
26 | > 开发版本:``
27 | > 产品版本:``
28 |
29 | 如果使用cdn链接, 则应该添加如下的script标签
30 |
31 | >
32 | https://unpkg.com/domcom@0.8.4/dist/domcom.js
33 | https://unpkg.com/domcom@0.8.4/dist/domcom.min.js
34 | https://cdn.jsdelivr.net/npm/domcom@0.8.4/dist/domcom.js
35 | https://cdn.jsdelivr.net/npm/domcom@0.8.4/dist/domcom.min.js
36 |
37 |
38 | 在domcom的script标签之后添加自己的js脚本:
39 |
40 | > ``
41 |
42 | ### 导入和引用domcom提供的api
43 | 在浏览器环境下,添加domcom的`
16 |
17 |
18 | 或
19 |
20 |
21 |
22 |
23 | 然后添加Domcom script标签:
24 |
25 | * 开发版本: ``
26 | * 产品版本: ``
27 | * cdn:
28 |
29 | https://unpkg.com/domcom@0.8.4/dist/domcom.js
30 | https://unpkg.com/domcom@0.8.4/dist/domcom.min.js
31 | https://cdn.jsdelivr.net/npm/domcom@0.8.4/dist/domcom.js
32 | https://cdn.jsdelivr.net/npm/domcom@0.8.4/dist/domcom.min.js
33 |
34 |
35 | ## 使用dc
36 | 推荐使用Coffee-script, 使用ES6也很好。可以使用babel来支持ES6语法, 使用原生ES5也是很可行的,不管用何种语言,用Domcom都能比用JSX编写reactJS得到更简练的代码。
37 | 因为domcom建立中React基础之上,所有Domcom部件最终都会转化为ReactProxy部件。因此使用Domcom前第一步需要执行以下代码,
38 |
39 | import React, {Component} from 'react'
40 | import ReactDom from 'react-dom'
41 | dc.addReactProxy React, ReactDom, Component
42 |
43 | 如果用script标签方式引入请使用window对象下的全局变量:
44 |
45 | dc.addReactProxy window.React, window.ReactDOM, window.React.Component
46 |
47 |
48 | ## 说明
49 | domcom是用coffee-script开发的。本文档也借用了一些coffee-script的写法。特别是涉及到提供函数参数、回调的地方,比如,
50 | `->` 代表函数定义 `function () {... }`
51 | `(arg1, arg2...) -> ...` 代表函数定义`function (arg1, arg2...) { ... }`
52 |
53 |
54 | ## 部件
55 | 产生部件: `comp = dc(config={data, view, ....})`
56 | 引用部件的react元素和dom节点:`comp.reactElement`,`comp.node`
57 | 部件方法:`mount`, `unmount`, `update`, `copy`, `extend`, `on`, `off`, `emit`
58 | 部件事件:`mounting`, `mounted`, `unmounting`, `updating`, `updated`
59 |
60 | ## 标签元素
61 | `[tag, {props...}, children...]`
62 | `['div.class1.class2#id##styles...', {props...}, children...]`
63 | children可以包含别的标签元素、React部件元素(下文给出的数组形式表示的或者直接用原始的React部件元素)。**特别注意,当然也可以包含Domcom部件实例!**
64 |
65 | ###input标签
66 | `[type, {props...}]`
67 | `['text.class1.class2#id##styles...', {$model:field...}]`
68 | `['text', modelFiled]`
69 |
70 | ##React部件元素
71 | `[ReactClass, {props...}, children...]`
72 | `[ReactClass, '.class1.class2#id##styles...', {props...}, children...]`
73 |
74 | ##指令
75 | Domcom提供了如下内建指令,`$model`, `$show`, `$options`。开发者也可以根据需要方便地扩充自己的指令。
76 |
77 |
78 | ## 便利机制
79 | Domcom提供了一些使开发更方便的机制,列举如下:
80 |
81 | * 如果props中没有key, Domcom会自动产生唯一的key,缺点是每次render key值都是变化的。
82 | * props可以提供keepid,要求Domcom的ReactProxy每次render都为该项使用缓存的相同React元素。这适用于完全静态不变的元素。
83 | * props可以提供focusid,这可以帮助Domcom每次update后能正确处理节点focus,适合于TextInput,TextArea等标签。
84 | * props中可以写classes代替className,className可以使用object,并且能用value来控制
85 | key在classes中的有无。虽然Domcom没有使用[classnames](https://www.npmjs.com/package/classnames),但是理解使用上可参考[classnames](https://www.npmjs.com/package/classnames)
86 | * Domcom 会使用camelcase这个库处理props中的键。可以加入props.dontCamel取消这一处理, 对props.style总是会做这一处理。
87 | * 有的React元素不希望耽搁child元素不以数组形式提供,可以加入props.useSingleChildren满足这一需求。
88 |
89 | props中的指令,keepid,focusid, classes, dontCamel, useSingleChildren不会出现在后来产生的react 元素中,不用担心会因此产生警告或错误。
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/doc/From-react-to-domcom.md:
--------------------------------------------------------------------------------
1 | # From React to Domcom: A web framework that provides DOM components
2 |
3 | github.com/taijiweb/domcom
4 |
5 |
6 | ## design background
7 |
8 | ReactJS has brought a huge shift to the idea of front-end Web application development, subverting many previous ideas. I have had many opportunities to contact and learn ReactJS. While understanding its advanced concepts, I also found that it has some problems, initially focusing on performance, such as the virtual dom of repetitively generated components, the overall calculation of Diff and patching, and the update detection. The mechanism is not perfect enough. My idea is to identify the validity and variability of all Dom node characteristics and all other component characteristics in one way. The first thought was to distinguish between ordinary values and special objects. Later, it was found that using the response function is the most appropriate means. The adoption of the response function allowed me to further improve the component's update detection mechanism. Later, I found that these design decisions, which are evolved and evolved, are more obvious and important to the improvement and convenience of data management and application design than performance. In the end, Domcom achieved all of my design goals beyond expectations, not only avoiding the fundamental factors that affect ReactJS performance, but also making up for some of the obvious flaws that ReactJS's overall architecture affects application design and implementation complexity.
9 |
10 |
11 | ## Domcom overall characteristics
12 |
13 | Domcom is an acronym for the merger of DOM and Component, which is designed to provide DOM components for web applications. With Domcom, you can improve data management as a whole, minimize unnecessary DOM operations, and improve program efficiency. Domcom provides components that are declarative and responsive, leveraging both the functional paradigm and the object paradigm to improve code reuse and simplify design. The following is a list of the main features of this framework:
14 |
15 | * Declarative components based on response functions
16 |
17 | Any dom property and other suitable properties of the component can be response functions.
18 |
19 | * Minimize Dom access and updates
20 |
21 | Domcom pre-declares and describes the entire Dom through the component, and the web application does not need to access the Dom feature and state most of the time.
22 |
23 | * Easy to combine extended parts
24 |
25 | Domcom can use function paradigm combinations to generate parts and set pass parameters, or use object paradigms to define new parts through inheritance mechanisms. This can improve the code reuse of web applications, reduce complexity, and make the design simpler and clearer.
26 |
27 | * Maximize decoupling models and controllers
28 |
29 | As a provider of Dom components, Domcom focuses on solving view problems in the MVC or MVV* mode. Fully neutral and open perspective on models and controllers. Common values, variables, and functions become bridges and pipes to data. With this approach, domcom maximizes the decoupling of views into models and controllers, which facilitates implementation and simplifies design. With Domcom, many times we don't even need to make a deliberate design for a Model or Controller. Depending on the complexity of the application and the related requirements, Domcom can of course be used in conjunction with POJOs, events, observables, class-based extensions, Flux, immutable, etc., and may even borrow backbone.js, ember.js, react.js, etc. Related components in an existing framework or library.
30 |
31 |
32 | * No need for immutable data structures, no need for additional data management solutions
33 |
34 | Domcom itself has a relatively modest code size and is currently minimized by approximately 21K. Because of the mechanism used by the framework for update detection and data transfer, it allows for more flexible use of data without the additional need to use immutable data structures.
35 |
36 | * No template language required, no jsx required
37 |
38 | Domcom is designed to make it easy to use the Coffee-script and JavaScript languages directly. The code in the Javascript language has been very succinct and readable, and you can use Coffee-script to achieve better results, which is basically more flexible than the Jade template language. Domcom describes the React element hierarchy based on a nested array of javascript languages, which is very readable and easy to write, more flexible and usable than jsx.
39 |
40 | ## Links, Documents, and Downloads
41 |
42 | Can be downloaded and installed with npm
43 |
44 | Npm install --save domcom
45 |
46 | Or use cdn:
47 |
48 | https://unpkg.com/domcom/dist/domcom.js
49 | https://unpkg.com/domcom/dist/domcom.min.js
50 | https://cdn.jsdelivr.net/npm/domcom/dist/domcom.js
51 | https://cdn.jsdelivr.net/npm/domcom/dist/domcom.min.js
52 |
53 |
54 | [Github project address] (https://www.github.com/taijiweb/domcom)
55 |
56 | [Github releases] (https://github.com/taijiweb/domcom/releases)
57 |
58 | [npm package address] (https://www.npmjs.com/package/domcom)
59 |
60 | ### Document
61 |
62 | Domcom has provided comprehensive documentation (especially Chinese documentation), all in [doc/folder] (https://github.com/taijiweb/domcom/blob/master/doc). Chinese documentation is concentrated in the [doc/Chinese folder] (https://github.com/taijiweb/domcom/blob/master/doc/Chinese):
63 |
64 | [README](https://github.com/taijiweb/domcom)
65 |
66 | [Introduction and tutorials] (https://github.com/taijiweb/domcom/blob/master/doc/Chinese/introduction and tutorials.md): Domcom's basic introduction and getting started tutorials.
67 |
68 | [Concepts and Principles] (https://github.com/taijiweb/domcom/blob/master/doc/Chinese/Concepts and Principles.md): Learn about the basic concepts and principles of Domcom.
69 |
70 | [API Reference] (https://github.com/taijiweb/domcom/blob/master/doc/Chinese/API Reference.md): A formal and detailed reference to all of Domcom's public APIs.
71 |
72 | [Quick Table] (https://github.com/taijiweb/domcom/blob/master/doc/Chinese/Cheat Table.md): Familiar with Domcom's API, common techniques and idioms.
73 |
74 | [Frequently Asked Questions] (https://github.com/taijiweb/domcom/blob/master/doc/Chinese/Frequently Asked Questions.md): Some questions you often want to know about Domcom.
75 |
76 | The [doc/](https://github.com/taijiweb/domcom/blob/master/doc) folder has more documentation content.
77 |
78 | ##社区
79 |
80 | QQ Group: DomcomJS Community 371409830
81 |
82 | Google groups: [Domcom](https://groups.google.com/d/forum/domcom)
83 |
84 | ## Description
85 |
86 | This framework generated ideas from January 2015 and began development in April. During this period, it has undergone several iterations. The core code has undergone many major refactorings. The current implementation is reasonable, concise, optimized, and has rich tests. In the first half of this year, one of my projects has completely abandoned jQuery and Angular to Domcom, which has a good experience. Welcome to participate in the Domcom project, build a community, make web development easier, and develop more world-changing applications. In 2018, I made a thorough redesign and implementation of the project based on the React framework. The whole framework concept is simpler, the implementation is greatly simplified (from 80k to 20k), and the resources of React can be utilized to greatly improve the usability of the framework. .
--------------------------------------------------------------------------------
/doc/cheat-sheet.md:
--------------------------------------------------------------------------------
1 | # domcom cheat sheet
2 |
3 | ## Download
4 |
5 | Npm install --save domcom
6 |
7 | Git clone https://www.github.com/taijiweb/domcom
8 |
9 | ## Code size
10 | Domcom itself minimizes the file domcom.min.js by approximately 21k. A total of about 500 lines of code for src/7.coffee source files.
11 |
12 | ## Add domcom to the page
13 | First add the React and ReactDom links.
14 |
15 |
16 |
17 |
18 | or
19 |
20 |
21 |
22 |
23 | Then add the Domcom script tag:
24 |
25 | * Development version: ``
26 | * Product version: ``
27 | * cdn:
28 |
29 | Https://unpkg.com/domcom@0.8.4/dist/domcom.js
30 | Https://unpkg.com/domcom@0.8.4/dist/domcom.min.js
31 | Https://cdn.jsdelivr.net/npm/domcom@0.8.4/dist/domcom.js
32 | Https://cdn.jsdelivr.net/npm/domcom@0.8.4/dist/domcom.min.js
33 |
34 |
35 | ## Using dc
36 | It is recommended to use Coffee-script, and it is also good to use ES6. You can use babel to support ES6 syntax. It is also very feasible to use native ES5. No matter which language you use, Domcom can get more concise code than writing reactJS with JSX.
37 | Because domcom is built on top of React, all Domcom components will eventually be converted to ReactProxy components. So the first step before using Domcom requires the following code.
38 |
39 | Import React, {Component} from 'react'
40 | Import ReactDom from 'react-dom'
41 | dc.addReactProxy React, ReactDom, Component
42 |
43 | If you use the script tag method, please use the global variable under the window object:
44 |
45 | dc.addReactProxy window.React, window.ReactDOM, window.React.Component
46 |
47 |
48 | ## Description
49 | Domcom was developed using coffee-script. This document also borrows some coffee-script writing. Especially when it comes to providing function parameters and callbacks, for example,
50 | `->` stands for function definition `function () {... }`
51 | `(arg1, arg2...) -> ...` represents the function definition `function (arg1, arg2...) { ... }`
52 |
53 |
54 | ## Components
55 | Generate component: `comp = dc(config={data, view, ....})`
56 | Reference the component's react element and dom node: `comp.reactElement`, `comp.node`
57 | Component methods: `mount`, `unmount`, `update`, `copy`, `extend`, `on`, `off`, `emit`
58 | Component events: `mounting`, `mounted`, `unmounting`, `updating`, `updated`
59 |
60 | ## tag element
61 | `[tag, {props...}, children...]`
62 | `['div.class1.class2#id##styles...', {props...}, children...]`
63 | Children can contain other label elements, React component elements (in the form of arrays given below or directly using the original React component elements). **Special attention, of course, can also include Domcom component examples! **
64 |
65 | ###input label
66 | `[type, {props...}]`
67 | `['text.class1.class2#id##styles...', {$model:field...}]`
68 | `['text', modelFiled]`
69 |
70 | ##React components
71 | `[ReactClass, {props...}, children...]`
72 | `[ReactClass, '.class1.class2#id##styles...', {props...}, children...]`
73 |
74 | ##directives
75 | Domcom provides the following built-in directives, `$model`, `$show`, `$options`. Developers can also easily expand their own instructions as needed.
76 |
77 |
78 | ## Facility mechanism
79 | Domcom provides some mechanisms to make development easier, listed below:
80 |
81 | * If there is no key in props, Domcom will automatically generate a unique key. The disadvantage is that while every rendering the key value changes.
82 | * props can provide **keepid**, requiring Domcom's ReactProxy to use the same React element for the item each time the render is used. This applies to elements that are completely static and invariant.
83 | * props can provide **focusid**, which can help Domcom correctly handle node focus after each update, suitable for TextInput, TextArea and other tags.
84 | * Props can write **classes** instead of className, className can use object, and can be controlled with value
85 | The presence or absence of key in the classes. Although Domcom does not use [classnames] (https://www.npmjs.com/package/classnames), you can refer to [classnames] (https://www.npmjs.com/package/classnames) for your understanding.
86 | * Domcom will use the camelcase library to handle keys in props. This can be done by adding **props.dontCamel**, camelcase is always done for props.style.
87 | * Some React elements do not want to delay the child element is not provided as an array, you can add **props.useSingleChildren** to meet this need.
88 |
89 | The directives in props, keepid, focusid, classes, dontCamel, useSingleChildren will not appear in the later generated react element, do not worry about warnings or errors.
90 |
--------------------------------------------------------------------------------
/doc/faq.md:
--------------------------------------------------------------------------------
1 | Domcom FAQ
2 |
3 | * How big is Domcom?
4 |
5 | About 21K+ after the current minimization. A total of about 500 lines of code for src/7.coffee source files.
6 |
7 | * What types and browsers does Domcom support?
8 |
9 | Same as React.
10 |
11 | * Is there an external library that Domcom relies on?
12 |
13 | React and ReactDOM are required.
14 |
15 | * Is there a conflict between using Domcom and directly operating Dom?
16 |
17 | This requires caution. The good news is that after using Domcom, you can basically eliminate the need to directly operate Dom. Regardless, unless there is a real need and you already know the internals of domcom, please be careful not to directly manipulate Domcom managed Dom nodes, including using native APIs or library programs such as jQuery. Of course this does not include Dom nodes that are not generated by Domcom.
18 |
19 | * Domcom need a supporting library to complete the MVC mode?
20 |
21 | Simply put, no need. If there are special needs, Domcom can work with other libraries more easily and better.
22 |
23 | Similar to ReactJS, Domcom is a framework focused on View. But the two are significantly different in data binding and delivery and update mechanisms. This difference makes Domcom not need to use immutable data and other auxiliary libraries to make up for the defects in the framework. Domcom does not require the use of the specified Model class and Controller and its derived classes as the frameworks such as Backbone or EmberJS. Nor does it require the Model to be included in the Scope level as in AngularJS. It is managed by a dedicated Controller, and does not even require data like ReactJS. Organized into props and state hierarchies, defining the Controller method as a component method. The Domcom application is the most free choice for Model and Controller solutions. The most common Javascript language features (such as variables, functions, common classes and objects) can be easily integrated into Domcom as data binding and data. The tool passed. In most cases, not using a dedicated auxiliary library is the most appropriate option. These features can be learned from Domcom's [todomvc] (https://github.com/taijiweb/domcom/blob/master/demo/coffee/todomvc.coffee) demo. Of course, in the face of special needs, Domcom can also be well integrated with the existing libraries.
24 |
25 |
26 | This advantage can shorten the process of learning and using Domcom, reduce the learning curve, and reduce the complexity of the application and reduce the amount of code.
27 |
28 | * Does Domcom have its own template language?
29 |
30 | No additional template language is required to use Domcom, because the nested array `[tag, {...props}, ...children]` syntax using the js language is more concise.
--------------------------------------------------------------------------------
/doc/package-directory.md:
--------------------------------------------------------------------------------
1 | # Domcom package directory hierarchy
2 |
3 | Below is the directory hierarchy of domcom package. They are ordered by their importance, the more important folders and files are put in the front.
4 |
5 | The folders (domcom/lib/, domcom/dist/test/, domcom/dist/demo/) are not expanded, they are just the generated javascript files from the coffee-script source files. They is not intended to be used in engineering, except being as a reference for the programer who prefer to javascript.
6 |
7 | The folders (domcom/static/, domcom/node_modules/ domcom/bower_modules/) are not expanded, because they are public libraries folder.
8 |
9 | domcom ................................... // the web framework to provide dom component
10 | |- src/ ................................... // the coffee-script source code folder
11 | | |- domcom.coffee ...................... // the index file to enable require this folder as domcom package
12 | | |- dc.coffee .......................... // the methods and properties which belong to dc directly
13 | | |- dc-render.coffee ................... // render method properties for dc
14 | | |- dc-util.coffee ..................... // some utilties for domcom
15 | | | (UPDATE: has been moved to the npm package dc-util, and imported to domcom)
16 | | |
17 | |- doc ................................... // document folder
18 | | |- api-reference.md ..................... // API references(English)
19 | | |- api-type-description.md .............. // type description for API references(English)
20 | | |- class-hierarchy.md .................. // class hierarchy(English)
21 | | |- package-directory.md ................ // package directory hierarchy(English)
22 | | \- Chinese ............................ // Chinese document
23 | | |- README.CN.md .................... // README for github and npm, in Chinese
24 | | |- 介绍和辅导教程.md ................. // introduction and tutorial (Chinese)
25 | | |- 概念和原理.md ...................... // Concepts and theory (Chinese)
26 | | |- API参考.md ........................ // API references (Chinese)
27 | | |- 目录结构.md ........................ // package directory hierarchy (Chinese)
28 | | |- 文件夹和模块介绍.md .................. // introduction to folder and module (Chinese)
29 | | |- 速查表.md .......................... // cheatsheet (Chinese)
30 | | |- 从React到Domcom.md ................. // declaration to release (Chinese)
31 | | |- 常问问题.md ........................ // frequently asked questions (Chinese)
32 | |
33 | |- test/ ................................. // test folder
34 | | |- mocha-runner.html .................. // mocha runner html for test, using webpack-hot-server
35 | | |- mocha-runner-dist.html ............. // mocha runner html for test, using the js fiels in dist/ folder
36 | | |- mocha-test.css ..................... // css for mocha test
37 | | |
38 | |- demo/ ................................. // demo folder
39 | | | |- index.html ...................... // todoMVC html page for webpack-server
40 | | | |- index-dist.html ................. // todoMVC html page for distribution
41 | | | |- learn.json ...................... // the learn.json file need by todoMVC site
42 | | |
43 | | |- app.css ............................ // css for demo, used in index.html
44 | | |- demo-controls.coffee ............... // demo for some controls
45 | | |- demo-counter.coffee ................ // demo for a simple counter
46 | | |- demo-show-hide.coffee .............. // demo for $show / $hide directives
47 | | |- demo-sum.coffee .................... // demo for sum
48 | | |- demo-text-model.coffee ............. // demo text input with $model directive
49 | | |- util.coffee ......................... // some utilities for demos
50 | | |- index.coffee ....................... // the index file to start all demos
51 | | |- index.html ......................... // index.html for the demo, using webpack-hot-server
52 | | \- index-dist.html .................... // index.html for the demo, using the code in dist/ folder
53 | |
54 | |- dist .................................. // distribution folder
55 | | |- domcom.js .......................... // full domcom distribution, include basic domcom and addon, development version, in most time it's better to use this file
56 | | |- domcom.min.js ...................... // minified full domcom distribution, include basic domcom and addon, production version, in most time it's better to use this file
57 | | |- todomvc.js ......................... // the code for todoMVC, using webpack-hot-server
58 | | |- test/ ................................. // generated javascript files from domcom/test/ coffee-script files
59 | | \- demo/ ................................. // generated javascript files from domcom/demo/ coffee-script files
60 | |
61 | |- lib/ .................................. // generated javascript files from domcom/src/ coffee-script files
62 | |- static/ ............................... // static library files for demonstration and testing, including bootstrap and sinon
63 | |
64 | |- README.md ............................. // README for github and npm
65 | |- LICENSE ............................... // MIT LICENSE of domcom
66 | |- gulpfile.js ........................... // gulpfile, just require the gulpfile.coffee
67 | |- gulpfile.coffee ....................... // gulpfile in coffee-script
68 | |- webpack.config.coffee ................. // webpack.config.coffee, for webpack workflow
69 | |- package.json ......................... // node_modules package json file for npm
70 | |- bower.json ........................... // bower json file for bower
71 | |- .bowerrc ............................. // .bowerrc for bower, bower config file
72 | |- .gitattribute ........................ // .gitattribute for git
73 | |- .gitignore ........................... // .gitignore for git, ignored files and folder
74 | |
75 | |- node_modules/ ......................... // node modules folder for npm package.json
76 | \- bower_components/ ..................... // bower components folder
77 |
78 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 2.3.2
2 | require('coffeescript/register')
3 | var gulp, i, len, ref, tsk;
4 |
5 | gulp = require('gulp');
6 |
7 | ref = 'clean coffee rename typescript remove webpack build-tasks'.split(/\s+/);
8 | for (i = 0, len = ref.length; i < len; i++) {
9 | tsk = ref[i];
10 | require('./scripts/tasks/' + tsk);
11 | }
12 |
13 | gulp.task('default', ['wpserver']);
14 |
--------------------------------------------------------------------------------
/history.md:
--------------------------------------------------------------------------------
1 | ### 2016-6-30 v0.5.1 on github, npm
2 | * add api: dc.clean(): remove all components in dc.removingChildren
3 | * fix bugs in domcom/demo
4 | wrong renderDom after the component is removed ( check component.removing and component.removed before calling renderDom in component.render())
5 | the component is not refreshed after switching each3, each4 (caused by wrong component.valid, component.attachValid field)
6 |
7 | ### 2016-6-14
8 | * bug fix: core/index is removed
9 |
10 | ### 2016-6-14: v0.5.0 on github, npm
11 | * big refactor: restore to update from top to bottom
12 | * separate the code for updating and attaching/detaching dom
13 |
14 | ### 2016-3-17: v0.2.0 on github, npm
15 | * a very big refactor: do not update from top to bottom, directly update the component in renderingMap instead
16 | * Each component is removed, in favor of the new every, each, funcEacn and mapEach function, which generate watched list component
17 | * Case component supports to use array as map
18 | * set attributes by using attr_xxx
19 | * new api: ListMixin.insertChildBefore, insertAfter;
20 | * fix bug about Component.destroy
21 | * rename life time events to willXXXX/didXXXX,
22 | * make Component.listeners and Tag.events field always be available, avoid to add .on or .bind after component super(...)
23 |
24 | ### 2016-1-31: v0.1.6 on github, npm
25 | * fix a bug in webpack.config, remove the file path in .min.js file
26 | * stop pass component as the parameter to eventHandlerFromArray, just use "node.component"
27 | * always emit "attach" event while component to the tree, and "detach" event while removing the component from the tree
28 |
29 | ### 2016-1-16: v0.1.5 on github, npm
30 | * api(new): new api for delegating event: Tag.delegateByHolder, Tag.delegateByComponent, Tag.delegate
31 | * api(new): dc.error, dc.onerror
32 | * api(change): rename component event: beforeMount -> mount, afterUnmount -> 'unmount', beforeAttatch -> attach, afterDetach -> detach; remove afterMount
33 | * api(change): Hmtl component's super class becomes Tag, wrap with div, add Html.text setter, add attrs
34 | * api(change): Component.renderDom() -> Component.renderDom(oldBaseComponent), delete Component.getBaseComponent
35 | * api(change): remove Component.setNode and Component.setFirstNode
36 | * api(change): add BaseComponent.removing field to mark removing, split Component.setParentAndNextNode to setParentNode and setNextNode
37 | * api(change): api for new If(test, then, else, , merge, recursive), dc.mergeIf, dc.recursiveIf
38 | * improvement: Tag.propBind, Tag.cssBind: return bound reactive function for Tag props or style props
39 | * improvement: Tag.prop(propName) and Tag.css(propName) return domValue of props at first, if undefined then return cache value
40 | * improvement: Component.on, Component.off
41 | * refactor: split property.coffee to core/property/ folder (classFn, events, style, ...), and move core/prop-util.coffee to core/property/css-arith.coffee
42 | * refactor: Both Tag and List use ListMixin
43 | * refactor: remove constructTextLikeComponent for Text, Comment, Html, Cdata, use super(text) directly
44 | * fix two bug: one in Component.setParentAndNextNode, another one in Component.on
45 | * fix a bug about List.nextNode
46 |
47 | ### 2015-12-19: v0.1.4 on github, npm
48 | * a group of new utilities: unitAdd, unitSub, unitMul, unitDiv, Tag.cssAdd, Tag.cssSub, Tag.cssMul, Tag.cssDiv
49 | * add lost minify js back (uglifyjs)
50 |
51 | ### 2015-12-12: v0.1.3 on github, npm
52 | * let domcom can be required out of browser
53 | * new component: Cdata component (core/base/Cdata)
54 | * new component: Pick component (core/base/Pick )
55 | * change the behavior of the namespace for svg and math tag
56 | * refactor gulp scripts
57 | * new utility: toBlockList
58 | * add default arguments for List and Tag (by using toBlockList)
59 | * getBaseComponent method back: [issue #1](github.com/taijiweb/domcom/issues/1)
60 | * decrease dom operation: [issure #5 ](https://github.com/taijiweb/domcom/issues/5)
61 | * add some tests on every, all, nItems, fix bug in it.
62 | * bug fix: Text Component's field "textValid" is processed wrongly
63 | * fix bug: fail to emit beforeAttach for content component in some cases
64 | * fix bug: should mark removing transform component
65 | * update document
66 |
67 | ### 2015-11-15: v0.1.2 on github, npm
68 | * add method: Component.addController
69 | * add method: Tag.extendAttrs
70 | * remove extend.coffee, use npm package [extend](https://github.com/justmoon/node-extend) instead
71 | * dc-util becomes the npm package "dc-util"
72 | * flow/ becomes the npm package "lazy-flow"
73 | * flow/watch-list becomes the npm package "dc-watch-list"
74 | * update document: add some content, doc/api-reference.html(English) is finished
75 |
76 | ### 2015-11-1: v0.1.1 on github, npm
77 | * bug fix: Each compnent attach and detach
78 | * bug fix: flow.bind and flow.duplex caused after browser compatibility
79 | * small update to demo, documents
80 | * run test and demo html for the code in dist
81 |
82 | ### 2015-10-31: v0.1 on github
83 | * finish Chinese document, add some English document
84 | * browser compatibility
85 | * requestAnimationFrame, updateWhen/renderWhen
86 | * Component Event: on, off, emit
87 | * jquery style api for Tag: prop, css, addClass, removeClass, bind, unbind
88 | * allow to modify the component
89 | * rename Repeat to Each, support object with Each
90 | * every, all utility function
91 | * Cond, Html and Nothing component
92 | * Defer Component and other support to promise
93 | * route
94 | * big refactor
95 |
96 | ### 2015-8-9: v0.0.2 on github
97 | * add autoWidthInput component
98 |
99 | ### 2015-8-9: v0.0.1 on github
100 | * the value of any property and other stuffs can be a function
101 | The stuffs, like dom property, the test condition of If, Case component, the items of Repeat component, can be functions. Different from the usage of function as property value in jQuery, the functions are used to declares the computating method for every rendering.
102 | * update only the difference between the dom and the cache
103 | * skip the inative component when updating dom
104 | * decouple with model and controller
105 | * composable component
106 | * the root of the component may be multiple dom elements
107 |
108 | ### 2015-8-2: becomes an independent project and git repository
109 |
110 | ### 2015-3: start coding for this framework, embedded another bigger project and git repository
111 |
112 | ### 2014-11: Got the idea for this project
113 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "domcom",
3 | "version": "0.8.5",
4 | "description": "dom component",
5 | "keywords": [
6 | "domcom",
7 | "web",
8 | "framework",
9 | "dom",
10 | "component",
11 | "javascript",
12 | "coffeescript",
13 | "front end"
14 | ],
15 | "author": "Caoxingming",
16 | "license": "MIT",
17 | "licenses": [
18 | {
19 | "type": "MIT",
20 | "url": "https://raw.github.com/taijiweb/domcom/master/LICENSE"
21 | }
22 | ],
23 | "main": "dist/domcom.js",
24 | "homepage": "https://github.com/taijiweb/domcom",
25 | "bugs": "https://github.com/taijiweb/domcom/issues",
26 | "repository": {
27 | "type": "git",
28 | "url": "git://github.com/taijiweb/domcom.git"
29 | },
30 | "dependencies": {
31 | "@material-ui/icons": "^3.0.1",
32 | "create-react-class": "^15.6.3",
33 | "react": "^16.6.1",
34 | "react-dom": "^16.6.1"
35 | },
36 | "devDependencies": {
37 | "@babel/core": "^7.0",
38 | "@babel/preset-env": "^7.1.0",
39 | "@babel/preset-es2016": "^7.0.0-beta.53",
40 | "babel-core": "^6.26.3",
41 | "babel-loader": "^7",
42 | "babel-preset-env": "^1.7.0",
43 | "babel-preset-es2015": "^6.24.1",
44 | "babel-preset-react": "^6.24.1",
45 | "bdd-test-helper": "latest",
46 | "chai": "~1.9.1",
47 | "coffee-loader": "0.9.0",
48 | "coffeescript": "^2.3.2",
49 | "del": "^2.0.2",
50 | "extend": "latest",
51 | "gulp": "~3.9.0",
52 | "gulp-cached": "~1.0.1",
53 | "gulp-changed": "~1.0.0",
54 | "gulp-coffee2": "^2.3.4",
55 | "gulp-copy": "0.0.2",
56 | "gulp-github-release": "1.0.3",
57 | "gulp-mocha": "~1.1.0",
58 | "gulp-plumber": "~0.6.5",
59 | "gulp-rename": "^1.2.3",
60 | "gulp-task-helper": "0.0.3",
61 | "gulp-typescript": "^5.0.0-alpha.3",
62 | "gulp-util": "~3.0.1",
63 | "lazy-flow": "latest",
64 | "minimist": "1.2.3",
65 | "mocha": "1.14.0",
66 | "rimraf": "^2.6.2",
67 | "run-sequence": "~1.0.2",
68 | "shelljs": "^0.8.2",
69 | "todomvc-app-css": "~2.0.1",
70 | "todomvc-common": "~1.0.1",
71 | "typescript": "^3.1.3",
72 | "webpack": "^4.0",
73 | "webpack-dev-server": "^3.0"
74 | },
75 | "engines": {
76 | "node": ">=0.8.0"
77 | },
78 | "scripts": {
79 | "dev": "gulp",
80 | "dist": "gulp dist"
81 | },
82 | "jsdelivr": "dist/domcom.min.js"
83 | }
84 |
--------------------------------------------------------------------------------
/scripts/tasks/build-tasks.coffee:
--------------------------------------------------------------------------------
1 | gulp = require 'gulp'
2 |
3 | watchcb = ->
4 | gulp.watch([
5 | './src/**/*.coffee',
6 | './scripts-coffee/**/*.coffee',
7 | './test/coffee/**/*.coffee',
8 | './demo/coffee/**/*.coffee'
9 | ],
10 | ['coffee'])
11 | runSequence = require 'run-sequence'
12 |
13 | gulp.task 'js', (callback) -> runSequence 'clean', 'coffee', callback
14 |
15 | gulp.task 'dev', (callback) -> runSequence 'clean', 'wpserver', watchcb
16 |
17 | gulp.task 'dist', (callback) -> runSequence 'clean','webpack-dist', callback
18 |
--------------------------------------------------------------------------------
/scripts/tasks/clean.coffee:
--------------------------------------------------------------------------------
1 | gulp = require 'gulp'
2 | del = require 'del'
3 |
4 | # !!! be careful !!!
5 | # do not remove the stuff by mistake !!!
6 |
7 | # keep the code as simple as possible !!!
8 |
9 | gulp.task 'clean', (cb) -> del(['dist', 'lib', 'demo/js', 'test/js', 'scripts-js','./scripts-coffee/**/*.js',
10 | './src/**/*.js', './test/**/*.js', './demo/**/*.js'], cb)
11 |
--------------------------------------------------------------------------------
/scripts/tasks/coffee.coffee:
--------------------------------------------------------------------------------
1 | gulp = require 'gulp'
2 | {task, from, dest, CombineStream, logTime} = require "gulp-task-helper"
3 |
4 | coffee = require ('gulp-coffee2')
5 |
6 | compileCoffee = (fromFiles, toFolder) ->
7 | from(fromFiles, {cache:'coffee'}).pipelog(coffee({bare: true})).pipe(dest(toFolder))
8 |
9 | gulp.task 'coffee', (cb) ->
10 | streamList = []
11 |
12 | streamList.push compileCoffee('./src/**/*.coffee', './lib')
13 | # below is just for who prefer to reading javascript
14 | streamList.push compileCoffee('./scripts/**/*.coffee', './scripts-js')
15 | streamList.push compileCoffee('./test/**/*.coffee', './test/js')
16 | streamList.push compileCoffee('./demo/**/*.coffee', './demo/js')
17 |
18 | combineStream = new CombineStream(streamList)
19 | #combineStream.end -> logTime('finish coffee')
20 |
21 | combineStream
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/scripts/tasks/remove.coffee:
--------------------------------------------------------------------------------
1 | gulp = require 'gulp'
2 | task = require("gulp-task-helper").task
3 |
4 | rimraf = require 'rimraf'
5 |
6 | gulp.task 'remove', (cb) ->
7 | rimraf('./src/**/*.js', ->)
8 | rimraf('./src/**/*.ts', ->)
9 | rimraf('./test/**/*.js', ->)
10 | rimraf('./test/**/*.ts', ->)
11 | rimraf('./demo/**/*.js', ->)
12 | rimraf('./demo/**/*.ts', ->)
13 | rimraf('./scripts/**/*.js', ->)
14 | rimraf('./scripts/**/*.ts', ->)
15 | cb()
16 |
--------------------------------------------------------------------------------
/scripts/tasks/rename.coffee:
--------------------------------------------------------------------------------
1 | gulp = require 'gulp'
2 | {task, from, dest, CombineStream, logTime} = require "gulp-task-helper"
3 |
4 | rename = require "gulp-rename"
5 |
6 | renameFn = (path) -> path.extname = ".ts"
7 |
8 |
9 | renameJs = (fromFiles, toFolder) ->
10 | return from(fromFiles).pipelog(rename(renameFn)).pipe(dest(toFolder))
11 |
12 | gulp.task 'rename', (cb) ->
13 | streamList = [];
14 | streamList.push(renameJs('./src/**/*.js', './src'))
15 | streamList.push(renameJs('./test/**/*.js', './test'))
16 | streamList.push(renameJs('./demo/**/*.js', './demo'))
17 | streamList.push(renameJs('./scripts/**/*.js', './scripts'))
18 | combineStream = new CombineStream(streamList)
19 | combineStream
20 |
--------------------------------------------------------------------------------
/scripts/tasks/typescript.coffee:
--------------------------------------------------------------------------------
1 | {task, from, dest, CombineStream, logTime} = require "gulp-task-helper"
2 |
3 | shell = require 'shelljs'
4 |
5 | typescript = require ('gulp-typescript')
6 | compileTypescript = (fromFiles, toFolder) ->
7 | from(fromFiles, {cache:'coffee'}).pipelog(typescript({bare: true})).pipe(dest(toFolder))
8 |
9 | task 'typescript', (cb) ->
10 | # streamList = []
11 |
12 | # streamList.push compileTypescript('./src/**/*.coffee', './lib')
13 | # below is just for who prefer to reading javascript
14 | # streamList.push compileTypescript('./test/**/*.coffee', './dist/test')
15 | # streamList.push compileTypescript('./demo/**/*.coffee', './dist/demo')
16 |
17 | # combineStream = new CombineStream(streamList)
18 | # combineStream.end -> logTime('finish compiling typescript')
19 |
20 | # combineStream
21 | if (shell.exec('tsc ./**/*.ts').code != 0)
22 | shell.echo('Error: typescript compile failed')
23 | shell.exit(1)
24 | else
25 | console.log('finish typescript')
--------------------------------------------------------------------------------
/scripts/tasks/webpack.coffee:
--------------------------------------------------------------------------------
1 | gulp = require 'gulp'
2 | {logTime} = require "gulp-task-helper"
3 |
4 | webpack = require 'webpack'
5 |
6 | {makeConfig, makeWebpackDevServer} = require '../webpack-config'
7 |
8 | onTaskDone = -> (err, stats) ->
9 | if err then console.log('Error', err)
10 | else console.log(stats.toString())
11 | logTime("finished 'webpack'")
12 | return
13 |
14 | webpack = require "webpack"
15 | #ClosureCompilerPlugin from 'webpack-closure-compiler'
16 |
17 | domcomEntry = {
18 | 'domcom': './src/index',
19 | }
20 | runWebPack = (entry, filename, options) ->
21 | config = makeConfig(entry, filename, options)
22 | webpackCompiler = webpack(config)
23 | webpackCompiler.run onTaskDone()
24 |
25 | webpackDistribute = (mode) ->
26 | plugins = [new webpack.NoEmitOnErrorsPlugin(),
27 | new webpack.LoaderOptionsPlugin({
28 | options: {}
29 | })
30 | ]
31 | runWebPack(domcomEntry, '[name].js', {mode:'development',path:'../dist', pathinfo:true, libraryTarget:'umd', library:'dc', plugins})
32 | runWebPack('./test/index', 'test.js', {mode:'development',path:'../dist', pathinfo:true, plugins})
33 | runWebPack('./demo/index', 'demo.js', {mode:'development',path:'../dist', pathinfo:true, plugins})
34 | runWebPack('./demo/todomvc', 'todomvc.js', {mode:'development',path:'../dist', pathinfo:true, plugins})
35 | if mode=='production'
36 | # webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead.
37 | # plugins.push new webpack.optimize.UglifyJsPlugin({minimize: true})
38 | runWebPack(domcomEntry, '[name].min.js', {mode,path:'../dist', pathinfo:false, libraryTarget:'umd', library:'dc', plugins})
39 |
40 | gulp.task 'webpack-dist', () -> webpackDistribute('production')
41 | gulp.task 'webpack-dev', () -> webpackDistribute('development')
42 |
43 | gulp.task 'wpserver', ->
44 | webServerPlugins = [
45 | new webpack.HotModuleReplacementPlugin()
46 | new webpack.NoEmitOnErrorsPlugin()
47 | ]
48 | makeWebpackDevServer(["webpack/hot/dev-server", './test/index'], 'test.js', {port:8088, plugins:webServerPlugins})
49 | makeWebpackDevServer(["webpack/hot/dev-server", './src/index'], 'domcom.js', {port:8083, inline:true, plugins:webServerPlugins})
50 | makeWebpackDevServer(["webpack/hot/dev-server", './demo/index'], 'demo.js', {port:8084, plugins:webServerPlugins})
51 | makeWebpackDevServer(["webpack/hot/dev-server", './demo/todomvc'], 'todomvc.js', {port:8090, plugins:webServerPlugins})
52 |
--------------------------------------------------------------------------------
/scripts/webpack-config.coffee:
--------------------------------------------------------------------------------
1 | _ = require 'lodash'
2 | path = require 'path'
3 | webpack = require "webpack"
4 | WebpackDevServer = require "webpack-dev-server"
5 |
6 | exports.makeConfig = makeConfig = (entry, filename, options={}, makingServer) ->
7 | plugins = options.plugins || [ new webpack.NoEmitOnErrorsPlugin() ]
8 |
9 | config =
10 | mode: options.mode || 'development'
11 | entry: entry
12 |
13 | output:
14 | path: path.join(__dirname, options.path || '../public'),
15 | filename: filename
16 | pathinfo: if options.pathinfo? then options.pathinfo else true
17 | publicPath: options.publicPath || "/assets/",
18 |
19 | externals: { chai: "chai"}
20 |
21 | node: {fs: "empty"}
22 |
23 | cache:true
24 |
25 | module:
26 | rules: [
27 | {
28 | test: /\.js$/,
29 | loader: 'babel-loader',
30 | exclude: /(node_modules|bower_compontents)/,
31 | options: {presets: ['env']}
32 | },
33 | {
34 | test: /\.coffee$/,
35 | use: [{
36 | loader:'coffee-loader',
37 | options:{
38 | sourceMap: false,
39 | transpile: {
40 | presets: ['@babel/env']
41 | }
42 | }
43 | }]
44 | }
45 | ]
46 |
47 | resolve:
48 | extensions: ['.js', '.coffee']
49 | alias:
50 | "gulp-task-helper": path.resolve(__dirname, '../packages/gulp-task-helper/')
51 | "dc-util": path.resolve(__dirname, '../src/dc-util/')
52 | 'domcom': path.resolve(__dirname, '../src/')
53 |
54 | plugins: plugins
55 | optimization:
56 | minimize: options.mode == 'production'
57 |
58 | if makingServer
59 | config.devServer =
60 | contentBase: "http://localhost/",
61 | noInfo: false,
62 | hot: true,
63 | inline: true
64 | config
65 |
66 | exports.makeWebpackDevServer = (entry, filename, options={}) ->
67 |
68 | options.plugins = options.plugins || [
69 | new webpack.HotModuleReplacementPlugin()
70 | new webpack.NoEmitOnErrorsPlugin()
71 | ]
72 |
73 | compilerConfig = makeConfig(entry, filename, options)
74 | webpackCompiler = webpack(compilerConfig)
75 |
76 | serverConfig =
77 | proxy: {'*': "http://localhost/"},
78 | publicPath: options.publicPath || "/assets/",
79 |
80 | hot: true,
81 |
82 | quiet: false,
83 | noInfo: false,
84 |
85 | lazy: false,
86 | filename: filename
87 |
88 | watchOptions:
89 | aggregateTimeout: 300,
90 | poll: 1000
91 |
92 | headers: { "X-Custom-Header": "yes" }
93 | inline:options.inline
94 |
95 | webpackDevServer = new WebpackDevServer(webpackCompiler, serverConfig)
96 | webpackDevServer.listen options.port || 8080, "localhost", ->
97 |
--------------------------------------------------------------------------------
/src/Component.coffee:
--------------------------------------------------------------------------------
1 | import Emitter from './Emitter'
2 |
3 | {newDcid, isArray, isObject, normalizeDomElement, watchField, watchDataField, isMap} = require 'dc-util'
4 |
5 | updateKey = 1
6 | ###
7 | 部件基类
8 | @params config: the config object for the component, it can have the fileds below
9 | data: the real data of the component or a function to generate the data
10 | view: the view object or a function to generate the view
11 | any other fields that do not conflict with component itself
12 | ###
13 | export default module.exports = class Component extends Emitter
14 | constructor: (config) ->
15 | super()
16 | this.init()
17 | this.checkConfig(config)
18 | this.config = config
19 | Object.assign this, config
20 | this.watch()
21 | return
22 |
23 | init: ->
24 | this.dcid = newDcid()
25 | this.base = null
26 | this.reactElement = null
27 | this.node = null
28 | this.mounted = false
29 | return
30 |
31 | getReactElement: (key) ->
32 | if this.reactElement
33 | return this.reactElement
34 | if key?
35 | props = {component:this, key}
36 | else
37 | props = {component:this}
38 | props.key = props.key || this.dcid
39 | this.reactElement = dc.React.createElement(dc.ReactProxy, props)
40 | return this.reactElement
41 |
42 | checkConfig: (config) ->
43 | illegals = []
44 | for own key, value of config
45 | if this[key] != undefined
46 | illegals.push key
47 | if illegals.length
48 | dc.error "illegal key in config: #{illegals.join(', ')}, they are used by dc.Component itself!"
49 | return
50 |
51 | watch: ->
52 | if config = this.config
53 | for own key of config
54 | watchField this, key, this
55 | if (data = this.getData(dc.store)) && isMap data
56 | components = data.watchingComponents
57 | if !components
58 | data.watchingComponents = [this]
59 | else
60 | if components.indexOf(this) == -1
61 | components.push this
62 | if data && isMap(data)
63 | for own key of config.data
64 | watchDataField config.data, key, this
65 | return this
66 |
67 | stopWatch: ->
68 | if config = this.config
69 | for own key of config
70 | v = this[key]
71 | delete this[key]
72 | this[key] = v
73 | if (data = config.data) && isMap data
74 | components = data.watchingComponents
75 | index = components.indexOf(this)
76 | if index >= 0
77 | components.splice index, 1
78 | delete data.watchingComponents
79 | if !components.length
80 | for own key of data
81 | v = data[key]
82 | delete data[key]
83 | data[key] = v
84 | return this
85 |
86 | copy: ->
87 | comp = new Component({})
88 | Object.assign comp, this
89 | comp.init()
90 | comp.watch()
91 | return comp
92 |
93 | extend: (config) ->
94 | comp = new Component({})
95 | component.base = this
96 | comp.checkConfig config
97 | Object.assign comp, this, config
98 | comp.init()
99 | comp.config = Object.assign {}, this.config, config
100 | return comp
101 |
102 | mount: (mountNode) ->
103 | this._prepareMount(mountNode)
104 | dc.mountMap[this.dcid] = this
105 | reactElement = this.getReactElement()
106 | dc.ReactDom.render(reactElement, this.parentNode)
107 | this.emit('mounted')
108 | return
109 |
110 | update: ->
111 | if this.mounted
112 | # any object to trigger the update
113 | this.proxy.setState({})
114 | return
115 |
116 | _prepareMount: (mountNode) ->
117 | parentNode = normalizeDomElement(mountNode) || document.body
118 | if parentNode.childNodes.length
119 | dc.error('should not mount to node which is not empty:', mountNode)
120 | this.parentNode = parentNode
121 | return
122 |
123 | getData: ->
124 | if typeof this.data == 'function'
125 | return this.data.call(this, dc.store)
126 | else
127 | return this.data
128 |
129 | getView: ->
130 | data = this.getData()
131 | if this.view
132 | if typeof this.view == 'function'
133 | view = this.view.call(this, data)
134 | else
135 | view = this.view
136 | return view
137 | unmount: () ->
138 | this.emit('unmounting')
139 | {parentNode} = this
140 | if parentNode.childNodes.length
141 | node = parentNode.childNodes[0]
142 | parentNode.removeChild(node)
143 | #tell React do not warn about this
144 | parentNode._reactRootContainer = undefined
145 | this.proxy.component = null
146 | this.proxy = null
147 | delete dc.mountMap[this.dcid]
148 | this.unmounted = false
149 | return
150 |
--------------------------------------------------------------------------------
/src/Emitter.coffee:
--------------------------------------------------------------------------------
1 | ###
2 | Model, View, Component等很多类的基类
3 | 管理注册/注销回调函数(on/once/off/offall)和事件发布(emit)
4 | ###
5 | export default module.exports = class Emitter
6 | constructor: () ->
7 |
8 | ###注册事件
9 | comp.on({name:callbacks...})
10 | comp.on(name, callback, before = false)
11 | comp.on(name, callbacks, before = false)
12 | ###
13 | on: (event, callback) ->
14 | if !arguments.length
15 | dc.error('missing arguments for Component.on(event, callback)')
16 | if arguments.length == 1
17 | if !event || typeof event != 'object'
18 | dc.error('wrong arguments for Component.on(event, callback)')
19 | else
20 | for eventName, callback of event
21 | this.on(eventName, callback)
22 | else
23 | if !callback
24 | dc.error('Component.on: callback is undefined for event: '+ event)
25 | if !(listeners = this.listeners)
26 | this.listeners = listeners = {}
27 | for event in event.split(/\s*,\s*|\s+/)
28 | if callbacks = listeners[event]
29 | if callbacks.indexOf(callback) < 0
30 | callbacks.push(callback)
31 | else
32 | listeners[event] = [callback]
33 | this
34 |
35 | ###注册事件
36 | # comp.off(name, callback)
37 | # 注销部件上指定name的指定回调函数
38 |
39 | # comp.off(name)
40 | # 注销部件上指定name的所有回调函数
41 |
42 | # comp.off()
43 | #注销部件上的所有事件的所有回调函数
44 |
45 | ###
46 | off: (event, callback) ->
47 | if !arguments.length
48 | this.listeners = {}
49 | else if arguments.length==1
50 | listeners = this.listeners
51 | for event in event.split(/\s*,\s*|\s+/)
52 | listeners[event] = null
53 | else
54 | listeners = this.listeners
55 | for event in event.split(/\s*,\s*|\s+/)
56 | callbacks = listeners[event]
57 | if callbacks && (index = callbacks.indexOf(callback)) >= 0
58 | callbacks.splice(index, 1)
59 | if !callbacks.length
60 | listeners[event] = null
61 | this
62 |
63 | once: (event, callback) ->
64 | if !callback
65 | dc.error('Component.once: callback is undefined for event: '+ event)
66 | onceCallback = (args...) ->
67 | this.off(event, onceCallback)
68 | callback.apply(this, args)
69 | this.on(event, onceCallback)
70 |
71 | emit: (event, args...) ->
72 | if !this.destroyed
73 | if this.listeners && callbacks = this.listeners[event]
74 | # need to be copied, because onceCallback will be removed from this.listeners[event]
75 | callbacks = callbacks.slice()
76 | for callback in callbacks
77 | callback.apply(this, args)
78 | else
79 | if method = this['on'+event]
80 | method.apply(this, args)
81 | this
82 |
--------------------------------------------------------------------------------
/src/dc-directive.coffee:
--------------------------------------------------------------------------------
1 | import {isArray} from './dc-util'
2 |
3 | export default exports = module.exports = {}
4 |
5 | modelProps = {
6 | 'checkbox': 'checked'
7 | }
8 | exports.$model = (item, options) ->
9 | [tag, props, children] = item
10 | comp = this
11 | if typeof options == 'string'
12 | field = options
13 | else if options
14 | field = options.field
15 | event = options.event || 'onChange'
16 | prop = options.prop
17 | props = Object.assign {}, props
18 | prop = prop || modelProps[props.type] || 'value'
19 | props[prop] = comp[field]
20 | props[event || 'onChange'] = (event) =>
21 | comp[field] = event.target[prop]
22 | return [tag, props, children]
23 |
24 |
25 | #$output just reverse node.value to field, but NOT bind field to props.value
26 | exports.$output = (item, options) ->
27 | [tag, props, children] = item
28 | comp = this
29 | if typeof options == 'string'
30 | field = options
31 | else if options
32 | field = options.field
33 | event = options.event || 'onChange'
34 | prop = options.prop
35 | props = Object.assign {}, props
36 | props[event || 'onChange'] = (event) =>
37 | comp[field] = event.target[prop]
38 | return [tag, props, children]
39 |
40 |
41 | exports.$options = (item, options) ->
42 | [tag, props, children] = item
43 | if isArray(options)
44 | children = options.map((child) -> ['option', {}, [child]])
45 | return [tag, props, children]
46 |
47 | exports.$show = (item, options) ->
48 | [tag, props, children] = item
49 | if typeof options == 'string' || typeof options == 'number'
50 | value = this[options]
51 | else
52 | value = options
53 | style = props.style || props.style = {}
54 | if value
55 | style.display = 'block'
56 | else
57 | style.display = 'none'
58 | return [tag, props, children]
59 |
--------------------------------------------------------------------------------
/src/dc-error.coffee:
--------------------------------------------------------------------------------
1 | slice = [].slice
2 |
3 | stackReg = /at\s+(.*)\s+\((.*):(\d*):(\d*)\)/gi
4 | stackReg2 = /at\s+()(.*):(\d*):(\d*)/gi
5 |
6 | #export default
7 | module.exports = exports = {}
8 |
9 | stacktraceMessage = (message, stackIndex = 1) ->
10 | if message
11 | if !dc.prodution
12 | console.log(message)
13 | message += ':\n'
14 | else
15 | message = ""
16 |
17 | error = new Error()
18 | if !dc.prodution
19 | console.log(error)
20 | stacklist = error.stack.split('\n').slice(3)
21 |
22 | stackIndex = 1
23 | stacklistLength = stacklist.length
24 | while stackIndex < stacklistLength
25 | stackItem = stacklist[stackIndex]
26 | itemInfo = stackReg.exec(stackItem) || stackReg2.exec(stackItem)
27 | if itemInfo && itemInfo.length == 5
28 | method = itemInfo[1]
29 | file = itemInfo[2]
30 | line = itemInfo[3]
31 | pos = itemInfo[4]
32 | message += file + ':' + line + ':' + pos + ':' + method + '\n'
33 | stackIndex++
34 |
35 | message
36 |
37 | exports.DomcomError = class DomcomError extends Error
38 | constructor: (@message, @component) ->
39 | super()
40 |
41 | toString: ->
42 | if this.component
43 | this.component.toString()+'\n'+this.message
44 | else
45 | this.message
46 |
47 | exports.error = (message, component) ->
48 | message = stacktraceMessage(message, 2)
49 | throw new DomcomError(message, component)
50 |
51 | export default exports
--------------------------------------------------------------------------------
/src/dc-util.coffee:
--------------------------------------------------------------------------------
1 | camelCase = require('camelcase')
2 |
3 | export default exports = module.exports = {}
4 |
5 | exports.normalizeItem = normalizeItem = (item, index) ->
6 | if typeof item == 'string'
7 | return item
8 | else if item instanceof dc.Component
9 | return item.getReactElement(index)
10 | else if isArray(item)
11 | return normalizeArrayViewItem(item)
12 | else if item?
13 | return ''+item
14 | else
15 | return null
16 |
17 | normalizeArrayViewItem = (item) ->
18 | i = 0
19 | it = item[i]
20 | if it instanceof dc.Component || isArray(it)
21 | tag = 'div'
22 | props = {}
23 | children = item.map((child, index) -> normalizeItem(child, index))
24 | return [tag, props, children]
25 | else if typeof it== 'string'
26 | [tag, classes, id, css, inputType] = parseTagString(item[i])
27 | classes = classname(classes)
28 | css = styleFrom(css)
29 | i++
30 | else if isReactClass(it)
31 | tag = it
32 | i++
33 | it = item[i]
34 | if typeof it == 'string' && it
35 | if it.match /^\.|^#/
36 | [_, classes, id, css, inputType] = parseTagString(item[i])
37 | classes = classname(classes)
38 | css = styleFrom(css)
39 | i++
40 | it = item[i]
41 | else if typeof it == 'function'
42 | x = it(item[1...]...)
43 | return normalizeItem x
44 | # props and children
45 | props = null
46 | it = item[i]
47 | while isMap(it)
48 | props = Object.assign({id}, it)
49 | tag = tag || it.tag || 'div'
50 | delete props.tag
51 | classes = classname(classes, it.classes, it.className)
52 | delete props.classes
53 | props.className = classes
54 | css = styleFrom(css, it.css, it.style)
55 | delete props.css
56 | props.style = css
57 | i++
58 | it = item[i]
59 | if !props
60 | props = {className:classes, id, style:styleFrom(css)}
61 | if inputType
62 | props.type = inputType
63 | children = item[i...].map((child, index) -> normalizeItem(child, index))
64 | props = normalizeReactProps(props, typeof tag == 'string')
65 | return [tag || 'div', props, children]
66 |
67 | dontCamelCaseReactProps = ['dangerouslySetInnerHTML', 'aria-hidden']
68 | normalizeReactProps = (props, camel = true) ->
69 | if props.dontCamel
70 | camel = false
71 | delete props.dontCamel
72 | for key of props
73 | value = props[key]
74 | if camel
75 | delete props[key]
76 | if !dontCamelCaseReactProps.includes(key)
77 | key = camelCase key
78 | if value == undefined
79 | delete props[key]
80 | else if key == 'className'
81 | classMap = classname(value)
82 | if classes = Object.keys(classMap).filter((key) -> classMap[key]).join(' ')
83 | props.className = classes
84 | else
85 | delete props.className
86 | else if key == 'style'
87 | if Object.keys(value).length
88 | props.style = camelCaseStyle value
89 | else
90 | delete props.style
91 | else if camel
92 | props[key] = value
93 | props
94 |
95 | camelCaseStyle = (style) ->
96 | result = {}
97 | for key of style
98 | value = style[key]
99 | if !(key.startsWith('MozOsx') || key.startsWith('Webkit'))
100 | key = camelCase key
101 | result[key] = value
102 | result
103 | inputTypes = {}
104 |
105 | for type in 'text checkbox radio date email tel number password'.split(' ')
106 | inputTypes[type] = 1
107 |
108 | # tag.class#id##css
109 | parseTagString = (str) ->
110 | str = str.trim()
111 | list = str.split('##')
112 | if list.length == 2
113 | css = list[1].trim()
114 | str = list[0].trim()
115 | list = str.split('#')
116 | if list.length == 2
117 | id = list[1].trim()
118 | str = list[0].trim()
119 | list = str.split('.')
120 | if list.length > 1
121 | tag = list[0]
122 | classes = classname list.slice(1)
123 | else
124 | tag = str
125 | classes = []
126 | if inputTypes[tag]
127 | inputType = tag
128 | tag = 'input'
129 | [tag || 'div', classes, id, css, inputType]
130 |
131 | styleFrom = (items...) ->
132 | result = {}
133 | for item in items
134 | if typeof item == 'string'
135 | item = item.trim()
136 | if !item
137 | continue
138 | defs = item.split(/\s*;\s*/)
139 | for def in defs
140 | if !def
141 | continue
142 | [key, value] = def.split /\s*:\s*/
143 | if !key || !value
144 | dc.error 'format error in css rules: empty key'
145 | else if !value
146 | dc.error 'format error in css rules: empty value'
147 | key = camelCase key
148 | result[key] = value
149 | else if isMap item
150 | Object.assign result, item
151 | return result
152 |
153 | classname = (items...) ->
154 | classMap = {}
155 | for item in items
156 | if !item
157 | continue
158 | else if typeof item == 'string'
159 | names = item.trim().split(/(?:\s*,\s*)|\s+/)
160 | for name in names
161 | classMap[name] = 1
162 | else if isArray item
163 | for name in item
164 | classMap[name] = 1
165 | else if isObject item
166 | Object.assign(classMap, item)
167 |
168 | return classMap
169 |
170 | isReactClass = (item) ->
171 | # investigated on both CreateClass and ES6 extends react.Component
172 | item && item.prototype && item.prototype.isReactComponent
173 |
174 |
175 | exports.watchField = (data, prop, comp) ->
176 | closure = ->
177 | value = data[prop]
178 | Object.defineProperty data, prop, {
179 | get: -> value
180 | set:(v) ->
181 | value = v
182 | comp.update()
183 | }
184 | return
185 | closure()
186 | return
187 |
188 | exports.watchDataField = (data, prop, comp) ->
189 | do ->
190 | value = data[prop]
191 | Object.defineProperty data, prop, {
192 | get: -> value
193 | set:(v) ->
194 | if v != value
195 | value = v
196 | for comp in data.watchingComponents
197 | comp.update()
198 | return v
199 | }
200 | return
201 | return
202 |
203 | exports.normalizeDomElement = (domElement) ->
204 | if typeof domElement == 'string'
205 | domElement = document.querySelector(domElement)
206 | domElement
207 |
208 | exports.isArray = isArray =
209 | isArray = (item) ->
210 | Object::toString.call(item) == '[object Array]'
211 |
212 | exports.isObject = isObject = (item) ->
213 | typeof item == 'object' and item != null
214 |
215 | exports.isMap = isMap = (item) ->
216 | typeof item == 'object' and item != null && item.constructor == Object
217 |
218 | dcid = 0
219 | exports.newDcid = ->
220 | return 'dcid-' + dcid++
--------------------------------------------------------------------------------
/src/index.coffee:
--------------------------------------------------------------------------------
1 | import Emitter from './Emitter'
2 | import Component from './Component'
3 |
4 | ###
5 | # a utility to do almost everything
6 | # generate Component instance
7 | # as the dc framework namespace
8 | # hold convinent getter, setter and method, etc...
9 | # @params template: the template for the component
10 | # @params config: the config object for the component, it can have the fileds below
11 | model can be the real value of data or a function to generate the model data
12 | data: the data of the component
13 | view: the view object or a function to generate the view
14 | ###
15 | dc = (config) ->
16 | comp = new Component(config)
17 | return comp
18 |
19 | dc.on = Emitter::on
20 | dc.off = Emitter::off
21 | dc.emit = Emitter::emit
22 |
23 | dc.focusNodeMap = {}
24 |
25 | dc.on 'updated', ->
26 | if dc.focusNode
27 | dc.focusNode.focus()
28 | return
29 |
30 | dc.dcid = 0
31 |
32 | dc.mountMap = {}
33 | dc.keepReactElementMap = {}
34 |
35 | dc.update = ->
36 | for key, comp of dc.mountMap
37 | comp.update()
38 | return
39 |
40 | if typeof window != 'undefined'
41 | window.dc = dc
42 |
43 | dc.Component = require('./Component')
44 | Object.assign(dc,
45 | require('./dc-error')
46 | require('dc-util'),
47 | )
48 | dc.directives = {}
49 | Object.assign dc.directives, require('./dc-directive')
50 | dc.addReactProxy = require './react-proxy'
51 | window.dc = dc
52 | export default module.exports = dc
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/react-proxy.coffee:
--------------------------------------------------------------------------------
1 | {normalizeItem} = require 'dc-util'
2 |
3 |
4 | globalKey = 0
5 | ###
6 | addReactProxy should be attached to dc and called like below:
7 | dc.addReactProxy(React, ReactDom, ReactComponent)
8 | ###
9 | export default module.exports = addReactProxy = (React, ReactDom, ReactComponent) ->
10 | dc = this
11 | dc.React = React
12 | dc.ReactDom = ReactDom
13 |
14 | dc.ReactProxy = class ReactProxy extends ReactComponent
15 | constructor: (props) ->
16 | super(props)
17 | {component} = props
18 | this.component = component
19 | component.proxy = this
20 | return
21 |
22 | UNSAFE_componentWillMount: ->
23 | this.component.emit 'mounting'
24 | return
25 |
26 | componentDidMount: ->
27 | this.component.node = ReactDom.findDOMNode(this)
28 | this.component.watch()
29 | if this.component.mounted
30 | dc.error 'component should be mounted under only one place'
31 | this.component.emit 'mounted'
32 | dc.emit 'mounted'
33 | this.component.mounted = true
34 |
35 | return
36 |
37 | componentWillUnmount: ->
38 | dc.emit 'unmounting'
39 | this.component.emit 'unmounting'
40 | this.component.mounted = false
41 | this.component.node = null
42 | return
43 |
44 | componentDidCatch: (error) ->
45 | dc.error(error)
46 |
47 | UNSAFE_componentWillUpdate: (prevProps, prevState) ->
48 | dc.emit 'updating'
49 | this.component.emit 'updating'
50 | return
51 |
52 | componentDidUpdate: (prevProps, prevState) ->
53 | this.component.node = ReactDom.findDOMNode(this)
54 | this.component.emit 'updated'
55 | dc.emit 'updated'
56 | return
57 |
58 | renderNormalized: (item, index) =>
59 | if !item?
60 | return null
61 | else if typeof item == 'string'
62 | return item
63 | else if dc.React.isValidElement(item)
64 | # item.key = item.key || index
65 | return item
66 | else
67 | props = Object.assign({}, item[1])
68 | for key, value of props
69 | if key[0] == '$'
70 | directive = dc.directives[key]
71 | delete item[1][key]
72 | item = directive.call(this.component, item, value)
73 | [tag, props, children] = item
74 | if tag == 'input' || tag == 'textarea'
75 | if children.length > 1
76 | dc.error "#{tag} element should not have multiple children"
77 | else if children.length == 1
78 | if typeof children[0] != 'string'
79 | dc.error 'the child of #{tag} is used as model field, it should be a string'
80 | item = dc.directives.$model.call(this.component, item, children[0])
81 | props = item[1]
82 | children = []
83 |
84 | if !props.key
85 | props.key = index
86 |
87 | if (focusid = props.focusid)?
88 | ref = props.ref
89 | props.ref = (el) ->
90 | ref && ref(el)
91 | if focusid == dc.focusid
92 | dc.focusNode = el
93 | return
94 | onFocus = props.onFocus
95 | props.onFocus = (event) ->
96 | onFocus && onFocus(event)
97 | dc.focusid = focusid
98 | return
99 | onBlur = props.onBlur
100 | props.onBlur = (event) ->
101 | onBlur &&onBlur(event)
102 | dc.focusid = null
103 | return
104 |
105 | children = children.map (child, index) => this.renderNormalized(child, index)
106 | if !children.length
107 | children = null
108 | else if children.length == 1
109 | if props.useSingleChildren
110 | delete props.useSingleChildren
111 | children = children[0]
112 | if keepid = props.keepid
113 | delete props.keepid
114 | if reactElement = dc.keepReactElementMap[keepid]
115 | return reactElement
116 | else
117 | reactElement = React.createElement(tag, props, children)
118 | dc.keepReactElementMap[keepid] = reactElement
119 | return reactElement
120 | else
121 | return React.createElement(tag, props, children)
122 |
123 | renderItem: (item) =>
124 | item = normalizeItem(item)
125 | return this.renderNormalized(item)
126 |
127 | render: ->
128 | {component} = this
129 | view = component.getView()
130 | reactElement = this.renderItem(view)
131 | return reactElement
132 |
--------------------------------------------------------------------------------
/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taijiweb/domcom/c5b8e13c68c0aee0045d08d674c5135b9307f4c7/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taijiweb/domcom/c5b8e13c68c0aee0045d08d674c5135b9307f4c7/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taijiweb/domcom/c5b8e13c68c0aee0045d08d674c5135b9307f4c7/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/taijiweb/domcom/c5b8e13c68c0aee0045d08d674c5135b9307f4c7/static/bootstrap-3.3.4-dist/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/static/bootstrap-3.3.4-dist/js/npm.js:
--------------------------------------------------------------------------------
1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
2 | require('../../js/transition.js')
3 | require('../../js/alert.js')
4 | require('../../js/button.js')
5 | require('../../js/carousel.js')
6 | require('../../js/collapse.js')
7 | require('../../js/dropdown.js')
8 | require('../../js/modal.js')
9 | require('../../js/tooltip.js')
10 | require('../../js/popover.js')
11 | require('../../js/scrollspy.js')
12 | require('../../js/tab.js')
13 | require('../../js/affix.js')
--------------------------------------------------------------------------------
/test/draft.html:
--------------------------------------------------------------------------------
1 | domcom info.json
2 |
3 | author = "Caoxingming"
4 | github = "https://github.com/taijiweb/domcom"
5 | homepage = "https://github.com/taijiweb/domcom"
6 | description = "a web framework to provide dom component"
7 | mainfile = "domcom.min.js"
8 |
9 | domcom update.json
10 |
11 | {
12 | "packageManager": "github",
13 | "name": "domcom",
14 | "repo": "taijiweb/domcom",
15 | "files": {
16 | "basePath": "dist/",
17 | "include": ["domcom.min.js"]
18 | }
19 | }
20 |
21 | 960 info.ini
22 |
23 | author = "Nathan Smith"
24 | github = "https://github.com/nathansmith/960-Grid-System"
25 | homepage = "http://960.gs/"
26 | mainfile = "960.css"
27 | description = "The 960 Grid System is an effort to streamline web development workflow"
28 |
29 |
30 | agile update.json
31 |
32 | { "packageManager": "github"
33 | , "name": "ajile"
34 | , "repo": "iskitz/ajile"
35 |
36 | , "files":
37 | { "basePath": "/use/"
38 | , "include":
39 | [ "com.iskitz.ajile.src.js"
40 | , "com.iskitz.ajile.js"
41 | , "index.js"
42 | ]
43 | }
44 | }
45 |
46 | ajile info.json
47 |
48 |
49 | mainfile = "com.iskitz.ajile.js"
50 | description = "ajile enables namespacing, dependency-management, and on-demand loading of cross-domain, local, and inline JavaScript within browsers."
51 | author = "Michael Lee"
52 | homepage = "http://ajile.net/"
53 | github = "https://github.com/iskitz/ajile"
--------------------------------------------------------------------------------
/test/helper.coffee:
--------------------------------------------------------------------------------
1 | #export default
2 | module.exports = exports = {}
3 |
4 | exports.newDemoNode = (id) ->
5 | node = document.createElement('div')
6 | document.body.appendChild(node)
7 | id && node.setAttribute('id', id)
8 | node
9 |
10 | exports.fakeEvent = (targetNode, type='click', keyCodeOrOptions) ->
11 | if typeof keyCodeOrOptions == 'number'
12 | {
13 | target:targetNode
14 | type
15 | keyCode: keyCodeOrOptions
16 | preventDefault: ->
17 | stopPropagation: ->
18 | }
19 | else
20 | Object.assign(
21 | {
22 | target: targetNode
23 | type
24 | preventDefault: ->
25 | stopPropagation: ->
26 | },
27 | keyCodeOrOptions
28 | )
29 |
30 | export default exports
--------------------------------------------------------------------------------
/test/index.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 | dc = require 'domcom'
3 | describe "all tests", ->
4 | this.timeout(5000000)
5 |
6 | require('./test-new-dc')
7 | require('./test-directive')
8 | require('./test-event')
9 | require('./test-react-proxy')
10 | require('./test-material-ui')
11 | require('./test-antd')
--------------------------------------------------------------------------------
/test/mocha-runner-dist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Mocha Test Runner
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | clear demo
18 | clear log
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/test/mocha-runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Mocha Test Runner
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | clear demo
18 | clear log
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/test/mocha-test.css:
--------------------------------------------------------------------------------
1 | #demo {
2 | border: 1px solid #000000;
3 | width: 100%;
4 | }
5 |
6 | #debug-log {
7 | position: fixed;
8 | top: 60px;
9 | left: 800px;
10 | overflow: scroll;
11 | width: 550px;
12 | height: 500px;
13 | }
14 |
15 | #clear-demo {
16 | position: fixed;
17 | top: 20px;
18 | left: 600px;
19 | padding: 10px;
20 | border: 1px solid blue;
21 | background-color: lightgray;
22 | }
23 |
24 | #clear-log {
25 | position: fixed;
26 | top: 20px;
27 | left: 800px;
28 | padding: 10px;
29 | border: 1px solid blue;
30 | background-color: lightgray;
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/test/react-vue-components/HelloReact.coffee:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 |
3 | export default module.exports = class HelloReact extends Component
4 | render: ->
5 | return React.createElement('div', {}, ['hello react in vue ', this.props.who])
--------------------------------------------------------------------------------
/test/react-vue-components/HelloVue.coffee:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | export default module.exports = Vue.component('hello-vue', {
4 | props: ['who'],
5 | render: (h) ->
6 | return h('div', {}, ['hello vue ', this.who])
7 | })
--------------------------------------------------------------------------------
/test/test-antd.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 |
3 | {normalizeItem, normalizeDomElement} = require 'dc-util'
4 |
5 | import React, {Component} from 'react'
6 | import ReactDom from 'react-dom'
7 | dc.addReactProxy React, ReactDom, Component
8 |
9 | import Chip from '@material-ui/core/Chip'
10 |
11 | import { Button } from 'antd'
12 |
13 | describe "test-antd", ->
14 | beforeEach ->
15 | demoNode = normalizeDomElement('#demo')
16 | if demoNode.childNodes.length
17 | node = demoNode.childNodes[0]
18 | demoNode.removeChild(node)
19 |
20 | # tell React do not warn about this
21 | demoNode._reactRootContainer = undefined
22 | return
23 |
24 | describe 'mount some material-ui dc components', ->
25 | it 'simple Button', ->
26 | view = ['div'
27 | [Button, {type:"primary"},'Default'],
28 | [Button, 'Default'],
29 | [Button, {type:"dashed"},'dashed'],
30 | [Button, {type:"danger"},'danger']
31 | ]
32 |
33 | comp = dc {view}
34 | comp.mount('#demo')
--------------------------------------------------------------------------------
/test/test-directive.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 |
3 | {normalizeItem, normalizeDomElement} = require 'dc-util'
4 |
5 | import React, {Component} from 'react'
6 | import ReactDom from 'react-dom'
7 | dc.addReactProxy React, ReactDom, Component
8 |
9 | import Button from '@material-ui/core/Button'
10 |
11 | describe "test-directive", ->
12 | beforeEach ->
13 | demoNode = normalizeDomElement('#demo')
14 | if demoNode.childNodes.length
15 | node = demoNode.childNodes[0]
16 | demoNode.removeChild(node)
17 |
18 | # tell React do not warn about this
19 | demoNode._reactRootContainer = undefined
20 | return
21 |
22 | describe 'model directive 1', ->
23 | it 'should process model directive', ->
24 | view = ->
25 | [['text', {$model:'message'}], ['p', @message]]
26 | comp = dc({view, message:'hello'})
27 | comp.mount('#demo')
28 |
29 | it 'should process model directive 2', ->
30 | view = ->
31 | ['div', ['text', {$model:'message'}], ['p', this.message]]
32 | comp = dc({view, message:'hello'})
33 | comp.mount('#demo')
34 |
35 | it 'should process model directive 3', ->
36 | view = ->
37 | ['div', ['text', 'message'], ['p', this.message]]
38 | comp = dc({view, message:'hello'})
39 | comp.mount('#demo')
40 |
41 | it 'should process view event without model directive', ->
42 | view = () ->
43 | onChange = (event) =>
44 | comp.message = event.target.value
45 | ['div', ['text', {value: this.message, onChange}], ['p', this.message]]
46 | message = 'hello'
47 | comp = dc({view, message})
48 | comp.mount('#demo')
49 |
--------------------------------------------------------------------------------
/test/test-event.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 |
3 | {normalizeItem, normalizeDomElement} = require 'dc-util'
4 |
5 | import React, {Component} from 'react'
6 | import ReactDom from 'react-dom'
7 | dc.addReactProxy React, ReactDom, Component
8 |
9 | describe "test events", ->
10 | beforeEach ->
11 | demoNode = normalizeDomElement('#demo')
12 | if demoNode.childNodes.length
13 | node = demoNode.childNodes[0]
14 | demoNode.removeChild(node)
15 |
16 | # tell React do not warn about this
17 | demoNode._reactRootContainer = undefined
18 | return
19 |
20 | it 'should process onClick', ->
21 | data = {message:"click me!"}
22 | onClick = ->
23 | data.message = "you clicked!"
24 | dc.update()
25 | view = (data) -> ['div', {onClick}, data.message]
26 | embedded = dc({data, view})
27 | comp = dc({view:embedded})
28 | comp.mount('#demo')
--------------------------------------------------------------------------------
/test/test-material-ui.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 |
3 | {normalizeItem, normalizeDomElement} = require 'dc-util'
4 |
5 | import React, {Component} from 'react'
6 | import ReactDom from 'react-dom'
7 | dc.addReactProxy React, ReactDom, Component
8 |
9 |
10 | import Button from '@material-ui/core/Button'
11 | import Avatar from '@material-ui/core/Avatar'
12 | import List from '@material-ui/core/List'
13 | import ListItem from '@material-ui/core/ListItem'
14 | import ListItemAvatar from '@material-ui/core/ListItemAvatar'
15 | import ListItemText from '@material-ui/core/ListItemText'
16 | import DialogTitle from '@material-ui/core/DialogTitle'
17 | import Dialog from '@material-ui/core/Dialog'
18 | import Typography from '@material-ui/core/Typography'
19 | import blue from '@material-ui/core/colors/blue'
20 | import PersonIcon from '@material-ui/icons/Person'
21 | import AddIcon from '@material-ui/icons/Add'
22 | import AppBar from '@material-ui/core/AppBar'
23 | import Toolbar from '@material-ui/core/Toolbar'
24 | import IconButton from '@material-ui/core/IconButton'
25 | import CloseIcon from '@material-ui/icons/Close'
26 | import Divider from '@material-ui/core/Divider'
27 | import Slide from '@material-ui/core/Slide'
28 |
29 | window.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true
30 |
31 | describe "test-material-ui", ->
32 | beforeEach ->
33 | demoNode = normalizeDomElement('#demo')
34 | if demoNode.childNodes.length
35 | node = demoNode.childNodes[0]
36 | demoNode.removeChild(node)
37 |
38 | # tell React do not warn about this
39 | demoNode._reactRootContainer = undefined
40 | return
41 |
42 | describe 'mount some material-ui dc components', ->
43 | it 'simple material-ui Button', ->
44 | view = ['div'
45 | [Button, {variant:"text", color:"primary"},'primary'],
46 | [Button, 'Default'],
47 | [Button, {variant:"outlined", color:"secondary", disabled:true},'secondary'],
48 | [Button, {variant:"contained", color:"inherit"},'danger']
49 | ]
50 | comp = dc {view}
51 | comp.mount('#demo')
52 |
53 | it 'test a dialog', ->
54 | data = {
55 | emails:['x1@y.z', 'x2@y.z', 'x3@y.z'],
56 | open:true,
57 | handleListItemClick: (message)->
58 | alert 'handleListItemClick: ' + message
59 | data.open = false
60 | dc.update()
61 | handleClose: ->
62 | data.open = false
63 | dc.update()
64 | }
65 | view = (data) ->
66 | [Dialog, {onClose:data.handleClose, 'aria-labelledby':"simple-dialog-title", open:data.open, className:''},
67 | [DialogTitle, "#simple-dialog-title", {key:++dc.dcid}, "Set backup account"],
68 | ['div', {key:++dc.dcid}
69 | [List,{className:'', key:++dc.dcid}
70 | data.emails.map((email) =>
71 | [ListItem, {button:true, onClick:(() => data.handleListItemClick(email)), key:email, className:''},
72 | #[ListItemAvatar, [Avatar, [PersonIcon]]],
73 | [ListItemText, {primary:email, className:'', key:email}, email]
74 | ]),
75 | [ListItem, {button:true, key:dc.dcid++, onClick:() => data.handleListItemClick('addAccount')}, 'add account']]]]
76 | comp = dc({data, view})
77 | comp.mount('#demo')
78 |
79 |
80 | it 'test a dialog 2', ->
81 | Transition = (props) ->
82 | React.createElement(Slide, {direction:"up", props...})
83 | data =
84 | emails: ['x1@y.z', 'x2@y.z', 'x3@y.z'],
85 | open:true,
86 | handleListItemClick: (message)->
87 | alert 'handleListItemClick: ' + message
88 | data.open = false
89 | dc.update()
90 |
91 | handleClickOpen: ->
92 | data.open = true
93 | dc.update()
94 |
95 | close: ->
96 | data.open = false
97 | dc.update()
98 |
99 | view = (data) ->
100 | ['div',
101 | [Button, {onClick:data.handleClickOpen}, "Open full-screen dialog"],
102 | [Dialog, {fullScreen:true, open:data.open, onClose:data.close, TransitionComponent:Transition},
103 | [AppBar, '.appBar',
104 | [Toolbar,
105 | [IconButton, {color:"inherit", onClick:data.close, 'aria-label':"Close"}, [CloseIcon]],
106 | [Typography, {variant:"h6", color:"inherit", className:'.flex'}, 'Sound']
107 | [Button, {color:"inherit", onClick:data.close}, "save"]
108 | ]],
109 | [List,
110 | [ListItem, {button:true},
111 | [ListItemText, {primary:"Phone ringtone", secondary:"Titania"}]],
112 | [Divider]
113 | [ListItem, {button:true},
114 | [ListItemText, {primary:"Default notification ringtone", secondary:"Tethys"}]]]]]
115 | comp = dc({data, view})
116 | comp.mount('#demo')
--------------------------------------------------------------------------------
/test/test-new-dc.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 |
3 | {normalizeItem, normalizeDomElement} = require 'dc-util'
4 |
5 | import React, {Component} from 'react'
6 | import ReactDom from 'react-dom'
7 | dc.addReactProxy React, ReactDom, Component
8 |
9 | import Button from '@material-ui/core/Button'
10 |
11 | describe "test-new-dc", ->
12 | beforeEach ->
13 | demoNode = normalizeDomElement('#demo')
14 | if demoNode.childNodes.length
15 | node = demoNode.childNodes[0]
16 | demoNode.removeChild(node)
17 |
18 | # tell React do not warn about this
19 | demoNode._reactRootContainer = undefined
20 | return
21 |
22 | describe 'mount simple dc components', ->
23 | it 'should dc generate a component', ->
24 | comp = dc()
25 | expect(comp instanceof dc.Component).to.be.true
26 |
27 | it ' dc should check fields', ->
28 | expect(-> dc {dcid:'should error'}).to.throw dc.DomcomError
29 |
30 | it 'simple data view', ->
31 | data = {x:1, y:2}
32 | view = (data) ->
33 | {x, y} = data
34 | return ['div', ['div', x], ['div', y]]
35 | comp = dc {data, view}
36 |
37 | it 'config.view should work 1', ->
38 | view = -> ['div', {}, 'hello domcom mvc']
39 | comp = dc({view})
40 | comp.mount('#demo')
41 | node = document.querySelector('#demo')
42 | expect(node.innerHTML).to.equal 'hello domcom mvc
'
43 |
44 | it 'config.view should work 2', ->
45 | view = ['div', 'hello domcom mvc']
46 | comp = dc({view})
47 | comp.mount('#demo')
48 |
49 | it 'normalizeItem should work', ->
50 | view = ['div', 'hello domcom mvc']
51 | comp = dc({view})
52 | item = normalizeItem(comp.view)
53 | s = JSON.stringify(item)
54 | expect(s).to.equal '["div",{},["hello domcom mvc"]]'
55 |
56 | it 'class in both tag string and props', ->
57 | view = ['div.btn', {classes:"active"}, 'hello domcom mvc']
58 | comp = dc({view})
59 | item = normalizeItem(comp.view)
60 | s = JSON.stringify(item)
61 | expect(s).to.equal '["div",{"className":"btn active"},["hello domcom mvc"]]'
62 |
63 | it 'style in both tag string and props 1', ->
64 | view = ['div##width:100;', {css:"height:200px"}, 'hello domcom mvc']
65 | item = normalizeItem(view)
66 | s = JSON.stringify(item)
67 | expect(s).to.equal '["div",{"style":{"width":"100","height":"200px"}},["hello domcom mvc"]]'
68 |
69 | it 'style and css in both tag string and props 2', ->
70 | red = 'red'
71 | view = ['div##width:100;', {css:"height:200px", style:{backgroundColor:red}}, 'hello domcom mvc']
72 | item = normalizeItem(view)
73 | s = JSON.stringify(item)
74 | expect(s).to.equal '["div",{"style":{"width":"100","height":"200px","backgroundColor":"red"}},["hello domcom mvc"]]'
75 |
76 | it 'camelCase style and css in both tag string and props 1', ->
77 | red = 'red'
78 | view = ['div##width:100;', {css:"height:200px", style:{'background-color':red}}, 'hello domcom mvc']
79 | comp = dc({view})
80 | item = normalizeItem(comp.view)
81 | s = JSON.stringify(item)
82 | expect(s).to.equal '["div",{"style":{"width":"100","height":"200px","backgroundColor":"red"}},["hello domcom mvc"]]'
83 |
84 | it 'multiple props 1', ->
85 | red = 'red'
86 | view = ['div##width:100;', {css:"height:200px"}, {'background-color':red}, 'hello domcom mvc']
87 | comp = dc({view})
88 | item = normalizeItem(comp.view)
89 | s = JSON.stringify(item)
90 | expect(s).to.equal '["div",{"backgroundColor":"red","style":{"width":"100","height":"200px"}},["hello domcom mvc"]]'
91 |
92 | it 'classname with true value', ->
93 | red = 'red'
94 | active = true
95 | view = ['div##width:100;', {classes:{active}}, {'background-color':red}, 'hello domcom mvc']
96 | comp = dc({view})
97 | item = normalizeItem(comp.view)
98 | s = JSON.stringify(item)
99 | expect(s).to.equal '["div",{"backgroundColor":"red","className":"active","style":{"width":"100"}},["hello domcom mvc"]]'
100 |
101 | it 'classname with falsy value', ->
102 | red = 'red'
103 | active = false
104 | view = ['div##width:100;', {classes:{active}}, {'background-color':red}, 'hello domcom mvc']
105 | comp = dc({view})
106 | item = normalizeItem(comp.view)
107 | s = JSON.stringify(item)
108 | expect(s).to.equal '["div",{"backgroundColor":"red","style":{"width":"100"}},["hello domcom mvc"]]'
109 |
110 | it 'tagstr follow ReactClass', ->
111 | red = 'red'
112 | active = false
113 | view = [Button, '##width:100;', {classes:{active}}, {'background-color':red}, 'hello domcom mvc']
114 | comp = dc({view})
115 | item = normalizeItem(comp.view)
116 | s = JSON.stringify(item[1...])
117 | # will not camelcase in props in ReactClass element
118 | expect(s).to.equal '[{"background-color":"red","style":{"width":"100"}},["hello domcom mvc"]]'
119 |
120 | it 'should work on view function', ->
121 | view = (data) -> ['div', 'hello', data]
122 | comp = dc({data:" data", view})
123 | view = comp.getView()
124 | item = normalizeItem(view)
125 | s = JSON.stringify(item)
126 | expect(s).to.equal '["div",{},["hello"," data"]]'
127 | comp.mount('#demo')
128 |
129 | it 'view function should work on data', ->
130 | view = (data) -> ['div', 'hello', data]
131 | comp = dc({view})
132 | comp.mount('#demo')
133 | node = document.querySelector('#demo')
134 | expect(node.innerHTML).to.equal 'hello
'
135 |
136 | it 'tag.classes#id should work', ->
137 | view = (data) -> ['div.btn#button1', 'hello', data]
138 | comp = dc({view})
139 | comp.mount('#demo')
140 | node = document.querySelector('#demo')
141 | expect(node.innerHTML).to.equal 'hello
'
142 |
143 | it 'tag.classes#id::css should work 1', ->
144 | view = (data) -> ['div.btn#button1##width:20px;', 'hello', data]
145 | comp = dc({view})
146 | comp.mount('#demo')
147 | node = document.querySelector('#demo')
148 | expect(node.innerHTML).to.equal 'hello
'
149 |
150 | it 'tag.classes#id::css should work 2', ->
151 | view = (data) -> ['.btn#button1##width:20px;color:red', 'hello', data]
152 | comp = dc({view})
153 | comp.mount('#demo')
154 | node = document.querySelector('#demo')
155 | expect(node.innerHTML).to.equal 'hello
'
156 |
157 | it 'inputtype.classes#id::css should work 2', ->
158 | view = -> ['text.btn#button1##width:200px;color:red']
159 | comp = dc({view})
160 | comp.mount('#demo')
161 | node = document.querySelector('#demo')
162 | expect(node.innerHTML).to.equal ''
163 |
164 | it 'password.classes#id::css should work', ->
165 | view = -> ['password.btn#button1##width:200px;color:red']
166 | comp = dc({view})
167 | comp.mount('#demo')
168 | node = document.querySelector('#demo')
169 | expect(node.innerHTML).to.equal ''
170 |
171 | describe 'mount embedded dc components', ->
172 | it 'should mount embedded component and auto watch it', ->
173 | data = {message:"I am embedded"}
174 | view = (data) -> ['div', data.message]
175 | embedded = dc({data, view})
176 | comp = dc({view:embedded})
177 | comp.mount('#demo')
178 | expect(comp.node.innerHTML).to.equal 'I am embedded'
179 | data.message = "new embedded message"
180 | comp.update()
181 | expect(comp.node.innerHTML).to.equal "new embedded message"
182 |
183 | it 'embedded component will not auto update if stop watching it', ->
184 | data = {message:"I am embedded"}
185 | view = (data) -> ['div', data.message]
186 | embedded = dc({data, view})
187 | comp = dc({view:embedded})
188 | comp.mount('#demo')
189 | embedded.stopWatch()
190 | expect(comp.node.innerHTML).to.equal 'I am embedded'
191 | data.message = "new embedded message"
192 | comp.update()
193 | expect(comp.node.innerHTML).to.equal "I am embedded"
194 |
195 | xit 'should NOT mount the same one component in different places', ->
196 | # this test will result 9 errors in devtools console:
197 | # unique keys warnings
198 | # Uncaught Error: component should be mounted under only one place
199 | # the above should be the expected behaviour.
200 | data = {message:"I am embedded"}
201 | view = (data) -> ['div', data.message]
202 | embedded = dc({data, view})
203 | comp = dc({view:['div', embedded, embedded]})
204 | window.onerror = (error) ->
205 | throw error
206 | expect(-> comp.mount('#demo')).to.throw()
207 |
208 | it 'should mount the embedded component copy', ->
209 | data = {message:"I am embedded"}
210 | view = (data) -> ['div', data.message]
211 | embedded = dc({data, view})
212 | embedded.watch()
213 | embedded2 = embedded.copy()
214 | embedded2.watch()
215 | comp = dc({view:['div', embedded, embedded2]})
216 | comp.mount('#demo')
217 | expect(comp.node.innerHTML).to.equal 'I am embedded
I am embedded
'
218 | data.message = "new embedded message"
219 | expect(comp.node.innerHTML).to.equal 'new embedded message
new embedded message
'
220 |
221 | it 'should mount the embedded component copy 2', ->
222 | data = {show1:true, message1:"I am embedded 1", message2:"I am embedded 2"}
223 | view = (data) ->
224 | if data.show1
225 | ['div', data.message1]
226 | else
227 | ['div', data.message2]
228 | embedded = dc({data, view})
229 | embedded.watch()
230 | embedded2 = embedded.copy().watch()
231 | comp = dc({view:['div', embedded, embedded2]})
232 | comp.mount('#demo')
233 | expect(embedded.node.innerHTML).to.equal 'I am embedded 1'
234 | expect(comp.node.innerHTML).to.equal 'I am embedded 1
I am embedded 1
'
235 | data.show1 = false
236 | expect(comp.node.innerHTML).to.equal 'I am embedded 2
I am embedded 2
'
237 |
238 | it 'should process rebol style function call in view item', ->
239 | if_ = (test, then_, else_) ->
240 | if test
241 | then_
242 | else
243 | else_
244 | item = normalizeItem [if_, 0, 1, 2]
245 |
246 | expect(item).to.equal '2'
247 |
248 | iit 'should update embedded sub components', ->
249 | view = ->
250 | @selected
251 | comp = dc {view}
252 | view = ->
253 | ['div', 'comp1']
254 | comp1 = dc {view}
255 | view = ->
256 | ['div', 'comp2']
257 | comp2 = dc {view}
258 | comp.selected = comp1
259 | comp.mount('#demo')
260 | comp.selected = comp2
261 | console.log('comps: ', comp, comp1, comp2)
262 | comp.update()
--------------------------------------------------------------------------------
/test/test-react-proxy.coffee:
--------------------------------------------------------------------------------
1 | {expect, iit, idescribe, nit, ndescribe} = require 'bdd-test-helper'
2 |
3 | import React, {Component} from 'react'
4 | import ReactDom from 'react-dom'
5 |
6 | dc.addReactProxy React, ReactDom, Component
7 |
8 | {newDemoNode} = require './helper'
9 |
10 | {normalizeDomElement} = require 'dc-util'
11 |
12 |
13 | describe "test react proxy", ->
14 | beforeEach ->
15 | demoNode = normalizeDomElement('#demo2')
16 |
17 | if demoNode.childNodes.length
18 | node = demoNode.childNodes[0]
19 | demoNode.removeChild(node)
20 |
21 | # tell React do not warn about this
22 | demoNode._reactRootContainer = undefined
23 | return
24 |
25 |
26 | describe 'update ReactBlock', ->
27 |
28 | it 'should mount simple react div block 1', ->
29 | comp = dc {view: ['div', 'hello']}
30 | comp.mount('#demo2')
31 |
32 | it 'should mount embedded react div block 2', ->
33 | comp = dc {view: ['div', {}, ['div', {key:1}, 'hello']]}
34 | comp.mount('#demo2')
35 | comp.update()
36 |
37 | it 'should mount react dc + div block', ->
38 | data = {showing:true, message:'dc'}
39 | view = (data) ->
40 | if data.showing
41 | return ['div', data.message]
42 | else
43 | return null
44 | comp = dc {data, view}
45 | data.showing = true
46 | data.message = 'hello dc'
47 | comp.mount('#demo2')
48 | data.showing = false
49 | comp.update()
50 |
51 |
52 | it 'should mount and update react dc + if-else div block 1', ->
53 | data = {showing:true, message1: 'hello dc 1', message2: 'hello dc 2'}
54 | view = (data) ->
55 | if data.showing
56 | ['div', data.message1]
57 | else
58 | ['div', data.message2]
59 | comp = dc {data, view}
60 | data.showing = true
61 | comp.mount('#demo2')
62 | data.showing = false
63 | comp.update()
64 |
65 | it 'should mount and update react dc + if_ div block 2', ->
66 | data = {showing:true, message1: 'hello dc 1', message2:'hello dc 2'}
67 | view = () ->
68 | if this.showing
69 | ['div', data.message1]
70 | else
71 | ['div', data.message2]
72 | comp = dc {data, view}
73 | data.showing = true
74 | comp.mount('#demo2')
75 | data.showing = false
76 | comp.update()
77 | data.showing = true
78 | comp.update()
79 | data.showing = false
80 | comp.update()
81 |
--------------------------------------------------------------------------------
/todo.md:
--------------------------------------------------------------------------------
1 | # todo
2 | test material-ui in domcom 11.25
3 | self controled component, e.g. dialog.open(), dialog.close() 11.25
4 | test antd in domcom 11.25
5 | documents 11.25
6 | VueProxy 11.25
7 |
8 | ### done
9 | ####new design
10 | dc-directive 12.8-12.8
11 | watch component and it's data 12.8-12.8
12 | check only one instance of the component in th dom tree. 11.28-12.8
13 | Component.extend, Component.copy 11.28-12.8
14 | go on removing unused code 11.27-11.28
15 | decouple dc and dc-proxy 11.27--11.28
16 | binds function in config field 11.27-11.27
17 | check the fields in config, do not conflict with Component' prototype 11.27-11.27
18 | test event in domcom 11.25-11.25 add one test
19 | test antd in domcom 11.25-11.25 started with simple button
20 | test material-ui in domcom 11.25-11.25 started with simple button
21 | component in view 11.25-11.25
22 | test classname with false value 11.25-11.25
23 | multiple props in on list element 11.25-11.25
24 | class in both head string and props 11.2-11.25
25 | style in both head string and props 11.25-11.25
26 | dc.update: update all mounted component 11.25-11.25
27 |
28 | data:Component.getData()
29 | view:Component.getView
30 | [] (embedded list) style view
31 | ReactProxy.render(), renderItem, dcutil normalizeItem
32 |
33 | ====================================================================================================================
34 | ####old design
35 | * separate the code for updating dom and attach/detach dom
36 | * flatten the render hierarchy, skip the call to inactive component level
37 | * delegation to events
38 | * error processing: dc.onerror and dc.error
39 | * minify the removing and reinsert of node as possible
40 | * rendering html on the server side: https://github.com/taijiweb/dc-html
41 | * instead of comparing TransformComponent.content too early, only refresh while new baseComponent is different from old baseComponent
42 | * browser compatibility
43 | * use requestAnimationFrame
44 | * Component Event: on, off, emit
45 | * beforeMount, afterMount
46 | * jquery style api
47 | * support object with Each
48 | * rename Repeat to Each
49 | * multiple test if-else if-else, similar to "cond" in lisp
50 | cond: (test, component, ..., otherwise)
51 | * promise
52 | * allow to modify the component
53 | * route
54 | * the property of the component can only be a function or a value
55 | * the value of the private events property of the component always be array of functions, provide bind/unbind method
56 | * skip the inactive component
57 | * only render the difference, according the cache
58 |
59 |
--------------------------------------------------------------------------------