{"id":19097214,"url":"https://github.com/atmajs/maskjs","last_synced_at":"2025-04-09T23:13:55.227Z","repository":{"id":4264588,"uuid":"5391781","full_name":"atmajs/MaskJS","owner":"atmajs","description":"Markup | Template | HMVC","archived":false,"fork":false,"pushed_at":"2025-03-04T23:45:21.000Z","size":23620,"stargazers_count":92,"open_issues_count":5,"forks_count":6,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-09T23:13:49.754Z","etag":null,"topics":["components","hmvc","modules","templating-language"],"latest_commit_sha":null,"homepage":"http://atmajs.com/mask","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"mozilla/django-badger","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atmajs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-08-12T20:42:34.000Z","updated_at":"2025-03-04T23:45:25.000Z","dependencies_parsed_at":"2024-06-18T17:00:51.598Z","dependency_job_id":"44032d89-512f-4a51-9f91-007d14d8bab1","html_url":"https://github.com/atmajs/MaskJS","commit_stats":{"total_commits":1455,"total_committers":5,"mean_commits":291.0,"dds":0.0907216494845361,"last_synced_commit":"db35336dc3eccfbc0b85d0b7927f62382ae2a48c"},"previous_names":[],"tags_count":233,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atmajs%2FMaskJS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atmajs%2FMaskJS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atmajs%2FMaskJS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atmajs%2FMaskJS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atmajs","download_url":"https://codeload.github.com/atmajs/MaskJS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125589,"owners_count":21051770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["components","hmvc","modules","templating-language"],"created_at":"2024-11-09T03:39:25.991Z","updated_at":"2025-04-09T23:13:55.205Z","avatar_url":"https://github.com/atmajs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align='center'\u003e\n    \u003cimg src='assets/logo.png'/\u003e\n    \u003c/p\u003e\n\n----\n\n\u003cp align=\"center\"\u003e\n    \u003ca href='https://travis-ci.com/atmajs/MaskJS' target='_blank'\u003e\n        \u003cimg src='https://api.travis-ci.com/atmajs/MaskJS.png?branch=master' /\u003e\n        \u003c/a\u003e\n    \u003ca href='http://badge.fury.io/js/maskjs' target='_blank'\u003e\n        \u003cimg src='https://badge.fury.io/js/maskjs.svg' /\u003e\n        \u003c/a\u003e\n    \u003ca href='http://badge.fury.io/bo/maskjs' target='_blank'\u003e\n        \u003cimg src='https://badge.fury.io/bo/maskjs.svg' /\u003e\n        \u003c/a\u003e\n    \u003c/p\u003e\n\nMaskJS — is a markup | template | modular **HMVC** engine for\nmodern and fast web(_Browser_), server(_NodeJS_) or mobile(_PhoneGap_) applications. Component-based architecture\nsimplifies defining, implementing and composing loosely coupled independent elements into a single application.\n\nResources:\n\n- :link: [Atma.js](http://atmajs.com/mask)\n- :link: [MaskFiddle](http://atmajs.com/mask-try)\n- :books: [Wiki](https://github.com/atmajs/maskjs/wiki)\n\n- **Examples**\n    - :link: [Samples](/examples)\n    - :link: [TodoMVC app](http://todomvc.com/examples/atmajs/)\n- **Tools**\n    - :link: [Chrome Debug Plugin](https://chrome.google.com/webstore/detail/atmajs-devtool/bpaepkmcmoablpdahclhdceapndfhdpo)\n    - :link: [Sublime Package](https://github.com/tenbits/sublime-mask)\n    - :link: [Atom Package](https://github.com/tenbits/package-atom)\n\n----\n\n##### \u0026#9776;\n\n- `1` [Markup](#1-markup)\n    - `1.1` [Mask](#11-mask-syntax)\n    - `1.2` [HTML](#12-html-syntax)\n- `2` [Libraries](#2-libraries)\n    - `2.1` [Components](#21-components)\n    - `2.2` [Bindings](#22-bindings)\n    - `2.3` [jMask](#23-jmask)\n    - `2.4` [jQuery](#24-jquery)\n    - `2.5` [Dependency Injection](#25-dependency-injection)\n- `3` [Performance](#3-performance)\n- `4` [NodeJS](#4-nodejs)\n- `5` [Browser Support](#5-browser-support)\n- `6` [Plugins](#6-plugins)\n- `7` [Quick Start](#7-quick-start)\n- `8` [Contribute](#8-contribute)\n    - `8.1` [Build](#82-build)\n    - `8.2` [Test ](#81-test)\n- `9` [Changelog](#9-changelog)\n\n----\n\n# `1` Markup\n\nWe support `mask` and `html` syntax for writing your templates. And you can even mix them within one template, as each of them has its advantages.\n\n##### `1.1` Mask Syntax\n- Component and element-based markup\n- Statements, Expressions, Interpolations\n- Performance. _No precompilation is required_\n- Small size. _~30% smaller than HTML_ Additionaly, there is a minification tool - [Optimizer](https://github.com/atmajs/mask-optimizer).\n- DOM Builder\n    `[Template → Mask AST → Shadow DOM → Live DOM]`\n- HTML Builder (_nodejs_)\n    `[Template → Mask AST → HTML]`\n\n```mask\nimport CustomComponent from 'Foo.mask'\n\n.container {\n    h4 \u003e 'Title'\n    section.content {\n        span \u003e 'Hello ~name!'\n\n        if (admins.includes(name)) {\n            em \u003e 'Admin'\n        }\n    }\n    CustomComponent {\n        button x-tap='changeName' \u003e\n            '~[bind: name]'\n\n        for (tag of tags) {\n            h4 \u003e '~tag.title'\n        }\n    }\n}\n```\n\n##### `1.2` HTML Syntax\n\nHere is nothing new for you. Old good HTML syntax to define the templates. But we highly encourage to use the mask syntax, as the templates are smaller, cleaner and with additional features.\n\n```html\n\u003ch4\u003e~[name]\u003c/h4\u003e\n\u003cDialog\u003e\n    \u003cdiv\u003eHello Foo\u003c/div\u003e\n\u003c/Dialog\u003e\n```\n\n##### `1.3` HTML within Mask\n\nYou can even use html blocks in a mask syntax\n\n```mask\nul {\n    \u003cli\u003e Foo\n    \u003cli\u003e Bar\n}\n```\n\n\u003e MaskJS has extremely extendable API based on interfaces and contracts. It supports **Custom Tag** Handlers, **Custom Attribute** Handlers, Model **Utils**.\n\n\u003e MaskJS default build contains sub projects: `CompoJS`, `Bindings`, `jMask`.\n\n# `2` Libaries\n\n\u003e :package: All packages are already embedded into MaskJS sources.\n\n## `2.1` Components\n\n:orange_book: [Read more...**\u0026crarr;**](https://github.com/atmajs/mask-compo)\n\nCore of the HMVC engine. Simple compo sample:\n\n```ts\nexport class CustomComponentCtr {\n\n    // slots example\n    @mask.deco.slot()\n    onRefreshDate (){\n        this.model.date = new Date();\n    }\n    @mask.deco.slot()\n    domInsert (){\n        alert(this.$.innerWidth());\n    }\n\n    // events example\n    @mask.deco.event('click: button')\n    onButtonClicked (){\n        alert(this.model.date);\n    }\n\n    onRenderStart (model, ctx) {\n        // override model\n        this.model = { date: new Date(); }\n    }\n    onRenderEnd: function(elements, model, ctx){\n        this.$ // is a domLibrary (jQuery-lite, jQuery/Zepto/Kimbo) wrapper over `elements`\n    }\n\n    dispose () {\n        // do some cleanup\n    }\n};\n```\n\n```mask\nimport './CustomComponent.less'\nimport CustomComponentCtr from './CustomComponentCtr.ts'\n\ndefine CustomComponent extends CustomComponentCtr {\n    h1 {\n        'Date ~[bind: _.formatDate(date)]'\n    }\n    button .btn x-tap='onRefreshDate' {\n        i.material-icons \u003e 'update'\n        'Refresh'\n    }\n}\n```\n\n# `2.2` Bindings\n\n:orange_book: [Read more...**\u0026crarr;**](https://github.com/atmajs/mask-binding) _`IE9+`_\n\nMaskJS itself supports simple interpolations. It means the models are only accessed while render, but with this feature you can define single or dual bindings. As MaskJS is a DOM based engine, the bindings are instant.\n\nSimple bindings sample:\n\n```mask\nh4 \u003e '~[bind: fooDate.getSeconds() * barAge ]'\n\ninput type=date \u003e\n    dualbind value='fooDate';\n\ninput type=number \u003e\n    dualbind value='barAge';\n/*\\\n * `dualbind` component also supports much more properties and configurations\n\\*/\n```\n\n# `2.3` jMask\n\n:orange_book: [Read more...**\u0026crarr;**](https://github.com/atmajs/mask-j)\n\njMask offers jQuery-alike syntax for the dynamic MaskDOM Manipulations.\n\n\n# `2.4` jQuery\n\nMaskJS is loosely coupled with the DOM Library, like jQuery-Zepto-Kimbo. It means, that it does not depend on any DOM library, but it is highly recommended to use one. Additionally there are some extensions, like\n```javascript\n$.fn.appendMask\n$.fn.prependMask\n$.fn.beforeMask\n$.fn.afterMask\n$.fn.emptyAndDispose\n$.fn.removeAndDispose\n//e.g.\n$('.foo').appendMask('h4 \u003e \"~[title]\"', { title: 'Hello' });\n```\n_So you would never need to use the HTML._\n\n# `2.5` Dependency Injection\n\n:orange_book: [Read more...**\u0026crarr;**](https://github.com/tenbits/di)\n\nYou can annotate arguments for `define` declaration or for its constructor and if you don't provide the values on initialization MaskJS will do it for you using registered IoC container.\n\nThe library is not include, you can use any other DI library. MaskJS only requires an IoC container with a single method: `.resolve(Type):Any`.\n\n```mask\nimport * as IStore from services;\n\ndefine UserList (store: IStore) {\n\n    foreach (user of store.getUsers()) {\n        div \u003e '~user.username'\n    }\n\n    // or in constructor\n    function constructor (store: IStore) {\n        this.store = store;\n    }\n}\n```\n\n# `3` Performance\n\nWe thoroughly pay attention to the performance, especially on the mobile CPU. _The DOM based and the Shadow DOM approach is the fastest way to create hierarchical component structure._\n\nSome benchmarks:\n- Mask vs raw HTML Template Engines - [:jsperf](https://jsperf.com/dom-vs-innerhtml-based-templating/1097)\n- Mask vs Angular - [:jsperf](http://jsperf.com/mask-vs-angular/6)\n- MaskDOM AST vs JSON parse - [:jsperf](http://jsperf.com/maskjs-vs-json/13)\n- Mask Markup vs HTML - [:jsperf](http://jsperf.com/mask-vs-contextual-fragment/9)\n- Mask Expressions vs Eval - [:jsperf](http://jsperf.com/mask-expression-vs-function-vs-eval/2)\n\n# `4` Node.JS\n\nMaskJS on the server\n\n:orange_book: [Mask.Node **\u0026crarr;**](https://github.com/atmajs/mask-node) [Server.Lib **\u0026crarr;**](https://github.com/atmajs/atma-server)\n\n- HMVC benefits\n- Models serialization/de-serialization\n- Components render mode - `server`, `client` or `both`\n- HTML rendered output with further bootstrapping on the client, so that the components are initialized, all events and bindings are attached\n- Application start performance: browser receives ready html for rendering.\n- SEO\n\n\n# `5` Browser Support\n\n- IE7+\n\n# `6` Plugins\nThere are already many plugins, components and useful utilities. Some of them worth to checking out:\n- [Formatter Util](https://github.com/atmajs/util-format)\n- [Localization](https://github.com/atmajs/i18n)\n- [Animations](https://github.com/atmajs/mask-animation)\n- [Components](https://github.com/atmajs/Compos)\n\n\n# `7` Quick Start\n\nMost simple MaskJS sample to show where you could start from:\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n    \u003cbody\u003e\n        \u003cscript type='text/mask' data-run='auto'\u003e\n            import Counter from './Counter';\n\n            h4 \u003e 'Counter with 1 second step'\n            Counter x-interval=1;\n\n            h4 \u003e 'Counter with 5 seconds step'\n            Counter x-interval=5;\n        \u003c/script\u003e\n        \u003cscript src='https://unpkg.com/maskjs'\u003e\u003c/script\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n```mask\n// Create the file `Counter.mask`\ndefine Counter {\n    var meta = {\n        attributes: {\n            'x-interval': 'number'\n        }\n    };\n\n    var scope = {\n        counter: 0,\n        timer: null\n    };\n\n    slot domInsert () {\n        this.scope.timer = setTimeout(() =\u003e {\n            ++this.scope.counter;\n        }, this.xInterval)\n    }\n\n    function dispose () {\n        clearTimeout(this.scope.timer);\n    }\n\n    div \u003e '~[bind: this.scope.counter]\n}\n```\n\n# `8` Contribute\n### `8.1` Build\n```bash\n$ git submodule init \u0026\u0026 git submodule update\n$ npm install\n$ npm run build\n```\n\n### `8.2` Test\n```bash\n$ npm install\n$ npm test\n```\n\n# `9` Changelog\n------------\n\n:bookmark: [View complete list...**\u0026crarr;**](CHANGELOG.md)\n_`@latest`_\n- `0.64.0`\n- **Properties**\n\n    ```mask\n    div [style.backgroundColor] = 'red';\n    ```\n\n\n_`@latest`_\n- `0.60.0`\n- **Await** statements, components and also modules\n\n    ```mask\n    define Foo {\n        function async onRenderStart () {\n            this.model = await LoadUserExample();\n        }\n        h4 \u003e '~userName'\n    }\n\n    // Component\n    await Foo {\n        @progress \u003e i \u003e 'Loading user';\n    }\n\n    // Promises\n    await (this.getCurrentUser()) {\n        @progress \u003e i \u003e 'Loading user';\n        @done (user) {\n            h4 \u003e '~user.userName'\n        }\n        @fail (error) {\n            .danger \u003e '~error.message'\n        }\n    }\n\n    // Modules\n    import async Foo from './Foo';\n\n    heading \u003e 'Some heading'\n    await Foo {\n        @progress \u003e 'Loading and initilizing the module'\n    }\n    ```\n\n- `0.58.0`\n- **Decorators** for methods and nodes\n\n    ```mask\n    [IsAuthorized]\n    div \u003e 'Hello ~user'\n\n    [LogCall]\n    function doSmth () {\n        // ...\n    }\n    ```\n- Async and Private methods. For browsers which do not yet support `async/await` es2017 feature, please use `postmask-babel` plugin.\n\n    ```mask\n    slot private async upload () {\n        await MyService.doSmth();\n    }\n    ```\n\n\n- `0.57.13`\n- **Modules**\n    - Namespace routing\n\n        ```mask\n        import FooService from services;\n\n        h4 \u003e '~FooService.doSmth()'\n        ```\n\n        You can also configurate the base path for the routing, e.g. `mask.Module.cfg('baseNs', '/src/')`\n\n        \u003e If the module is not loaded or not set to the namespace repository, we will load it for you by the resolved path, e.g. `'/src/services/FooService.js'`\n\n    - Prefix routing\n\n        ```mask\n        import MyButton from '@controls/MyButton';\n        MyButton x-tap='clicked';\n        ```\n\n        You have to configurate the prefix first, e.g.:\n\n        ```js\n        mask.Module.cfg('prefixes.controls', '/src/controls/{0}/{1}.mask');\n        ```\n\n- `0.57.0`\n- Typa annotations for arguments: `(argumentName: argumentType, ...)`\n\n    ```mask\n    import * as IFoo from '/service/IFoo.js';\n    import * as IBar from '/service/IBar.js';\n    define MyCompo (foo: IFoo) {\n        function constructor (bar: IBar) {\n            this.bar = bar;\n        }\n        span \u003e `~[foo.someMethod()]`\n    }\n    ```\n\n- `0.56.5`\n- Function scope: imports and define arguments\n\n    ```mask\n    import * as Service from '/services/UserService.js';\n    define UserEditor (user) {\n\n        slot save () {\n            Service\n                .changeUserName(user.id, user.name)\n                .then(() =\u003e console.log('saved!'));\n        }\n\n        input \u003e dualbind value='user.name';\n        button x-tap=save \u003e 'Save'\n    }\n    ```\n\n- `sync` imports, as import loading for better performance is parallel, but bundles should be loaded in sync, as they register all resources then.\n\n```mask\nimport sync from './MyComponentsBundle';\nimport FooCompo from './Foo';\n// ...\n```\n\n- `0.55.1`\n    - HTML markup within Mask templates\n\n- `0.55.0`\n    - Async imports.\n\n        ```mask\n        import async Foo from './Foo.mask';\n        h4 \u003e 'MyHeader'\n        await Foo;\n        ```\n\n        `h4` header is rendered during the `Foo` may still being loaded.\n\n    - `define` and `let` support arguments\n\n        ```mask\n        define Foo (user) {\n            h4 \u003e '~user.name'\n        }\n\n        Foo(me);\n        ```\n        ```javascript\n        mask.render(template, { me: { name: 'TestUser' }});\n        ```\n\n----\n:copyright: MIT - 2021 Atma.js Project\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatmajs%2Fmaskjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatmajs%2Fmaskjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatmajs%2Fmaskjs/lists"}