├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── advices ├── counter.js ├── flow.js ├── memoize.js ├── time.js └── trace.js ├── advise.js ├── amd ├── advices │ ├── counter.js │ ├── flow.js │ ├── memoize.js │ ├── time.js │ └── trace.js ├── advise.js ├── bases │ ├── Mixer.js │ └── Replacer.js ├── dcl.js ├── debug.js ├── mixins │ ├── Cleanup.js │ └── Destroyable.js └── utils │ └── registry.js ├── bases ├── Mixer.js └── Replacer.js ├── bower.json ├── dcl.js ├── debug.js ├── dist ├── advices │ ├── counter.js │ ├── flow.js │ ├── memoize.js │ ├── time.js │ └── trace.js ├── advise.js ├── bases │ ├── Mixer.js │ └── Replacer.js ├── dcl.js ├── debug.js ├── mixins │ ├── Cleanup.js │ └── Destroyable.js └── utils │ └── registry.js ├── es6 ├── advices │ ├── counter.js │ ├── flow.js │ ├── memoize.js │ ├── time.js │ └── trace.js ├── advise.js ├── bases │ ├── Mixer.js │ └── Replacer.js ├── dcl.js ├── debug.js ├── mixins │ ├── Cleanup.js │ └── Destroyable.js └── utils │ └── registry.js ├── mixins ├── Cleanup.js └── Destroyable.js ├── package-lock.json ├── package.json ├── tests ├── manual │ ├── test_ctr_aop.js │ ├── test_modern1.js │ ├── test_modern2.js │ ├── test_modern3.js │ ├── test_modern4.js │ ├── test_modern5.js │ ├── test_modern6.js │ └── test_modern7.js ├── phantom.js ├── puppeteer.js ├── test_accessors.js ├── test_advices.js ├── test_advise.js ├── test_advise_accessors.js ├── test_bases.js ├── test_dcl.js ├── test_debug.js ├── test_mini.js ├── test_mixins.js ├── test_raw.js ├── test_registry.js ├── tests.html └── tests.js └── utils └── registry.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | indent_size = 4 10 | 11 | [*.{json,yml,md}] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: uhop 2 | buy_me_a_coffee: uhop 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | *.iml 3 | .idea 4 | *.sublime-* 5 | report/* 6 | coverage/* 7 | .AppleDouble 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | addon: 4 | chrome: stable 5 | 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - "10" 11 | - "12" 12 | - "14" 13 | 14 | env: 15 | - BROWSER=true 16 | - BROWSER=false 17 | 18 | matrix: 19 | exclude: 20 | - node_js: "8" 21 | env: BROWSER=true 22 | - node_js: "10" 23 | env: BROWSER=true 24 | - node_js: "12" 25 | env: BROWSER=true 26 | 27 | before_script: if [ "$BROWSER" == "true" ]; then export RUN_TEST="npm run test-browser"; else export RUN_TEST="npm test"; fi 28 | 29 | script: $RUN_TEST 30 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Eugene Lazutkin (http://lazutkin.com/) 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contribution guidelines 2 | ======================= 3 | 4 | ## For end-users 5 | 6 | If you are experiencing problems, and need help, please start with [the support web site](http://www.dcljs.org/support/). 7 | 8 | ## For contributors 9 | 10 | * Please search the [issue tracker](https://github.com/uhop/dcl/issues) before submitting new issues 11 | to avoid duplicates. 12 | * For any non-trivial contributions (new features, etc.), please create a new issue in the issue tracker 13 | to track discussion prior to submitting a [pull request](http://help.github.com/send-pull-requests). 14 | * For the time being, you must have a signed [Dojo Foundation CLA](http://dojofoundation.org/about/claForm) 15 | for any non-trivial patches to be accepted. 16 | * Any submitted code should conform to the project’s code style guidelines. 17 | * If appropriate, a test case should be part of the pull request. 18 | * Thank you for your contribution! 19 | 20 | ## For committers 21 | 22 | * Provide rigorous code review for contributors. 23 | * When in doubt, ask for a second review; don’t commit code that smells wrong just because it exists. 24 | * Squash all pull requests into a single commit. 25 | * Put `[ci skip]` at the end of commit messages for commits that do not modify any code 26 | (readme changes, etc.) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This library is available under *either* the terms of the modified BSD license 2 | *or* the Academic Free License version 2.1. As a recipient of this work, you 3 | may choose which license to receive this code under. No external contributions 4 | are allowed under licenses which are fundamentally incompatible with the AFL 5 | or BSD licenses that this library is distributed under. 6 | 7 | The text of the AFL and BSD licenses is reproduced below. 8 | 9 | ------------------------------------------------------------------------------- 10 | The "New" BSD License: 11 | ********************** 12 | 13 | Copyright (c) 2005-2012, Eugene Lazutkin 14 | All rights reserved. 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions are met: 18 | 19 | * Redistributions of source code must retain the above copyright notice, this 20 | list of conditions and the following disclaimer. 21 | * Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | * Neither the name of Eugene Lazutkin nor the names of other contributors 25 | may be used to endorse or promote products derived from this software 26 | without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 29 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 32 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 34 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 35 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 36 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | ------------------------------------------------------------------------------- 40 | The Academic Free License, v. 2.1: 41 | ********************************** 42 | 43 | This Academic Free License (the "License") applies to any original work of 44 | authorship (the "Original Work") whose owner (the "Licensor") has placed the 45 | following notice immediately following the copyright notice for the Original 46 | Work: 47 | 48 | Licensed under the Academic Free License version 2.1 49 | 50 | 1) Grant of Copyright License. Licensor hereby grants You a world-wide, 51 | royalty-free, non-exclusive, perpetual, sublicenseable license to do the 52 | following: 53 | 54 | a) to reproduce the Original Work in copies; 55 | 56 | b) to prepare derivative works ("Derivative Works") based upon the Original 57 | Work; 58 | 59 | c) to distribute copies of the Original Work and Derivative Works to the 60 | public; 61 | 62 | d) to perform the Original Work publicly; and 63 | 64 | e) to display the Original Work publicly. 65 | 66 | 2) Grant of Patent License. Licensor hereby grants You a world-wide, 67 | royalty-free, non-exclusive, perpetual, sublicenseable license, under patent 68 | claims owned or controlled by the Licensor that are embodied in the Original 69 | Work as furnished by the Licensor, to make, use, sell and offer for sale the 70 | Original Work and Derivative Works. 71 | 72 | 3) Grant of Source Code License. The term "Source Code" means the preferred 73 | form of the Original Work for making modifications to it and all available 74 | documentation describing how to modify the Original Work. Licensor hereby 75 | agrees to provide a machine-readable copy of the Source Code of the Original 76 | Work along with each copy of the Original Work that Licensor distributes. 77 | Licensor reserves the right to satisfy this obligation by placing a 78 | machine-readable copy of the Source Code in an information repository 79 | reasonably calculated to permit inexpensive and convenient access by You for as 80 | long as Licensor continues to distribute the Original Work, and by publishing 81 | the address of that information repository in a notice immediately following 82 | the copyright notice that applies to the Original Work. 83 | 84 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names 85 | of any contributors to the Original Work, nor any of their trademarks or 86 | service marks, may be used to endorse or promote products derived from this 87 | Original Work without express prior written permission of the Licensor. Nothing 88 | in this License shall be deemed to grant any rights to trademarks, copyrights, 89 | patents, trade secrets or any other intellectual property of Licensor except as 90 | expressly stated herein. No patent license is granted to make, use, sell or 91 | offer to sell embodiments of any patent claims other than the licensed claims 92 | defined in Section 2. No right is granted to the trademarks of Licensor even if 93 | such marks are included in the Original Work. Nothing in this License shall be 94 | interpreted to prohibit Licensor from licensing under different terms from this 95 | License any Original Work that Licensor otherwise would have a right to 96 | license. 97 | 98 | 5) This section intentionally omitted. 99 | 100 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative 101 | Works that You create, all copyright, patent or trademark notices from the 102 | Source Code of the Original Work, as well as any notices of licensing and any 103 | descriptive text identified therein as an "Attribution Notice." You must cause 104 | the Source Code for any Derivative Works that You create to carry a prominent 105 | Attribution Notice reasonably calculated to inform recipients that You have 106 | modified the Original Work. 107 | 108 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that 109 | the copyright in and to the Original Work and the patent rights granted herein 110 | by Licensor are owned by the Licensor or are sublicensed to You under the terms 111 | of this License with the permission of the contributor(s) of those copyrights 112 | and patent rights. Except as expressly stated in the immediately proceeding 113 | sentence, the Original Work is provided under this License on an "AS IS" BASIS 114 | and WITHOUT WARRANTY, either express or implied, including, without limitation, 115 | the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR 116 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. 117 | This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No 118 | license to Original Work is granted hereunder except under this disclaimer. 119 | 120 | 8) Limitation of Liability. Under no circumstances and under no legal theory, 121 | whether in tort (including negligence), contract, or otherwise, shall the 122 | Licensor be liable to any person for any direct, indirect, special, incidental, 123 | or consequential damages of any character arising as a result of this License 124 | or the use of the Original Work including, without limitation, damages for loss 125 | of goodwill, work stoppage, computer failure or malfunction, or any and all 126 | other commercial damages or losses. This limitation of liability shall not 127 | apply to liability for death or personal injury resulting from Licensor's 128 | negligence to the extent applicable law prohibits such limitation. Some 129 | jurisdictions do not allow the exclusion or limitation of incidental or 130 | consequential damages, so this exclusion and limitation may not apply to You. 131 | 132 | 9) Acceptance and Termination. If You distribute copies of the Original Work or 133 | a Derivative Work, You must make a reasonable effort under the circumstances to 134 | obtain the express assent of recipients to the terms of this License. Nothing 135 | else but this License (or another written agreement between Licensor and You) 136 | grants You permission to create Derivative Works based upon the Original Work 137 | or to exercise any of the rights granted in Section 1 herein, and any attempt 138 | to do so except under the terms of this License (or another written agreement 139 | between Licensor and You) is expressly prohibited by U.S. copyright law, the 140 | equivalent laws of other countries, and by international treaty. Therefore, by 141 | exercising any of the rights granted to You in Section 1 herein, You indicate 142 | Your acceptance of this License and all of its terms and conditions. 143 | 144 | 10) Termination for Patent Action. This License shall terminate automatically 145 | and You may no longer exercise any of the rights granted to You by this License 146 | as of the date You commence an action, including a cross-claim or counterclaim, 147 | against Licensor or any licensee alleging that the Original Work infringes a 148 | patent. This termination provision shall not apply for an action alleging 149 | patent infringement by combinations of the Original Work with other software or 150 | hardware. 151 | 152 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this 153 | License may be brought only in the courts of a jurisdiction wherein the 154 | Licensor resides or in which Licensor conducts its primary business, and under 155 | the laws of that jurisdiction excluding its conflict-of-law provisions. The 156 | application of the United Nations Convention on Contracts for the International 157 | Sale of Goods is expressly excluded. Any use of the Original Work outside the 158 | scope of this License or after its termination shall be subject to the 159 | requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et 160 | seq., the equivalent laws of other countries, and international treaty. This 161 | section shall survive the termination of this License. 162 | 163 | 12) Attorneys Fees. In any action to enforce the terms of this License or 164 | seeking damages relating thereto, the prevailing party shall be entitled to 165 | recover its costs and expenses, including, without limitation, reasonable 166 | attorneys' fees and costs incurred in connection with such action, including 167 | any appeal of such action. This section shall survive the termination of this 168 | License. 169 | 170 | 13) Miscellaneous. This License represents the complete agreement concerning 171 | the subject matter hereof. If any provision of this License is held to be 172 | unenforceable, such provision shall be reformed only to the extent necessary to 173 | make it enforceable. 174 | 175 | 14) Definition of "You" in This License. "You" throughout this License, whether 176 | in upper or lower case, means an individual or a legal entity exercising rights 177 | under, and complying with all of the terms of, this License. For legal 178 | entities, "You" includes any entity that controls, is controlled by, or is 179 | under common control with you. For purposes of this definition, "control" means 180 | (i) the power, direct or indirect, to cause the direction or management of such 181 | entity, whether by contract or otherwise, or (ii) ownership of fifty percent 182 | (50%) or more of the outstanding shares, or (iii) beneficial ownership of such 183 | entity. 184 | 185 | 15) Right to Use. You may use the Original Work in all ways not otherwise 186 | restricted or conditioned by this License or by law, and Licensor promises not 187 | to interfere with or be responsible for such uses by You. 188 | 189 | This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. 190 | Permission is hereby granted to copy and distribute this license without 191 | modification. This license may not be modified without the express written 192 | permission of its copyright owner. 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `dcl` 2 | 3 | 4 | [![Build status][travis-image]][travis-url] 5 | [![NPM version][npm-image]][npm-url] 6 | 7 | [![Greenkeeper][greenkeeper-image]][greenkeeper-url] 8 | [![Dependencies][deps-image]][deps-url] 9 | [![devDependencies][dev-deps-image]][dev-deps-url] 10 | 11 | A minimalistic yet complete JavaScript package for [node.js](http://nodejs.org) 12 | and modern browsers that implements OOP with mixins + AOP at both "class" and 13 | object level. Implements [C3 MRO](http://www.python.org/download/releases/2.3/mro/) 14 | to support a Python-like multiple inheritance, efficient supercalls, chaining, 15 | full set of advices, and provides some useful generic building blocks. The whole 16 | package comes with an extensive test set, and it is fully compatible with the strict mode. 17 | 18 | The package was written with debuggability of your code in mind. It comes with 19 | a special debug module that explains mistakes, verifies created objects, and helps 20 | to keep track of AOP advices. Because the package uses direct static calls to super 21 | methods, you don't need to step over unnecessary stubs. In places where stubs are 22 | unavoidable (chains or advices) they are small, and intuitive. 23 | 24 | Based on ES5, the `dcl 2.x` works on Node and all ES5-compatible browsers. It fully 25 | supports property descriptors, including AOP advices for getters and setters, 26 | as well as regular values. If your project needs to support legacy browsers, 27 | please consider `dcl 1.x`. 28 | 29 | The library includes a small library of useful base classes, mixins, and advices. 30 | 31 | The main hub of everything `dcl`-related is [dcljs.org](http://www.dcljs.org/), 32 | which hosts [extensive documentation](http://www.dcljs.org/docs/). 33 | 34 | # Examples 35 | 36 | Create simple class: 37 | 38 | ```js 39 | var A = dcl({ 40 | constructor: function (x) { this.x = x; }, 41 | m: function () { return this.x; } 42 | }); 43 | ``` 44 | 45 | Single inheritance: 46 | 47 | ```js 48 | var B = dcl(A, { 49 | // no constructor 50 | // constructor of A will be called automatically 51 | 52 | m: function () { return this.x + 1; } 53 | }); 54 | ``` 55 | 56 | Multiple inheritance with mixins: 57 | 58 | ```js 59 | var M = dcl({ 60 | sqr: function () { var x = this.m(); return x * x; } 61 | }); 62 | 63 | var AM = dcl([A, M]); 64 | var BM = dcl([B, M]); 65 | 66 | var am = new AM(2); 67 | console.log(am.sqr()); // 4 68 | 69 | var bm = new BM(2); 70 | console.log(bm.sqr()); // 9 71 | ``` 72 | 73 | Super call: 74 | 75 | ```js 76 | var AMSuper = dcl([A, M], { 77 | m: dcl.superCall(function (sup) { 78 | return function () { 79 | return sup.call(this) + 1; 80 | }; 81 | }) 82 | }); 83 | 84 | var ams = new AMSuper(3); 85 | console.log(ams.sqr()); // 16 86 | ``` 87 | 88 | AOP advices: 89 | 90 | ```js 91 | var C = dcl(AMSuper, { 92 | constructor: dcl.advise({ 93 | before: function (x) { 94 | console.log('ctr arg:', x); 95 | }, 96 | after: function () { 97 | console.log('this.x:', this.x); 98 | } 99 | }), 100 | m: dcl.after(function (args, result, makeReturn) { 101 | console.log('m() returned:', result); 102 | // let's fix it 103 | makeReturn(5); 104 | }) 105 | }); 106 | 107 | var c = new C(1); 108 | // prints: 109 | // ctr arg: 1 110 | // this.x: 1 111 | console.log(c.sqr()); 112 | // prints: 113 | // m() returned: 2 114 | // 25 115 | ``` 116 | 117 | Super call with getters: 118 | 119 | ```js 120 | var G = dcl({ 121 | constructor: function (x) { this._x = x; }, 122 | get x () { return this._x; } 123 | }); 124 | 125 | var g = new G(1); 126 | console.log(g.x); // 1 127 | 128 | var F = dcl(G, { 129 | x: dcl.prop({ 130 | get: dcl.superCall(function (sup) { 131 | return function () { 132 | return sup.call(this) + 1; 133 | }; 134 | }) 135 | }) 136 | }); 137 | 138 | var f = new F(1); 139 | console.log(f.x); // 2 140 | ``` 141 | 142 | Advise an object: 143 | 144 | ```js 145 | function D (x) { this.x = x; } 146 | D.prototype.m = function (y) { return this.x + y; } 147 | 148 | var d = new D(1); 149 | console.log(d.m(2)); // 3 150 | 151 | advise(d, 'm', { 152 | before: function (y) { console.log('y:', y); }, 153 | around: function (sup) { 154 | return function (y) { 155 | console.log('around'); 156 | return 2 * sup.call(this, y + 1); 157 | }; 158 | }, 159 | after: function (args, result) { 160 | console.log('# of args:', args.length); 161 | console.log('args[0]:', args[0]); 162 | console.log('result:', result); 163 | } 164 | }); 165 | 166 | console.log(d.m(2)); 167 | // prints: 168 | // y: 2 169 | // around 170 | // # of args: 1 171 | // args[0]: 2 172 | // result: 8 173 | ``` 174 | 175 | Additionally `dcl` provides a small library of predefined 176 | [base classes](http://www.dcljs.org/2.x/docs/bases/), 177 | [mixins](http://www.dcljs.org/2.x/docs/mixins/), 178 | and [useful advices](http://www.dcljs.org/2.x/docs/advices/). Check them out too. 179 | 180 | For more examples, details, howtos, and why, please read [the docs](http://www.dcljs.org/docs/). 181 | 182 | # How to install 183 | 184 | With `npm`: 185 | 186 | ``` 187 | npm install --save dcl 188 | ``` 189 | 190 | With `yarn`: 191 | 192 | ``` 193 | yarn add dcl 194 | ``` 195 | 196 | With `bower`: 197 | 198 | ``` 199 | bower install --save dcl 200 | ``` 201 | 202 | ## How to use 203 | 204 | `dcl` can be installed with `npm`, `yarn`, or `bower` with files available from 205 | `node_modules/` or `bower_components/`. By default, it uses UMD, and ready 206 | to be used with Node's `require()`: 207 | 208 | ```js 209 | // if you run node.js, or CommonJS-compliant system 210 | var dcl = require('dcl'); 211 | var advise = require('dcl/advise'); 212 | ``` 213 | 214 | [Babel](https://babeljs.io/) can have problems while compiling UMD modules, because it appears to generate calls to `require()` dynamically. Specifically for that `dcl` comes with a special ES6 distribution located in `"/es6/"` directory: 215 | 216 | ```js 217 | // ES6 FTW! 218 | import dcl from 'dcl/es6/dcl'; 219 | import advise from 'dcl/es6/advise'; 220 | ``` 221 | 222 | *Warning:* make sure that when you use Babel you include `dcl/es6` sources into the compilation set usually by adding `node_modules/dcl/es6` directory. 223 | 224 | It can be used with AMD out of box: 225 | 226 | ```js 227 | // if you use dcl in a browser with AMD (like RequireJS): 228 | require(['dcl'], function (dcl) { 229 | // the same code that uses dcl 230 | }); 231 | 232 | // or when you define your own module: 233 | define(['dcl'], function (dcl) { 234 | // your dcl-using code goes here 235 | }); 236 | ``` 237 | 238 | If you prefer to use globals in a browser, include files with ` 242 | ``` 243 | 244 | Alternatively, you can use https://unpkg.com/ with AMD or globals. For example: 245 | 246 | ```html 247 | 248 | ``` 249 | 250 | # Documentation 251 | 252 | `dcl` is extensively documented in [the docs](http://www.dcljs.org/docs/). 253 | 254 | # Versions 255 | 256 | ## 2.x 257 | 258 | - 2.0.11 — *Technical release.* 259 | - 2.0.10 — *Refreshed dev dependencies.* 260 | - 2.0.9 — *Refreshed dev dependencies, removed `yarn.lock`.* 261 | - 2.0.8 — *Added AMD distro.* 262 | - 2.0.7 — *A bugfix. Thx [Bill Keese](https://github.com/wkeese)!* 263 | - 2.0.6 — *Bugfixes. Thx [Bill Keese](https://github.com/wkeese)!* 264 | - 2.0.5 — *Regenerated ES6 distro.* 265 | - 2.0.4 — *Refreshed dev dependencies, fixed ES6 distro.* 266 | - 2.0.3 — *Added ES6 distro.* 267 | - 2.0.2 — *Small stability fix + new utility: registry.* 268 | - 2.0.1 — *Small corrections to README.* 269 | - 2.0.0 — *The initial release of 2.x.* 270 | 271 | ## 1.x 272 | 273 | - 1.1.3 — *1.x version before forking for 2.x* 274 | 275 | # License 276 | 277 | BSD or AFL — your choice. 278 | 279 | [npm-image]: https://img.shields.io/npm/v/dcl.svg 280 | [npm-url]: https://npmjs.org/package/dcl 281 | [deps-image]: https://img.shields.io/david/uhop/dcl.svg 282 | [deps-url]: https://david-dm.org/uhop/dcl 283 | [dev-deps-image]: https://img.shields.io/david/dev/uhop/dcl.svg 284 | [dev-deps-url]: https://david-dm.org/uhop/dcl?type=dev 285 | [travis-image]: https://img.shields.io/travis/uhop/dcl.svg 286 | [travis-url]: https://travis-ci.org/uhop/dcl 287 | [greenkeeper-image]: https://badges.greenkeeper.io/uhop/dcl.svg 288 | [greenkeeper-url]: https://greenkeeper.io/ 289 | -------------------------------------------------------------------------------- /advices/counter.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Counter = new dcl({ 6 | declaredClass: 'dcl/advices/counter/Counter', 7 | constructor: function () { 8 | this.reset(); 9 | }, 10 | reset: function () { 11 | this.calls = this.errors = 0; 12 | }, 13 | advice: function () { 14 | var self = this; 15 | return { 16 | before: function () { 17 | ++self.calls; 18 | }, 19 | after: function (args, result) { 20 | if (result instanceof Error) { 21 | ++self.errors; 22 | } 23 | } 24 | }; 25 | } 26 | }); 27 | 28 | return function(){ return new Counter; }; 29 | }); 30 | -------------------------------------------------------------------------------- /advices/flow.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var flowStack = [], flowCount = {}; 6 | 7 | return { 8 | advice: function (name) { 9 | return { 10 | before: function () { 11 | flowStack.push(name); 12 | if (flowCount[name]) { 13 | ++flowCount[name]; 14 | } else { 15 | flowCount[name] = 1; 16 | } 17 | }, 18 | after: function () { 19 | --flowCount[name]; 20 | flowStack.pop(); 21 | } 22 | }; 23 | }, 24 | inFlowOf: function (name) { 25 | return flowCount[name]; 26 | }, 27 | getStack: function () { 28 | return flowStack; 29 | }, 30 | getCount: function () { 31 | return flowCount; 32 | } 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /advices/memoize.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | return { 6 | advice: function (name, keyMaker) { 7 | return keyMaker ? 8 | { 9 | around: function (sup) { 10 | return function () { 11 | var key = keyMaker(this, arguments), cache = this.__memoizerCache, dict; 12 | if (!cache) { 13 | cache = this.__memoizerCache = {}; 14 | } 15 | if (cache.hasOwnProperty(name)) { 16 | dict = cache[name]; 17 | } else { 18 | dict = cache[name] = {}; 19 | } 20 | if (dict.hasOwnProperty(key)) { 21 | return dict[key]; 22 | } 23 | return dict[key] = sup ? sup.apply(this, arguments) : void 0; 24 | }; 25 | } 26 | } : 27 | { 28 | around: function (sup) { 29 | return function (first) { 30 | var cache = this.__memoizerCache, dict; 31 | if (!cache) { 32 | cache = this.__memoizerCache = {}; 33 | } 34 | if (cache.hasOwnProperty(name)) { 35 | dict = cache[name]; 36 | } else { 37 | dict = cache[name] = {}; 38 | } 39 | if (dict.hasOwnProperty(first)) { 40 | return dict[first]; 41 | } 42 | return dict[first] = sup ? sup.apply(this, arguments) : undefined; 43 | }; 44 | } 45 | }; 46 | }, 47 | guard: function (name) { 48 | return { 49 | after: function () { 50 | var cache = this.__memoizerCache; 51 | if (cache && name) { 52 | delete cache[name]; 53 | } else { 54 | this.__memoizerCache = {}; 55 | } 56 | } 57 | }; 58 | } 59 | }; 60 | }); 61 | -------------------------------------------------------------------------------- /advices/time.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var uniq = 0; 6 | 7 | return function (name) { 8 | var inCall = 0, label = name || ('Timer #' + uniq++); 9 | return { 10 | before: function () { 11 | if (!(inCall++)) { 12 | console.time(label); 13 | } 14 | }, 15 | after: function () { 16 | if (!--inCall) { 17 | console.timeEnd(label); 18 | } 19 | } 20 | }; 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /advices/trace.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var lvl = 0; 6 | 7 | function rep (ch, n) { 8 | if (n < 1) { return ''; } 9 | if (n == 1) { return ch; } 10 | var h = rep(ch, Math.floor(n / 2)); 11 | return h + h + ((n & 1) ? ch : ''); 12 | 13 | } 14 | 15 | function pad (value, width, ch) { 16 | var v = value.toString(); 17 | return v + rep(ch || ' ', width - v.length); 18 | } 19 | 20 | return function (name, level) { 21 | return { 22 | before: function () { 23 | ++lvl; 24 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 25 | name + '(' + Array.prototype.join.call(arguments, ', ') + ')'); 26 | }, 27 | after: function (args, result) { 28 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 29 | name + (result && result instanceof Error ? ' throws' : ' returns') + 30 | ' ' + result); 31 | --lvl; 32 | } 33 | }; 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /advise.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var pname = 'prototype'; 6 | 7 | function Node (parent) { 8 | this.parent = parent || this; 9 | } 10 | 11 | Node[pname] = { 12 | removeTopic: function (topic) { 13 | var n = 'next_' + topic, p = 'prev_' + topic; 14 | if (this[n] && this[p]) { 15 | this[n][p] = this[p]; 16 | this[p][n] = this[n]; 17 | } 18 | }, 19 | remove: function () { 20 | this.removeTopic('before'); 21 | this.removeTopic('around'); 22 | 23 | // remove & recreate around advices 24 | var parent = this.parent, next = this.next_around; 25 | this.removeTopic('after'); 26 | for (; next && next !== parent; next = next.next_around) { 27 | next.around = next.originalAround(next.prev_around.around); 28 | } 29 | }, 30 | addTopic: function (node, topic) { 31 | var n = 'next_' + topic, p = 'prev_' + topic, 32 | prev = node[p] = this[p] || this; 33 | node[n] = this; 34 | prev[n] = this[p] = node; 35 | }, 36 | addAdvice: function (advice, instance, name, type) { 37 | var node = new Node(this); 38 | if (advice.before) { 39 | node.before = advice.before; 40 | this.addTopic(node, 'before'); 41 | } 42 | if (advice.around) { 43 | if (typeof advice.around != 'function') { 44 | advise._error('wrong super call', instance, name, type); 45 | } 46 | node.originalAround = advice.around; 47 | this.addTopic(node, 'around'); 48 | if (node.prev_around.around && typeof node.prev_around.around != 'function') { 49 | advise._error('wrong super arg', instance, name, type); 50 | } 51 | node.around = advice.around(node.prev_around.around || null); 52 | if (typeof node.around != 'function') { 53 | advise._error('wrong super result', instance, name, type); 54 | } 55 | } 56 | if (advice.after) { 57 | node.after = advice.after; 58 | this.addTopic(node, 'after'); 59 | } 60 | return node; 61 | } 62 | }; 63 | 64 | Node[pname].destroy = Node[pname].unadvise = Node[pname].remove; 65 | 66 | function addNode (root, topic) { 67 | return function (f) { 68 | var node = new Node(root); 69 | node[topic] = f; 70 | root.addTopic(node, topic); 71 | }; 72 | } 73 | 74 | function makeStub (value) { 75 | var root = new Node(); 76 | if (value) { 77 | if (typeof value.advices == 'object') { 78 | var advices = value.advices; 79 | advices.before.forEach(addNode(root, 'before')); 80 | advices.after. forEach(addNode(root, 'after')); 81 | advices.around && addNode(root, 'around')(advices.around); 82 | } else { 83 | addNode(root, 'around')(value); 84 | } 85 | } 86 | function stub () { 87 | var result, thrown, p; 88 | // running the before chain 89 | for (p = root.prev_before; p && p !== root; p = p.prev_before) { 90 | p.before.apply(this, arguments); 91 | } 92 | // running the around chain 93 | if (root.prev_around && root.prev_around !== root) { 94 | try { 95 | result = root.prev_around.around.apply(this, arguments); 96 | } catch (error) { 97 | result = error; 98 | thrown = true; 99 | } 100 | } 101 | // running the after chain 102 | for (p = root.next_after; p && p !== root; p = p.next_after) { 103 | p.after.call(this, arguments, result, makeReturn, makeThrow); 104 | } 105 | if (thrown) { 106 | throw result; 107 | } 108 | return result; 109 | 110 | function makeReturn (value) { result = value; thrown = false; } 111 | function makeThrow (value) { result = value; thrown = true; } 112 | }; 113 | stub.node = root; 114 | return stub; 115 | } 116 | 117 | function convert (value, advice, instance, name, type) { 118 | if (!value || !(value.node instanceof Node)) { 119 | value = makeStub(value); 120 | value.node.instance = instance; 121 | value.node.name = name; 122 | value.node.type = type; 123 | } 124 | var node = value.node.addAdvice(advice, instance, name, type); 125 | return {value: value, handle: node}; 126 | } 127 | 128 | function combineHandles (handles) { 129 | var handle = { 130 | remove: function () { 131 | handles.forEach(function (handle) { handle.remove(); }); 132 | } 133 | } 134 | handle.destroy = handle.unadvise = handle.remove; 135 | return handle; 136 | } 137 | 138 | function advise (instance, name, advice) { 139 | var prop = getPropertyDescriptor(instance, name), handles = []; 140 | if (prop) { 141 | if (prop.get || prop.set) { 142 | var result; 143 | if (prop.get && advice.get) { 144 | result = convert(prop.get, advice.get, instance, name, 'get'); 145 | prop.get = result.value; 146 | handles.push(result.handle); 147 | } 148 | if (prop.set && advice.set) { 149 | result = convert(prop.set, advice.set, instance, name, 'set'); 150 | prop.set = result.value; 151 | handles.push(result.handle); 152 | } 153 | } else { 154 | if (prop.value && advice) { 155 | result = convert(prop.value, advice, instance, name, 'value'); 156 | prop.value = result.value; 157 | handles.push(result.handle); 158 | } 159 | } 160 | } else { 161 | prop = {writable: true, configurable: true, enumerable: true}; 162 | if (advice.get || advice.set) { 163 | if (advice.get) { 164 | result = convert(null, advice.get, instance, name, 'get'); 165 | prop.get = result.value; 166 | handles.push(result.handle); 167 | } 168 | if (advice.set) { 169 | result = convert(null, advice.set, instance, name, 'set'); 170 | prop.set = result.value; 171 | handles.push(result.handle); 172 | } 173 | } else { 174 | result = convert(null, advice, instance, name, 'value'); 175 | prop.value = result.value; 176 | handles.push(result.handle); 177 | } 178 | } 179 | Object.defineProperty(instance, name, prop); 180 | return combineHandles(handles); 181 | } 182 | 183 | // export 184 | 185 | // guts, do not use them! 186 | advise._error = function (msg) { 187 | throw new Error(msg); 188 | }; 189 | 190 | advise.before = function (instance, name, f) { return advise(instance, name, {before: f}); }; 191 | advise.after = function (instance, name, f) { return advise(instance, name, {after: f}); }; 192 | advise.around = function (instance, name, f) { return advise(instance, name, {around: f}); }; 193 | advise.Node = Node; 194 | 195 | return advise; 196 | 197 | // copied from dcl.js so we can be independent 198 | function getPropertyDescriptor (o, name) { 199 | while (o && o !== Object[pname]) { 200 | if (o.hasOwnProperty(name)) { 201 | return Object.getOwnPropertyDescriptor(o, name); 202 | } 203 | o = Object.getPrototypeOf(o); 204 | } 205 | return; // undefined 206 | } 207 | }); 208 | -------------------------------------------------------------------------------- /amd/advices/counter.js: -------------------------------------------------------------------------------- 1 | define 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Counter = new dcl({ 6 | declaredClass: 'dcl/advices/counter/Counter', 7 | constructor: function () { 8 | this.reset(); 9 | }, 10 | reset: function () { 11 | this.calls = this.errors = 0; 12 | }, 13 | advice: function () { 14 | var self = this; 15 | return { 16 | before: function () { 17 | ++self.calls; 18 | }, 19 | after: function (args, result) { 20 | if (result instanceof Error) { 21 | ++self.errors; 22 | } 23 | } 24 | }; 25 | } 26 | }); 27 | 28 | return function(){ return new Counter; }; 29 | }); 30 | -------------------------------------------------------------------------------- /amd/advices/flow.js: -------------------------------------------------------------------------------- 1 | define 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var flowStack = [], flowCount = {}; 6 | 7 | return { 8 | advice: function (name) { 9 | return { 10 | before: function () { 11 | flowStack.push(name); 12 | if (flowCount[name]) { 13 | ++flowCount[name]; 14 | } else { 15 | flowCount[name] = 1; 16 | } 17 | }, 18 | after: function () { 19 | --flowCount[name]; 20 | flowStack.pop(); 21 | } 22 | }; 23 | }, 24 | inFlowOf: function (name) { 25 | return flowCount[name]; 26 | }, 27 | getStack: function () { 28 | return flowStack; 29 | }, 30 | getCount: function () { 31 | return flowCount; 32 | } 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /amd/advices/memoize.js: -------------------------------------------------------------------------------- 1 | define 2 | ([], function () { 3 | 'use strict'; 4 | 5 | return { 6 | advice: function (name, keyMaker) { 7 | return keyMaker ? 8 | { 9 | around: function (sup) { 10 | return function () { 11 | var key = keyMaker(this, arguments), cache = this.__memoizerCache, dict; 12 | if (!cache) { 13 | cache = this.__memoizerCache = {}; 14 | } 15 | if (cache.hasOwnProperty(name)) { 16 | dict = cache[name]; 17 | } else { 18 | dict = cache[name] = {}; 19 | } 20 | if (dict.hasOwnProperty(key)) { 21 | return dict[key]; 22 | } 23 | return dict[key] = sup ? sup.apply(this, arguments) : void 0; 24 | }; 25 | } 26 | } : 27 | { 28 | around: function (sup) { 29 | return function (first) { 30 | var cache = this.__memoizerCache, dict; 31 | if (!cache) { 32 | cache = this.__memoizerCache = {}; 33 | } 34 | if (cache.hasOwnProperty(name)) { 35 | dict = cache[name]; 36 | } else { 37 | dict = cache[name] = {}; 38 | } 39 | if (dict.hasOwnProperty(first)) { 40 | return dict[first]; 41 | } 42 | return dict[first] = sup ? sup.apply(this, arguments) : undefined; 43 | }; 44 | } 45 | }; 46 | }, 47 | guard: function (name) { 48 | return { 49 | after: function () { 50 | var cache = this.__memoizerCache; 51 | if (cache && name) { 52 | delete cache[name]; 53 | } else { 54 | this.__memoizerCache = {}; 55 | } 56 | } 57 | }; 58 | } 59 | }; 60 | }); 61 | -------------------------------------------------------------------------------- /amd/advices/time.js: -------------------------------------------------------------------------------- 1 | define 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var uniq = 0; 6 | 7 | return function (name) { 8 | var inCall = 0, label = name || ('Timer #' + uniq++); 9 | return { 10 | before: function () { 11 | if (!(inCall++)) { 12 | console.time(label); 13 | } 14 | }, 15 | after: function () { 16 | if (!--inCall) { 17 | console.timeEnd(label); 18 | } 19 | } 20 | }; 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /amd/advices/trace.js: -------------------------------------------------------------------------------- 1 | define 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var lvl = 0; 6 | 7 | function rep (ch, n) { 8 | if (n < 1) { return ''; } 9 | if (n == 1) { return ch; } 10 | var h = rep(ch, Math.floor(n / 2)); 11 | return h + h + ((n & 1) ? ch : ''); 12 | 13 | } 14 | 15 | function pad (value, width, ch) { 16 | var v = value.toString(); 17 | return v + rep(ch || ' ', width - v.length); 18 | } 19 | 20 | return function (name, level) { 21 | return { 22 | before: function () { 23 | ++lvl; 24 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 25 | name + '(' + Array.prototype.join.call(arguments, ', ') + ')'); 26 | }, 27 | after: function (args, result) { 28 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 29 | name + (result && result instanceof Error ? ' throws' : ' returns') + 30 | ' ' + result); 31 | --lvl; 32 | } 33 | }; 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /amd/advise.js: -------------------------------------------------------------------------------- 1 | define 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var pname = 'prototype'; 6 | 7 | function Node (parent) { 8 | this.parent = parent || this; 9 | } 10 | 11 | Node[pname] = { 12 | removeTopic: function (topic) { 13 | var n = 'next_' + topic, p = 'prev_' + topic; 14 | if (this[n] && this[p]) { 15 | this[n][p] = this[p]; 16 | this[p][n] = this[n]; 17 | } 18 | }, 19 | remove: function () { 20 | this.removeTopic('before'); 21 | this.removeTopic('around'); 22 | 23 | // remove & recreate around advices 24 | var parent = this.parent, next = this.next_around; 25 | this.removeTopic('after'); 26 | for (; next && next !== parent; next = next.next_around) { 27 | next.around = next.originalAround(next.prev_around.around); 28 | } 29 | }, 30 | addTopic: function (node, topic) { 31 | var n = 'next_' + topic, p = 'prev_' + topic, 32 | prev = node[p] = this[p] || this; 33 | node[n] = this; 34 | prev[n] = this[p] = node; 35 | }, 36 | addAdvice: function (advice, instance, name, type) { 37 | var node = new Node(this); 38 | if (advice.before) { 39 | node.before = advice.before; 40 | this.addTopic(node, 'before'); 41 | } 42 | if (advice.around) { 43 | if (typeof advice.around != 'function') { 44 | advise._error('wrong super call', instance, name, type); 45 | } 46 | node.originalAround = advice.around; 47 | this.addTopic(node, 'around'); 48 | if (node.prev_around.around && typeof node.prev_around.around != 'function') { 49 | advise._error('wrong super arg', instance, name, type); 50 | } 51 | node.around = advice.around(node.prev_around.around || null); 52 | if (typeof node.around != 'function') { 53 | advise._error('wrong super result', instance, name, type); 54 | } 55 | } 56 | if (advice.after) { 57 | node.after = advice.after; 58 | this.addTopic(node, 'after'); 59 | } 60 | return node; 61 | } 62 | }; 63 | 64 | Node[pname].destroy = Node[pname].unadvise = Node[pname].remove; 65 | 66 | function addNode (root, topic) { 67 | return function (f) { 68 | var node = new Node(root); 69 | node[topic] = f; 70 | root.addTopic(node, topic); 71 | }; 72 | } 73 | 74 | function makeStub (value) { 75 | var root = new Node(); 76 | if (value) { 77 | if (typeof value.advices == 'object') { 78 | var advices = value.advices; 79 | advices.before.forEach(addNode(root, 'before')); 80 | advices.after. forEach(addNode(root, 'after')); 81 | advices.around && addNode(root, 'around')(advices.around); 82 | } else { 83 | addNode(root, 'around')(value); 84 | } 85 | } 86 | function stub () { 87 | var result, thrown, p; 88 | // running the before chain 89 | for (p = root.prev_before; p && p !== root; p = p.prev_before) { 90 | p.before.apply(this, arguments); 91 | } 92 | // running the around chain 93 | if (root.prev_around && root.prev_around !== root) { 94 | try { 95 | result = root.prev_around.around.apply(this, arguments); 96 | } catch (error) { 97 | result = error; 98 | thrown = true; 99 | } 100 | } 101 | // running the after chain 102 | for (p = root.next_after; p && p !== root; p = p.next_after) { 103 | p.after.call(this, arguments, result, makeReturn, makeThrow); 104 | } 105 | if (thrown) { 106 | throw result; 107 | } 108 | return result; 109 | 110 | function makeReturn (value) { result = value; thrown = false; } 111 | function makeThrow (value) { result = value; thrown = true; } 112 | }; 113 | stub.node = root; 114 | return stub; 115 | } 116 | 117 | function convert (value, advice, instance, name, type) { 118 | if (!value || !(value.node instanceof Node)) { 119 | value = makeStub(value); 120 | value.node.instance = instance; 121 | value.node.name = name; 122 | value.node.type = type; 123 | } 124 | var node = value.node.addAdvice(advice, instance, name, type); 125 | return {value: value, handle: node}; 126 | } 127 | 128 | function combineHandles (handles) { 129 | var handle = { 130 | remove: function () { 131 | handles.forEach(function (handle) { handle.remove(); }); 132 | } 133 | } 134 | handle.destroy = handle.unadvise = handle.remove; 135 | return handle; 136 | } 137 | 138 | function advise (instance, name, advice) { 139 | var prop = getPropertyDescriptor(instance, name), handles = []; 140 | if (prop) { 141 | if (prop.get || prop.set) { 142 | var result; 143 | if (prop.get && advice.get) { 144 | result = convert(prop.get, advice.get, instance, name, 'get'); 145 | prop.get = result.value; 146 | handles.push(result.handle); 147 | } 148 | if (prop.set && advice.set) { 149 | result = convert(prop.set, advice.set, instance, name, 'set'); 150 | prop.set = result.value; 151 | handles.push(result.handle); 152 | } 153 | } else { 154 | if (prop.value && advice) { 155 | result = convert(prop.value, advice, instance, name, 'value'); 156 | prop.value = result.value; 157 | handles.push(result.handle); 158 | } 159 | } 160 | } else { 161 | prop = {writable: true, configurable: true, enumerable: true}; 162 | if (advice.get || advice.set) { 163 | if (advice.get) { 164 | result = convert(null, advice.get, instance, name, 'get'); 165 | prop.get = result.value; 166 | handles.push(result.handle); 167 | } 168 | if (advice.set) { 169 | result = convert(null, advice.set, instance, name, 'set'); 170 | prop.set = result.value; 171 | handles.push(result.handle); 172 | } 173 | } else { 174 | result = convert(null, advice, instance, name, 'value'); 175 | prop.value = result.value; 176 | handles.push(result.handle); 177 | } 178 | } 179 | Object.defineProperty(instance, name, prop); 180 | return combineHandles(handles); 181 | } 182 | 183 | // export 184 | 185 | // guts, do not use them! 186 | advise._error = function (msg) { 187 | throw new Error(msg); 188 | }; 189 | 190 | advise.before = function (instance, name, f) { return advise(instance, name, {before: f}); }; 191 | advise.after = function (instance, name, f) { return advise(instance, name, {after: f}); }; 192 | advise.around = function (instance, name, f) { return advise(instance, name, {around: f}); }; 193 | advise.Node = Node; 194 | 195 | return advise; 196 | 197 | // copied from dcl.js so we can be independent 198 | function getPropertyDescriptor (o, name) { 199 | while (o && o !== Object[pname]) { 200 | if (o.hasOwnProperty(name)) { 201 | return Object.getOwnPropertyDescriptor(o, name); 202 | } 203 | o = Object.getPrototypeOf(o); 204 | } 205 | return; // undefined 206 | } 207 | }); 208 | -------------------------------------------------------------------------------- /amd/bases/Mixer.js: -------------------------------------------------------------------------------- 1 | define 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Mixer', 7 | constructor: function (x) { 8 | Object.defineProperties(this, dcl.collectPropertyDescriptors({}, x)); 9 | } 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /amd/bases/Replacer.js: -------------------------------------------------------------------------------- 1 | define 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Replacer', 7 | constructor: function (x) { 8 | var props = dcl.collectPropertyDescriptors({}, x); 9 | Object.keys(props).forEach(function (name) { 10 | if (name in this) { 11 | Object.defineProperty(this, name, props[name]); 12 | } 13 | }, this); 14 | } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /amd/dcl.js: -------------------------------------------------------------------------------- 1 | define 2 | ([], function () { 3 | 'use strict'; 4 | 5 | // set up custom names 6 | var mname = '_meta', pname = 'prototype', cname = 'constructor'; 7 | 8 | 9 | // MODULE: restricted Map shim (used by C3 MRO) 10 | 11 | var M; // our map implementation if not defined 12 | if (typeof Map == 'undefined') { 13 | // our fake, inefficient, incomplete, yet totally correct Map 14 | M = function () { 15 | this.list = []; 16 | this.size = 0; 17 | }; 18 | M.prototype = { 19 | has: function (key) { return this.get(key); }, 20 | get: function (key) { 21 | for (var i = 0, n = this.list.length; i < n; i += 2) { 22 | if (key === this.list[i]) { 23 | return this.list[i + 1]; 24 | } 25 | } 26 | // returns undefined if not found 27 | }, 28 | set: function (key, value) { 29 | for (var i = 0, n = this.list.length; i < n; i += 2) { 30 | if (key === this.list[i]) { 31 | this.list[i + 1] = value; 32 | return this; 33 | } 34 | } 35 | this.list.push(key, value); 36 | ++this.size; 37 | return this; 38 | } 39 | }; 40 | } else { 41 | M = Map; 42 | } 43 | 44 | 45 | // MODULE: C3 MRO 46 | 47 | function c3mro (bases, props) { 48 | // build a connectivity matrix 49 | var connectivity = new M(); 50 | bases.forEach(function (base) { 51 | (base[mname] ? base[mname].bases : [base]).forEach(function (base, index, array) { 52 | if (connectivity.has(base)) { 53 | var value = connectivity.get(base); 54 | index + 1 != array.length && ++value.counter; 55 | if (index) { 56 | value.links.push(array[index - 1]); 57 | } 58 | } else { 59 | connectivity.set(base, { 60 | links: index ? [array[index - 1]] : [], 61 | counter: index + 1 == array.length ? 0 : 1 62 | }); 63 | } 64 | }); 65 | }); 66 | // Kahn's algorithm 67 | var output = [], unreferenced = []; 68 | // find unreferenced bases 69 | bases.forEach(function (base) { 70 | var last = base[mname] ? base[mname].bases[base[mname].bases.length - 1] : base; 71 | if (!connectivity.get(last).counter) { 72 | unreferenced.push(last); 73 | } 74 | }); 75 | while (unreferenced.length) { 76 | var base = unreferenced.pop(); 77 | output.push(base); 78 | var value = connectivity.get(base); 79 | value.links.forEach(updateCounter); 80 | } 81 | // final checks and return 82 | if (connectivity.size != output.length) { 83 | dcl._error('cycle', bases, props); 84 | } 85 | return output; 86 | 87 | function updateCounter (base) { 88 | var value = connectivity.get(base); 89 | if (!--value.counter) { 90 | unreferenced.push(base); 91 | } 92 | } 93 | } 94 | 95 | 96 | // MODULE: handling properties 97 | 98 | function updateProps (props, defaults, augmentDescriptor, augmentWritable) { 99 | var augmenter; 100 | if ('configurable' in defaults) { 101 | augmenter = augmentDescriptor('configurable', defaults.configurable); 102 | Object.keys(props).forEach(function (name) { augmenter(props[name], name); }); 103 | } 104 | if ('enumerable' in defaults) { 105 | augmenter = augmentDescriptor('enumerable', defaults.enumerable); 106 | Object.keys(props).forEach(function (name) { augmenter(props[name], name); }); 107 | } 108 | if ('writable' in defaults) { 109 | augmenter = augmentWritable(defaults.writable); 110 | Object.keys(props).forEach(function (name) { augmenter(props[name], name); }); 111 | } 112 | return props; 113 | } 114 | 115 | var descriptorProperties = {configurable: 1, enumerable: 1, value: 1, writable: 1, get: 1, set: 1}; 116 | 117 | function toProperties(x, defaults) { 118 | var props, descriptors; 119 | if (x instanceof dcl.Prop) { 120 | props = x.x; 121 | } else { 122 | Object.getOwnPropertyNames(x).forEach(function(key) { 123 | var prop = Object.getOwnPropertyDescriptor(x, key); 124 | if (prop.get || prop.set) { 125 | // accessor descriptor 126 | descriptors = descriptors || {}; 127 | descriptors[key] = prop; 128 | } else { 129 | // data descriptor 130 | var value = prop.value; 131 | if (value instanceof dcl.Prop) { 132 | props = props || {}; 133 | props[key] = value.x; 134 | } else { 135 | if (defaults.detectProps && value && typeof value == 'object') { 136 | if (Object.keys(value).every(function (name) { return descriptorProperties[name] === 1; })) { 137 | props = props || {}; 138 | props[key] = value; 139 | return; 140 | } 141 | } 142 | descriptors = descriptors || {}; 143 | descriptors[key] = prop; 144 | } 145 | } 146 | }); 147 | } 148 | if (props) { 149 | props = updateProps(props, defaults, augmentDescriptor, augmentWritable); 150 | } 151 | if (descriptors) { 152 | descriptors = updateProps(descriptors, defaults, replaceDescriptor, replaceWritable); 153 | } 154 | if (descriptors && props) { 155 | Object.keys(props).forEach(function(key) { 156 | descriptors[key] = props[key]; 157 | }); 158 | } 159 | return descriptors || props || {}; 160 | } 161 | 162 | function cloneDescriptor (descriptor) { // shallow copy 163 | var newDescriptor = {}; 164 | Object.keys(descriptor).forEach(function (name) { 165 | newDescriptor[name] = descriptor[name]; 166 | }); 167 | return newDescriptor; 168 | } 169 | 170 | function augmentDescriptor(type, value) { 171 | return typeof value == 'function' ? value : function(descriptor) { 172 | if (!descriptor.hasOwnProperty(type)) { 173 | descriptor[type] = value; 174 | } 175 | }; 176 | } 177 | 178 | function augmentWritable(value) { 179 | return typeof value == 'function' ? value : function(descriptor) { 180 | if (!descriptor.get && !descriptor.set && !descriptor.hasOwnProperty('writable')) { 181 | descriptor.writable = value; 182 | } 183 | }; 184 | } 185 | 186 | function replaceDescriptor(type, value) { 187 | return typeof value == 'function' ? value : function(descriptor) { 188 | descriptor[type] = value; 189 | }; 190 | } 191 | 192 | function replaceWritable(value) { 193 | return typeof value == 'function' ? value : function(descriptor) { 194 | if (descriptor.hasOwnProperty('value')) { 195 | descriptor.writable = value; 196 | } 197 | }; 198 | } 199 | 200 | function getPropertyDescriptor (o, name) { 201 | while (o && o !== Object.prototype) { 202 | if (Object.prototype.hasOwnProperty.call(o, name)) { 203 | return Object.getOwnPropertyDescriptor(o, name); 204 | } 205 | o = Object.getPrototypeOf(o); 206 | } 207 | // otherwise: undefined 208 | } 209 | 210 | 211 | // MODULE: produce properties 212 | 213 | function recordProp(props, o, recorded) { 214 | return function (name) { 215 | if (recorded[name] !== 1) { 216 | recorded[name] = 1; 217 | props[name] = Object.getOwnPropertyDescriptor(o, name); 218 | } 219 | }; 220 | } 221 | 222 | function collectPropertyDescriptors (props, o) { 223 | var recorded = {}; 224 | while (o && o !== Object.prototype) { 225 | Object.getOwnPropertyNames(o).forEach(recordProp(props, o, recorded)); 226 | o = Object.getPrototypeOf(o); 227 | } 228 | return props; 229 | } 230 | 231 | // populate properties with simple properties 232 | function populateProps (props, mixins, special) { 233 | var newSpecial = {}; 234 | mixins.forEach(function (base) { 235 | // copy properties for dcl objects 236 | if (base[mname]) { 237 | var baseProps = base[mname].props; 238 | Object.keys(baseProps).forEach(function (name) { 239 | if (Object.prototype.hasOwnProperty.call(special, name)) { 240 | newSpecial[name] = special[name]; 241 | } else { 242 | props[name] = baseProps[name]; 243 | } 244 | }); 245 | return; 246 | } 247 | // copy properties for regular objects 248 | collectPropertyDescriptors(props, base[pname]); 249 | }); 250 | return newSpecial; 251 | } 252 | 253 | var empty = {}; 254 | 255 | function weaveProp (name, bases, weaver, props) { 256 | var state = {prop: null, backlog: []}; 257 | weaver.start && weaver.start(state); 258 | bases.forEach(function (base, index) { 259 | var prop; 260 | if (base[mname]) { 261 | var baseProps = base[mname].props; 262 | if (Object.prototype.hasOwnProperty.call(baseProps, name)) { 263 | prop = baseProps[name]; 264 | } 265 | } else { 266 | prop = getPropertyDescriptor(base[pname], name); 267 | if (!prop && name == cname) { 268 | prop = {configurable: true, enumerable: false, writable: true, value: base}; 269 | } 270 | } 271 | if (!prop) { 272 | return; 273 | } 274 | var newProp = cloneDescriptor(prop), prevProp, superArg; 275 | if (prop.get || prop.set) { 276 | // accessor 277 | var superGet = isSuper(prop.get) && prop.get.spr.around, 278 | superSet = isSuper(prop.set) && prop.set.spr.around; 279 | if (superGet || superSet) { 280 | processBacklog(state, weaver); 281 | prevProp = state.prop; 282 | } 283 | if (superGet) { 284 | if (typeof prop.get.spr.around != 'function') { 285 | dcl._error('wrong super get call', base, name, index, props); 286 | } 287 | superArg = null; 288 | if (prevProp) { 289 | superArg = prevProp.get || prevProp.set ? 290 | prevProp.get : adaptValue(prevProp.value); 291 | } 292 | if (superArg && typeof superArg != 'function') { 293 | dcl._error('wrong super get arg', base, name, index, props); 294 | } 295 | newProp.get = prop.get.spr.around(superArg); 296 | if (typeof newProp.get != 'function') { 297 | dcl._error('wrong super get result', base, name, index, props); 298 | } 299 | state.prop = null; 300 | } 301 | if (superSet) { 302 | if (typeof prop.set.spr.around != 'function') { 303 | dcl._error('wrong super set call', base, name, index, props); 304 | } 305 | superArg = prevProp && prevProp.set; 306 | if (superArg && typeof superArg != 'function') { 307 | dcl._error('wrong super set arg', base, name, index, props); 308 | } 309 | newProp.set = prop.set.spr.around(superArg); 310 | if (typeof newProp.set != 'function') { 311 | dcl._error('wrong super set result', base, name, index, props); 312 | } 313 | state.prop = null; 314 | } 315 | if ((!prop.get || isSuper(prop.get) && !prop.get.spr.around) && (!prop.set || isSuper(prop.set) && !prop.set.spr.around)) { 316 | return; // skip descriptor: no actionable value 317 | } 318 | } else { 319 | // data 320 | if (isSuper(prop.value) && prop.value.spr.around) { 321 | processBacklog(state, weaver); 322 | prevProp = state.prop; 323 | if (typeof prop.value.spr.around != 'function') { 324 | dcl._error('wrong super value call', base, name, index, props); 325 | } 326 | if (prevProp) { 327 | superArg = prevProp.get || prevProp.set ? 328 | adaptGet(prevProp.get) : prevProp.value; 329 | } else { 330 | superArg = name !== cname && empty[name]; 331 | } 332 | if (superArg && typeof superArg != 'function') { 333 | dcl._error('wrong super value arg', base, name, index, props); 334 | } 335 | newProp.value = prop.value.spr.around(superArg); 336 | if (typeof newProp.value != 'function') { 337 | dcl._error('wrong super value result', base, name, index, props); 338 | } 339 | state.prop = null; 340 | } 341 | if (!prop.value || isSuper(prop.value) && !prop.value.spr.around) { 342 | return; // skip descriptor: no actionable value 343 | } 344 | } 345 | if (state.prop) { 346 | if (newProp.get || newProp.set) { 347 | if (state.backlog.length) { 348 | state.backlog = []; 349 | } 350 | } else { 351 | state.backlog.push(convertToValue(state.prop)); 352 | } 353 | } 354 | state.prop = newProp; 355 | }); 356 | processBacklog(state, weaver); 357 | return weaver.stop ? weaver.stop(state) : state.prop; 358 | } 359 | 360 | var dclUtils = {adaptValue: adaptValue, adaptGet: adaptGet, 361 | convertToValue: convertToValue, cloneDescriptor: cloneDescriptor, 362 | augmentDescriptor: augmentDescriptor, augmentWritable: augmentWritable, 363 | replaceDescriptor: replaceDescriptor, replaceWritable: replaceWritable 364 | }; 365 | 366 | function processBacklog (state, weaver) { 367 | if (state.backlog.length) { 368 | state.backlog.push(convertToValue(state.prop)); 369 | state.prop = weaver.weave(state.backlog, dclUtils); 370 | state.backlog = []; 371 | } 372 | } 373 | 374 | function adaptValue (f) { 375 | return f ? function () { return f; } : null; 376 | } 377 | 378 | function adaptGet (f) { 379 | return f ? function () { return f.call(this).apply(this, arguments); } : null; 380 | } 381 | 382 | function convertToValue (prop) { 383 | if (prop.get || prop.set) { 384 | var newProp = cloneDescriptor(prop); 385 | delete newProp.get; 386 | delete newProp.set; 387 | newProp.value = adaptGet(prop.get); 388 | return newProp; 389 | } 390 | return prop; 391 | } 392 | 393 | 394 | // dcl helpers 395 | 396 | function getAccessorSideAdvices (name, bases, propName) { 397 | var before = [], after = []; 398 | bases.forEach(function (base) { 399 | if (base[mname]) { 400 | var prop = base[mname].props[name]; 401 | if (typeof prop == 'object') { 402 | if (prop.get || prop.set) { 403 | var f = prop[propName]; 404 | if (isSuper(f)) { 405 | f.spr.before && before.push(f.spr.before); 406 | f.spr.after && after .push(f.spr.after); 407 | } 408 | } 409 | } 410 | } 411 | }); 412 | if (before.length > 1) { before.reverse(); } 413 | return {before: before, after: after}; 414 | } 415 | 416 | function getDataSideAdvices (name, bases) { 417 | var before = [], after = []; 418 | bases.forEach(function (base) { 419 | if (base[mname]) { 420 | var prop = base[mname].props[name]; 421 | if (typeof prop == 'object') { 422 | var f = prop.get || prop.set ? prop.get : prop.value; 423 | if (isSuper(f)) { 424 | f.spr.before && before.push(f.spr.before); 425 | f.spr.after && after .push(f.spr.after); 426 | } 427 | } 428 | } 429 | }); 430 | if (before.length > 1) { before.reverse(); } 431 | return {before: before, after: after}; 432 | } 433 | 434 | function makeStub (aroundStub, beforeChain, afterChain) { 435 | var beforeLength = beforeChain.length, afterLength = afterChain.length, 436 | stub = function () { 437 | var result, thrown, i; 438 | // running the before chain 439 | for (i = 0; i < beforeLength; ++i) { 440 | beforeChain[i].apply(this, arguments); 441 | } 442 | // running the around chain 443 | if (aroundStub) { 444 | try { 445 | result = aroundStub.apply(this, arguments); 446 | } catch (error) { 447 | result = error; 448 | thrown = true; 449 | } 450 | } 451 | // running the after chain 452 | for (i = 0; i < afterLength; ++i) { 453 | afterChain[i].call(this, arguments, result, makeReturn, makeThrow); 454 | } 455 | if (thrown) { 456 | throw result; 457 | } 458 | return result; 459 | 460 | function makeReturn (value) { result = value; thrown = false; } 461 | function makeThrow (value) { result = value; thrown = true; } 462 | }; 463 | stub.advices = {around: aroundStub, before: beforeChain, after: afterChain}; 464 | return stub; 465 | } 466 | 467 | function makeCtr (baseClass, finalProps, meta) { 468 | var proto = baseClass ? 469 | Object.create(baseClass[pname], finalProps) : 470 | Object.defineProperties({}, finalProps), 471 | ctr = proto[cname]; 472 | 473 | ctr[mname] = meta; 474 | ctr[pname] = proto; 475 | meta.bases[meta.bases.length - 1] = ctr; 476 | 477 | return ctr; 478 | } 479 | 480 | 481 | // MODULE: weavers 482 | 483 | function weaveAround (chain, utils) { 484 | var newProp = utils.cloneDescriptor(chain[chain.length - 1]); 485 | 486 | if (newProp.get || newProp.set) { 487 | // convert to value 488 | newProp.value = utils.adaptGet(newProp.get); 489 | delete newProp.get; 490 | delete newProp.set; 491 | } 492 | 493 | return newProp; 494 | } 495 | 496 | function weaveChain (chain, utils) { 497 | if (this.reverse) { 498 | chain.reverse(); 499 | } 500 | 501 | var newProp = utils.cloneDescriptor(chain[chain.length - 1]); 502 | 503 | // extract functions 504 | chain = chain.map(function (prop) { 505 | return prop.get || prop.set ? utils.adaptGet(prop.get) : prop.value; 506 | }); 507 | 508 | newProp.value = function () { 509 | for (var i = 0; i < chain.length; ++i) { 510 | chain[i].apply(this, arguments); 511 | } 512 | }; 513 | 514 | return newProp; 515 | } 516 | 517 | 518 | // MODULE: dcl (the main function) 519 | 520 | function dcl (superClass, props, options) { 521 | // parse arguments 522 | if (superClass instanceof Array) { 523 | // skip 524 | } else if (typeof superClass == 'function') { 525 | superClass = [superClass]; 526 | } else if (!superClass) { 527 | superClass = []; 528 | } else { 529 | options = props; 530 | props = superClass; 531 | superClass = []; 532 | } 533 | props = toProperties(props || {}, options || {}); 534 | 535 | // find the base class, and mixins 536 | var bases = [], baseClass = superClass[0], mixins = [], baseIndex = -1; 537 | if (superClass.length) { 538 | if (superClass.length > 1) { 539 | bases = c3mro(superClass, props).reverse(); 540 | if (baseClass[mname]) { 541 | baseIndex = baseClass[mname].bases.length - 1; 542 | } else { 543 | mixins = bases.slice(1); 544 | baseIndex = 0; 545 | } 546 | } else { 547 | if (baseClass[mname]) { 548 | bases = baseClass[mname].bases.slice(0); 549 | baseIndex = bases.length - 1; 550 | } else { 551 | bases = [baseClass]; 552 | baseIndex = 0; 553 | } 554 | } 555 | if (bases[baseIndex] === baseClass) { 556 | mixins = bases.slice(baseIndex + 1); 557 | } else { 558 | baseClass = null; 559 | mixins = bases.slice(0); 560 | } 561 | } 562 | 563 | // add a stand-in for our future constructor 564 | var faux = {}, special = {}; 565 | faux[mname] = {bases: bases, props: props, special: special}; 566 | dcl.chainAfter(faux, cname); 567 | bases.push(faux); 568 | mixins.push(faux); 569 | 570 | // collect meta 571 | 572 | // merge from bases 573 | superClass.forEach(function (base) { 574 | if (base[mname]) { 575 | var baseSpecial = base[mname].special; 576 | Object.keys(baseSpecial).forEach(function (name) { 577 | dcl.chainWith(faux, name, baseSpecial[name]); 578 | }); 579 | } 580 | }); 581 | 582 | // inspect own props 583 | Object.keys(props).forEach(function (name) { 584 | if (!Object.prototype.hasOwnProperty.call(special, name)) { 585 | var prop = props[name]; 586 | if (prop.get || prop.set) { 587 | if (isSuper(prop.get) || isSuper(prop.set)) { 588 | dcl.chainWith(faux, name, dcl.weaveSuper); 589 | } 590 | } else { 591 | if (isSuper(prop.value)) { 592 | dcl.chainWith(faux, name, dcl.weaveSuper); 593 | } 594 | } 595 | } 596 | }); 597 | 598 | // collect simple props, and a list of special props 599 | var finalProps = {}, finalSpecial = populateProps(finalProps, mixins, special); 600 | if (!Object.prototype.hasOwnProperty.call(finalSpecial, cname)) { 601 | finalSpecial[cname] = Object.prototype.hasOwnProperty.call(special, cname) ? special[cname] : dcl.weaveAfter; 602 | } 603 | 604 | // process special props 605 | Object.keys(finalSpecial).forEach(function (name) { 606 | var prop = weaveProp(name, bases, finalSpecial[name], props); 607 | if (!prop) { 608 | prop = {configurable: true, enumerable: false, writable: true, value: function nop () {}}; 609 | } 610 | var newProp = cloneDescriptor(prop), advices; 611 | if (prop.get || prop.set) { 612 | // accessor descriptor 613 | advices = getAccessorSideAdvices(name, bases, 'get'); 614 | newProp.get = dcl._makeStub(prop.get, advices.before, advices.after); 615 | advices = getAccessorSideAdvices(name, bases, 'set'); 616 | newProp.set = dcl._makeStub(prop.set, advices.before, advices.after); 617 | } else { 618 | // data descriptor 619 | advices = getDataSideAdvices(name, bases); 620 | var stub = dcl._makeStub(prop.value, advices.before, advices.after); 621 | advices = getAccessorSideAdvices(name, bases, 'set'); 622 | stub.advices.set = advices; 623 | newProp.value = stub; 624 | } 625 | finalProps[name] = newProp; 626 | }); 627 | 628 | return dcl._makeCtr(baseClass, finalProps, faux[mname]); 629 | } 630 | 631 | 632 | // build export 633 | 634 | // guts, do not use them! 635 | 636 | dcl._error = function (msg) { 637 | throw new Error(msg); 638 | }; 639 | dcl._makeSuper = makeSuper; 640 | dcl._makeCtr = makeCtr; 641 | dcl._makeStub = makeStub; 642 | 643 | // utilities 644 | 645 | dcl.collectPropertyDescriptors = collectPropertyDescriptors; 646 | dcl.getPropertyDescriptor = getPropertyDescriptor; 647 | 648 | // meta 649 | 650 | dcl.Prop = function Prop (x) { this.x = x; }; 651 | dcl.prop = function prop(x) { return new dcl.Prop(x); }; 652 | 653 | dcl.isInstanceOf = function isInstanceOf (o, ctr) { 654 | if (o instanceof ctr) { 655 | return true; 656 | } 657 | if (o && o[cname] && o[cname][mname]) { 658 | for (var bases = o[cname][mname].bases, i = bases.length - 2; i >= 0; --i) { 659 | var base = bases[i]; 660 | if (base === ctr || !base[mname] && base[pname] instanceof ctr) { 661 | return true; 662 | } 663 | } 664 | } 665 | return false; 666 | }; 667 | 668 | // chains 669 | 670 | function chainWith (ctr, name, weaver) { 671 | if (ctr && ctr[mname]) { 672 | var special = ctr[mname].special; 673 | if (Object.prototype.hasOwnProperty.call(special, name)) { 674 | var own = special[name]; 675 | if (own === weaver || own.name === weaver.name || weaver.name === 'super') { 676 | return true; 677 | } 678 | if (own.name !== 'super') { 679 | dcl._error('different weavers: ' + name, ctr, name, weaver, own); 680 | } 681 | } 682 | special[name] = weaver; 683 | return true; 684 | } 685 | return false; 686 | } 687 | 688 | dcl.weaveBefore = {name: 'before', weave: weaveChain, reverse: true}; 689 | dcl.weaveAfter = {name: 'after', weave: weaveChain}; 690 | dcl.weaveSuper = {name: 'super', weave: weaveAround}; 691 | 692 | dcl.chainWith = chainWith; 693 | dcl.chainBefore = function (ctr, name) { return dcl.chainWith(ctr, name, dcl.weaveBefore); }; 694 | dcl.chainAfter = function (ctr, name) { return dcl.chainWith(ctr, name, dcl.weaveAfter); }; 695 | 696 | // super & AOP 697 | 698 | function makeSuper (advice, S) { 699 | var f = function superNop () {}; 700 | f.spr = new S(advice); 701 | return f; 702 | } 703 | 704 | function isSuper (f) { return f && f.spr instanceof dcl.Super; } 705 | 706 | dcl.Super = function Super (f) { this.around = f; }; 707 | dcl.isSuper = isSuper; 708 | dcl.Super[pname].declaredClass = 'dcl.Super'; 709 | 710 | dcl.Advice = dcl(dcl.Super, { 711 | declaredClass: 'dcl.Advice', 712 | constructor: function () { 713 | this.before = this.around.before; 714 | this.after = this.around.after; 715 | this.around = this.around.around; 716 | } 717 | }); 718 | 719 | dcl.advise = function (advice) { return dcl._makeSuper(advice, dcl.Advice); }; 720 | 721 | dcl.superCall = dcl.around = function (f) { return dcl._makeSuper(f, dcl.Super); }; 722 | dcl.before = function (f) { return dcl.advise({before: f}); }; 723 | dcl.after = function (f) { return dcl.advise({after: f}); }; 724 | 725 | // export 726 | 727 | return dcl; 728 | }); 729 | -------------------------------------------------------------------------------- /amd/debug.js: -------------------------------------------------------------------------------- 1 | define 2 | (['./dcl', './advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | // set up custom names 6 | var mname = '_meta', pname = 'prototype', cname = 'constructor'; 7 | 8 | function DclError (message) { 9 | this.name = 'DclError'; 10 | this.message = message || 'Default Message'; 11 | this.stack = (new Error()).stack; 12 | } 13 | DclError.prototype = Object.create(Error.prototype); 14 | DclError.prototype.constructor = DclError; 15 | 16 | var CycleError = dcl(DclError, {declaredClass: "dcl/debug/CycleError"}), 17 | ChainingError = dcl(DclError, {declaredClass: "dcl/debug/ChainingError"}), 18 | SuperError = dcl(DclError, {declaredClass: "dcl/debug/SuperError"}); 19 | 20 | advise.around(dcl, '_error', function (sup) { 21 | return function (reason) { 22 | var name, ctr, method, props; 23 | if (reason === 'cycle') { 24 | var bases = arguments[1], 25 | names = bases.map(function (base, index) { 26 | return base[pname].declaredClass || ('UNNAMED_' + index); 27 | }); 28 | props = arguments[2]; 29 | name = props.declaredClass && props.declaredClass.value; 30 | if (!name || typeof name != 'string') { 31 | name = 'UNNAMED'; 32 | } 33 | throw new CycleError('dcl: base class cycle in ' + name + 34 | ', bases (' + names.join(', ') + ') are mutually dependent'); 35 | } 36 | if (/^different weavers\b/.test(reason)) { 37 | var weaver = arguments[3], own = arguments[4]; 38 | ctr = arguments[1]; 39 | method = arguments[2]; 40 | name = ctr[mname].props.declaredClass && ctr[mname].props.declaredClass.value || 'UNNAMED'; 41 | throw new ChainingError('dcl: conflicting chain directives in ' + 42 | name + ' for ' + method + ', was ' + own.name + ', set to ' + weaver.name); 43 | } 44 | if (/^wrong super\b/.test(reason)) { 45 | var index = arguments[3]; 46 | ctr = arguments[1]; 47 | method = arguments[2]; 48 | props = arguments[4]; 49 | name = props.declaredClass && props.declaredClass.value; 50 | if (!name || typeof name != 'string') { 51 | name = 'UNNAMED'; 52 | } 53 | var re = /^wrong super (\w+) (\w+)$/.exec(reason), 54 | baseName = ctr[mname].props.declaredClass && 55 | ctr[mname].props.declaredClass.value || 56 | ('UNNAMED_' + index); 57 | throw new SuperError('dcl: super call error in ' + 58 | name + ', while weaving ' + baseName + ', method ' + method + 59 | ' (' + re[1] + ') wrong ' + re[2]); 60 | } 61 | return sup.apply(this, arguments); 62 | }; 63 | }); 64 | 65 | advise.around(advise, '_error', function (sup) { 66 | return function (reason, instance, method, type) { 67 | var re = /^wrong super (\w+)$/.exec(reason); 68 | if (re) { 69 | var baseName = instance.declaredClass; 70 | if (!baseName || typeof baseName != 'string') { 71 | baseName = 'UNNAMED'; 72 | } 73 | throw new SuperError('dcl: super call error in object of ' + 74 | baseName + ', while weaving method ' + method + 75 | ' (' + type + ') wrong ' + re[1]); 76 | } 77 | return sup.apply(this, arguments); 78 | }; 79 | }); 80 | 81 | function logCtor (ctor) { 82 | var meta = ctor[mname]; 83 | if (!meta) { 84 | console.log('*** class does not have meta information compatible with dcl'); 85 | return; 86 | } 87 | var names = meta.bases.map(function (base, index) { 88 | return base[pname].declaredClass || ('UNNAMED_' + index); 89 | }); 90 | console.log('*** class ' + names[names.length - 1] + ' depends on ' + 91 | (names.length - 1) + (names.length == 2 ? ' class' : ' classes') + 92 | (names.length > 1 ? ': ' + names.slice(0, -1).join(', ') : '') 93 | ); 94 | var specialKeys = Object.keys(meta.special); 95 | if (specialKeys.length) { 96 | console.log('*** class ' + names[names.length - 1] + ' has ' + 97 | specialKeys.length + (specialKeys.length == 1 ? ' weaver: ' : ' weavers: ') + 98 | specialKeys.map(function (name) { 99 | return name + ': ' + meta.special[name].name; 100 | }).join(', ') 101 | ); 102 | } 103 | } 104 | 105 | function logAdvices (advices) { 106 | return 'class-level advice (before ' + advices.before.length + 107 | ', after ' + advices.after.length + ')'; 108 | } 109 | 110 | function countAdvices (root, chain) { 111 | var total = 0; 112 | for (var node = root[chain]; node && node !== root; node = node[chain], ++total); 113 | return total; 114 | } 115 | 116 | function logNode (node) { 117 | return 'object-level advice (before ' + countAdvices(node, 'next_before') + 118 | ', after ' + countAdvices(node, 'next_after') + ')'; 119 | } 120 | 121 | function log (o, suppressCtor) { 122 | if (typeof o == 'function') { 123 | logCtor(o); 124 | } else if (o && typeof o == 'object') { 125 | var base = o[cname]; 126 | if (base[pname].declaredClass) { 127 | console.log('*** object of class ' + base[pname].declaredClass); 128 | } 129 | if (!suppressCtor) { 130 | logCtor(base); 131 | } 132 | allKeys(o).forEach(function (name) { 133 | var prop = dcl.getPropertyDescriptor(o, name); 134 | if (prop.get || prop.set) { 135 | if (prop.get) { 136 | if (prop.get.node instanceof advise.Node) { 137 | console.log(' ' + name + ': getter with ' + logNode(prop.get.node)); 138 | } else if (typeof prop.get.advices == 'object') { 139 | console.log(' ' + name + ': getter with ' + logAdvices(prop.get.advices)); 140 | } 141 | } 142 | if (prop.set) { 143 | if (prop.set.node instanceof advise.Node) { 144 | console.log(' ' + name + ': setter with ' + logNode(prop.set.node)); 145 | } else if (typeof prop.set.advices == 'object') { 146 | console.log(' ' + name + ': setter with ' + logAdvices(prop.set.advices)); 147 | } 148 | } 149 | } else { 150 | if (prop.value.node instanceof advise.Node) { 151 | console.log(' ' + name + ': ' + logNode(prop.value.node)); 152 | } else if (typeof prop.value.advices == 'object') { 153 | console.log(' ' + name + ': ' + logAdvices(prop.value.advices)); 154 | } 155 | } 156 | }); 157 | } 158 | } 159 | 160 | dcl.log = log; 161 | dcl.DclError = DclError; 162 | dcl.CycleError = CycleError; 163 | dcl.ChainingError = ChainingError; 164 | dcl.SuperError = SuperError; 165 | 166 | return dcl; 167 | 168 | function allKeys (o) { 169 | var keys = []; 170 | for (var key in o) { 171 | keys.push(key); 172 | } 173 | return keys; 174 | } 175 | }); 176 | -------------------------------------------------------------------------------- /amd/mixins/Cleanup.js: -------------------------------------------------------------------------------- 1 | define 2 | (['../dcl', './Destroyable'], function (dcl, Destroyable) { 3 | 'use strict'; 4 | 5 | return dcl(Destroyable, { 6 | declaredClass: 'dcl/mixins/Cleanup', 7 | constructor: function () { 8 | this.__cleanupStack = []; 9 | }, 10 | pushCleanup: function (resource, cleanup) { 11 | var f = cleanup ? function () { cleanup(resource); } : function () { resource.destroy(); }; 12 | this.__cleanupStack.push(f); 13 | return f; 14 | }, 15 | popCleanup: function (dontRun) { 16 | if (dontRun) { 17 | return this.__cleanupStack.pop(); 18 | } 19 | this.__cleanupStack.pop()(); 20 | }, 21 | removeCleanup: function (f) { 22 | for (var i = this.__cleanupStack.length - 1; i >= 0; --i) { 23 | if (this.__cleanupStack[i] === f) { 24 | this.__cleanupStack.splice(i, 1); 25 | return true; 26 | } 27 | } 28 | }, 29 | cleanup: function () { 30 | while (this.__cleanupStack.length) { 31 | this.__cleanupStack.pop()(); 32 | } 33 | }, 34 | destroy: function () { 35 | this.cleanup(); 36 | } 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /amd/mixins/Destroyable.js: -------------------------------------------------------------------------------- 1 | define 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Destroyable = dcl({declaredClass: 'dcl/mixins/Destroyable'}); 6 | dcl.chainBefore(Destroyable, 'destroy'); 7 | 8 | return Destroyable; 9 | }); 10 | -------------------------------------------------------------------------------- /amd/utils/registry.js: -------------------------------------------------------------------------------- 1 | define 2 | (['../dcl', '../advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | var registry = {}; 6 | 7 | // register all named classes automatically 8 | advise.after(dcl, '_makeCtr', function (_, result) { 9 | if (result && typeof result.prototype.declaredClass == 'string') { 10 | registry[result.prototype.declaredClass] = result; 11 | } 12 | }); 13 | 14 | return { 15 | get: function (name) { return registry[name]; }, 16 | has: function (name) { return Object.prototype.hasOwnProperty.call(registry, name); }, 17 | delete: function (name) { return delete registry[name]; }, 18 | keys: function () { 19 | return Object.keys(registry).filter(function (name) { 20 | return Object.prototype.hasOwnProperty.call(registry, name); 21 | }); 22 | }, 23 | clear: function () { registry = {}; } 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /bases/Mixer.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Mixer', 7 | constructor: function (x) { 8 | Object.defineProperties(this, dcl.collectPropertyDescriptors({}, x)); 9 | } 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /bases/Replacer.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Replacer', 7 | constructor: function (x) { 8 | var props = dcl.collectPropertyDescriptors({}, x); 9 | Object.keys(props).forEach(function (name) { 10 | if (name in this) { 11 | Object.defineProperty(this, name, props[name]); 12 | } 13 | }, this); 14 | } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dcl", 3 | "description": "Elegant minimalistic implementation of OOP with mixins + AOP.", 4 | "main": "dcl.js", 5 | "authors": [ 6 | "Eugene Lazutkin (http://www.lazutkin.com/)" 7 | ], 8 | "license": "BSD-3-Clause", 9 | "keywords": [ 10 | "object-oriented", 11 | "programming", 12 | "aspect-oriented", 13 | "OOP", 14 | "AOP", 15 | "OO" 16 | ], 17 | "homepage": "https://github.com/uhop/dcl", 18 | "moduleType": [ 19 | "amd", 20 | "globals", 21 | "node" 22 | ], 23 | "ignore": [ 24 | "**/.*", 25 | "node_modules", 26 | "bower_components", 27 | "test", 28 | "tests", 29 | "dist", 30 | "es6", 31 | "amd" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /debug.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['./dcl', './advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | // set up custom names 6 | var mname = '_meta', pname = 'prototype', cname = 'constructor'; 7 | 8 | function DclError (message) { 9 | this.name = 'DclError'; 10 | this.message = message || 'Default Message'; 11 | this.stack = (new Error()).stack; 12 | } 13 | DclError.prototype = Object.create(Error.prototype); 14 | DclError.prototype.constructor = DclError; 15 | 16 | var CycleError = dcl(DclError, {declaredClass: "dcl/debug/CycleError"}), 17 | ChainingError = dcl(DclError, {declaredClass: "dcl/debug/ChainingError"}), 18 | SuperError = dcl(DclError, {declaredClass: "dcl/debug/SuperError"}); 19 | 20 | advise.around(dcl, '_error', function (sup) { 21 | return function (reason) { 22 | var name, ctr, method, props; 23 | if (reason === 'cycle') { 24 | var bases = arguments[1], 25 | names = bases.map(function (base, index) { 26 | return base[pname].declaredClass || ('UNNAMED_' + index); 27 | }); 28 | props = arguments[2]; 29 | name = props.declaredClass && props.declaredClass.value; 30 | if (!name || typeof name != 'string') { 31 | name = 'UNNAMED'; 32 | } 33 | throw new CycleError('dcl: base class cycle in ' + name + 34 | ', bases (' + names.join(', ') + ') are mutually dependent'); 35 | } 36 | if (/^different weavers\b/.test(reason)) { 37 | var weaver = arguments[3], own = arguments[4]; 38 | ctr = arguments[1]; 39 | method = arguments[2]; 40 | name = ctr[mname].props.declaredClass && ctr[mname].props.declaredClass.value || 'UNNAMED'; 41 | throw new ChainingError('dcl: conflicting chain directives in ' + 42 | name + ' for ' + method + ', was ' + own.name + ', set to ' + weaver.name); 43 | } 44 | if (/^wrong super\b/.test(reason)) { 45 | var index = arguments[3]; 46 | ctr = arguments[1]; 47 | method = arguments[2]; 48 | props = arguments[4]; 49 | name = props.declaredClass && props.declaredClass.value; 50 | if (!name || typeof name != 'string') { 51 | name = 'UNNAMED'; 52 | } 53 | var re = /^wrong super (\w+) (\w+)$/.exec(reason), 54 | baseName = ctr[mname].props.declaredClass && 55 | ctr[mname].props.declaredClass.value || 56 | ('UNNAMED_' + index); 57 | throw new SuperError('dcl: super call error in ' + 58 | name + ', while weaving ' + baseName + ', method ' + method + 59 | ' (' + re[1] + ') wrong ' + re[2]); 60 | } 61 | return sup.apply(this, arguments); 62 | }; 63 | }); 64 | 65 | advise.around(advise, '_error', function (sup) { 66 | return function (reason, instance, method, type) { 67 | var re = /^wrong super (\w+)$/.exec(reason); 68 | if (re) { 69 | var baseName = instance.declaredClass; 70 | if (!baseName || typeof baseName != 'string') { 71 | baseName = 'UNNAMED'; 72 | } 73 | throw new SuperError('dcl: super call error in object of ' + 74 | baseName + ', while weaving method ' + method + 75 | ' (' + type + ') wrong ' + re[1]); 76 | } 77 | return sup.apply(this, arguments); 78 | }; 79 | }); 80 | 81 | function logCtor (ctor) { 82 | var meta = ctor[mname]; 83 | if (!meta) { 84 | console.log('*** class does not have meta information compatible with dcl'); 85 | return; 86 | } 87 | var names = meta.bases.map(function (base, index) { 88 | return base[pname].declaredClass || ('UNNAMED_' + index); 89 | }); 90 | console.log('*** class ' + names[names.length - 1] + ' depends on ' + 91 | (names.length - 1) + (names.length == 2 ? ' class' : ' classes') + 92 | (names.length > 1 ? ': ' + names.slice(0, -1).join(', ') : '') 93 | ); 94 | var specialKeys = Object.keys(meta.special); 95 | if (specialKeys.length) { 96 | console.log('*** class ' + names[names.length - 1] + ' has ' + 97 | specialKeys.length + (specialKeys.length == 1 ? ' weaver: ' : ' weavers: ') + 98 | specialKeys.map(function (name) { 99 | return name + ': ' + meta.special[name].name; 100 | }).join(', ') 101 | ); 102 | } 103 | } 104 | 105 | function logAdvices (advices) { 106 | return 'class-level advice (before ' + advices.before.length + 107 | ', after ' + advices.after.length + ')'; 108 | } 109 | 110 | function countAdvices (root, chain) { 111 | var total = 0; 112 | for (var node = root[chain]; node && node !== root; node = node[chain], ++total); 113 | return total; 114 | } 115 | 116 | function logNode (node) { 117 | return 'object-level advice (before ' + countAdvices(node, 'next_before') + 118 | ', after ' + countAdvices(node, 'next_after') + ')'; 119 | } 120 | 121 | function log (o, suppressCtor) { 122 | if (typeof o == 'function') { 123 | logCtor(o); 124 | } else if (o && typeof o == 'object') { 125 | var base = o[cname]; 126 | if (base[pname].declaredClass) { 127 | console.log('*** object of class ' + base[pname].declaredClass); 128 | } 129 | if (!suppressCtor) { 130 | logCtor(base); 131 | } 132 | allKeys(o).forEach(function (name) { 133 | var prop = dcl.getPropertyDescriptor(o, name); 134 | if (prop.get || prop.set) { 135 | if (prop.get) { 136 | if (prop.get.node instanceof advise.Node) { 137 | console.log(' ' + name + ': getter with ' + logNode(prop.get.node)); 138 | } else if (typeof prop.get.advices == 'object') { 139 | console.log(' ' + name + ': getter with ' + logAdvices(prop.get.advices)); 140 | } 141 | } 142 | if (prop.set) { 143 | if (prop.set.node instanceof advise.Node) { 144 | console.log(' ' + name + ': setter with ' + logNode(prop.set.node)); 145 | } else if (typeof prop.set.advices == 'object') { 146 | console.log(' ' + name + ': setter with ' + logAdvices(prop.set.advices)); 147 | } 148 | } 149 | } else { 150 | if (prop.value.node instanceof advise.Node) { 151 | console.log(' ' + name + ': ' + logNode(prop.value.node)); 152 | } else if (typeof prop.value.advices == 'object') { 153 | console.log(' ' + name + ': ' + logAdvices(prop.value.advices)); 154 | } 155 | } 156 | }); 157 | } 158 | } 159 | 160 | dcl.log = log; 161 | dcl.DclError = DclError; 162 | dcl.CycleError = CycleError; 163 | dcl.ChainingError = ChainingError; 164 | dcl.SuperError = SuperError; 165 | 166 | return dcl; 167 | 168 | function allKeys (o) { 169 | var keys = []; 170 | for (var key in o) { 171 | keys.push(key); 172 | } 173 | return keys; 174 | } 175 | }); 176 | -------------------------------------------------------------------------------- /dist/advices/counter.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window.dcl;g=g.advices||(g.advices={});g.counter=f(window.dcl);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Counter = new dcl({ 6 | declaredClass: 'dcl/advices/counter/Counter', 7 | constructor: function () { 8 | this.reset(); 9 | }, 10 | reset: function () { 11 | this.calls = this.errors = 0; 12 | }, 13 | advice: function () { 14 | var self = this; 15 | return { 16 | before: function () { 17 | ++self.calls; 18 | }, 19 | after: function (args, result) { 20 | if (result instanceof Error) { 21 | ++self.errors; 22 | } 23 | } 24 | }; 25 | } 26 | }); 27 | 28 | return function(){ return new Counter; }; 29 | }); 30 | -------------------------------------------------------------------------------- /dist/advices/flow.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window;g=g.dcl||(g.dcl={});g=g.advices||(g.advices={});g.flow=f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var flowStack = [], flowCount = {}; 6 | 7 | return { 8 | advice: function (name) { 9 | return { 10 | before: function () { 11 | flowStack.push(name); 12 | if (flowCount[name]) { 13 | ++flowCount[name]; 14 | } else { 15 | flowCount[name] = 1; 16 | } 17 | }, 18 | after: function () { 19 | --flowCount[name]; 20 | flowStack.pop(); 21 | } 22 | }; 23 | }, 24 | inFlowOf: function (name) { 25 | return flowCount[name]; 26 | }, 27 | getStack: function () { 28 | return flowStack; 29 | }, 30 | getCount: function () { 31 | return flowCount; 32 | } 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /dist/advices/memoize.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window;g=g.dcl||(g.dcl={});g=g.advices||(g.advices={});g.memoize=f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | return { 6 | advice: function (name, keyMaker) { 7 | return keyMaker ? 8 | { 9 | around: function (sup) { 10 | return function () { 11 | var key = keyMaker(this, arguments), cache = this.__memoizerCache, dict; 12 | if (!cache) { 13 | cache = this.__memoizerCache = {}; 14 | } 15 | if (cache.hasOwnProperty(name)) { 16 | dict = cache[name]; 17 | } else { 18 | dict = cache[name] = {}; 19 | } 20 | if (dict.hasOwnProperty(key)) { 21 | return dict[key]; 22 | } 23 | return dict[key] = sup ? sup.apply(this, arguments) : void 0; 24 | }; 25 | } 26 | } : 27 | { 28 | around: function (sup) { 29 | return function (first) { 30 | var cache = this.__memoizerCache, dict; 31 | if (!cache) { 32 | cache = this.__memoizerCache = {}; 33 | } 34 | if (cache.hasOwnProperty(name)) { 35 | dict = cache[name]; 36 | } else { 37 | dict = cache[name] = {}; 38 | } 39 | if (dict.hasOwnProperty(first)) { 40 | return dict[first]; 41 | } 42 | return dict[first] = sup ? sup.apply(this, arguments) : undefined; 43 | }; 44 | } 45 | }; 46 | }, 47 | guard: function (name) { 48 | return { 49 | after: function () { 50 | var cache = this.__memoizerCache; 51 | if (cache && name) { 52 | delete cache[name]; 53 | } else { 54 | this.__memoizerCache = {}; 55 | } 56 | } 57 | }; 58 | } 59 | }; 60 | }); 61 | -------------------------------------------------------------------------------- /dist/advices/time.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window;g=g.dcl||(g.dcl={});g=g.advices||(g.advices={});g.time=f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var uniq = 0; 6 | 7 | return function (name) { 8 | var inCall = 0, label = name || ('Timer #' + uniq++); 9 | return { 10 | before: function () { 11 | if (!(inCall++)) { 12 | console.time(label); 13 | } 14 | }, 15 | after: function () { 16 | if (!--inCall) { 17 | console.timeEnd(label); 18 | } 19 | } 20 | }; 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /dist/advices/trace.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window;g=g.dcl||(g.dcl={});g=g.advices||(g.advices={});g.trace=f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var lvl = 0; 6 | 7 | function rep (ch, n) { 8 | if (n < 1) { return ''; } 9 | if (n == 1) { return ch; } 10 | var h = rep(ch, Math.floor(n / 2)); 11 | return h + h + ((n & 1) ? ch : ''); 12 | 13 | } 14 | 15 | function pad (value, width, ch) { 16 | var v = value.toString(); 17 | return v + rep(ch || ' ', width - v.length); 18 | } 19 | 20 | return function (name, level) { 21 | return { 22 | before: function () { 23 | ++lvl; 24 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 25 | name + '(' + Array.prototype.join.call(arguments, ', ') + ')'); 26 | }, 27 | after: function (args, result) { 28 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 29 | name + (result && result instanceof Error ? ' throws' : ' returns') + 30 | ' ' + result); 31 | --lvl; 32 | } 33 | }; 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /dist/advise.js: -------------------------------------------------------------------------------- 1 | (function(_,f){window.advise=f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var pname = 'prototype'; 6 | 7 | function Node (parent) { 8 | this.parent = parent || this; 9 | } 10 | 11 | Node[pname] = { 12 | removeTopic: function (topic) { 13 | var n = 'next_' + topic, p = 'prev_' + topic; 14 | if (this[n] && this[p]) { 15 | this[n][p] = this[p]; 16 | this[p][n] = this[n]; 17 | } 18 | }, 19 | remove: function () { 20 | this.removeTopic('before'); 21 | this.removeTopic('around'); 22 | 23 | // remove & recreate around advices 24 | var parent = this.parent, next = this.next_around; 25 | this.removeTopic('after'); 26 | for (; next && next !== parent; next = next.next_around) { 27 | next.around = next.originalAround(next.prev_around.around); 28 | } 29 | }, 30 | addTopic: function (node, topic) { 31 | var n = 'next_' + topic, p = 'prev_' + topic, 32 | prev = node[p] = this[p] || this; 33 | node[n] = this; 34 | prev[n] = this[p] = node; 35 | }, 36 | addAdvice: function (advice, instance, name, type) { 37 | var node = new Node(this); 38 | if (advice.before) { 39 | node.before = advice.before; 40 | this.addTopic(node, 'before'); 41 | } 42 | if (advice.around) { 43 | if (typeof advice.around != 'function') { 44 | advise._error('wrong super call', instance, name, type); 45 | } 46 | node.originalAround = advice.around; 47 | this.addTopic(node, 'around'); 48 | if (node.prev_around.around && typeof node.prev_around.around != 'function') { 49 | advise._error('wrong super arg', instance, name, type); 50 | } 51 | node.around = advice.around(node.prev_around.around || null); 52 | if (typeof node.around != 'function') { 53 | advise._error('wrong super result', instance, name, type); 54 | } 55 | } 56 | if (advice.after) { 57 | node.after = advice.after; 58 | this.addTopic(node, 'after'); 59 | } 60 | return node; 61 | } 62 | }; 63 | 64 | Node[pname].destroy = Node[pname].unadvise = Node[pname].remove; 65 | 66 | function addNode (root, topic) { 67 | return function (f) { 68 | var node = new Node(root); 69 | node[topic] = f; 70 | root.addTopic(node, topic); 71 | }; 72 | } 73 | 74 | function makeStub (value) { 75 | var root = new Node(); 76 | if (value) { 77 | if (typeof value.advices == 'object') { 78 | var advices = value.advices; 79 | advices.before.forEach(addNode(root, 'before')); 80 | advices.after. forEach(addNode(root, 'after')); 81 | advices.around && addNode(root, 'around')(advices.around); 82 | } else { 83 | addNode(root, 'around')(value); 84 | } 85 | } 86 | function stub () { 87 | var result, thrown, p; 88 | // running the before chain 89 | for (p = root.prev_before; p && p !== root; p = p.prev_before) { 90 | p.before.apply(this, arguments); 91 | } 92 | // running the around chain 93 | if (root.prev_around && root.prev_around !== root) { 94 | try { 95 | result = root.prev_around.around.apply(this, arguments); 96 | } catch (error) { 97 | result = error; 98 | thrown = true; 99 | } 100 | } 101 | // running the after chain 102 | for (p = root.next_after; p && p !== root; p = p.next_after) { 103 | p.after.call(this, arguments, result, makeReturn, makeThrow); 104 | } 105 | if (thrown) { 106 | throw result; 107 | } 108 | return result; 109 | 110 | function makeReturn (value) { result = value; thrown = false; } 111 | function makeThrow (value) { result = value; thrown = true; } 112 | }; 113 | stub.node = root; 114 | return stub; 115 | } 116 | 117 | function convert (value, advice, instance, name, type) { 118 | if (!value || !(value.node instanceof Node)) { 119 | value = makeStub(value); 120 | value.node.instance = instance; 121 | value.node.name = name; 122 | value.node.type = type; 123 | } 124 | var node = value.node.addAdvice(advice, instance, name, type); 125 | return {value: value, handle: node}; 126 | } 127 | 128 | function combineHandles (handles) { 129 | var handle = { 130 | remove: function () { 131 | handles.forEach(function (handle) { handle.remove(); }); 132 | } 133 | } 134 | handle.destroy = handle.unadvise = handle.remove; 135 | return handle; 136 | } 137 | 138 | function advise (instance, name, advice) { 139 | var prop = getPropertyDescriptor(instance, name), handles = []; 140 | if (prop) { 141 | if (prop.get || prop.set) { 142 | var result; 143 | if (prop.get && advice.get) { 144 | result = convert(prop.get, advice.get, instance, name, 'get'); 145 | prop.get = result.value; 146 | handles.push(result.handle); 147 | } 148 | if (prop.set && advice.set) { 149 | result = convert(prop.set, advice.set, instance, name, 'set'); 150 | prop.set = result.value; 151 | handles.push(result.handle); 152 | } 153 | } else { 154 | if (prop.value && advice) { 155 | result = convert(prop.value, advice, instance, name, 'value'); 156 | prop.value = result.value; 157 | handles.push(result.handle); 158 | } 159 | } 160 | } else { 161 | prop = {writable: true, configurable: true, enumerable: true}; 162 | if (advice.get || advice.set) { 163 | if (advice.get) { 164 | result = convert(null, advice.get, instance, name, 'get'); 165 | prop.get = result.value; 166 | handles.push(result.handle); 167 | } 168 | if (advice.set) { 169 | result = convert(null, advice.set, instance, name, 'set'); 170 | prop.set = result.value; 171 | handles.push(result.handle); 172 | } 173 | } else { 174 | result = convert(null, advice, instance, name, 'value'); 175 | prop.value = result.value; 176 | handles.push(result.handle); 177 | } 178 | } 179 | Object.defineProperty(instance, name, prop); 180 | return combineHandles(handles); 181 | } 182 | 183 | // export 184 | 185 | // guts, do not use them! 186 | advise._error = function (msg) { 187 | throw new Error(msg); 188 | }; 189 | 190 | advise.before = function (instance, name, f) { return advise(instance, name, {before: f}); }; 191 | advise.after = function (instance, name, f) { return advise(instance, name, {after: f}); }; 192 | advise.around = function (instance, name, f) { return advise(instance, name, {around: f}); }; 193 | advise.Node = Node; 194 | 195 | return advise; 196 | 197 | // copied from dcl.js so we can be independent 198 | function getPropertyDescriptor (o, name) { 199 | while (o && o !== Object[pname]) { 200 | if (o.hasOwnProperty(name)) { 201 | return Object.getOwnPropertyDescriptor(o, name); 202 | } 203 | o = Object.getPrototypeOf(o); 204 | } 205 | return; // undefined 206 | } 207 | }); 208 | -------------------------------------------------------------------------------- /dist/bases/Mixer.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window.dcl;g=g.bases||(g.bases={});g.Mixer=f(window.dcl);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Mixer', 7 | constructor: function (x) { 8 | Object.defineProperties(this, dcl.collectPropertyDescriptors({}, x)); 9 | } 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /dist/bases/Replacer.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window.dcl;g=g.bases||(g.bases={});g.Replacer=f(window.dcl);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Replacer', 7 | constructor: function (x) { 8 | var props = dcl.collectPropertyDescriptors({}, x); 9 | Object.keys(props).forEach(function (name) { 10 | if (name in this) { 11 | Object.defineProperty(this, name, props[name]); 12 | } 13 | }, this); 14 | } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /dist/debug.js: -------------------------------------------------------------------------------- 1 | (function(_,f){f(window.dcl,window.advise);}) 2 | (['./dcl', './advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | // set up custom names 6 | var mname = '_meta', pname = 'prototype', cname = 'constructor'; 7 | 8 | function DclError (message) { 9 | this.name = 'DclError'; 10 | this.message = message || 'Default Message'; 11 | this.stack = (new Error()).stack; 12 | } 13 | DclError.prototype = Object.create(Error.prototype); 14 | DclError.prototype.constructor = DclError; 15 | 16 | var CycleError = dcl(DclError, {declaredClass: "dcl/debug/CycleError"}), 17 | ChainingError = dcl(DclError, {declaredClass: "dcl/debug/ChainingError"}), 18 | SuperError = dcl(DclError, {declaredClass: "dcl/debug/SuperError"}); 19 | 20 | advise.around(dcl, '_error', function (sup) { 21 | return function (reason) { 22 | var name, ctr, method, props; 23 | if (reason === 'cycle') { 24 | var bases = arguments[1], 25 | names = bases.map(function (base, index) { 26 | return base[pname].declaredClass || ('UNNAMED_' + index); 27 | }); 28 | props = arguments[2]; 29 | name = props.declaredClass && props.declaredClass.value; 30 | if (!name || typeof name != 'string') { 31 | name = 'UNNAMED'; 32 | } 33 | throw new CycleError('dcl: base class cycle in ' + name + 34 | ', bases (' + names.join(', ') + ') are mutually dependent'); 35 | } 36 | if (/^different weavers\b/.test(reason)) { 37 | var weaver = arguments[3], own = arguments[4]; 38 | ctr = arguments[1]; 39 | method = arguments[2]; 40 | name = ctr[mname].props.declaredClass && ctr[mname].props.declaredClass.value || 'UNNAMED'; 41 | throw new ChainingError('dcl: conflicting chain directives in ' + 42 | name + ' for ' + method + ', was ' + own.name + ', set to ' + weaver.name); 43 | } 44 | if (/^wrong super\b/.test(reason)) { 45 | var index = arguments[3]; 46 | ctr = arguments[1]; 47 | method = arguments[2]; 48 | props = arguments[4]; 49 | name = props.declaredClass && props.declaredClass.value; 50 | if (!name || typeof name != 'string') { 51 | name = 'UNNAMED'; 52 | } 53 | var re = /^wrong super (\w+) (\w+)$/.exec(reason), 54 | baseName = ctr[mname].props.declaredClass && 55 | ctr[mname].props.declaredClass.value || 56 | ('UNNAMED_' + index); 57 | throw new SuperError('dcl: super call error in ' + 58 | name + ', while weaving ' + baseName + ', method ' + method + 59 | ' (' + re[1] + ') wrong ' + re[2]); 60 | } 61 | return sup.apply(this, arguments); 62 | }; 63 | }); 64 | 65 | advise.around(advise, '_error', function (sup) { 66 | return function (reason, instance, method, type) { 67 | var re = /^wrong super (\w+)$/.exec(reason); 68 | if (re) { 69 | var baseName = instance.declaredClass; 70 | if (!baseName || typeof baseName != 'string') { 71 | baseName = 'UNNAMED'; 72 | } 73 | throw new SuperError('dcl: super call error in object of ' + 74 | baseName + ', while weaving method ' + method + 75 | ' (' + type + ') wrong ' + re[1]); 76 | } 77 | return sup.apply(this, arguments); 78 | }; 79 | }); 80 | 81 | function logCtor (ctor) { 82 | var meta = ctor[mname]; 83 | if (!meta) { 84 | console.log('*** class does not have meta information compatible with dcl'); 85 | return; 86 | } 87 | var names = meta.bases.map(function (base, index) { 88 | return base[pname].declaredClass || ('UNNAMED_' + index); 89 | }); 90 | console.log('*** class ' + names[names.length - 1] + ' depends on ' + 91 | (names.length - 1) + (names.length == 2 ? ' class' : ' classes') + 92 | (names.length > 1 ? ': ' + names.slice(0, -1).join(', ') : '') 93 | ); 94 | var specialKeys = Object.keys(meta.special); 95 | if (specialKeys.length) { 96 | console.log('*** class ' + names[names.length - 1] + ' has ' + 97 | specialKeys.length + (specialKeys.length == 1 ? ' weaver: ' : ' weavers: ') + 98 | specialKeys.map(function (name) { 99 | return name + ': ' + meta.special[name].name; 100 | }).join(', ') 101 | ); 102 | } 103 | } 104 | 105 | function logAdvices (advices) { 106 | return 'class-level advice (before ' + advices.before.length + 107 | ', after ' + advices.after.length + ')'; 108 | } 109 | 110 | function countAdvices (root, chain) { 111 | var total = 0; 112 | for (var node = root[chain]; node && node !== root; node = node[chain], ++total); 113 | return total; 114 | } 115 | 116 | function logNode (node) { 117 | return 'object-level advice (before ' + countAdvices(node, 'next_before') + 118 | ', after ' + countAdvices(node, 'next_after') + ')'; 119 | } 120 | 121 | function log (o, suppressCtor) { 122 | if (typeof o == 'function') { 123 | logCtor(o); 124 | } else if (o && typeof o == 'object') { 125 | var base = o[cname]; 126 | if (base[pname].declaredClass) { 127 | console.log('*** object of class ' + base[pname].declaredClass); 128 | } 129 | if (!suppressCtor) { 130 | logCtor(base); 131 | } 132 | allKeys(o).forEach(function (name) { 133 | var prop = dcl.getPropertyDescriptor(o, name); 134 | if (prop.get || prop.set) { 135 | if (prop.get) { 136 | if (prop.get.node instanceof advise.Node) { 137 | console.log(' ' + name + ': getter with ' + logNode(prop.get.node)); 138 | } else if (typeof prop.get.advices == 'object') { 139 | console.log(' ' + name + ': getter with ' + logAdvices(prop.get.advices)); 140 | } 141 | } 142 | if (prop.set) { 143 | if (prop.set.node instanceof advise.Node) { 144 | console.log(' ' + name + ': setter with ' + logNode(prop.set.node)); 145 | } else if (typeof prop.set.advices == 'object') { 146 | console.log(' ' + name + ': setter with ' + logAdvices(prop.set.advices)); 147 | } 148 | } 149 | } else { 150 | if (prop.value.node instanceof advise.Node) { 151 | console.log(' ' + name + ': ' + logNode(prop.value.node)); 152 | } else if (typeof prop.value.advices == 'object') { 153 | console.log(' ' + name + ': ' + logAdvices(prop.value.advices)); 154 | } 155 | } 156 | }); 157 | } 158 | } 159 | 160 | dcl.log = log; 161 | dcl.DclError = DclError; 162 | dcl.CycleError = CycleError; 163 | dcl.ChainingError = ChainingError; 164 | dcl.SuperError = SuperError; 165 | 166 | return dcl; 167 | 168 | function allKeys (o) { 169 | var keys = []; 170 | for (var key in o) { 171 | keys.push(key); 172 | } 173 | return keys; 174 | } 175 | }); 176 | -------------------------------------------------------------------------------- /dist/mixins/Cleanup.js: -------------------------------------------------------------------------------- 1 | (function(_,f){window.dcl.mixins.Cleanup=f(window.dcl,window.dcl.mixins.Destroyable);}) 2 | (['../dcl', './Destroyable'], function (dcl, Destroyable) { 3 | 'use strict'; 4 | 5 | return dcl(Destroyable, { 6 | declaredClass: 'dcl/mixins/Cleanup', 7 | constructor: function () { 8 | this.__cleanupStack = []; 9 | }, 10 | pushCleanup: function (resource, cleanup) { 11 | var f = cleanup ? function () { cleanup(resource); } : function () { resource.destroy(); }; 12 | this.__cleanupStack.push(f); 13 | return f; 14 | }, 15 | popCleanup: function (dontRun) { 16 | if (dontRun) { 17 | return this.__cleanupStack.pop(); 18 | } 19 | this.__cleanupStack.pop()(); 20 | }, 21 | removeCleanup: function (f) { 22 | for (var i = this.__cleanupStack.length - 1; i >= 0; --i) { 23 | if (this.__cleanupStack[i] === f) { 24 | this.__cleanupStack.splice(i, 1); 25 | return true; 26 | } 27 | } 28 | }, 29 | cleanup: function () { 30 | while (this.__cleanupStack.length) { 31 | this.__cleanupStack.pop()(); 32 | } 33 | }, 34 | destroy: function () { 35 | this.cleanup(); 36 | } 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /dist/mixins/Destroyable.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window.dcl;g=g.mixins||(g.mixins={});g.Destroyable=f(window.dcl);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Destroyable = dcl({declaredClass: 'dcl/mixins/Destroyable'}); 6 | dcl.chainBefore(Destroyable, 'destroy'); 7 | 8 | return Destroyable; 9 | }); 10 | -------------------------------------------------------------------------------- /dist/utils/registry.js: -------------------------------------------------------------------------------- 1 | (function(_,f,g){g=window.dcl;g=g.utils||(g.utils={});g.registry=f(window.dcl,window.advise);}) 2 | (['../dcl', '../advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | var registry = {}; 6 | 7 | // register all named classes automatically 8 | advise.after(dcl, '_makeCtr', function (_, result) { 9 | if (result && typeof result.prototype.declaredClass == 'string') { 10 | registry[result.prototype.declaredClass] = result; 11 | } 12 | }); 13 | 14 | return { 15 | get: function (name) { return registry[name]; }, 16 | has: function (name) { return Object.prototype.hasOwnProperty.call(registry, name); }, 17 | delete: function (name) { return delete registry[name]; }, 18 | keys: function () { 19 | return Object.keys(registry).filter(function (name) { 20 | return Object.prototype.hasOwnProperty.call(registry, name); 21 | }); 22 | }, 23 | clear: function () { registry = {}; } 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /es6/advices/counter.js: -------------------------------------------------------------------------------- 1 | import m0 from "../dcl";export default (function(_,f){return f(m0);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Counter = new dcl({ 6 | declaredClass: 'dcl/advices/counter/Counter', 7 | constructor: function () { 8 | this.reset(); 9 | }, 10 | reset: function () { 11 | this.calls = this.errors = 0; 12 | }, 13 | advice: function () { 14 | var self = this; 15 | return { 16 | before: function () { 17 | ++self.calls; 18 | }, 19 | after: function (args, result) { 20 | if (result instanceof Error) { 21 | ++self.errors; 22 | } 23 | } 24 | }; 25 | } 26 | }); 27 | 28 | return function(){ return new Counter; }; 29 | }); 30 | -------------------------------------------------------------------------------- /es6/advices/flow.js: -------------------------------------------------------------------------------- 1 | export default (function(_,f){return f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var flowStack = [], flowCount = {}; 6 | 7 | return { 8 | advice: function (name) { 9 | return { 10 | before: function () { 11 | flowStack.push(name); 12 | if (flowCount[name]) { 13 | ++flowCount[name]; 14 | } else { 15 | flowCount[name] = 1; 16 | } 17 | }, 18 | after: function () { 19 | --flowCount[name]; 20 | flowStack.pop(); 21 | } 22 | }; 23 | }, 24 | inFlowOf: function (name) { 25 | return flowCount[name]; 26 | }, 27 | getStack: function () { 28 | return flowStack; 29 | }, 30 | getCount: function () { 31 | return flowCount; 32 | } 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /es6/advices/memoize.js: -------------------------------------------------------------------------------- 1 | export default (function(_,f){return f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | return { 6 | advice: function (name, keyMaker) { 7 | return keyMaker ? 8 | { 9 | around: function (sup) { 10 | return function () { 11 | var key = keyMaker(this, arguments), cache = this.__memoizerCache, dict; 12 | if (!cache) { 13 | cache = this.__memoizerCache = {}; 14 | } 15 | if (cache.hasOwnProperty(name)) { 16 | dict = cache[name]; 17 | } else { 18 | dict = cache[name] = {}; 19 | } 20 | if (dict.hasOwnProperty(key)) { 21 | return dict[key]; 22 | } 23 | return dict[key] = sup ? sup.apply(this, arguments) : void 0; 24 | }; 25 | } 26 | } : 27 | { 28 | around: function (sup) { 29 | return function (first) { 30 | var cache = this.__memoizerCache, dict; 31 | if (!cache) { 32 | cache = this.__memoizerCache = {}; 33 | } 34 | if (cache.hasOwnProperty(name)) { 35 | dict = cache[name]; 36 | } else { 37 | dict = cache[name] = {}; 38 | } 39 | if (dict.hasOwnProperty(first)) { 40 | return dict[first]; 41 | } 42 | return dict[first] = sup ? sup.apply(this, arguments) : undefined; 43 | }; 44 | } 45 | }; 46 | }, 47 | guard: function (name) { 48 | return { 49 | after: function () { 50 | var cache = this.__memoizerCache; 51 | if (cache && name) { 52 | delete cache[name]; 53 | } else { 54 | this.__memoizerCache = {}; 55 | } 56 | } 57 | }; 58 | } 59 | }; 60 | }); 61 | -------------------------------------------------------------------------------- /es6/advices/time.js: -------------------------------------------------------------------------------- 1 | export default (function(_,f){return f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var uniq = 0; 6 | 7 | return function (name) { 8 | var inCall = 0, label = name || ('Timer #' + uniq++); 9 | return { 10 | before: function () { 11 | if (!(inCall++)) { 12 | console.time(label); 13 | } 14 | }, 15 | after: function () { 16 | if (!--inCall) { 17 | console.timeEnd(label); 18 | } 19 | } 20 | }; 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /es6/advices/trace.js: -------------------------------------------------------------------------------- 1 | export default (function(_,f){return f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var lvl = 0; 6 | 7 | function rep (ch, n) { 8 | if (n < 1) { return ''; } 9 | if (n == 1) { return ch; } 10 | var h = rep(ch, Math.floor(n / 2)); 11 | return h + h + ((n & 1) ? ch : ''); 12 | 13 | } 14 | 15 | function pad (value, width, ch) { 16 | var v = value.toString(); 17 | return v + rep(ch || ' ', width - v.length); 18 | } 19 | 20 | return function (name, level) { 21 | return { 22 | before: function () { 23 | ++lvl; 24 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 25 | name + '(' + Array.prototype.join.call(arguments, ', ') + ')'); 26 | }, 27 | after: function (args, result) { 28 | console.log((level ? pad(lvl, 2 * lvl) : '') + this + ' => ' + 29 | name + (result && result instanceof Error ? ' throws' : ' returns') + 30 | ' ' + result); 31 | --lvl; 32 | } 33 | }; 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /es6/advise.js: -------------------------------------------------------------------------------- 1 | export default (function(_,f){return f();}) 2 | ([], function () { 3 | 'use strict'; 4 | 5 | var pname = 'prototype'; 6 | 7 | function Node (parent) { 8 | this.parent = parent || this; 9 | } 10 | 11 | Node[pname] = { 12 | removeTopic: function (topic) { 13 | var n = 'next_' + topic, p = 'prev_' + topic; 14 | if (this[n] && this[p]) { 15 | this[n][p] = this[p]; 16 | this[p][n] = this[n]; 17 | } 18 | }, 19 | remove: function () { 20 | this.removeTopic('before'); 21 | this.removeTopic('around'); 22 | 23 | // remove & recreate around advices 24 | var parent = this.parent, next = this.next_around; 25 | this.removeTopic('after'); 26 | for (; next && next !== parent; next = next.next_around) { 27 | next.around = next.originalAround(next.prev_around.around); 28 | } 29 | }, 30 | addTopic: function (node, topic) { 31 | var n = 'next_' + topic, p = 'prev_' + topic, 32 | prev = node[p] = this[p] || this; 33 | node[n] = this; 34 | prev[n] = this[p] = node; 35 | }, 36 | addAdvice: function (advice, instance, name, type) { 37 | var node = new Node(this); 38 | if (advice.before) { 39 | node.before = advice.before; 40 | this.addTopic(node, 'before'); 41 | } 42 | if (advice.around) { 43 | if (typeof advice.around != 'function') { 44 | advise._error('wrong super call', instance, name, type); 45 | } 46 | node.originalAround = advice.around; 47 | this.addTopic(node, 'around'); 48 | if (node.prev_around.around && typeof node.prev_around.around != 'function') { 49 | advise._error('wrong super arg', instance, name, type); 50 | } 51 | node.around = advice.around(node.prev_around.around || null); 52 | if (typeof node.around != 'function') { 53 | advise._error('wrong super result', instance, name, type); 54 | } 55 | } 56 | if (advice.after) { 57 | node.after = advice.after; 58 | this.addTopic(node, 'after'); 59 | } 60 | return node; 61 | } 62 | }; 63 | 64 | Node[pname].destroy = Node[pname].unadvise = Node[pname].remove; 65 | 66 | function addNode (root, topic) { 67 | return function (f) { 68 | var node = new Node(root); 69 | node[topic] = f; 70 | root.addTopic(node, topic); 71 | }; 72 | } 73 | 74 | function makeStub (value) { 75 | var root = new Node(); 76 | if (value) { 77 | if (typeof value.advices == 'object') { 78 | var advices = value.advices; 79 | advices.before.forEach(addNode(root, 'before')); 80 | advices.after. forEach(addNode(root, 'after')); 81 | advices.around && addNode(root, 'around')(advices.around); 82 | } else { 83 | addNode(root, 'around')(value); 84 | } 85 | } 86 | function stub () { 87 | var result, thrown, p; 88 | // running the before chain 89 | for (p = root.prev_before; p && p !== root; p = p.prev_before) { 90 | p.before.apply(this, arguments); 91 | } 92 | // running the around chain 93 | if (root.prev_around && root.prev_around !== root) { 94 | try { 95 | result = root.prev_around.around.apply(this, arguments); 96 | } catch (error) { 97 | result = error; 98 | thrown = true; 99 | } 100 | } 101 | // running the after chain 102 | for (p = root.next_after; p && p !== root; p = p.next_after) { 103 | p.after.call(this, arguments, result, makeReturn, makeThrow); 104 | } 105 | if (thrown) { 106 | throw result; 107 | } 108 | return result; 109 | 110 | function makeReturn (value) { result = value; thrown = false; } 111 | function makeThrow (value) { result = value; thrown = true; } 112 | }; 113 | stub.node = root; 114 | return stub; 115 | } 116 | 117 | function convert (value, advice, instance, name, type) { 118 | if (!value || !(value.node instanceof Node)) { 119 | value = makeStub(value); 120 | value.node.instance = instance; 121 | value.node.name = name; 122 | value.node.type = type; 123 | } 124 | var node = value.node.addAdvice(advice, instance, name, type); 125 | return {value: value, handle: node}; 126 | } 127 | 128 | function combineHandles (handles) { 129 | var handle = { 130 | remove: function () { 131 | handles.forEach(function (handle) { handle.remove(); }); 132 | } 133 | } 134 | handle.destroy = handle.unadvise = handle.remove; 135 | return handle; 136 | } 137 | 138 | function advise (instance, name, advice) { 139 | var prop = getPropertyDescriptor(instance, name), handles = []; 140 | if (prop) { 141 | if (prop.get || prop.set) { 142 | var result; 143 | if (prop.get && advice.get) { 144 | result = convert(prop.get, advice.get, instance, name, 'get'); 145 | prop.get = result.value; 146 | handles.push(result.handle); 147 | } 148 | if (prop.set && advice.set) { 149 | result = convert(prop.set, advice.set, instance, name, 'set'); 150 | prop.set = result.value; 151 | handles.push(result.handle); 152 | } 153 | } else { 154 | if (prop.value && advice) { 155 | result = convert(prop.value, advice, instance, name, 'value'); 156 | prop.value = result.value; 157 | handles.push(result.handle); 158 | } 159 | } 160 | } else { 161 | prop = {writable: true, configurable: true, enumerable: true}; 162 | if (advice.get || advice.set) { 163 | if (advice.get) { 164 | result = convert(null, advice.get, instance, name, 'get'); 165 | prop.get = result.value; 166 | handles.push(result.handle); 167 | } 168 | if (advice.set) { 169 | result = convert(null, advice.set, instance, name, 'set'); 170 | prop.set = result.value; 171 | handles.push(result.handle); 172 | } 173 | } else { 174 | result = convert(null, advice, instance, name, 'value'); 175 | prop.value = result.value; 176 | handles.push(result.handle); 177 | } 178 | } 179 | Object.defineProperty(instance, name, prop); 180 | return combineHandles(handles); 181 | } 182 | 183 | // export 184 | 185 | // guts, do not use them! 186 | advise._error = function (msg) { 187 | throw new Error(msg); 188 | }; 189 | 190 | advise.before = function (instance, name, f) { return advise(instance, name, {before: f}); }; 191 | advise.after = function (instance, name, f) { return advise(instance, name, {after: f}); }; 192 | advise.around = function (instance, name, f) { return advise(instance, name, {around: f}); }; 193 | advise.Node = Node; 194 | 195 | return advise; 196 | 197 | // copied from dcl.js so we can be independent 198 | function getPropertyDescriptor (o, name) { 199 | while (o && o !== Object[pname]) { 200 | if (o.hasOwnProperty(name)) { 201 | return Object.getOwnPropertyDescriptor(o, name); 202 | } 203 | o = Object.getPrototypeOf(o); 204 | } 205 | return; // undefined 206 | } 207 | }); 208 | -------------------------------------------------------------------------------- /es6/bases/Mixer.js: -------------------------------------------------------------------------------- 1 | import m0 from "../dcl";export default (function(_,f){return f(m0);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Mixer', 7 | constructor: function (x) { 8 | Object.defineProperties(this, dcl.collectPropertyDescriptors({}, x)); 9 | } 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /es6/bases/Replacer.js: -------------------------------------------------------------------------------- 1 | import m0 from "../dcl";export default (function(_,f){return f(m0);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | return dcl({ 6 | declaredClass: 'dcl/bases/Replacer', 7 | constructor: function (x) { 8 | var props = dcl.collectPropertyDescriptors({}, x); 9 | Object.keys(props).forEach(function (name) { 10 | if (name in this) { 11 | Object.defineProperty(this, name, props[name]); 12 | } 13 | }, this); 14 | } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /es6/debug.js: -------------------------------------------------------------------------------- 1 | import m0 from "./dcl";import m1 from "./advise";export default (function(_,f){return f(m0,m1);}) 2 | (['./dcl', './advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | // set up custom names 6 | var mname = '_meta', pname = 'prototype', cname = 'constructor'; 7 | 8 | function DclError (message) { 9 | this.name = 'DclError'; 10 | this.message = message || 'Default Message'; 11 | this.stack = (new Error()).stack; 12 | } 13 | DclError.prototype = Object.create(Error.prototype); 14 | DclError.prototype.constructor = DclError; 15 | 16 | var CycleError = dcl(DclError, {declaredClass: "dcl/debug/CycleError"}), 17 | ChainingError = dcl(DclError, {declaredClass: "dcl/debug/ChainingError"}), 18 | SuperError = dcl(DclError, {declaredClass: "dcl/debug/SuperError"}); 19 | 20 | advise.around(dcl, '_error', function (sup) { 21 | return function (reason) { 22 | var name, ctr, method, props; 23 | if (reason === 'cycle') { 24 | var bases = arguments[1], 25 | names = bases.map(function (base, index) { 26 | return base[pname].declaredClass || ('UNNAMED_' + index); 27 | }); 28 | props = arguments[2]; 29 | name = props.declaredClass && props.declaredClass.value; 30 | if (!name || typeof name != 'string') { 31 | name = 'UNNAMED'; 32 | } 33 | throw new CycleError('dcl: base class cycle in ' + name + 34 | ', bases (' + names.join(', ') + ') are mutually dependent'); 35 | } 36 | if (/^different weavers\b/.test(reason)) { 37 | var weaver = arguments[3], own = arguments[4]; 38 | ctr = arguments[1]; 39 | method = arguments[2]; 40 | name = ctr[mname].props.declaredClass && ctr[mname].props.declaredClass.value || 'UNNAMED'; 41 | throw new ChainingError('dcl: conflicting chain directives in ' + 42 | name + ' for ' + method + ', was ' + own.name + ', set to ' + weaver.name); 43 | } 44 | if (/^wrong super\b/.test(reason)) { 45 | var index = arguments[3]; 46 | ctr = arguments[1]; 47 | method = arguments[2]; 48 | props = arguments[4]; 49 | name = props.declaredClass && props.declaredClass.value; 50 | if (!name || typeof name != 'string') { 51 | name = 'UNNAMED'; 52 | } 53 | var re = /^wrong super (\w+) (\w+)$/.exec(reason), 54 | baseName = ctr[mname].props.declaredClass && 55 | ctr[mname].props.declaredClass.value || 56 | ('UNNAMED_' + index); 57 | throw new SuperError('dcl: super call error in ' + 58 | name + ', while weaving ' + baseName + ', method ' + method + 59 | ' (' + re[1] + ') wrong ' + re[2]); 60 | } 61 | return sup.apply(this, arguments); 62 | }; 63 | }); 64 | 65 | advise.around(advise, '_error', function (sup) { 66 | return function (reason, instance, method, type) { 67 | var re = /^wrong super (\w+)$/.exec(reason); 68 | if (re) { 69 | var baseName = instance.declaredClass; 70 | if (!baseName || typeof baseName != 'string') { 71 | baseName = 'UNNAMED'; 72 | } 73 | throw new SuperError('dcl: super call error in object of ' + 74 | baseName + ', while weaving method ' + method + 75 | ' (' + type + ') wrong ' + re[1]); 76 | } 77 | return sup.apply(this, arguments); 78 | }; 79 | }); 80 | 81 | function logCtor (ctor) { 82 | var meta = ctor[mname]; 83 | if (!meta) { 84 | console.log('*** class does not have meta information compatible with dcl'); 85 | return; 86 | } 87 | var names = meta.bases.map(function (base, index) { 88 | return base[pname].declaredClass || ('UNNAMED_' + index); 89 | }); 90 | console.log('*** class ' + names[names.length - 1] + ' depends on ' + 91 | (names.length - 1) + (names.length == 2 ? ' class' : ' classes') + 92 | (names.length > 1 ? ': ' + names.slice(0, -1).join(', ') : '') 93 | ); 94 | var specialKeys = Object.keys(meta.special); 95 | if (specialKeys.length) { 96 | console.log('*** class ' + names[names.length - 1] + ' has ' + 97 | specialKeys.length + (specialKeys.length == 1 ? ' weaver: ' : ' weavers: ') + 98 | specialKeys.map(function (name) { 99 | return name + ': ' + meta.special[name].name; 100 | }).join(', ') 101 | ); 102 | } 103 | } 104 | 105 | function logAdvices (advices) { 106 | return 'class-level advice (before ' + advices.before.length + 107 | ', after ' + advices.after.length + ')'; 108 | } 109 | 110 | function countAdvices (root, chain) { 111 | var total = 0; 112 | for (var node = root[chain]; node && node !== root; node = node[chain], ++total); 113 | return total; 114 | } 115 | 116 | function logNode (node) { 117 | return 'object-level advice (before ' + countAdvices(node, 'next_before') + 118 | ', after ' + countAdvices(node, 'next_after') + ')'; 119 | } 120 | 121 | function log (o, suppressCtor) { 122 | if (typeof o == 'function') { 123 | logCtor(o); 124 | } else if (o && typeof o == 'object') { 125 | var base = o[cname]; 126 | if (base[pname].declaredClass) { 127 | console.log('*** object of class ' + base[pname].declaredClass); 128 | } 129 | if (!suppressCtor) { 130 | logCtor(base); 131 | } 132 | allKeys(o).forEach(function (name) { 133 | var prop = dcl.getPropertyDescriptor(o, name); 134 | if (prop.get || prop.set) { 135 | if (prop.get) { 136 | if (prop.get.node instanceof advise.Node) { 137 | console.log(' ' + name + ': getter with ' + logNode(prop.get.node)); 138 | } else if (typeof prop.get.advices == 'object') { 139 | console.log(' ' + name + ': getter with ' + logAdvices(prop.get.advices)); 140 | } 141 | } 142 | if (prop.set) { 143 | if (prop.set.node instanceof advise.Node) { 144 | console.log(' ' + name + ': setter with ' + logNode(prop.set.node)); 145 | } else if (typeof prop.set.advices == 'object') { 146 | console.log(' ' + name + ': setter with ' + logAdvices(prop.set.advices)); 147 | } 148 | } 149 | } else { 150 | if (prop.value.node instanceof advise.Node) { 151 | console.log(' ' + name + ': ' + logNode(prop.value.node)); 152 | } else if (typeof prop.value.advices == 'object') { 153 | console.log(' ' + name + ': ' + logAdvices(prop.value.advices)); 154 | } 155 | } 156 | }); 157 | } 158 | } 159 | 160 | dcl.log = log; 161 | dcl.DclError = DclError; 162 | dcl.CycleError = CycleError; 163 | dcl.ChainingError = ChainingError; 164 | dcl.SuperError = SuperError; 165 | 166 | return dcl; 167 | 168 | function allKeys (o) { 169 | var keys = []; 170 | for (var key in o) { 171 | keys.push(key); 172 | } 173 | return keys; 174 | } 175 | }); 176 | -------------------------------------------------------------------------------- /es6/mixins/Cleanup.js: -------------------------------------------------------------------------------- 1 | import m0 from "../dcl";import m1 from "./Destroyable";export default (function(_,f){return f(m0,m1);}) 2 | (['../dcl', './Destroyable'], function (dcl, Destroyable) { 3 | 'use strict'; 4 | 5 | return dcl(Destroyable, { 6 | declaredClass: 'dcl/mixins/Cleanup', 7 | constructor: function () { 8 | this.__cleanupStack = []; 9 | }, 10 | pushCleanup: function (resource, cleanup) { 11 | var f = cleanup ? function () { cleanup(resource); } : function () { resource.destroy(); }; 12 | this.__cleanupStack.push(f); 13 | return f; 14 | }, 15 | popCleanup: function (dontRun) { 16 | if (dontRun) { 17 | return this.__cleanupStack.pop(); 18 | } 19 | this.__cleanupStack.pop()(); 20 | }, 21 | removeCleanup: function (f) { 22 | for (var i = this.__cleanupStack.length - 1; i >= 0; --i) { 23 | if (this.__cleanupStack[i] === f) { 24 | this.__cleanupStack.splice(i, 1); 25 | return true; 26 | } 27 | } 28 | }, 29 | cleanup: function () { 30 | while (this.__cleanupStack.length) { 31 | this.__cleanupStack.pop()(); 32 | } 33 | }, 34 | destroy: function () { 35 | this.cleanup(); 36 | } 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /es6/mixins/Destroyable.js: -------------------------------------------------------------------------------- 1 | import m0 from "../dcl";export default (function(_,f){return f(m0);}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Destroyable = dcl({declaredClass: 'dcl/mixins/Destroyable'}); 6 | dcl.chainBefore(Destroyable, 'destroy'); 7 | 8 | return Destroyable; 9 | }); 10 | -------------------------------------------------------------------------------- /es6/utils/registry.js: -------------------------------------------------------------------------------- 1 | import m0 from "../dcl";import m1 from "../advise";export default (function(_,f){return f(m0,m1);}) 2 | (['../dcl', '../advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | var registry = {}; 6 | 7 | // register all named classes automatically 8 | advise.after(dcl, '_makeCtr', function (_, result) { 9 | if (result && typeof result.prototype.declaredClass == 'string') { 10 | registry[result.prototype.declaredClass] = result; 11 | } 12 | }); 13 | 14 | return { 15 | get: function (name) { return registry[name]; }, 16 | has: function (name) { return Object.prototype.hasOwnProperty.call(registry, name); }, 17 | delete: function (name) { return delete registry[name]; }, 18 | keys: function () { 19 | return Object.keys(registry).filter(function (name) { 20 | return Object.prototype.hasOwnProperty.call(registry, name); 21 | }); 22 | }, 23 | clear: function () { registry = {}; } 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /mixins/Cleanup.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['../dcl', './Destroyable'], function (dcl, Destroyable) { 3 | 'use strict'; 4 | 5 | return dcl(Destroyable, { 6 | declaredClass: 'dcl/mixins/Cleanup', 7 | constructor: function () { 8 | this.__cleanupStack = []; 9 | }, 10 | pushCleanup: function (resource, cleanup) { 11 | var f = cleanup ? function () { cleanup(resource); } : function () { resource.destroy(); }; 12 | this.__cleanupStack.push(f); 13 | return f; 14 | }, 15 | popCleanup: function (dontRun) { 16 | if (dontRun) { 17 | return this.__cleanupStack.pop(); 18 | } 19 | this.__cleanupStack.pop()(); 20 | }, 21 | removeCleanup: function (f) { 22 | for (var i = this.__cleanupStack.length - 1; i >= 0; --i) { 23 | if (this.__cleanupStack[i] === f) { 24 | this.__cleanupStack.splice(i, 1); 25 | return true; 26 | } 27 | } 28 | }, 29 | cleanup: function () { 30 | while (this.__cleanupStack.length) { 31 | this.__cleanupStack.pop()(); 32 | } 33 | }, 34 | destroy: function () { 35 | this.cleanup(); 36 | } 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /mixins/Destroyable.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['../dcl'], function (dcl) { 3 | 'use strict'; 4 | 5 | var Destroyable = dcl({declaredClass: 'dcl/mixins/Destroyable'}); 6 | dcl.chainBefore(Destroyable, 'destroy'); 7 | 8 | return Destroyable; 9 | }); 10 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dcl", 3 | "version": "2.0.11", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/mime-types": { 8 | "version": "2.1.0", 9 | "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz", 10 | "integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=", 11 | "dev": true 12 | }, 13 | "@types/node": { 14 | "version": "13.13.2", 15 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.2.tgz", 16 | "integrity": "sha512-LB2R1Oyhpg8gu4SON/mfforE525+Hi/M1ineICEDftqNVTyFg1aRIeGuTvXAoWHc4nbrFncWtJgMmoyRvuGh7A==", 17 | "dev": true, 18 | "optional": true 19 | }, 20 | "@types/yauzl": { 21 | "version": "2.9.1", 22 | "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", 23 | "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", 24 | "dev": true, 25 | "optional": true, 26 | "requires": { 27 | "@types/node": "*" 28 | } 29 | }, 30 | "agent-base": { 31 | "version": "5.1.1", 32 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", 33 | "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", 34 | "dev": true 35 | }, 36 | "balanced-match": { 37 | "version": "1.0.0", 38 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 39 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 40 | "dev": true 41 | }, 42 | "base64-js": { 43 | "version": "1.3.1", 44 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 45 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", 46 | "dev": true 47 | }, 48 | "bl": { 49 | "version": "4.0.2", 50 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", 51 | "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", 52 | "dev": true, 53 | "requires": { 54 | "buffer": "^5.5.0", 55 | "inherits": "^2.0.4", 56 | "readable-stream": "^3.4.0" 57 | }, 58 | "dependencies": { 59 | "inherits": { 60 | "version": "2.0.4", 61 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 62 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 63 | "dev": true 64 | } 65 | } 66 | }, 67 | "brace-expansion": { 68 | "version": "1.1.11", 69 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 70 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 71 | "dev": true, 72 | "requires": { 73 | "balanced-match": "^1.0.0", 74 | "concat-map": "0.0.1" 75 | } 76 | }, 77 | "buffer": { 78 | "version": "5.6.0", 79 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", 80 | "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", 81 | "dev": true, 82 | "requires": { 83 | "base64-js": "^1.0.2", 84 | "ieee754": "^1.1.4" 85 | } 86 | }, 87 | "buffer-crc32": { 88 | "version": "0.2.13", 89 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 90 | "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", 91 | "dev": true 92 | }, 93 | "chownr": { 94 | "version": "1.1.4", 95 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 96 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 97 | "dev": true 98 | }, 99 | "concat-map": { 100 | "version": "0.0.1", 101 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 102 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 103 | "dev": true 104 | }, 105 | "debug": { 106 | "version": "4.1.1", 107 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 108 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 109 | "dev": true, 110 | "requires": { 111 | "ms": "^2.1.1" 112 | } 113 | }, 114 | "end-of-stream": { 115 | "version": "1.4.4", 116 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 117 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 118 | "dev": true, 119 | "requires": { 120 | "once": "^1.4.0" 121 | } 122 | }, 123 | "extract-zip": { 124 | "version": "2.0.0", 125 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", 126 | "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", 127 | "dev": true, 128 | "requires": { 129 | "@types/yauzl": "^2.9.1", 130 | "debug": "^4.1.1", 131 | "get-stream": "^5.1.0", 132 | "yauzl": "^2.10.0" 133 | } 134 | }, 135 | "fd-slicer": { 136 | "version": "1.1.0", 137 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 138 | "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", 139 | "dev": true, 140 | "requires": { 141 | "pend": "~1.2.0" 142 | } 143 | }, 144 | "fs-constants": { 145 | "version": "1.0.0", 146 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 147 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 148 | "dev": true 149 | }, 150 | "fs.realpath": { 151 | "version": "1.0.0", 152 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 153 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 154 | "dev": true 155 | }, 156 | "get-stream": { 157 | "version": "5.1.0", 158 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", 159 | "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", 160 | "dev": true, 161 | "requires": { 162 | "pump": "^3.0.0" 163 | } 164 | }, 165 | "glob": { 166 | "version": "7.1.2", 167 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 168 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 169 | "dev": true, 170 | "requires": { 171 | "fs.realpath": "^1.0.0", 172 | "inflight": "^1.0.4", 173 | "inherits": "2", 174 | "minimatch": "^3.0.4", 175 | "once": "^1.3.0", 176 | "path-is-absolute": "^1.0.0" 177 | } 178 | }, 179 | "heya-globalize": { 180 | "version": "1.2.1", 181 | "resolved": "https://registry.npmjs.org/heya-globalize/-/heya-globalize-1.2.1.tgz", 182 | "integrity": "sha512-lnXF5Vu0je61PaeWrlUDMaBI5p8GynmIza/qg81IN5L1xHMaAqgPGBdrHzU44m+F5IVF1yAK4PCBtMqDAi5ojw==", 183 | "dev": true, 184 | "requires": { 185 | "glob": "^7.0.0", 186 | "minimist": "^1.2.0", 187 | "mkdirp": "^0.5.1" 188 | } 189 | }, 190 | "heya-ice": { 191 | "version": "0.1.11", 192 | "resolved": "https://registry.npmjs.org/heya-ice/-/heya-ice-0.1.11.tgz", 193 | "integrity": "sha1-XW2lnGC1nHAjqDRw+26XcddwWEk=", 194 | "dev": true 195 | }, 196 | "heya-unify": { 197 | "version": "0.2.7", 198 | "resolved": "https://registry.npmjs.org/heya-unify/-/heya-unify-0.2.7.tgz", 199 | "integrity": "sha512-d/4NacYl52tt4ofbP7gz+YmbjLrI2jkrRxSSd1a26yXfRS1vQxmZkZ6L+O1xUsgDSwx4HCDWR5U+ZFykdoHVig==", 200 | "dev": true, 201 | "requires": { 202 | "heya-ice": "^0.1.11" 203 | } 204 | }, 205 | "heya-unit": { 206 | "version": "0.3.0", 207 | "resolved": "https://registry.npmjs.org/heya-unit/-/heya-unit-0.3.0.tgz", 208 | "integrity": "sha1-eXR4IIyBnUxbf+NWrEwbhO67ubc=", 209 | "dev": true, 210 | "requires": { 211 | "heya-ice": ">=0.1", 212 | "heya-unify": ">=0.2" 213 | } 214 | }, 215 | "https-proxy-agent": { 216 | "version": "4.0.0", 217 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", 218 | "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", 219 | "dev": true, 220 | "requires": { 221 | "agent-base": "5", 222 | "debug": "4" 223 | } 224 | }, 225 | "ieee754": { 226 | "version": "1.1.13", 227 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 228 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", 229 | "dev": true 230 | }, 231 | "inflight": { 232 | "version": "1.0.6", 233 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 234 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 235 | "dev": true, 236 | "requires": { 237 | "once": "^1.3.0", 238 | "wrappy": "1" 239 | } 240 | }, 241 | "inherits": { 242 | "version": "2.0.3", 243 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 244 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 245 | "dev": true 246 | }, 247 | "mime": { 248 | "version": "2.4.4", 249 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", 250 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", 251 | "dev": true 252 | }, 253 | "mime-db": { 254 | "version": "1.44.0", 255 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 256 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", 257 | "dev": true 258 | }, 259 | "mime-types": { 260 | "version": "2.1.27", 261 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 262 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 263 | "dev": true, 264 | "requires": { 265 | "mime-db": "1.44.0" 266 | } 267 | }, 268 | "minimatch": { 269 | "version": "3.0.4", 270 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 271 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 272 | "dev": true, 273 | "requires": { 274 | "brace-expansion": "^1.1.7" 275 | } 276 | }, 277 | "minimist": { 278 | "version": "1.2.5", 279 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 280 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 281 | "dev": true 282 | }, 283 | "mkdirp": { 284 | "version": "0.5.5", 285 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 286 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 287 | "dev": true, 288 | "requires": { 289 | "minimist": "^1.2.5" 290 | } 291 | }, 292 | "mkdirp-classic": { 293 | "version": "0.5.2", 294 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", 295 | "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==", 296 | "dev": true 297 | }, 298 | "ms": { 299 | "version": "2.1.2", 300 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 301 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 302 | "dev": true 303 | }, 304 | "once": { 305 | "version": "1.4.0", 306 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 307 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 308 | "dev": true, 309 | "requires": { 310 | "wrappy": "1" 311 | } 312 | }, 313 | "path-is-absolute": { 314 | "version": "1.0.1", 315 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 316 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 317 | "dev": true 318 | }, 319 | "pend": { 320 | "version": "1.2.0", 321 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 322 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", 323 | "dev": true 324 | }, 325 | "progress": { 326 | "version": "2.0.3", 327 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 328 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 329 | "dev": true 330 | }, 331 | "proxy-from-env": { 332 | "version": "1.1.0", 333 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 334 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 335 | "dev": true 336 | }, 337 | "pump": { 338 | "version": "3.0.0", 339 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 340 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 341 | "dev": true, 342 | "requires": { 343 | "end-of-stream": "^1.1.0", 344 | "once": "^1.3.1" 345 | } 346 | }, 347 | "puppeteer": { 348 | "version": "3.0.1", 349 | "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-3.0.1.tgz", 350 | "integrity": "sha512-DxNnI9n4grVHC+9irUfNK2T6YFuRECJnvG7VzdVolxpVwWC5DQqI5ho9Z0af48K5MQW4sJY5cq3qQ5g6NkAjvw==", 351 | "dev": true, 352 | "requires": { 353 | "@types/mime-types": "^2.1.0", 354 | "debug": "^4.1.0", 355 | "extract-zip": "^2.0.0", 356 | "https-proxy-agent": "^4.0.0", 357 | "mime": "^2.0.3", 358 | "mime-types": "^2.1.25", 359 | "progress": "^2.0.1", 360 | "proxy-from-env": "^1.0.0", 361 | "rimraf": "^3.0.2", 362 | "tar-fs": "^2.0.0", 363 | "unbzip2-stream": "^1.3.3", 364 | "ws": "^7.2.3" 365 | } 366 | }, 367 | "readable-stream": { 368 | "version": "3.6.0", 369 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 370 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 371 | "dev": true, 372 | "requires": { 373 | "inherits": "^2.0.3", 374 | "string_decoder": "^1.1.1", 375 | "util-deprecate": "^1.0.1" 376 | } 377 | }, 378 | "rimraf": { 379 | "version": "3.0.2", 380 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 381 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 382 | "dev": true, 383 | "requires": { 384 | "glob": "^7.1.3" 385 | }, 386 | "dependencies": { 387 | "glob": { 388 | "version": "7.1.6", 389 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 390 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 391 | "dev": true, 392 | "requires": { 393 | "fs.realpath": "^1.0.0", 394 | "inflight": "^1.0.4", 395 | "inherits": "2", 396 | "minimatch": "^3.0.4", 397 | "once": "^1.3.0", 398 | "path-is-absolute": "^1.0.0" 399 | } 400 | } 401 | } 402 | }, 403 | "safe-buffer": { 404 | "version": "5.2.0", 405 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 406 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", 407 | "dev": true 408 | }, 409 | "string_decoder": { 410 | "version": "1.3.0", 411 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 412 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 413 | "dev": true, 414 | "requires": { 415 | "safe-buffer": "~5.2.0" 416 | } 417 | }, 418 | "tar-fs": { 419 | "version": "2.0.1", 420 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", 421 | "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", 422 | "dev": true, 423 | "requires": { 424 | "chownr": "^1.1.1", 425 | "mkdirp-classic": "^0.5.2", 426 | "pump": "^3.0.0", 427 | "tar-stream": "^2.0.0" 428 | } 429 | }, 430 | "tar-stream": { 431 | "version": "2.1.2", 432 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", 433 | "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", 434 | "dev": true, 435 | "requires": { 436 | "bl": "^4.0.1", 437 | "end-of-stream": "^1.4.1", 438 | "fs-constants": "^1.0.0", 439 | "inherits": "^2.0.3", 440 | "readable-stream": "^3.1.1" 441 | } 442 | }, 443 | "through": { 444 | "version": "2.3.8", 445 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 446 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 447 | "dev": true 448 | }, 449 | "unbzip2-stream": { 450 | "version": "1.4.2", 451 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz", 452 | "integrity": "sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg==", 453 | "dev": true, 454 | "requires": { 455 | "buffer": "^5.2.1", 456 | "through": "^2.3.8" 457 | } 458 | }, 459 | "util-deprecate": { 460 | "version": "1.0.2", 461 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 462 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 463 | "dev": true 464 | }, 465 | "wrappy": { 466 | "version": "1.0.2", 467 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 468 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 469 | "dev": true 470 | }, 471 | "ws": { 472 | "version": "7.2.5", 473 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", 474 | "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==", 475 | "dev": true 476 | }, 477 | "yauzl": { 478 | "version": "2.10.0", 479 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 480 | "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", 481 | "dev": true, 482 | "requires": { 483 | "buffer-crc32": "~0.2.3", 484 | "fd-slicer": "~1.1.0" 485 | } 486 | } 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dcl", 3 | "version": "2.0.11", 4 | "description": "Elegant minimalistic implementation of OOP with mixins + AOP.", 5 | "author": "Eugene Lazutkin (http://lazutkin.com/)", 6 | "license": "BSD-3-Clause", 7 | "homepage": "http://www.dcljs.org", 8 | "bugs": "http://github.com/uhop/dcl/issues", 9 | "main": "dcl.js", 10 | "directories": { 11 | "test": "tests" 12 | }, 13 | "dependencies": {}, 14 | "devDependencies": { 15 | "heya-globalize": "^1.2.1", 16 | "heya-unit": "^0.3.0", 17 | "puppeteer": "^3.0.1" 18 | }, 19 | "scripts": { 20 | "test": "node tests/tests.js", 21 | "test-browser": "node tests/puppeteer.js", 22 | "dist": "node node_modules/heya-globalize/index.js && node node_modules/heya-globalize/index.js --es6 --target=es6 && node node_modules/heya-globalize/index.js --amd --target=amd", 23 | "prepublishOnly": "npm run dist" 24 | }, 25 | "github": "http://github.com/uhop/dcl", 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/uhop/dcl.git" 29 | }, 30 | "keywords": [ 31 | "object-oriented", 32 | "programming", 33 | "aspect-oriented", 34 | "OOP", 35 | "AOP", 36 | "OO" 37 | ], 38 | "browserGlobals": { 39 | "!root": "dcl", 40 | "./dcl": "dcl", 41 | "./advise": "advise", 42 | "./debug": "!dcl" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/manual/test_ctr_aop.js: -------------------------------------------------------------------------------- 1 | var dcl = require("../dcl"); 2 | 3 | 4 | var A = dcl(null, { 5 | constructor: function(){ 6 | console.log("A::ctr"); 7 | } 8 | }); 9 | 10 | var M = dcl(null, { 11 | constructor: dcl.advise({ 12 | before: function(){ 13 | console.log("M::before"); 14 | }, 15 | after: function(){ 16 | console.log("M::after"); 17 | } 18 | }) 19 | }); 20 | 21 | var C = dcl([A, M], { 22 | constructor: function(){ 23 | console.log("B::ctr"); 24 | } 25 | }); 26 | 27 | var c = new C(); 28 | -------------------------------------------------------------------------------- /tests/manual/test_modern1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, {a: 1}); 8 | 9 | var x = new A; 10 | console.log(x); 11 | console.log(x instanceof A); 12 | console.log(x.a); 13 | 14 | var B = dcl(null, { 15 | constructor: function () { 16 | this.b = 2; 17 | }, 18 | a: dcl.prop({value: 1}) 19 | }); 20 | 21 | var y = new B; 22 | console.log(y); 23 | console.log(y instanceof B); 24 | console.log(y.a, y.b); 25 | 26 | var C = dcl(null, dcl.prop({ 27 | constructor: { 28 | value: function () { 29 | this.b = 2; 30 | } 31 | }, 32 | a: { 33 | value: 1 34 | } 35 | })); 36 | 37 | var z = new C; 38 | console.log(z); 39 | console.log(z instanceof C); 40 | console.log(z.a, z.b); 41 | -------------------------------------------------------------------------------- /tests/manual/test_modern2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, { 8 | a: function () { console.log('a'); } 9 | }); 10 | 11 | var x = new A; 12 | console.log(x); 13 | console.log(x instanceof A); 14 | console.log(x.a); 15 | x.a(); 16 | 17 | var B = dcl(A, { 18 | b: function () { console.log('b'); } 19 | }); 20 | 21 | var y = new B; 22 | console.log(y); 23 | console.log(y instanceof A, y instanceof B); 24 | console.log(y.a, y.b); 25 | y.a(); 26 | y.b(); 27 | 28 | var C = dcl(B, { 29 | c: function () { console.log('c'); } 30 | }); 31 | 32 | var z = new C; 33 | console.log(z); 34 | console.log(z instanceof A, z instanceof B, z instanceof C); 35 | console.log(z.a, z.b, z.c); 36 | z.a(); 37 | z.b(); 38 | z.c(); 39 | -------------------------------------------------------------------------------- /tests/manual/test_modern3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, { 8 | a: function () { console.log('a'); }, 9 | z: function () { console.log('a'); } 10 | }); 11 | 12 | var B = dcl(null, { 13 | b: function () { console.log('b'); }, 14 | z: function () { console.log('b'); } 15 | }); 16 | 17 | var C = dcl([A, B], { 18 | c: function () { console.log('c'); }, 19 | z: function () { console.log('c'); } 20 | }); 21 | 22 | var z = new C; 23 | console.log(z); 24 | console.log(z instanceof A, z instanceof B, z instanceof C); 25 | console.log(z.a, z.b, z.c, z.z); 26 | z.a(); 27 | z.b(); 28 | z.c(); 29 | z.z(); 30 | -------------------------------------------------------------------------------- /tests/manual/test_modern4.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, { 8 | a: function () { console.log('a'); }, 9 | z: function () { console.log('a'); } 10 | }); 11 | dcl.chainAfter(A, 'z'); 12 | 13 | var B = dcl(null, { 14 | b: function () { console.log('b'); }, 15 | z: function () { console.log('b'); } 16 | }); 17 | 18 | var C = dcl([A, B], { 19 | c: function () { console.log('c'); }, 20 | z: function () { console.log('c'); } 21 | }); 22 | 23 | var z = new C; 24 | console.log(z); 25 | console.log(z instanceof A, z instanceof B, z instanceof C); 26 | console.log(z.a, z.b, z.c, z.z); 27 | z.a(); 28 | z.b(); 29 | z.c(); 30 | z.z(); 31 | -------------------------------------------------------------------------------- /tests/manual/test_modern5.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, { 8 | a: function () { console.log('a'); }, 9 | z: function () { console.log('a'); } 10 | }); 11 | dcl.chainBefore(A, 'z'); 12 | 13 | var B = dcl(null, { 14 | b: function () { console.log('b'); }, 15 | z: function () { console.log('b'); } 16 | }); 17 | 18 | var C = dcl([A, B], { 19 | c: function () { console.log('c'); }, 20 | z: function () { console.log('c'); } 21 | }); 22 | 23 | var z = new C; 24 | console.log(z); 25 | console.log(z instanceof A, z instanceof B, z instanceof C); 26 | console.log(z.a, z.b, z.c, z.z); 27 | z.a(); 28 | z.b(); 29 | z.c(); 30 | z.z(); 31 | -------------------------------------------------------------------------------- /tests/manual/test_modern6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, { 8 | z: function (x) { 9 | return x + 'a'; 10 | } 11 | }); 12 | 13 | var x = new A; 14 | console.log(x); 15 | console.log(x instanceof A); 16 | console.log(x.z); 17 | console.log(x.z(1)); 18 | 19 | var B = dcl(A, { 20 | z: dcl.superCall(function (sup) { 21 | return function (x) { 22 | return sup.call(this, x + '|>') + '<|' + x; 23 | }; 24 | }) 25 | }); 26 | 27 | x = new A; 28 | console.log(x); 29 | console.log(x instanceof A); 30 | console.log(x.z); 31 | console.log(x.z(1)); 32 | 33 | var y = new B; 34 | console.log(y); 35 | console.log(y instanceof A, y instanceof B); 36 | console.log(y.z); 37 | console.log(y.z(1)); 38 | -------------------------------------------------------------------------------- /tests/manual/test_modern7.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var dcl = require('../modern'); 5 | 6 | 7 | var A = dcl(null, { 8 | a: dcl.advise({ 9 | before: function (x) { console.log('A.a:before', x); }, 10 | after: function (a) { console.log('A.a:after', a[0]); }, 11 | around: function (sup) { 12 | return function (x) { 13 | sup && sup.call(this, x); 14 | console.log('A.a:', x); 15 | }; 16 | } 17 | }), 18 | b: dcl.advise({ 19 | before: function (x) { console.log('A.b:before', x); }, 20 | after: function (a) { console.log('A.b:after', a[0]); }, 21 | around: function (sup) { 22 | return function (x) { console.log('A.b:', x); } 23 | } 24 | }) 25 | }); 26 | 27 | var x = new A; 28 | x.a(1); 29 | x.b(2); 30 | 31 | var B = dcl(A, { 32 | a: dcl.advise({ 33 | before: function (x) { console.log('B.a:before', x); }, 34 | after: function (a) { console.log('B.a:after', a[0]); }, 35 | around: function (sup) { 36 | return function (x) { 37 | sup && sup.call(this, x); 38 | console.log('B.a:', x); 39 | }; 40 | } 41 | }), 42 | b: function (x) { console.log('B.b:', x); } 43 | }); 44 | 45 | var y = new B; 46 | y.a(1); 47 | y.b(2); 48 | -------------------------------------------------------------------------------- /tests/phantom.js: -------------------------------------------------------------------------------- 1 | phantom.onError = function(msg, trace){ 2 | var msgStack = ["PHANTOM ERROR: " + msg]; 3 | if(trace){ 4 | msgStack.push("TRACE:"); 5 | trace.forEach(function(t){ 6 | msgStack.push(" -> " + (t.file || t.sourceURL) + ": " + t.line + 7 | (t.function ? " (in function " + t.function + ")" : "")); 8 | }); 9 | } 10 | console.error(msgStack.join('\n')); 11 | phantom.exit(1); 12 | }; 13 | 14 | var page = require("webpage").create(); 15 | 16 | page.onError = function(msg){ 17 | console.error("ERROR: " + msg); 18 | phantom.exit(1); 19 | }; 20 | 21 | page.onAlert = function(msg){ 22 | console.log("ALERT: " + msg); 23 | }; 24 | page.onConsoleMessage = function(msg){ 25 | console.log(msg); 26 | }; 27 | page.onCallback = function(msg){ 28 | switch(msg){ 29 | case "success": 30 | phantom.exit(0); 31 | break; 32 | case "failure": 33 | phantom.exit(1); 34 | break; 35 | } 36 | } 37 | 38 | var scriptPath = require("system").args[0], 39 | path = require("fs").absolute( 40 | (scriptPath.length && scriptPath.charAt(0) == "/" ? "" : "./") + scriptPath).split("/"); 41 | 42 | path.pop(); 43 | path.push("tests.html"); 44 | 45 | page.open(path.join("/"), function(status){ 46 | if(status !== "success"){ 47 | console.error("ERROR: Can't load a web page."); 48 | phantom.exit(1); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /tests/puppeteer.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const puppeteer = require('puppeteer'); 4 | 5 | (async () => { 6 | const browser = await puppeteer.launch(); 7 | const page = await browser.newPage(); 8 | 9 | page.on('console', msg => console[typeof console[msg.type()] == 'function' ? msg.type() : 'log'](msg.text())); 10 | page.on('error', e => console.error(e)); 11 | 12 | await page.exposeFunction('callPhantom', async text => { 13 | await browser.close(); 14 | switch (text) { 15 | case "success": 16 | process.exit(0); 17 | break; 18 | case "failure": 19 | process.exit(1); 20 | break; 21 | } 22 | }); 23 | 24 | await page.goto('file://' + path.join(__dirname, 'tests.html')); 25 | })(); 26 | -------------------------------------------------------------------------------- /tests/test_accessors.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl'], function (module, unit, dcl) { 3 | 'use strict'; 4 | 5 | // tests 6 | 7 | unit.add(module, [ 8 | function test_defaults (t) { 9 | var A = dcl({ 10 | life: 42, 11 | answer: function () { return this.life; } 12 | }); 13 | 14 | var a = new A(); 15 | 16 | eval(t.TEST('a.life === 42')); 17 | eval(t.TEST('a.answer() === 42')); 18 | eval(t.TEST('Object.getOwnPropertyDescriptor(A.prototype, "life").writable')); 19 | eval(t.TEST('Object.getOwnPropertyDescriptor(A.prototype, "answer").writable')); 20 | eval(t.TEST('Object.getOwnPropertyDescriptor(A.prototype, "constructor").writable')); 21 | 22 | var B = dcl({ 23 | life: 42, 24 | answer: function () { return this.life; } 25 | }, { 26 | writable: false 27 | }); 28 | 29 | var b = new B(); 30 | 31 | eval(t.TEST('b.life === 42')); 32 | eval(t.TEST('b.answer() === 42')); 33 | eval(t.TEST('!Object.getOwnPropertyDescriptor(B.prototype, "life").writable')); 34 | eval(t.TEST('!Object.getOwnPropertyDescriptor(B.prototype, "answer").writable')); 35 | eval(t.TEST('Object.getOwnPropertyDescriptor(B.prototype, "constructor").writable')); 36 | 37 | var C = dcl({ 38 | life: { 39 | value: 42, 40 | writable: false, 41 | configurable: true, 42 | enumerable: true 43 | }, 44 | answer: function () { return this.life; } 45 | }, { 46 | detectProps: true 47 | }); 48 | 49 | var c = new C(); 50 | 51 | eval(t.TEST('c.life === 42')); 52 | eval(t.TEST('c.answer() === 42')); 53 | eval(t.TEST('!Object.getOwnPropertyDescriptor(C.prototype, "life").writable')); 54 | eval(t.TEST('Object.getOwnPropertyDescriptor(C.prototype, "answer").writable')); 55 | eval(t.TEST('Object.getOwnPropertyDescriptor(C.prototype, "constructor").writable')); 56 | 57 | var D = dcl({ 58 | life: 42, 59 | answer: function () { return this.life; } 60 | }, { 61 | writable: function (descriptor, name) { 62 | if (name === 'life') { 63 | descriptor.writable = false; 64 | } 65 | } 66 | }); 67 | 68 | var d = new D(); 69 | 70 | eval(t.TEST('d.life === 42')); 71 | eval(t.TEST('d.answer() === 42')); 72 | eval(t.TEST('!Object.getOwnPropertyDescriptor(D.prototype, "life").writable')); 73 | eval(t.TEST('Object.getOwnPropertyDescriptor(D.prototype, "answer").writable')); 74 | eval(t.TEST('Object.getOwnPropertyDescriptor(D.prototype, "constructor").writable')); 75 | 76 | var E = dcl(dcl.prop({ 77 | life: { 78 | value: 42, 79 | writable: false, 80 | configurable: true, 81 | enumerable: true 82 | }, 83 | answer: { 84 | value: function () { return this.life; }, 85 | writable: true 86 | } 87 | })); 88 | 89 | var e = new E(); 90 | 91 | eval(t.TEST('e.life === 42')); 92 | eval(t.TEST('e.answer() === 42')); 93 | eval(t.TEST('!Object.getOwnPropertyDescriptor(E.prototype, "life").writable')); 94 | eval(t.TEST('Object.getOwnPropertyDescriptor(E.prototype, "answer").writable')); 95 | eval(t.TEST('Object.getOwnPropertyDescriptor(E.prototype, "constructor").writable')); 96 | }, 97 | function test_get_super (t) { 98 | var A = dcl({ 99 | constructor: function () { 100 | this.__value = 0; 101 | }, 102 | get value () { return this.__value; } 103 | }), 104 | B = dcl(A, { 105 | value: dcl.prop({ 106 | get: dcl.superCall(function (sup) { 107 | return function () { 108 | return sup.call(this) + 1; 109 | }; 110 | }) 111 | }) 112 | }), 113 | C = dcl(B, { 114 | get value () { return 42; } 115 | }); 116 | 117 | var a = new A(), b = new B(), c = new C(); 118 | 119 | eval(t.TEST('a.value === 0')); 120 | eval(t.TEST('b.value === 1')); 121 | eval(t.TEST('c.value === 42')); 122 | 123 | a.__value = b.__value = c.__value = 3; 124 | 125 | eval(t.TEST('a.value === 3')); 126 | eval(t.TEST('b.value === 4')); 127 | eval(t.TEST('c.value === 42')); 128 | }, 129 | function test_set_super (t) { 130 | var A = dcl({ 131 | constructor: function () { 132 | this.__value = 0; 133 | }, 134 | set value (x) { this.__value = x; } 135 | }), 136 | B = dcl(A, { 137 | value: dcl.prop({ 138 | set: dcl.superCall(function (sup) { 139 | return function (x) { 140 | sup.call(this, x + 1); 141 | }; 142 | }) 143 | }) 144 | }), 145 | C = dcl(B, { 146 | set value (x) { this.__value = 42; } 147 | }); 148 | 149 | var a = new A(), b = new B(), c = new C(); 150 | 151 | a.value = 5; 152 | eval(t.TEST('a.__value === 5')); 153 | 154 | b.value = 5; 155 | eval(t.TEST('b.__value === 6')); 156 | 157 | c.value = 5; 158 | eval(t.TEST('c.__value === 42')); 159 | }, 160 | function test_get_set_super (t) { 161 | var A = dcl({ 162 | constructor: function () { 163 | this.__value = 0; 164 | }, 165 | get value () { return this.__value; }, 166 | set value (x) { this.__value = x; } 167 | }), 168 | B = dcl(A, { 169 | value: dcl.prop({ 170 | get: dcl.superCall(function (sup) { 171 | return function () { 172 | return sup.call(this) + 1; 173 | }; 174 | }), 175 | set: dcl.superCall(function (sup) { 176 | return function (x) { 177 | sup.call(this, x + 1); 178 | }; 179 | }) 180 | }) 181 | }), 182 | C = dcl(B, { 183 | get value () { return 42; }, 184 | set value (x) { this.__value = 42; } 185 | }); 186 | 187 | var a = new A(), b = new B(), c = new C(); 188 | 189 | eval(t.TEST('a.value === 0 && a.__value === 0')); 190 | a.value = 5; 191 | eval(t.TEST('a.value === 5 && a.__value === 5')); 192 | 193 | eval(t.TEST('b.value === 1 && b.__value === 0')); 194 | b.value = 5; 195 | eval(t.TEST('b.value === 7 && b.__value === 6')); 196 | 197 | eval(t.TEST('c.value === 42 && c.__value === 0')); 198 | c.value = 5; 199 | eval(t.TEST('c.value === 42 && c.__value === 42')); 200 | }, 201 | { 202 | test: function test_get_side_advices (t) { 203 | var A = dcl({ 204 | constructor: function () { 205 | this.__value = 0; 206 | }, 207 | value: dcl.prop({ 208 | get: dcl.advise({ 209 | before: function () { t.info('Ab'); }, 210 | around: function (sup) { 211 | return function () { return this.__value; }; 212 | }, 213 | after: function () { t.info('Aa'); } 214 | }) 215 | }) 216 | }), 217 | B = dcl(A, { 218 | value: dcl.prop({ 219 | get: dcl.advise({ 220 | before: function () { t.info('Bb'); }, 221 | around: function (sup) { 222 | return function () { return sup.call(this) + 1; }; 223 | }, 224 | after: function () { t.info('Ba'); } 225 | }) 226 | }) 227 | }), 228 | C = dcl(B, { 229 | get value () { return 42; } 230 | }); 231 | 232 | var a = new A(), b = new B(), c = new C(); 233 | 234 | t.info('read a'); 235 | t.info(a.value + ''); 236 | t.info('read b'); 237 | t.info(b.value + ''); 238 | t.info('read c'); 239 | t.info(c.value + ''); 240 | }, 241 | logs: [ 242 | 'read a', 'Ab', 'Aa', '0', 243 | 'read b', 'Bb', 'Ab', 'Aa', 'Ba', '1', 244 | 'read c', 'Bb', 'Ab', 'Aa', 'Ba', '42' 245 | ] 246 | }, 247 | { 248 | test: function test_set_side_advices (t) { 249 | var A = dcl({ 250 | constructor: function () { 251 | this.__value = 0; 252 | }, 253 | value: dcl.prop({ 254 | set: dcl.advise({ 255 | before: function () { t.info('Ab'); }, 256 | around: function (sup) { 257 | return function (x) { this.__value = x; }; 258 | }, 259 | after: function () { t.info('Aa'); } 260 | }) 261 | }) 262 | }), 263 | B = dcl(A, { 264 | value: dcl.prop({ 265 | set: dcl.advise({ 266 | before: function () { t.info('Bb'); }, 267 | around: function (sup) { 268 | return function (x) { sup.call(this, x + 1); }; 269 | }, 270 | after: function () { t.info('Ba'); } 271 | }) 272 | }) 273 | }), 274 | C = dcl(B, { 275 | set value (x) { this.__value = 42; } 276 | }); 277 | 278 | var a = new A(), b = new B(), c = new C(); 279 | 280 | t.info('write a'); 281 | a.value = 5; 282 | t.info(a.__value + ''); 283 | t.info('write b'); 284 | b.value = 5; 285 | t.info(b.__value + ''); 286 | t.info('write c'); 287 | c.value = 5; 288 | t.info(c.__value + ''); 289 | }, 290 | logs: [ 291 | 'write a', 'Ab', 'Aa', '5', 292 | 'write b', 'Bb', 'Ab', 'Aa', 'Ba', '6', 293 | 'write c', 'Bb', 'Ab', 'Aa', 'Ba', '42' 294 | ] 295 | }, 296 | { 297 | test: function test_get_set_side_advices (t) { 298 | var A = dcl({ 299 | constructor: function () { 300 | this.__value = 0; 301 | }, 302 | value: dcl.prop({ 303 | get: dcl.advise({ 304 | before: function () { t.info('Abg'); }, 305 | around: function (sup) { 306 | return function () { return this.__value; }; 307 | }, 308 | after: function () { t.info('Aag'); } 309 | }), 310 | set: dcl.advise({ 311 | before: function () { t.info('Ab'); }, 312 | around: function (sup) { 313 | return function (x) { this.__value = x; }; 314 | }, 315 | after: function () { t.info('Aa'); } 316 | }) 317 | }) 318 | }), 319 | B = dcl(A, { 320 | value: dcl.prop({ 321 | get: dcl.advise({ 322 | before: function () { t.info('Bbg'); }, 323 | around: function (sup) { 324 | return function () { return sup.call(this) + 1; }; 325 | }, 326 | after: function () { t.info('Bag'); } 327 | }), 328 | set: dcl.advise({ 329 | before: function () { t.info('Bb'); }, 330 | around: function (sup) { 331 | return function (x) { sup.call(this, x + 1); }; 332 | }, 333 | after: function () { t.info('Ba'); } 334 | }) 335 | }) 336 | }), 337 | C = dcl(B, { 338 | get value () { return 42; }, 339 | set value (x) { this.__value = 42; } 340 | }); 341 | 342 | var a = new A(), b = new B(), c = new C(); 343 | 344 | t.info('read a'); 345 | t.info(a.value + ''); 346 | t.info(a.__value + ''); 347 | t.info('write a'); 348 | a.value = 5; 349 | t.info(a.value + ''); 350 | t.info(a.__value + ''); 351 | 352 | t.info('read b'); 353 | t.info(b.value + ''); 354 | t.info(b.__value + ''); 355 | t.info('write b'); 356 | b.value = 5; 357 | t.info(b.value + ''); 358 | t.info(b.__value + ''); 359 | 360 | t.info('read c'); 361 | t.info(c.value + ''); 362 | t.info(c.__value + ''); 363 | t.info('write c'); 364 | c.value = 5; 365 | t.info(c.value + ''); 366 | t.info(c.__value + ''); 367 | }, 368 | logs: [ 369 | 'read a', 'Abg', 'Aag', '0', '0', 370 | 'write a', 'Ab', 'Aa', 'Abg', 'Aag', '5', '5', 371 | 'read b', 'Bbg', 'Abg', 'Aag', 'Bag', '1', '0', 372 | 'write b', 'Bb', 'Ab', 'Aa', 'Ba', 'Bbg', 'Abg', 'Aag', 'Bag', '7', '6', 373 | 'read c', 'Bbg', 'Abg', 'Aag', 'Bag', '42', '0', 374 | 'write c', 'Bb', 'Ab', 'Aa', 'Ba', 'Bbg', 'Abg', 'Aag', 'Bag', '42', '42' 375 | ] 376 | }, 377 | function test_get_in_super_chain (t) { 378 | var A = dcl({ 379 | m: dcl.prop({ 380 | get: function () { 381 | return this.up ? this.m3 : this.m2; 382 | } 383 | }), 384 | m2: function (x) { 385 | return 2 * x; 386 | }, 387 | m3: function (x) { 388 | return 3 * x; 389 | } 390 | }), 391 | B = dcl(A, { 392 | m: dcl.superCall(function (sup) { 393 | return function (x) { 394 | if (sup) { 395 | return sup.call(this, x + 1); 396 | } 397 | return 0; 398 | }; 399 | }) 400 | }); 401 | 402 | var a = new A(), b = new B(); 403 | 404 | eval(t.TEST('a.m(5) === 10')); 405 | a.up = true; 406 | eval(t.TEST('a.m(5) === 15')); 407 | 408 | eval(t.TEST('b.m(5) === 12')); 409 | b.up = true; 410 | eval(t.TEST('b.m(5) === 18')); 411 | }, 412 | function test_super_in_get_chain (t) { 413 | var A = dcl({ 414 | m: function (x) { 415 | return x + 1; 416 | } 417 | }), 418 | B = dcl(A, { 419 | m: dcl.prop({ 420 | get: dcl.superCall(function (sup) { 421 | return function () { 422 | if (sup) { 423 | return function (x) { 424 | return sup.call(this).call(this, 2 * x); 425 | }; 426 | } 427 | return function (x) { 428 | return x + 2; 429 | }; 430 | }; 431 | }) 432 | }) 433 | }); 434 | 435 | var a = new A(), b = new B(); 436 | 437 | eval(t.TEST('a.m(5) === 6')); 438 | eval(t.TEST('b.m(5) === 11')); 439 | }, 440 | { 441 | test: function test_get_super_side_advices (t) { 442 | var A = dcl({ 443 | m: dcl.advise({ 444 | before: function (x) { 445 | t.info('A.m:before - ' + x); 446 | }, 447 | after: function (_, x) { 448 | t.info('A.m:after - ' + x); 449 | }, 450 | around: function (sup) { 451 | return function (x) { 452 | t.info('A.m:around - ' + x); 453 | return x + 1; 454 | }; 455 | } 456 | }) 457 | }), 458 | B = dcl(A, { 459 | m: dcl.prop({ 460 | get: dcl.advise({ 461 | before: function (x) { 462 | t.info('B.m:before'); 463 | }, 464 | after: function (_, x) { 465 | t.info('B.m:after'); 466 | }, 467 | around: function (sup) { 468 | return function () { 469 | t.info('B.m:around'); 470 | return function (x) { 471 | return sup.call(this).call(this, x + 1); 472 | }; 473 | }; 474 | } 475 | }) 476 | }) 477 | }), 478 | C = dcl(B, { 479 | m: dcl.advise({ 480 | before: function (x) { 481 | t.info('C.m:before - ' + x); 482 | }, 483 | after: function (_, x) { 484 | t.info('C.m:after - ' + x); 485 | }, 486 | around: function (sup) { 487 | return function (x) { 488 | t.info('C.m:around - ' + x); 489 | return sup.call(this, x + 1); 490 | }; 491 | } 492 | }) 493 | }); 494 | 495 | var a = new A(), b = new B(), c = new C(); 496 | 497 | t.info('A'); 498 | t.info('Result: ' + a.m(5)); 499 | 500 | t.info('B'); 501 | t.info('Result: ' + b.m(5)); 502 | 503 | t.info('C'); 504 | t.info('Result: ' + c.m(5)); 505 | }, 506 | logs: [ 507 | 'A', 'A.m:before - 5', 'A.m:around - 5', 'A.m:after - 6', 'Result: 6', 508 | 'B', 'B.m:before', 'B.m:around', 'B.m:after', 'A.m:around - 6', 'Result: 7', 509 | 'C', 'C.m:before - 5', 'B.m:before', 'A.m:before - 5', 510 | 'C.m:around - 5', 'B.m:around', 'A.m:around - 7', 511 | 'A.m:after - 8', 'B.m:after', 'C.m:after - 8', 'Result: 8' 512 | ] 513 | } 514 | ]); 515 | 516 | return {}; 517 | }); 518 | -------------------------------------------------------------------------------- /tests/test_advices.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../advise', '../advices/counter', '../advices/flow', 3 | '../advices/time', '../advices/memoize', '../advices/trace'], 4 | function (module, unit, dcl, advise, counter, flow, time, memoize, trace) { 5 | 'use strict'; 6 | 7 | var Ackermann = dcl(null, { 8 | declaredName: 'Ackermann', 9 | m0: function (n) { 10 | return n + 1; 11 | }, 12 | n0: function (m) { 13 | return this.a(m - 1, 1); 14 | }, 15 | a: function (m, n) { 16 | if (m == 0) { 17 | return this.m0(n); 18 | } 19 | if (n == 0) { 20 | return this.n0(m); 21 | } 22 | return this.a(m - 1, this.a(m, n - 1)); 23 | } 24 | }); 25 | 26 | // tests 27 | 28 | unit.add(module, [ 29 | function test_counter(t){ 30 | var x = new Ackermann(); 31 | 32 | var counterM0 = counter(); 33 | advise(x, 'm0', counterM0.advice()); 34 | 35 | var counterN0 = counter(); 36 | advise(x, 'n0', counterN0.advice()); 37 | 38 | var counterA = counter(); 39 | advise(x, 'a', counterA.advice()); 40 | 41 | x.a(3, 2); 42 | 43 | eval(t.TEST("counterM0.calls === 258")); 44 | eval(t.TEST("counterM0.errors === 0")); 45 | eval(t.TEST("counterN0.calls === 26")); 46 | eval(t.TEST("counterN0.errors === 0")); 47 | eval(t.TEST("counterA .calls === 541")); 48 | eval(t.TEST("counterA .errors === 0")); 49 | }, 50 | { 51 | test: function test_flow(t){ 52 | // our advised version: 53 | var AdvisedAckermann = dcl(Ackermann, { 54 | declaredName: 'AdvisedAckermann', 55 | m0: dcl.advise(flow.advice('m0')), 56 | n0: dcl.advise(flow.advice('n0')), 57 | a: dcl.advise(flow.advice('a')) 58 | }); 59 | 60 | // our instrumented version: 61 | var InstrumentedAckermann = dcl(AdvisedAckermann, { 62 | declaredName: 'InstrumentedAckermann', 63 | m0: dcl.around(function (sup) { 64 | return function (n) { 65 | t.info('a() called: ' + (flow.inFlowOf('a') || 0)); 66 | t.info('n0() called: ' + (flow.inFlowOf('n0') || 0)); 67 | var stack = flow.getStack(); 68 | var previous = stack[stack.length - 2] || '(none)'; 69 | t.info('m0() called from: ' + previous); 70 | return sup.call(this, n); 71 | } 72 | }) 73 | }); 74 | 75 | var x = new InstrumentedAckermann(); 76 | x.a(1, 1); 77 | }, 78 | logs: [ 79 | {text: 'a() called: 3'}, 80 | {text: 'n0() called: 1'}, 81 | {text: 'm0() called from: a'}, 82 | {text: 'a() called: 2'}, 83 | {text: 'n0() called: 0'}, 84 | {text: 'm0() called from: a'} 85 | ] 86 | }, 87 | function test_memoize(t){ 88 | // TODO: redirect console to ice 89 | 90 | if(console.time && console.timeEnd){ 91 | var x = new Ackermann(); 92 | 93 | advise(x, 'a', time('x.a')); 94 | 95 | x.a(3, 3); 96 | x.a(3, 3); 97 | 98 | var y = new Ackermann(); 99 | 100 | advise(y, 'm0', memoize.advice('m0')); 101 | advise(y, 'n0', memoize.advice('n0')); 102 | advise(y, 'a', memoize.advice('a', function (self, args) { 103 | return args[0] + '-' + args[1]; 104 | })); 105 | 106 | advise(y, 'a', time('y.a')); 107 | 108 | y.a(3, 3); 109 | y.a(3, 3); 110 | } 111 | }, 112 | function test_trace(t){ 113 | // TODO: redirect console to ice 114 | 115 | // our instance: 116 | var x = new Ackermann(); 117 | 118 | advise(x, 'm0', trace('m0', true)); 119 | advise(x, 'n0', trace('n0', true)); 120 | advise(x, 'a', trace('a', true)); 121 | 122 | x.a(1, 1); 123 | } 124 | ]); 125 | 126 | return {}; 127 | }); 128 | -------------------------------------------------------------------------------- /tests/test_advise.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../advise'], function (module, unit, dcl, advise) { 3 | 'use strict'; 4 | 5 | // tests 6 | 7 | unit.add(module, [ 8 | function test_dclAdvise_with_advise (t) { 9 | 'use strict'; 10 | 11 | var A = dcl(null, { 12 | m1: dcl.advise({ 13 | before: function () { 14 | if (!this.a) { this.a = ''; } 15 | this.a += 'b'; 16 | }, 17 | after: function () { 18 | if (!this.a) { this.a = ''; } 19 | this.a += 'a'; 20 | } 21 | }) 22 | }); 23 | var B = dcl(A, { 24 | m1: function (x) { 25 | if (!this.a) { this.a = ''; } 26 | this.a += x; 27 | } 28 | }); 29 | 30 | var x = new B; 31 | x.m1('-'); 32 | eval(t.TEST('x.a === "b-a"')); 33 | 34 | var h1 = advise(x, 'm1', { 35 | after: function () { 36 | if (!this.a) { this.a = ''; } 37 | this.a += 'A1'; 38 | } 39 | }); 40 | x.a = ''; 41 | x.m1('-'); 42 | eval(t.TEST('x.a === "b-aA1"')); 43 | 44 | var h2 = advise(x, 'm1', { 45 | before: function () { 46 | if (!this.a) { this.a = ''; } 47 | this.a += 'B1'; 48 | } 49 | }); 50 | x.a = ''; 51 | x.m1('-'); 52 | eval(t.TEST('x.a === "B1b-aA1"')); 53 | 54 | var h3 = advise(x, 'm1', { 55 | around: function (sup) { 56 | return function () { 57 | if (!this.a) { this.a = ''; } 58 | this.a += 'F1'; 59 | if (sup) { sup.apply(this, arguments); } 60 | this.a += 'F2'; 61 | }; 62 | } 63 | }); 64 | x.a = ''; 65 | x.m1('-'); 66 | eval(t.TEST('x.a === "B1bF1-F2aA1"')); 67 | 68 | h1.unadvise(); 69 | x.a = ''; 70 | x.m1('-'); 71 | eval(t.TEST('x.a === "B1bF1-F2a"')); 72 | 73 | h2.unadvise(); 74 | x.a = ''; 75 | x.m1('-'); 76 | eval(t.TEST('x.a === "bF1-F2a"')); 77 | 78 | h3.unadvise(); 79 | x.a = ''; 80 | x.m1('-'); 81 | eval(t.TEST('x.a === "b-a"')); 82 | }, 83 | function test_advise (t) { 84 | 'use strict'; 85 | 86 | var x = new (dcl(null, { 87 | constructor: function () { 88 | this.a = ''; 89 | }, 90 | m1: function () { 91 | this.a += '*'; 92 | } 93 | })); 94 | 95 | x.m1(); 96 | eval(t.TEST('x.a === "*"')); 97 | 98 | var h1 = advise(x, 'm1', { 99 | around: function (sup) { 100 | return function () { 101 | this.a += 'b1'; 102 | sup.call(this); 103 | this.a += 'a1'; 104 | }; 105 | } 106 | }); 107 | 108 | x.a = ''; 109 | x.m1(); 110 | eval(t.TEST('x.a === "b1*a1"')); 111 | 112 | var h2 = advise(x, 'm1', { 113 | around: function (sup) { 114 | return function () { 115 | this.a += 'b2'; 116 | sup.call(this); 117 | this.a += 'a2'; 118 | }; 119 | } 120 | }); 121 | 122 | x.a = ''; 123 | x.m1(); 124 | eval(t.TEST('x.a === "b2b1*a1a2"')); 125 | 126 | var h3 = advise(x, 'm1', { 127 | around: function (sup) { 128 | return function () { 129 | this.a += 'b3'; 130 | sup.call(this); 131 | this.a += 'a3'; 132 | }; 133 | } 134 | }); 135 | 136 | x.a = ''; 137 | x.m1(); 138 | eval(t.TEST('x.a === "b3b2b1*a1a2a3"')); 139 | 140 | var h4 = advise(x, 'm1', { 141 | around: function (sup) { 142 | return function () { 143 | this.a += 'b4'; 144 | sup.call(this); 145 | this.a += 'a4'; 146 | }; 147 | } 148 | }); 149 | 150 | x.a = ''; 151 | x.m1(); 152 | eval(t.TEST('x.a === "b4b3b2b1*a1a2a3a4"')); 153 | 154 | h2.unadvise(); 155 | x.a = ''; 156 | x.m1(); 157 | eval(t.TEST('x.a === "b4b3b1*a1a3a4"')); 158 | 159 | h1.unadvise(); 160 | x.a = ''; 161 | x.m1(); 162 | eval(t.TEST('x.a === "b4b3*a3a4"')); 163 | 164 | h3.unadvise(); 165 | x.a = ''; 166 | x.m1(); 167 | eval(t.TEST('x.a === "b4*a4"')); 168 | 169 | h4.unadvise(); 170 | x.a = ''; 171 | x.m1(); 172 | eval(t.TEST('x.a === "*"')); 173 | } 174 | ]); 175 | 176 | return {}; 177 | }); 178 | -------------------------------------------------------------------------------- /tests/test_advise_accessors.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../advise'], function (module, unit, dcl, advise) { 3 | 'use strict'; 4 | 5 | // tests 6 | 7 | unit.add(module, [ 8 | { 9 | test: function test_simple (t) { 10 | 'use strict'; 11 | 12 | var A = dcl({ 13 | constructor: function () { this.__value = 0; }, 14 | get m () { return this.__value; }, 15 | set m (v) { this.__value = v; } 16 | }); 17 | 18 | var a = new A(); 19 | t.info('interrogate a'); 20 | t.info('' + a.m); 21 | a.m = 5; 22 | t.info('' + a.m); 23 | 24 | var h = advise(a, 'm', { 25 | get: { 26 | before: function () { t.info('Xbg'); }, 27 | after: function () { t.info('Xag'); }, 28 | around: function (sup) { 29 | return function () { 30 | return sup.call(this) + 1; 31 | }; 32 | } 33 | }, 34 | set: { 35 | before: function () { t.info('Xbs'); }, 36 | after: function () { t.info('Xas'); }, 37 | around: function (sup) { 38 | return function (v) { 39 | sup.call(this, v + 1); 40 | }; 41 | } 42 | } 43 | }); 44 | 45 | t.info('interrogate a'); 46 | t.info('' + a.m); 47 | a.m = 5; 48 | t.info('' + a.m); 49 | 50 | h.unadvise(); 51 | 52 | t.info('interrogate a'); 53 | t.info('' + a.m); 54 | a.m = 5; 55 | t.info('' + a.m); 56 | }, 57 | logs: [ 58 | 'interrogate a', '0', '5', 59 | 'interrogate a', 'Xbg', 'Xag', '6', 'Xbs', 'Xas', 'Xbg', 'Xag', '7', 60 | 'interrogate a', '6', '5' 61 | ] 62 | }, 63 | { 64 | test: function test_AB (t) { 65 | 'use strict'; 66 | 67 | var A = dcl({ 68 | constructor: function () { this.__value = 0; }, 69 | get m () { return this.__value; }, 70 | set m (v) { this.__value = v; } 71 | }); 72 | 73 | var B = dcl(A, { 74 | m: dcl.prop({ 75 | get: dcl.advise({ 76 | before: function () { t.info('Bbg'); }, 77 | after: function () { t.info('Bag'); }, 78 | around: function (sup) { 79 | return function () { 80 | return sup.call(this) + 1; 81 | }; 82 | } 83 | }), 84 | set: dcl.advise({ 85 | before: function () { t.info('Bbs'); }, 86 | after: function () { t.info('Bas'); }, 87 | around: function (sup) { 88 | return function (v) { 89 | sup.call(this, v + 1); 90 | }; 91 | } 92 | }) 93 | }) 94 | }); 95 | 96 | var b = new B(); 97 | t.info('interrogate b'); 98 | t.info('' + b.m); 99 | b.m = 5; 100 | t.info('' + b.m); 101 | 102 | var h = advise(b, 'm', { 103 | get: { 104 | before: function () { t.info('Xbg'); }, 105 | after: function () { t.info('Xag'); }, 106 | around: function (sup) { 107 | return function () { 108 | return sup.call(this) + 1; 109 | }; 110 | } 111 | }, 112 | set: { 113 | before: function () { t.info('Xbs'); }, 114 | after: function () { t.info('Xas'); }, 115 | around: function (sup) { 116 | return function (v) { 117 | sup.call(this, v + 1); 118 | }; 119 | } 120 | } 121 | }); 122 | 123 | t.info('interrogate b'); 124 | t.info('' + b.m); 125 | b.m = 5; 126 | t.info('' + b.m); 127 | 128 | h.unadvise(); 129 | 130 | t.info('interrogate b'); 131 | t.info('' + b.m); 132 | b.m = 5; 133 | t.info('' + b.m); 134 | }, 135 | logs: [ 136 | 'interrogate b', 'Bbg', 'Bag', '1', 'Bbs', 'Bas', 'Bbg', 'Bag', '7', 137 | 'interrogate b', 138 | 'Xbg', 'Bbg', 'Bag', 'Xag', '8', 139 | 'Xbs', 'Bbs', 'Bas', 'Xas', 140 | 'Xbg', 'Bbg', 'Bag', 'Xag', '9', 141 | 'interrogate b', 'Bbg', 'Bag', '8', 'Bbs', 'Bas', 'Bbg', 'Bag', '7' 142 | ] 143 | }, 144 | { 145 | test: function test_12 (t) { 146 | 'use strict'; 147 | 148 | var A = dcl({ 149 | constructor: function () { this.__value = 0; }, 150 | get m () { return this.__value; }, 151 | set m (v) { this.__value = v; } 152 | }); 153 | 154 | var a = new A(); 155 | t.info('interrogate a'); 156 | t.info('' + a.m); 157 | a.m = 5; 158 | t.info('' + a.m); 159 | 160 | var h1 = advise(a, 'm', { 161 | get: { 162 | before: function () { t.info('Xbg'); }, 163 | after: function () { t.info('Xag'); }, 164 | around: function (sup) { 165 | return function () { 166 | return sup.call(this) + 1; 167 | }; 168 | } 169 | }, 170 | set: { 171 | before: function () { t.info('Xbs'); }, 172 | after: function () { t.info('Xas'); }, 173 | around: function (sup) { 174 | return function (v) { 175 | sup.call(this, v + 1); 176 | }; 177 | } 178 | } 179 | }); 180 | 181 | t.info('interrogate a'); 182 | t.info('' + a.m); 183 | a.m = 5; 184 | t.info('' + a.m); 185 | 186 | var h2 = advise(a, 'm', { 187 | get: { 188 | before: function () { t.info('Ybg'); }, 189 | after: function () { t.info('Yag'); }, 190 | around: function (sup) { 191 | return function () { 192 | return sup.call(this) + 1; 193 | }; 194 | } 195 | }, 196 | set: { 197 | before: function () { t.info('Ybs'); }, 198 | after: function () { t.info('Yas'); }, 199 | around: function (sup) { 200 | return function (v) { 201 | sup.call(this, v + 1); 202 | }; 203 | } 204 | } 205 | }); 206 | 207 | t.info('interrogate a'); 208 | t.info('' + a.m); 209 | a.m = 5; 210 | t.info('' + a.m); 211 | 212 | h2.unadvise(); 213 | 214 | t.info('interrogate a'); 215 | t.info('' + a.m); 216 | a.m = 5; 217 | t.info('' + a.m); 218 | 219 | h1.unadvise(); 220 | 221 | t.info('interrogate a'); 222 | t.info('' + a.m); 223 | a.m = 5; 224 | t.info('' + a.m); 225 | }, 226 | logs: [ 227 | 'interrogate a', '0', '5', 228 | 'interrogate a', 229 | 'Xbg', 'Xag', '6', 'Xbs', 'Xas', 'Xbg', 'Xag', '7', 230 | 'interrogate a', 231 | 'Ybg', 'Xbg', 'Xag', 'Yag', '8', 232 | 'Ybs', 'Xbs', 'Xas', 'Yas', 233 | 'Ybg', 'Xbg', 'Xag', 'Yag', '9', 234 | 'interrogate a', 235 | 'Xbg', 'Xag', '8', 'Xbs', 'Xas', 'Xbg', 'Xag', '7', 236 | 'interrogate a', '6', '5' 237 | ] 238 | }, 239 | { 240 | test: function test_21 (t) { 241 | 'use strict'; 242 | 243 | var A = dcl({ 244 | constructor: function () { this.__value = 0; }, 245 | get m () { return this.__value; }, 246 | set m (v) { this.__value = v; } 247 | }); 248 | 249 | var a = new A(); 250 | t.info('interrogate a'); 251 | t.info('' + a.m); 252 | a.m = 5; 253 | t.info('' + a.m); 254 | 255 | var h1 = advise(a, 'm', { 256 | get: { 257 | before: function () { t.info('Xbg'); }, 258 | after: function () { t.info('Xag'); }, 259 | around: function (sup) { 260 | return function () { 261 | return sup.call(this) + 1; 262 | }; 263 | } 264 | }, 265 | set: { 266 | before: function () { t.info('Xbs'); }, 267 | after: function () { t.info('Xas'); }, 268 | around: function (sup) { 269 | return function (v) { 270 | sup.call(this, v + 1); 271 | }; 272 | } 273 | } 274 | }); 275 | 276 | t.info('interrogate a'); 277 | t.info('' + a.m); 278 | a.m = 5; 279 | t.info('' + a.m); 280 | 281 | var h2 = advise(a, 'm', { 282 | get: { 283 | before: function () { t.info('Ybg'); }, 284 | after: function () { t.info('Yag'); }, 285 | around: function (sup) { 286 | return function () { 287 | return sup.call(this) + 1; 288 | }; 289 | } 290 | }, 291 | set: { 292 | before: function () { t.info('Ybs'); }, 293 | after: function () { t.info('Yas'); }, 294 | around: function (sup) { 295 | return function (v) { 296 | sup.call(this, v + 1); 297 | }; 298 | } 299 | } 300 | }); 301 | 302 | t.info('interrogate a'); 303 | t.info('' + a.m); 304 | a.m = 5; 305 | t.info('' + a.m); 306 | 307 | h1.unadvise(); 308 | 309 | t.info('interrogate a'); 310 | t.info('' + a.m); 311 | a.m = 5; 312 | t.info('' + a.m); 313 | 314 | h2.unadvise(); 315 | 316 | t.info('interrogate a'); 317 | t.info('' + a.m); 318 | a.m = 5; 319 | t.info('' + a.m); 320 | }, 321 | logs: [ 322 | 'interrogate a', '0', '5', 323 | 'interrogate a', 324 | 'Xbg', 'Xag', '6', 'Xbs', 'Xas', 'Xbg', 'Xag', '7', 325 | 'interrogate a', 326 | 'Ybg', 'Xbg', 'Xag', 'Yag', '8', 327 | 'Ybs', 'Xbs', 'Xas', 'Yas', 328 | 'Ybg', 'Xbg', 'Xag', 'Yag', '9', 329 | 'interrogate a', 330 | 'Ybg', 'Yag', '8', 'Ybs', 'Yas', 'Ybg', 'Yag', '7', 331 | 'interrogate a', '6', '5' 332 | ] 333 | } 334 | ]); 335 | 336 | return {}; 337 | }); 338 | -------------------------------------------------------------------------------- /tests/test_bases.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=='function'&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../bases/Mixer', '../bases/Replacer'], 3 | function (module, unit, dcl, Mixer, Replacer) { 4 | 'use strict'; 5 | 6 | // tests 7 | 8 | unit.add(module, [ 9 | function test_Mixer (t) { 10 | var f = function () {}; 11 | 12 | var A = dcl(Mixer, { 13 | declaredClass: 'A', 14 | a: 1, 15 | b: 'two', 16 | c: null, 17 | d: f 18 | }); 19 | 20 | var x = new A({ 21 | a: 5, 22 | b: false, 23 | f: f 24 | }); 25 | 26 | eval(t.TEST('x.a === 5')); 27 | eval(t.TEST('x.b === false')); 28 | eval(t.TEST('x.c === null')); 29 | eval(t.TEST('x.d === f')); 30 | eval(t.TEST('x.f === f')); 31 | }, 32 | function test_Replacer (t) { 33 | var f = function () {}; 34 | 35 | var A = dcl(Replacer, { 36 | declaredClass: 'A', 37 | a: 1, 38 | b: 'two', 39 | c: null, 40 | d: f 41 | }); 42 | 43 | var x = new A({ 44 | a: 5, 45 | b: false, 46 | f: f 47 | }); 48 | 49 | eval(t.TEST('x.a === 5')); 50 | eval(t.TEST('x.b === false')); 51 | eval(t.TEST('x.c === null')); 52 | eval(t.TEST('x.d === f')); 53 | eval(t.TEST('!("f" in x)')); 54 | }, 55 | function test_Replacer_with_mixins (t) { 56 | var A = dcl(Replacer, {a: 0}), 57 | B = dcl(Replacer, {b: 0}), 58 | C = dcl([Replacer, A, B]); 59 | 60 | var x = new C({a: 1, b: 2, c: 3}); 61 | 62 | eval(t.TEST('x.a === 1')); 63 | eval(t.TEST('x.b === 2')); 64 | eval(t.TEST('!("c" in x)')); 65 | } 66 | ]); 67 | 68 | return {}; 69 | }); 70 | -------------------------------------------------------------------------------- /tests/test_dcl.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl'], function (module, unit, dcl) { 3 | 'use strict'; 4 | 5 | // tests 6 | 7 | unit.add(module, [ 8 | function test_chaining (t) { 9 | var A = dcl(null, {}); 10 | dcl.chainBefore(A, 'm1'); 11 | dcl.chainAfter (A, 'm2'); 12 | 13 | var B = dcl(null, { 14 | m1: function () { 15 | if (!this.b) { this.b = ''; } 16 | this.b += 'B'; 17 | }, 18 | m2: function () { 19 | if (!this.c) { this.c = ''; } 20 | this.c += 'B'; 21 | } 22 | }); 23 | 24 | var C = dcl(null, { 25 | m1: function () { 26 | if (!this.b) { this.b = ''; } 27 | this.b += 'C'; 28 | }, 29 | m2: function () { 30 | if (!this.c) { this.c = ''; } 31 | this.c += 'C'; 32 | } 33 | }); 34 | 35 | var D = dcl(null, { 36 | m1: function () { 37 | if (!this.b) { this.b = ''; } 38 | this.b += 'D'; 39 | }, 40 | m2: function () { 41 | if (!this.c) { this.c = ''; } 42 | this.c += 'D'; 43 | } 44 | }); 45 | 46 | var ABCD = dcl([A, B, C, D], {}); 47 | var x = new ABCD; 48 | x.m1(); 49 | x.m2(); 50 | 51 | eval(t.TEST('x.b === "DCB"')); 52 | eval(t.TEST('x.c === "BCD"')); 53 | }, 54 | function test_chain_with_super (t) { 55 | var A = dcl(null, { 56 | declaredClass: 'A', 57 | constructor: function () { 58 | this.reset(); 59 | this.flag = true; 60 | }, 61 | reset: function () { 62 | this.b = this.c = ''; 63 | }, 64 | m1: function () { 65 | this.b += 'A'; 66 | }, 67 | m2: function () { 68 | this.c += 'A'; 69 | } 70 | }); 71 | dcl.chainBefore(A, 'm1'); 72 | dcl.chainAfter (A, 'm2'); 73 | 74 | var B = dcl(null, { 75 | declaredClass: 'B', 76 | m1: function () { 77 | this.b += 'B'; 78 | }, 79 | m2: function () { 80 | this.c += 'B'; 81 | } 82 | }); 83 | 84 | var C = dcl(null, { 85 | declaredClass: 'C', 86 | m1: dcl.superCall(function (sup) { 87 | return function () { 88 | this.b += 'Cb'; 89 | if (this.flag && sup) { sup.call(this); } 90 | this.b += 'Ca'; 91 | }; 92 | }), 93 | m2: dcl.superCall(function (sup) { 94 | return function () { 95 | this.c += 'Cb'; 96 | if (this.flag && sup) { sup.call(this); } 97 | this.c += 'Ca'; 98 | }; 99 | }) 100 | }); 101 | 102 | var D = dcl(null, { 103 | declaredClass: 'D', 104 | m1: function () { 105 | this.b += 'D'; 106 | }, 107 | m2: function () { 108 | this.c += 'D'; 109 | } 110 | }); 111 | 112 | var E = dcl(null, { 113 | declaredClass: 'E', 114 | m1: function () { 115 | this.b += 'E'; 116 | }, 117 | m2: function () { 118 | this.c += 'E'; 119 | } 120 | }); 121 | 122 | var x = new (dcl([A, B, C, D, E], {})); 123 | x.m1(); 124 | x.m2(); 125 | 126 | eval(t.TEST('x.b === "EDCbBACa"')); 127 | eval(t.TEST('x.c === "CbABCaDE"')); 128 | 129 | x.reset(); 130 | x.flag = false; 131 | x.m1(); 132 | x.m2(); 133 | 134 | eval(t.TEST('x.b === "EDCbCa"')); 135 | eval(t.TEST('x.c === "CbCaDE"')); 136 | }, 137 | function test_isInstanceOf (t) { 138 | var A = dcl(null, {}); 139 | var B = dcl(null, {}); 140 | var C = dcl(null, {}); 141 | var D = dcl(null, {}); 142 | 143 | var AC = dcl([A, C], {}); 144 | var BD = dcl([B, D], {}); 145 | 146 | var x = new AC, y = new BD; 147 | 148 | eval(t.TEST('dcl.isInstanceOf(x, A)')); 149 | eval(t.TEST('!dcl.isInstanceOf(x, B)')); 150 | eval(t.TEST('dcl.isInstanceOf(x, C)')); 151 | eval(t.TEST('!dcl.isInstanceOf(x, D)')); 152 | 153 | eval(t.TEST('!dcl.isInstanceOf(y, A)')); 154 | eval(t.TEST('dcl.isInstanceOf(y, B)')); 155 | eval(t.TEST('!dcl.isInstanceOf(y, C)')); 156 | eval(t.TEST('dcl.isInstanceOf(y, D)')); 157 | }, 158 | function test_postscript (t) { 159 | var A = dcl(null, { 160 | constructor: dcl.advise({ 161 | around: function (sup) { 162 | return function () { 163 | if (!this.a) { this.a = ''; } 164 | this.a += 'A'; 165 | }; 166 | }, 167 | after: function () { 168 | this.postscript(); 169 | } 170 | }), 171 | postscript: function () { 172 | this.b = 'A'; 173 | } 174 | }); 175 | 176 | var B = dcl(null, { 177 | constructor: function () { 178 | if (!this.a) { this.a = ''; } 179 | this.a += 'B'; 180 | }, 181 | postscript: function () { 182 | this.b = 'B'; 183 | } 184 | }); 185 | 186 | var C = dcl(null, { 187 | constructor: function () { 188 | if (!this.a) { this.a = ''; } 189 | this.a += 'C'; 190 | }, 191 | postscript: function () { 192 | this.b = 'C'; 193 | } 194 | }); 195 | 196 | var x = new (dcl(A, {})); 197 | eval(t.TEST('x.a === "A"')); 198 | eval(t.TEST('x.b === "A"')); 199 | 200 | var y = new (dcl([A, B], {})); 201 | eval(t.TEST('y.a === "AB"')); 202 | eval(t.TEST('y.b === "B"')); 203 | 204 | var z = new (dcl([A, B, C], {})); 205 | eval(t.TEST('z.a === "ABC"')); 206 | eval(t.TEST('z.b === "C"')); 207 | }, 208 | function test_postscript2 (t) { 209 | var A = dcl(null, { 210 | constructor: dcl.advise({ 211 | around: function (sup) { 212 | return function () { 213 | if (!this.a) { this.a = ''; } 214 | this.a += 'A'; 215 | }; 216 | }, 217 | after: function () { 218 | this.postscript(); 219 | } 220 | }), 221 | postscript: function () { 222 | if (!this.a) { this.a = ''; } 223 | this.a += 'P'; 224 | } 225 | }); 226 | 227 | var B = dcl(null, { 228 | constructor: function () { 229 | if (!this.a) { this.a = ''; } 230 | this.a += 'B'; 231 | } 232 | }); 233 | 234 | var C = dcl(null, { 235 | constructor: function () { 236 | if (!this.a) { this.a = ''; } 237 | this.a += 'C'; 238 | } 239 | }); 240 | 241 | var x = new (dcl(A, {})); 242 | eval(t.TEST('x.a === "AP"')); 243 | 244 | var y = new (dcl([A, B], {})); 245 | eval(t.TEST('y.a === "ABP"')); 246 | 247 | var z = new (dcl([A, B, C], {})); 248 | eval(t.TEST('z.a === "ABCP"')); 249 | 250 | var m = new (dcl([B, A], {})); 251 | eval(t.TEST('m.a === "AP"')); 252 | 253 | var n = new (dcl([C, A, B], {})); 254 | eval(t.TEST('n.a === "ABP"')); 255 | }, 256 | function test_advise (t) { 257 | var A = dcl(null, { 258 | m1: dcl.advise({ 259 | after: function () { 260 | if (!this.a) { this.a = ''; } 261 | this.a += 'Aa'; 262 | } 263 | }) 264 | }); 265 | var B = dcl(null, { 266 | m1: dcl.advise({ 267 | before: function () { 268 | if (!this.a) { this.a = ''; } 269 | this.a += 'Bb'; 270 | } 271 | }) 272 | }); 273 | var C = dcl(null, { 274 | m1: dcl.superCall(function (sup) { 275 | return function () { 276 | if (!this.a) { this.a = ''; } 277 | this.a += 'Cfb'; 278 | if (sup) { 279 | sup.apply(this, arguments); 280 | } 281 | this.a += 'Cfa'; 282 | }; 283 | }) 284 | }); 285 | var D = dcl(null, { 286 | m1: dcl.advise({ 287 | before: function () { 288 | if (!this.a) { this.a = ''; } 289 | this.a += 'Db'; 290 | }, 291 | around: function (sup) { 292 | return function () { 293 | if (!this.a) { this.a = ''; } 294 | this.a += 'Dfb'; 295 | if (sup) { 296 | sup.apply(this, arguments); 297 | } 298 | this.a += 'Dfa'; 299 | }; 300 | }, 301 | after: function () { 302 | if (!this.a) { this.a = ''; } 303 | this.a += 'Da'; 304 | } 305 | }) 306 | }); 307 | var E = dcl(null, { 308 | m1: function () { 309 | if (!this.a) { this.a = ''; } 310 | this.a += 'E'; 311 | } 312 | }); 313 | 314 | var x = new (dcl([E, A, B, C, D], {})); 315 | x.m1(); 316 | eval(t.TEST('x.a === "DbBbDfbCfbECfaDfaAaDa"')); 317 | 318 | var y = new (dcl([A, B, C, D, E], {})); 319 | y.m1(); 320 | eval(t.TEST('y.a === "DbBbEAaDa"')); 321 | }, 322 | function test_advise2 (t) { 323 | var A = dcl(null, { 324 | m1: dcl.advise({ 325 | before: function () { 326 | if (!this.a) { this.a = ''; } 327 | this.a += 'Ab'; 328 | }, 329 | after: function () { 330 | if (!this.a) { this.a = ''; } 331 | this.a += 'Aa'; 332 | } 333 | }) 334 | }); 335 | var B = dcl(null, { 336 | m1: dcl.advise({ 337 | before: function () { 338 | if (!this.a) { this.a = ''; } 339 | this.a += 'Bb'; 340 | }, 341 | after: function () { 342 | if (!this.a) { this.a = ''; } 343 | this.a += 'Ba'; 344 | } 345 | }) 346 | }); 347 | 348 | var x = new (dcl([A, B], {})); 349 | x.m1(); 350 | eval(t.TEST('x.a === "BbAbAaBa"')); 351 | 352 | var y = new (dcl([B, A], {})); 353 | y.m1(); 354 | eval(t.TEST('y.a === "AbBbBaAa"')); 355 | } 356 | ]); 357 | 358 | return {}; 359 | }); 360 | -------------------------------------------------------------------------------- /tests/test_debug.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../debug'], function(module, unit, dcl){ 3 | 4 | 'use strict'; 5 | 6 | // tests 7 | 8 | unit.add(module, [ 9 | function test_imposible_cycle (t) { 10 | 'use strict'; 11 | 12 | var A = dcl(null, { 13 | declaredClass: 'A' 14 | }); 15 | 16 | var B = dcl(null, { 17 | declaredClass: 'B' 18 | }); 19 | 20 | var AB = dcl([A, B], { 21 | declaredClass: 'AB' 22 | }); 23 | 24 | var BA = dcl([B, A], { 25 | declaredClass: 'BA' 26 | }); 27 | 28 | try { 29 | var Impossible = dcl([AB, BA], { 30 | declaredClass: 'Impossible' 31 | }); 32 | // we should never be there 33 | t.assert(false, 'cycle error should be triggered'); 34 | } catch (e) { 35 | eval(t.TEST('e instanceof dcl.DclError')); 36 | eval(t.TEST('e instanceof dcl.CycleError')); 37 | } 38 | }, 39 | function test_chaining_conflict (t) { 40 | 'use strict'; 41 | 42 | var A = dcl(null, { 43 | declaredClass: 'A' 44 | }); 45 | dcl.chainAfter(A, 'm'); 46 | 47 | var B = dcl(null, { 48 | declaredClass: 'B' 49 | }); 50 | dcl.chainBefore(B, 'm'); 51 | 52 | try { 53 | var ChainConflict = dcl([A, B], { 54 | declaredClass: 'ChainConflict' 55 | }); 56 | // we should never be there 57 | t.assert(false, 'chain error is triggered'); 58 | } catch (e) { 59 | eval(t.TEST('e instanceof dcl.DclError')); 60 | eval(t.TEST('e instanceof dcl.ChainingError')); 61 | } 62 | }, 63 | function test_chaining_error (t) { 64 | 'use strict'; 65 | 66 | var A = dcl(null, { 67 | declaredClass: 'A' 68 | }); 69 | dcl.chainAfter(A, 'm'); 70 | 71 | try { 72 | dcl.chainBefore(A, 'm'); 73 | // we should never be there 74 | t.assert(false, 'set chaining error is triggered'); 75 | } catch (e) { 76 | eval(t.TEST('e instanceof dcl.DclError')); 77 | eval(t.TEST('e instanceof dcl.ChainingError')); 78 | } 79 | }, 80 | function test_superCall_argument_error (t) { 81 | 'use strict'; 82 | 83 | try { 84 | var A = dcl(null, { 85 | declaredClass: 'A', 86 | m: dcl.superCall('Should be a function, but it is a string.') 87 | }); 88 | // we should never be there 89 | t.assert(false, 'supercall error is triggered'); 90 | } catch (e) { 91 | eval(t.TEST('e instanceof dcl.DclError')); 92 | eval(t.TEST('e instanceof dcl.SuperError')); 93 | } 94 | }, 95 | function test_superCall_super_error (t) { 96 | 'use strict'; 97 | 98 | var A = dcl(null, { 99 | declaredClass: 'A', 100 | m: 42 // not a function 101 | }); 102 | try { 103 | var B = dcl(A, { 104 | declaredClass: 'B', 105 | m: dcl.superCall(function(sup){ 106 | return sup ? sup.call(this) : 0; 107 | }) 108 | }); 109 | // we should never be there 110 | t.assert(false, 'super error is triggered'); 111 | } catch (e) { 112 | eval(t.TEST('e instanceof dcl.DclError')); 113 | eval(t.TEST('e instanceof dcl.SuperError')); 114 | } 115 | }, 116 | function test_superCall_wrapper (t) { 117 | 'use strict'; 118 | 119 | try { 120 | var A = dcl(null, { 121 | declaredClass: 'A', 122 | m: dcl.superCall(function(sup){ 123 | return 'Instead of a function I return a string.'; 124 | }) 125 | }); 126 | // we should never be there 127 | t.assert(false, 'super result error is triggered'); 128 | } catch (e) { 129 | eval(t.TEST('e instanceof dcl.DclError')); 130 | eval(t.TEST('e instanceof dcl.SuperError')); 131 | } 132 | } 133 | ]); 134 | 135 | return {}; 136 | }); 137 | -------------------------------------------------------------------------------- /tests/test_mini.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl'], function (module, unit, dcl) { 3 | 'use strict'; 4 | 5 | function getNames(ctr) { 6 | return ctr._meta.bases.map(function (base) { 7 | return base.prototype.declaredClass; 8 | }).join(','); 9 | } 10 | 11 | // tests 12 | 13 | unit.add(module, [ 14 | function test_simple (t) { 15 | var A = dcl(null, {declaredClass: 'A'}); 16 | var B = dcl(A, {declaredClass: 'B'}); 17 | var C = dcl(B, {declaredClass: 'C'}); 18 | 19 | eval(t.TEST('getNames(A) === "A"')); 20 | eval(t.TEST('getNames(B) === "A,B"')); 21 | eval(t.TEST('getNames(C) === "A,B,C"')); 22 | }, 23 | function test_superCall (t) { 24 | var A = dcl(null, { 25 | constructor: function () { 26 | if(!this.a){ this.a = ''; } 27 | this.a += 'A'; 28 | }, 29 | m1: function () { 30 | this.b = 'X'; 31 | }, 32 | m2: dcl.superCall(function (sup) { 33 | return function () { 34 | if (sup) { sup.call(this); } 35 | if (!this.c) { this.c = ''; } 36 | this.c += '1'; 37 | }; 38 | }), 39 | m3: dcl.superCall(function (sup) { 40 | return function () { 41 | if (!this.d) { this.d = ''; } 42 | this.d += 'M'; 43 | if (sup) { sup.call(this); } 44 | }; 45 | }) 46 | }); 47 | 48 | var a = new A; 49 | a.m1(); 50 | a.m2(); 51 | a.m3(); 52 | 53 | eval(t.TEST('a.a === "A"')); 54 | eval(t.TEST('a.b === "X"')); 55 | eval(t.TEST('a.c === "1"')); 56 | eval(t.TEST('a.d === "M"')); 57 | 58 | var B = dcl(A, { 59 | constructor: function () { 60 | if (!this.a) { this.a = ''; } 61 | this.a += 'B'; 62 | }, 63 | m1: function () { 64 | this.b = 'Y'; 65 | }, 66 | m2: dcl.superCall(function (sup) { 67 | return function () { 68 | if (sup) { sup.call(this); } 69 | if (!this.c) { this.c = ''; } 70 | this.c += '2'; 71 | }; 72 | }), 73 | m3: dcl.superCall(function (sup) { 74 | return function () { 75 | if (!this.d) { this.d = ''; } 76 | this.d += 'N'; 77 | if (sup) { sup.call(this); } 78 | }; 79 | }) 80 | }); 81 | 82 | var b = new B; 83 | b.m1(); 84 | b.m2(); 85 | b.m3(); 86 | 87 | eval(t.TEST('b.a === "AB"')); 88 | eval(t.TEST('b.b === "Y"')); 89 | eval(t.TEST('b.c === "12"')); 90 | eval(t.TEST('b.d === "NM"')); 91 | 92 | var C = dcl(B, { 93 | constructor: function () { 94 | if (!this.a) { this.a = ''; } 95 | this.a += 'C'; 96 | }, 97 | m1: function () { 98 | this.b = 'Z'; 99 | }, 100 | m2: dcl.superCall(function (sup) { 101 | return function () { 102 | if (sup) { sup.call(this); } 103 | if (!this.c) { this.c = ''; } 104 | this.c += '3'; 105 | }; 106 | }), 107 | m3: dcl.superCall(function (sup) { 108 | return function () { 109 | if (!this.d) { this.d = ''; } 110 | this.d += 'O'; 111 | if (sup) { sup.call(this); } 112 | }; 113 | }) 114 | }); 115 | 116 | var c = new C; 117 | c.m1(); 118 | c.m2(); 119 | c.m3(); 120 | 121 | eval(t.TEST('c.a === "ABC"')); 122 | eval(t.TEST('c.b === "Z"')); 123 | eval(t.TEST('c.c === "123"')); 124 | eval(t.TEST('c.d === "ONM"')); 125 | }, 126 | function test_diamonds (t) { 127 | var A = dcl(null, {declaredClass: 'A'}); 128 | var B = dcl(null, {declaredClass: 'B'}); 129 | var C = dcl(null, {declaredClass: 'C'}); 130 | var D = dcl(null, {declaredClass: 'D'}); 131 | 132 | var ABC = dcl([A, B, C], {declaredClass: 'ABC'}); 133 | var ADC = dcl([A, D, C], {declaredClass: 'ADC'}); 134 | 135 | eval(t.TEST('getNames(ABC) === "A,B,C,ABC"')); 136 | eval(t.TEST('getNames(ADC) === "A,D,C,ADC"')); 137 | 138 | var ABCD1 = dcl([ABC, ADC], {declaredClass: 'ABCD1'}); 139 | var ABCD2 = dcl([ADC, ABC], {declaredClass: 'ABCD2'}); 140 | 141 | eval(t.TEST('getNames(ABCD1) === "A,B,D,C,ABC,ADC,ABCD1"')); 142 | eval(t.TEST('getNames(ABCD2) === "A,D,B,C,ADC,ABC,ABCD2"')); 143 | }, 144 | function test_triangles (t) { 145 | var A = dcl(null, {declaredClass: 'A'}); 146 | var B = dcl(null, {declaredClass: 'B'}); 147 | var C = dcl(null, {declaredClass: 'C'}); 148 | 149 | var ABC = dcl([A, B, C], {declaredClass: 'ABC'}); 150 | var AC = dcl([A, C], {declaredClass: 'AC'}); 151 | var BC = dcl([B, C], {declaredClass: 'BC'}); 152 | 153 | eval(t.TEST('getNames(ABC) === "A,B,C,ABC"')); 154 | eval(t.TEST('getNames(AC) === "A,C,AC"')); 155 | eval(t.TEST('getNames(BC) === "B,C,BC"')); 156 | 157 | var ABC1 = dcl([ABC, AC], {declaredClass: 'ABC1'}); 158 | var ABC2 = dcl([AC, ABC], {declaredClass: 'ABC2'}); 159 | 160 | eval(t.TEST('getNames(ABC1) === "A,B,C,ABC,AC,ABC1"')); 161 | eval(t.TEST('getNames(ABC2) === "A,B,C,AC,ABC,ABC2"')); 162 | 163 | var ABC3 = dcl([ABC, BC], {declaredClass: 'ABC3'}); 164 | var ABC4 = dcl([BC, ABC], {declaredClass: 'ABC4'}); 165 | 166 | eval(t.TEST('getNames(ABC3) === "A,B,C,ABC,BC,ABC3"')); 167 | eval(t.TEST('getNames(ABC4) === "A,B,C,BC,ABC,ABC4"')); 168 | }, 169 | function test_superCall_int (t) { 170 | var a = new (dcl(null, { 171 | toString: dcl.superCall(function (sup) { 172 | return function () { 173 | return 'PRE-' + sup.call(this) + '-POST'; 174 | }; 175 | }) 176 | })); 177 | eval(t.TEST('a.toString() === "PRE-[object Object]-POST"')); 178 | }, 179 | function test_impossible (t) { 180 | var A = dcl(null, {declaredClass: 'A'}); 181 | var B = dcl(null, {declaredClass: 'B'}); 182 | 183 | var AB = dcl([A, B], {declaredClass: 'AB'}); 184 | var BA = dcl([B, A], {declaredClass: 'BA'}); 185 | 186 | var failed = false; 187 | try { 188 | var X = dcl([AB, BA], {declaredClass: 'X'}); 189 | } catch (e) { 190 | failed = true; 191 | } finally { 192 | eval(t.TEST('failed')); 193 | } 194 | }, 195 | function test_four_classes (t) { 196 | var A = dcl(null, {declaredClass: 'A'}); 197 | var B = dcl(null, {declaredClass: 'B'}); 198 | var C = dcl(A, {declaredClass: 'C'}); 199 | var D = dcl([B, C], {declaredClass: 'D'}); 200 | 201 | eval(t.TEST('getNames(A) === "A"')); 202 | eval(t.TEST('getNames(B) === "B"')); 203 | eval(t.TEST('getNames(C) === "A,C"')); 204 | eval(t.TEST('getNames(D) === "B,A,C,D"')); 205 | 206 | var E = dcl([D, A], {declaredClass: 'E'}); 207 | 208 | eval(t.TEST('getNames(E) === "B,A,C,D,E"')); 209 | } 210 | ]); 211 | 212 | return {}; 213 | }); 214 | -------------------------------------------------------------------------------- /tests/test_mixins.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../mixins/Cleanup'], function (module, unit, dcl, Cleanup) { 3 | 'use strict'; 4 | 5 | // tests 6 | 7 | unit.add(module, [ 8 | function test_Cleanup (t) { 9 | var msgs = []; 10 | 11 | var A = dcl(null, { 12 | constructor: function (n) { 13 | this.n = n; 14 | msgs.push(this.n); 15 | }, 16 | destroy: function () { 17 | msgs.push(-this.n); 18 | } 19 | }); 20 | 21 | var cleanup = function (n) { 22 | msgs.push(-n); 23 | }; 24 | 25 | var B = dcl(Cleanup, { 26 | constructor: function () { 27 | var f1 = this.pushCleanup(new A(1)); 28 | this.f2 = this.pushCleanup(2, cleanup); 29 | this.pushCleanup(new A(3)); 30 | this.pushCleanup(new A(4)); 31 | this.removeCleanup(f1); 32 | f1(); 33 | this.popCleanup(); 34 | }, 35 | remove2: function () { 36 | if (this.removeCleanup(this.f2)) { 37 | this.f2(); 38 | this.f2 = null; 39 | } 40 | }, 41 | destroy: function () { 42 | msgs.push(-99); 43 | } 44 | }); 45 | 46 | var b = new B(); 47 | eval(t.TEST('msgs.join(",") == "1,3,4,-1,-4"')); 48 | 49 | b.remove2(); 50 | eval(t.TEST('msgs.join(",") == "1,3,4,-1,-4,-2"')); 51 | 52 | b.remove2(); 53 | eval(t.TEST('msgs.join(",") == "1,3,4,-1,-4,-2"')); 54 | 55 | b.destroy(); 56 | eval(t.TEST('msgs.join(",") == "1,3,4,-1,-4,-2,-99,-3"')); 57 | } 58 | ]); 59 | 60 | return {}; 61 | }); 62 | -------------------------------------------------------------------------------- /tests/test_raw.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl'], function (module, unit, dcl) { 3 | 'use strict'; 4 | 5 | // tests 6 | 7 | unit.add(module, [ 8 | { 9 | test: function test_si (t) { 10 | function A (x) { t.info('A: ' + x); } 11 | A.prototype = { 12 | m: function (x) { t.info('A.m: ' + x); } 13 | }; 14 | 15 | var a = new A(1); 16 | a.m(2); 17 | 18 | var B = dcl(A, { 19 | constructor: function (x) { 20 | t.info('B: ' + x); 21 | }, 22 | m: function (x) { 23 | t.info('B.m: ' + x); 24 | } 25 | }); 26 | 27 | var b = new B(3); 28 | b.m(4); 29 | }, 30 | logs: [ 31 | {text: 'A: 1'}, 32 | {text: 'A.m: 2'}, 33 | {text: 'A: 3'}, 34 | {text: 'B: 3'}, 35 | {text: 'B.m: 4'} 36 | ] 37 | }, 38 | { 39 | test: function test_superCall (t) { 40 | function A (x) { t.info('A: ' + x); } 41 | A.prototype = { 42 | m: function (x) { t.info('A.m: ' + x); } 43 | }; 44 | 45 | var a = new A(1); 46 | a.m(2); 47 | 48 | var B = dcl(A, { 49 | constructor: function (x) { 50 | t.info('B: ' + x); 51 | }, 52 | m: dcl.superCall(function (sup) { 53 | return function (x) { 54 | t.info('B.m before: ' + x); 55 | sup.apply(this, arguments); 56 | t.info('B.m middle: ' + x); 57 | sup.call(this, x + 1); 58 | t.info('B.m after: ' + x); 59 | }; 60 | }) 61 | }); 62 | 63 | var b = new B(3); 64 | b.m(4); 65 | }, 66 | logs: [ 67 | {text: 'A: 1'}, 68 | {text: 'A.m: 2'}, 69 | {text: 'A: 3'}, 70 | {text: 'B: 3'}, 71 | {text: 'B.m before: 4'}, 72 | {text: 'A.m: 4'}, 73 | {text: 'B.m middle: 4'}, 74 | {text: 'A.m: 5'}, 75 | {text: 'B.m after: 4'} 76 | ] 77 | }, 78 | function test_superCall_int(t){ 79 | function A (x) {} 80 | A.prototype = { 81 | toString: function (x) { return '[object A]'; } 82 | }; 83 | 84 | var b = new (dcl(A, { 85 | toString: dcl.superCall(function (sup) { 86 | return function () { 87 | return 'PRE-' + sup.call(this) + '-POST'; 88 | }; 89 | }) 90 | })); 91 | eval(t.TEST('b.toString() === "PRE-[object A]-POST"')); 92 | }, 93 | { 94 | test: function test_mi(t){ 95 | function A (x) { t.info('A: ' + x); } 96 | A.prototype = { 97 | m1: function (x) { t.info('A.m1: ' + x); }, 98 | m2: function (x) { t.info('A.m2: ' + x); }, 99 | m3: function (x) { t.info('A.m3: ' + x); } 100 | }; 101 | 102 | function B (x) { t.info('B: ' + x); } 103 | B.prototype = { 104 | m1: function (x) { t.info('B.m1: ' + x); }, 105 | m2: function (x) { t.info('B.m2: ' + x); }, 106 | m3: function (x) { t.info('B.m3: ' + x); } 107 | }; 108 | 109 | function C (x) { t.info('C: ' + x); } 110 | C.prototype = { 111 | m1: function (x) { t.info('C.m1: ' + x); }, 112 | m2: function (x) { t.info('C.m2: ' + x); }, 113 | m3: function (x) { t.info('C.m3: ' + x); } 114 | }; 115 | 116 | var abc = new (dcl([A, B, C], { 117 | constructor: function (x) { 118 | t.info('abc: ' + x); 119 | }, 120 | m1: dcl.superCall(function (sup) { 121 | return function (x) { 122 | t.info('abc.m1: ' + x); 123 | sup.call(this, x); 124 | }; 125 | }), 126 | m2: dcl.superCall(function (sup) { 127 | return function (x) { 128 | t.info('abc.m2: ' + x); 129 | sup.call(this, x); 130 | }; 131 | }), 132 | m3: dcl.superCall(function (sup) { 133 | return function (x) { 134 | t.info('abc.m3: ' + x); 135 | sup.call(this, x); 136 | }; 137 | }) 138 | }))(0); 139 | abc.m1(1); 140 | abc.m2(2); 141 | abc.m3(3); 142 | 143 | var O = dcl(null, {}); 144 | dcl.chainBefore(O, 'm1'); 145 | dcl.chainAfter (O, 'm3'); 146 | 147 | var oabc = new (dcl([O, A, B, C], { 148 | constructor: function (x) { 149 | t.info('oabc: ' + x); 150 | }, 151 | m1: function (x) { 152 | t.info('oabc.m1: ' + x); 153 | }, 154 | m2: function (x) { 155 | t.info('oabc.m2: ' + x); 156 | }, 157 | m3: function (x) { 158 | t.info('oabc.m3: ' + x); 159 | } 160 | }))(0); 161 | oabc.m1(1); 162 | oabc.m2(2); 163 | oabc.m3(3); 164 | }, 165 | logs: [ 166 | // abc 167 | {text: 'A: 0'}, 168 | {text: 'B: 0'}, 169 | {text: 'C: 0'}, 170 | {text: 'abc: 0'}, 171 | {text: 'abc.m1: 1'}, 172 | {text: 'C.m1: 1'}, 173 | {text: 'abc.m2: 2'}, 174 | {text: 'C.m2: 2'}, 175 | {text: 'abc.m3: 3'}, 176 | {text: 'C.m3: 3'}, 177 | // oabc 178 | {text: 'A: 0'}, 179 | {text: 'B: 0'}, 180 | {text: 'C: 0'}, 181 | {text: 'oabc: 0'}, 182 | {text: 'oabc.m1: 1'}, 183 | {text: 'C.m1: 1'}, 184 | {text: 'B.m1: 1'}, 185 | {text: 'A.m1: 1'}, 186 | {text: 'oabc.m2: 2'}, 187 | {text: 'A.m3: 3'}, 188 | {text: 'B.m3: 3'}, 189 | {text: 'C.m3: 3'}, 190 | {text: 'oabc.m3: 3'} 191 | ] 192 | } 193 | ]); 194 | 195 | return {}; 196 | }); 197 | -------------------------------------------------------------------------------- /tests/test_registry.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=='function'&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['module', 'heya-unit', '../dcl', '../utils/registry'], 3 | function (module, unit, dcl, registry) { 4 | 'use strict'; 5 | 6 | // tests 7 | 8 | unit.add(module, [ 9 | function test_registry (t) { 10 | registry.clear(); 11 | eval(t.TEST('registry.keys().length === 0')); 12 | 13 | var A = dcl({declaredClass: 'A'}); 14 | eval(t.TEST('registry.keys().length === 1')); 15 | eval(t.TEST('registry.has("A")')); 16 | eval(t.TEST('registry.get("A") === A')); 17 | 18 | var B = dcl({declaredClass: 'B'}); 19 | eval(t.TEST('registry.keys().length === 2')); 20 | eval(t.TEST('registry.has("A")')); 21 | eval(t.TEST('registry.get("A") === A')); 22 | eval(t.TEST('registry.has("B")')); 23 | eval(t.TEST('registry.get("B") === B')); 24 | 25 | eval(t.TEST('registry.delete("A")')); 26 | eval(t.TEST('registry.keys().length === 1')); 27 | eval(t.TEST('registry.has("B")')); 28 | eval(t.TEST('registry.get("B") === B')); 29 | 30 | var C = dcl({}); // unnamed class 31 | eval(t.TEST('registry.keys().length === 1')); 32 | eval(t.TEST('registry.has("B")')); 33 | eval(t.TEST('registry.get("B") === B')); 34 | 35 | registry.clear(); 36 | eval(t.TEST('registry.keys().length === 0')); 37 | } 38 | ]); 39 | 40 | return {}; 41 | }); 42 | -------------------------------------------------------------------------------- /tests/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dcl test runner 5 | 7 | 8 | 9 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([ 3 | 'heya-unit', 4 | './test_mini', 5 | './test_dcl', 6 | './test_advise', 7 | './test_raw', 8 | './test_bases', 9 | './test_mixins', 10 | './test_advices', 11 | './test_accessors', 12 | './test_advise_accessors', 13 | './test_debug', 14 | './test_registry' 15 | ], 16 | function (unit) { 17 | 'use strict'; 18 | unit.run(); 19 | }); 20 | -------------------------------------------------------------------------------- /utils/registry.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (['../dcl', '../advise'], function (dcl, advise) { 3 | 'use strict'; 4 | 5 | var registry = {}; 6 | 7 | // register all named classes automatically 8 | advise.after(dcl, '_makeCtr', function (_, result) { 9 | if (result && typeof result.prototype.declaredClass == 'string') { 10 | registry[result.prototype.declaredClass] = result; 11 | } 12 | }); 13 | 14 | return { 15 | get: function (name) { return registry[name]; }, 16 | has: function (name) { return Object.prototype.hasOwnProperty.call(registry, name); }, 17 | delete: function (name) { return delete registry[name]; }, 18 | keys: function () { 19 | return Object.keys(registry).filter(function (name) { 20 | return Object.prototype.hasOwnProperty.call(registry, name); 21 | }); 22 | }, 23 | clear: function () { registry = {}; } 24 | }; 25 | }); 26 | --------------------------------------------------------------------------------