├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md └── workflows │ └── node.js.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── HELPERS.md ├── LICENSE.txt ├── README.md ├── babel-plugins.json ├── cjs ├── classes │ └── Component.js ├── hyper │ ├── render.js │ └── wire.js ├── index.js ├── objects │ ├── Intent.js │ └── Updates.js ├── package.json └── shared │ ├── constants.js │ └── utils.js ├── esm.js ├── esm ├── .eslintrc ├── classes │ └── Component.js ├── hyper │ ├── render.js │ └── wire.js ├── index.d.ts ├── index.js ├── objects │ ├── Intent.js │ └── Updates.js └── shared │ └── constants.js ├── index.d.ts ├── index.js ├── logo.txt ├── logo ├── basichtml.inkscape.svg ├── basichtml.png ├── basichtml.svg ├── creepy-html.jpg ├── hyperhtml.inkscape.svg ├── hyperhtml.png ├── hyperhtml.svg ├── nativehtml.inkscape.svg ├── nativehtml.png ├── nativehtml.svg ├── viper.png ├── viper.svg ├── viperhtml.inkscape.svg ├── viperhtml.png └── viperhtml.svg ├── min.js ├── no.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── rollup.ie.js ├── test ├── OpenExchangeRates.js ├── abstracts.md ├── adopt.html ├── adopt.js ├── article.html ├── attributes.html ├── basic-table.html ├── basic.html ├── boot-speed.html ├── bootstrap.min.css ├── bundle.html ├── cars-mithril.html ├── cars-wired.html ├── cars.html ├── ce.html ├── connected.html ├── dbmonster-bench.html ├── dbmonster.css ├── dbmonster.html ├── dbmonster.js ├── dbmonster100.js ├── diff.html ├── dist │ ├── double-rainbow.js │ ├── hyper.js │ └── ie.js ├── dom-splicer.js ├── domdiff.js ├── double-rainbow.html ├── e-icon.html ├── edge.html ├── esm.html ├── exchange.html ├── form.html ├── fuzzysort.html ├── haunted.html ├── hello-world-ce.html ├── ie.html ├── ie │ ├── index.html │ └── test │ │ └── test.js ├── incremental.html ├── index.html ├── infinite.html ├── js │ ├── double-rainbow.js │ ├── hyper.js │ └── ie.js ├── lib.html ├── many-rows-adopted.html ├── many-rows.html ├── mjs.html ├── multi-wire.html ├── mutations.html ├── my-button-ns.js ├── my-button.css ├── my-button.html ├── my-button.js ├── repl.html ├── runner.js ├── select.html ├── shared │ └── main.js ├── svgclass.html ├── tabindex.html ├── table.html ├── test-attribute-value.html ├── test.js ├── tick.html ├── tick │ └── index.html ├── todo.html ├── value.html ├── virtual.html └── webkit.html ├── umd.d.ts └── umd.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # not working due missing www. 5 | open_collective: hyperHTML 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | custom: https://www.patreon.com/webreflection 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: build 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v2 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | cache: 'npm' 24 | - run: npm ci 25 | - run: npm run build --if-present 26 | - run: npm test 27 | - run: npm run coverage --if-present 28 | - name: Coveralls 29 | uses: coverallsapp/github-action@master 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | coverage/ 4 | 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | coverage/* 2 | esm/.eslintrc 3 | logo/* 4 | node_modules/* 5 | test/* 6 | _config.yml 7 | .DS_Store 8 | .gitignore 9 | .travis.yml 10 | .github/ISSUE_TEMPLATE.md 11 | babel-plugins.json 12 | CHANGELOG.md 13 | package-lock.json 14 | rollup.config.js 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | git: 5 | depth: 1 6 | branches: 7 | only: 8 | - master 9 | - /^greenkeeper/.*$/ 10 | after_success: 11 | - "npm run coveralls" 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # hyper(html) Changelog 2 | 3 | ### v.2.23 4 | * monkey patched rollup generated code to export once the same module shared within sub-modules 5 | 6 | ### v2.22 7 | * using latest domtagger 8 | 9 | ### v2.21 10 | * refactored out all dependencies 11 | 12 | ### v2.20 13 | * re-tested every single supported browser nd fixed few outstanding issues with the 2.19 release 14 | 15 | ### v2.19 16 | * refactored out most of the code 17 | * finally managed to have coveralls show coverage stats 18 | * attributes can have spaces around as per DOM standard - [#244](https://github.com/WebReflection/hyperHTML/issues/224) 19 | * fixed SVG (non-critical) errors when interpolations are used for numerically expected values 20 | * fixed minor issues with Edge attributes 21 | * changed the unique id so if any of your logic was trusting `_hyper: ....;` comments you need to update your logic - [#300](https://github.com/WebReflection/hyperHTML/issues/300) 22 | 23 | ### v2.16.8 24 | * improved MutationObserver and fallback so that double `dis/connected` events won't happen again 25 | * exposed `observe` utility for 3rd parts so that it is possible to observe any node, not only those defined via template literals. Once observed, a node can have `connected` and `disconnected` listeners that will be triggered automatically. 26 | 27 | ### v2.16 28 | * modified `Wire` class to better handle "_same target_" case, making the `haunted.html` demo work same way as if it was bound to the node, through `valueOf()` invoke which would result in just exactly the same node if the wired content produced a node instead of a fragment. While regular users won't be affected, this is an implementation detail that changes a lot for libraries integrating `hyperHTML.wire` in their logic, making wires as fast as `bind` in most component related use cases. 29 | 30 | ### v2.15 31 | * added [invokable slots](https://github.com/WebReflection/hyperHTML/pull/282#issuecomment-433614081) to let developers explore patterns through callbacks that will receive a unique live node for weak references while rendered. 32 | 33 | ### v2.14 34 | * updated [domdiff](https://github.com/WebReflection/domdiff#domdiff) to match [petit-dom](https://github.com/yelouafi/petit-dom) performance 35 | * up to 3X performance on huge lists 36 | * improved reliability over random changes 37 | * unfortunately there's a +0.6K overall size increase due amount of extra logic involved 38 | 39 | ### v2.13.2 40 | * added support for custom CSS properties as object keys. 41 | 42 | ### v2.13.1 43 | * worked around [TypeScript transpilation bug with Template Literals](https://twitter.com/WebReflection/status/1038115439539363840). 44 | 45 | ### v2.13 46 | * added the ability to define custom attributes via `hyperHTML.define("hyper-attribute", callback)`, so that `

` would invoke `callback(target, anyValue)` where `p` would be the target. 47 | 48 | ### v2.12 49 | * added `hyper.Component#dispatch(type, detail)` method to simplify events dispatching between lightweight components, bubbling a cancelable Custom Event with a `.component` property that points at the dispatcher, while the `event.currentTarget` will be the first node found within the component render. 50 | 51 | ### v2.11 52 | * updated [domdiff](https://github.com/WebReflection/domdiff#domdiff) to v1.0 53 | 54 | ### v2.10.12 55 | * patched missing `.children` in SVG node in IE / Edge https://github.com/WebReflection/hyperHTML/issues/244 56 | 57 | ### v2.10.10 58 | * updated [domdiff](https://github.com/WebReflection/domdiff#domdiff) to solve issue #243 (breaking with some sorted list) 59 | 60 | ### v2.10.5 61 | * various fixes and changes after [changes applied to ECMAScript 2015](https://github.com/tc39/ecma262/pull/890) 62 | 63 | ### v2.8.0 64 | * updated [domdiff](https://github.com/WebReflection/domdiff#domdiff) engine to boost performance with segments and lists 65 | 66 | ### v2.7.2 67 | * fixed #218 which was a variant of #200 68 | 69 | ### v2.7.0 70 | * the `Component.for(obj)` is now created first time via `new Component(obj)` - #216 71 | 72 | ### v2.6.0 73 | * declarative hyper.Component via `Component.for(context, uid?)` - #202 74 | * hyperHTML TypeScript information - #201 75 | 76 | ### v2.5.12 77 | * fixed #200: textarea/style with initial undefined value 78 | 79 | ### v2.5.11 80 | * fixed #198: connected/disconnected events for nested components 81 | 82 | ### v2.5.10 83 | * more rigid / explicit RegExp to avoid glitches with self-closing tags 84 | 85 | ### v2.5.8 86 | * improved `VOID_ELEMENTS` regular expression (aligned with the _viperHTML_ one) 87 | 88 | ### v2.5.7 89 | * fixed `no.js` patch when wrong count of args is passed 90 | 91 | ### v2.5.6 92 | * added `no.js` file for environments without the ability to use modern JS or based on other languages such Dart. 93 | 94 | ### v2.5.5 95 | * build runs on macOS too 96 | * added umd.js file 97 | 98 | ### v2.5.2 99 | * fixed weird SVG case (see #172) 100 | 101 | ### v2.5.1 102 | * improved self-closing reliability recycling and sharing attributes RegExp 103 | 104 | ### v2.5.0 105 | * updated `domdiff` library to the latest version 106 | * implemented self-closing tags (and after various tests) 107 | 108 | ### v2.4.3 109 | * ensure attributes values are updated when different from previous one 110 | * avoid the usage of the word `global` in the whole code 111 | 112 | ### v2.4.2 113 | * fix scripts with actual content too. 114 | 115 | ### v2.4.1 116 | * fix a bug with scripts that don't trigger network requests in both Firefox and Safari (see bug #152) 117 | 118 | ### v2.4.0 119 | * created a `Wire` class to handle via `domdiff` multiple wired nodes. 120 | * brought back multi nodes per wire, a feature lost since **v2.0** 121 | * simplified `Component` handling too, making it compatible again with multi wired content. 122 | * fixed some check to make IE9+ tests green again 123 | 124 | ### v2.3.0 125 | * dropped the `engine` already. Too complex, no real benefits, refactored the whole internal logic to use [domdiff](https://github.com/WebReflection/domdiff) instead. Deprecated [hyperhtml-majinbuu](https://github.com/WebReflection/hyperhtml-majinbuu) and solved diffing "_forever_". 126 | 127 | ### v2.2.0 128 | * the whole `hyperHTML.engine` has been refactored to use [dom-splicer](https://github.com/WebReflection/dom-splicer) as an effort to make engine development easier 129 | 130 | ### v2.1.3 131 | * the MutationObserver is installed only once and only if there are components that have _on(dis)?connect_ handlers. 132 | 133 | ### v2.1.2 134 | * using a new folders convention with `esm/index.js` as main module and `cjs/index.js` as transformed artifact. This plays very well with bundlers when you `import {hyper} from 'hyperhtml/esm'` or `const {hyper} = require('hyperhtml/cjs');` 135 | 136 | ### v2.1.1 137 | * fast changes where prepending or appending same lists; now dropping upfront or removing at the end are part of the fast path too. 138 | 139 | ### v2.1.0 140 | 141 | * created a simple default merge engine focused on performance 142 | * remove majinbuu as core dependency, created [hyperhtml-majinbuu](https://github.com/WebReflection/hyperhtml-majinbuu) project to swap it back via `hyperHTML.engine = require('hyperhtml-majinbuu')` or as ESM 143 | * reduced final bundle size down to 4.1K via brotli 144 | 145 | ## v2.0.0 146 | 147 | Refactoring following ticket #140 148 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | ## Introduction 4 | 5 | First, thank you for considering contributing to hyperhtml! It's people like you that make the open source community such a great community! 😊 6 | 7 | We welcome any type of contribution, not only code. You can help with 8 | - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) 9 | - **Marketing**: writing blog posts, howto's, printing stickers, ... 10 | - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... 11 | - **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. 12 | - **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/hyperhtml). 13 | 14 | ## Your First Contribution 15 | 16 | Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 17 | 18 | ## Submitting code 19 | 20 | Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests. 21 | 22 | ## Code review process 23 | 24 | The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge. 25 | It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? 26 | 27 | ## Financial contributions 28 | 29 | We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/hyperhtml). 30 | Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. 31 | 32 | ## Questions 33 | 34 | If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!). 35 | You can also reach us at hello@hyperhtml.opencollective.com. 36 | 37 | ## Credits 38 | 39 | ### Contributors 40 | 41 | Thank you to all the people who have already contributed to hyperhtml! 42 | 43 | 44 | 45 | ### Backers 46 | 47 | Thank you to all our backers! [[Become a backer](https://opencollective.com/hyperhtml#backer)] 48 | 49 | 50 | 51 | 52 | ### Sponsors 53 | 54 | Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/hyperhtml#sponsor)) 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /HELPERS.md: -------------------------------------------------------------------------------- 1 | ## [babel-plugin-remove-ungap] 2 | 3 | Remove the [@ungap ponyfill modules] from your bundle. This will decrease the size of 4 | your bundle if you are targeting modern browsers only or if your build already includes 5 | other polyfills. This has been tested with [hyperHTML] and [lighterhtml] bundles. 6 | 7 | 8 | ## [babel-plugin-template-html-minifier] 9 | 10 | Run [html-minifier] on hyperHTML templates. 11 | 12 | 13 | ## [babel-plugin-bare-import-rewrite] 14 | 15 | This can be used as an alternative to [rollup-plugin-node-resolve], or can be used with certain node.js 16 | web servers to allow browsing live from source. 17 | 18 | Known web server integrations: 19 | * [fastify-babel] plugin for [fastify] enables running any babel plugins, generally expects `payload.filename` as set by [fastify-static] 20 | * [express-transform-bare-module-specifiers] for [express] servers 21 | 22 | 23 | ## [vinyl-rollup] 24 | 25 | This module copies the output of rollup builds to a stream of vinyl-fs objects for [gulp]. 26 | In addition it optionally adds files from modules that were bundled into the stream. This 27 | makes it easy to ensure that LICENSE and package.json files associated with bundled modules 28 | are published on the web server without publishing node.js server-side dependencies to the web. 29 | This can also be used to copy complete modules if required for licensing or if bundled code 30 | requires additional assets that are not part of the bundled JS (images for example). 31 | 32 | 33 | ## [babel-plugin-bundled-import-meta] 34 | 35 | If `node_modules/some-web-component/index.js` uses `import.meta.url` to calculate the actual 36 | path to `node_modules/some-web-components/image.png`, rollup does not compensate. This babel 37 | plugin rewrites references to `import.meta.url` so it points to the original location where 38 | it is expected that the additional assets (images and such) can be found. This plugin works 39 | well with `vinyl-rollup` with `copyModules: true`. 40 | 41 | 42 | [babel-plugin-remove-ungap]: https://github.com/cfware/babel-plugin-remove-ungap#readme 43 | [@ungap ponyfill modules]: https://github.com/ungap/ungap.github.io#readme 44 | [hyperHTML]: https://github.com/WebReflection/hyperHTML#readme 45 | [lighterhtml]: https://github.com/WebReflection/lighterhtml#readme 46 | [babel-plugin-template-html-minifier]: https://github.com/cfware/babel-plugin-template-html-minifier#readme 47 | [html-minifier]: https://github.com/kangax/html-minifier#readme 48 | [babel-plugin-bare-import-rewrite]: https://github.com/cfware/babel-plugin-bare-import-rewrite#readme 49 | [rollup-plugin-node-resolve]: https://github.com/rollup/rollup-plugin-node-resolve#readme 50 | [fastify]: https://github.com/fastify/fastify#readme 51 | [fastify-babel]: https://github.com/cfware/fastify-babel#readme 52 | [fastify-static]: https://github.com/fastify/fastify-static#readme 53 | [express-transform-bare-module-specifiers]: https://github.com/nodecg/express-transform-bare-module-specifiers#readme 54 | [express]: https://github.com/expressjs/express#readme 55 | [vinyl-rollup]: https://github.com/cfware/vinyl-rollup#readme 56 | [gulp]: https://github.com/gulpjs/gulp#readme 57 | [babel-plugin-bundled-import-meta]: https://github.com/cfware/babel-plugin-bundled-import-meta#readme 58 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2017, Andrea Giammarchi, @WebReflection 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /babel-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "check-es2015-constants", 3 | "transform-es2015-arrow-functions", 4 | "transform-es2015-block-scoped-functions", 5 | "transform-es2015-block-scoping", 6 | "transform-es2015-computed-properties", 7 | "transform-es2015-destructuring", 8 | "transform-es2015-duplicate-keys", 9 | "transform-es2015-function-name", 10 | "transform-es2015-literals", 11 | "transform-es2015-shorthand-properties", 12 | "transform-es2015-spread" 13 | ] -------------------------------------------------------------------------------- /cjs/classes/Component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const CustomEvent = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/custom-event')); 3 | const Map = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/essential-map')); 4 | const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap')); 5 | 6 | // hyperHTML.Component is a very basic class 7 | // able to create Custom Elements like components 8 | // including the ability to listen to connect/disconnect 9 | // events via onconnect/ondisconnect attributes 10 | // Components can be created imperatively or declaratively. 11 | // The main difference is that declared components 12 | // will not automatically render on setState(...) 13 | // to simplify state handling on render. 14 | function Component() { 15 | return this; // this is needed in Edge !!! 16 | } 17 | Object.defineProperty(exports, '__esModule', {value: true}).default = Component 18 | 19 | // Component is lazily setup because it needs 20 | // wire mechanism as lazy content 21 | function setup(content) { 22 | // there are various weakly referenced variables in here 23 | // and mostly are to use Component.for(...) static method. 24 | const children = new WeakMap; 25 | const create = Object.create; 26 | const createEntry = (wm, id, component) => { 27 | wm.set(id, component); 28 | return component; 29 | }; 30 | const get = (Class, info, context, id) => { 31 | const relation = info.get(Class) || relate(Class, info); 32 | switch (typeof id) { 33 | case 'object': 34 | case 'function': 35 | const wm = relation.w || (relation.w = new WeakMap); 36 | return wm.get(id) || createEntry(wm, id, new Class(context)); 37 | default: 38 | const sm = relation.p || (relation.p = create(null)); 39 | return sm[id] || (sm[id] = new Class(context)); 40 | } 41 | }; 42 | const relate = (Class, info) => { 43 | const relation = {w: null, p: null}; 44 | info.set(Class, relation); 45 | return relation; 46 | }; 47 | const set = context => { 48 | const info = new Map; 49 | children.set(context, info); 50 | return info; 51 | }; 52 | // The Component Class 53 | Object.defineProperties( 54 | Component, 55 | { 56 | // Component.for(context[, id]) is a convenient way 57 | // to automatically relate data/context to children components 58 | // If not created yet, the new Component(context) is weakly stored 59 | // and after that same instance would always be returned. 60 | for: { 61 | configurable: true, 62 | value(context, id) { 63 | return get( 64 | this, 65 | children.get(context) || set(context), 66 | context, 67 | id == null ? 68 | 'default' : id 69 | ); 70 | } 71 | } 72 | } 73 | ); 74 | Object.defineProperties( 75 | Component.prototype, 76 | { 77 | // all events are handled with the component as context 78 | handleEvent: {value(e) { 79 | const ct = e.currentTarget; 80 | this[ 81 | ('getAttribute' in ct && ct.getAttribute('data-call')) || 82 | ('on' + e.type) 83 | ](e); 84 | }}, 85 | // components will lazily define html or svg properties 86 | // as soon as these are invoked within the .render() method 87 | // Such render() method is not provided by the base class 88 | // but it must be available through the Component extend. 89 | // Declared components could implement a 90 | // render(props) method too and use props as needed. 91 | html: lazyGetter('html', content), 92 | svg: lazyGetter('svg', content), 93 | // the state is a very basic/simple mechanism inspired by Preact 94 | state: lazyGetter('state', function () { return this.defaultState; }), 95 | // it is possible to define a default state that'd be always an object otherwise 96 | defaultState: {get() { return {}; }}, 97 | // dispatch a bubbling, cancelable, custom event 98 | // through the first known/available node 99 | dispatch: {value(type, detail) { 100 | const {_wire$} = this; 101 | if (_wire$) { 102 | const event = new CustomEvent(type, { 103 | bubbles: true, 104 | cancelable: true, 105 | detail 106 | }); 107 | event.component = this; 108 | return (_wire$.dispatchEvent ? 109 | _wire$ : 110 | _wire$.firstChild 111 | ).dispatchEvent(event); 112 | } 113 | return false; 114 | }}, 115 | // setting some property state through a new object 116 | // or a callback, triggers also automatically a render 117 | // unless explicitly specified to not do so (render === false) 118 | setState: {value(state, render) { 119 | const target = this.state; 120 | const source = typeof state === 'function' ? state.call(this, target) : state; 121 | for (const key in source) target[key] = source[key]; 122 | if (render !== false) 123 | this.render(); 124 | return this; 125 | }} 126 | } 127 | ); 128 | } 129 | exports.setup = setup 130 | 131 | // instead of a secret key I could've used a WeakMap 132 | // However, attaching a property directly will result 133 | // into better performance with thousands of components 134 | // hanging around, and less memory pressure caused by the WeakMap 135 | const lazyGetter = (type, fn) => { 136 | const secret = '_' + type + '$'; 137 | return { 138 | get() { 139 | return this[secret] || setValue(this, secret, fn.call(this, type)); 140 | }, 141 | set(value) { 142 | setValue(this, secret, value); 143 | } 144 | }; 145 | }; 146 | 147 | // shortcut to set value on get or set(value) 148 | const setValue = (self, secret, value) => 149 | Object.defineProperty(self, secret, { 150 | configurable: true, 151 | value: typeof value === 'function' ? 152 | function () { 153 | return (self._wire$ = value.apply(this, arguments)); 154 | } : 155 | value 156 | })[secret] 157 | ; 158 | 159 | Object.defineProperties( 160 | Component.prototype, 161 | { 162 | // used to distinguish better than instanceof 163 | ELEMENT_NODE: {value: 1}, 164 | nodeType: {value: -1} 165 | } 166 | ); 167 | -------------------------------------------------------------------------------- /cjs/hyper/render.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap')); 3 | const tta = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/template-tag-arguments')); 4 | 5 | const {OWNER_SVG_ELEMENT} = require('../shared/constants.js'); 6 | const {Tagger} = require('../objects/Updates.js'); 7 | 8 | // a weak collection of contexts that 9 | // are already known to hyperHTML 10 | const bewitched = new WeakMap; 11 | 12 | // better known as hyper.bind(node), the render is 13 | // the main tag function in charge of fully upgrading 14 | // or simply updating, contexts used as hyperHTML targets. 15 | // The `this` context is either a regular DOM node or a fragment. 16 | function render() { 17 | const wicked = bewitched.get(this); 18 | const args = tta.apply(null, arguments); 19 | if (wicked && wicked.template === args[0]) { 20 | wicked.tagger.apply(null, args); 21 | } else { 22 | upgrade.apply(this, args); 23 | } 24 | return this; 25 | } 26 | 27 | // an upgrade is in charge of collecting template info, 28 | // parse it once, if unknown, to map all interpolations 29 | // as single DOM callbacks, relate such template 30 | // to the current context, and render it after cleaning the context up 31 | function upgrade(template) { 32 | const type = OWNER_SVG_ELEMENT in this ? 'svg' : 'html'; 33 | const tagger = new Tagger(type); 34 | bewitched.set(this, {tagger, template: template}); 35 | this.textContent = ''; 36 | this.appendChild(tagger.apply(null, arguments)); 37 | } 38 | 39 | Object.defineProperty(exports, '__esModule', {value: true}).default = render; 40 | -------------------------------------------------------------------------------- /cjs/hyper/wire.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap')); 3 | const tta = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/template-tag-arguments')); 4 | 5 | const Wire = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('hyperhtml-wire')); 6 | 7 | const {Tagger} = require('../objects/Updates.js'); 8 | 9 | // all wires used per each context 10 | const wires = new WeakMap; 11 | 12 | // A wire is a callback used as tag function 13 | // to lazily relate a generic object to a template literal. 14 | // hyper.wire(user)`

${user.name}
`; => the div#user 15 | // This provides the ability to have a unique DOM structure 16 | // related to a unique JS object through a reusable template literal. 17 | // A wire can specify a type, as svg or html, and also an id 18 | // via html:id or :id convention. Such :id allows same JS objects 19 | // to be associated to different DOM structures accordingly with 20 | // the used template literal without losing previously rendered parts. 21 | const wire = (obj, type) => obj == null ? 22 | content(type || 'html') : 23 | weakly(obj, type || 'html'); 24 | 25 | // A wire content is a virtual reference to one or more nodes. 26 | // It's represented by either a DOM node, or an Array. 27 | // In both cases, the wire content role is to simply update 28 | // all nodes through the list of related callbacks. 29 | // In few words, a wire content is like an invisible parent node 30 | // in charge of updating its content like a bound element would do. 31 | const content = type => { 32 | let wire, tagger, template; 33 | return function () { 34 | const args = tta.apply(null, arguments); 35 | if (template !== args[0]) { 36 | template = args[0]; 37 | tagger = new Tagger(type); 38 | wire = wireContent(tagger.apply(tagger, args)); 39 | } else { 40 | tagger.apply(tagger, args); 41 | } 42 | return wire; 43 | }; 44 | }; 45 | 46 | // wires are weakly created through objects. 47 | // Each object can have multiple wires associated 48 | // and this is thanks to the type + :id feature. 49 | const weakly = (obj, type) => { 50 | const i = type.indexOf(':'); 51 | let wire = wires.get(obj); 52 | let id = type; 53 | if (-1 < i) { 54 | id = type.slice(i + 1); 55 | type = type.slice(0, i) || 'html'; 56 | } 57 | if (!wire) 58 | wires.set(obj, wire = {}); 59 | return wire[id] || (wire[id] = content(type)); 60 | }; 61 | 62 | // A document fragment loses its nodes 63 | // as soon as it is appended into another node. 64 | // This has the undesired effect of losing wired content 65 | // on a second render call, because (by then) the fragment would be empty: 66 | // no longer providing access to those sub-nodes that ultimately need to 67 | // stay associated with the original interpolation. 68 | // To prevent hyperHTML from forgetting about a fragment's sub-nodes, 69 | // fragments are instead returned as an Array of nodes or, if there's only one entry, 70 | // as a single referenced node which, unlike fragments, will indeed persist 71 | // wire content throughout multiple renderings. 72 | // The initial fragment, at this point, would be used as unique reference to this 73 | // array of nodes or to this single referenced node. 74 | const wireContent = node => { 75 | const childNodes = node.childNodes; 76 | const {length} = childNodes; 77 | return length === 1 ? 78 | childNodes[0] : 79 | (length ? new Wire(childNodes) : node); 80 | }; 81 | 82 | exports.content = content; 83 | exports.weakly = weakly; 84 | Object.defineProperty(exports, '__esModule', {value: true}).default = wire; 85 | -------------------------------------------------------------------------------- /cjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*! (c) Andrea Giammarchi (ISC) */ 3 | const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap')); 4 | const WeakSet = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/essential-weakset')); 5 | 6 | const diff = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('domdiff')); 7 | const Component = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./classes/Component.js')); 8 | const {setup} = require('./classes/Component.js'); 9 | const Intent = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./objects/Intent.js')); 10 | const {observe, Tagger} = require('./objects/Updates.js'); 11 | const wire = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./hyper/wire.js')); 12 | const {content, weakly} = require('./hyper/wire.js'); 13 | const render = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./hyper/render.js')); 14 | 15 | // all functions are self bound to the right context 16 | // you can do the following 17 | // const {bind, wire} = hyperHTML; 18 | // and use them right away: bind(node)`hello!`; 19 | const bind = context => render.bind(context); 20 | const define = Intent.define; 21 | const tagger = Tagger.prototype; 22 | 23 | hyper.Component = Component; 24 | hyper.bind = bind; 25 | hyper.define = define; 26 | hyper.diff = diff; 27 | hyper.hyper = hyper; 28 | hyper.observe = observe; 29 | hyper.tagger = tagger; 30 | hyper.wire = wire; 31 | 32 | // exported as shared utils 33 | // for projects based on hyperHTML 34 | // that don't necessarily need upfront polyfills 35 | // i.e. those still targeting IE 36 | hyper._ = { 37 | WeakMap, 38 | WeakSet 39 | }; 40 | 41 | // the wire content is the lazy defined 42 | // html or svg property of each hyper.Component 43 | setup(content); 44 | 45 | // everything is exported directly or through the 46 | // hyperHTML callback, when used as top level script 47 | exports.Component = Component; 48 | exports.bind = bind; 49 | exports.define = define; 50 | exports.diff = diff; 51 | exports.hyper = hyper; 52 | exports.observe = observe; 53 | exports.tagger = tagger; 54 | exports.wire = wire; 55 | 56 | // by default, hyperHTML is a smart function 57 | // that "magically" understands what's the best 58 | // thing to do with passed arguments 59 | function hyper(HTML) { 60 | return arguments.length < 2 ? 61 | (HTML == null ? 62 | content('html') : 63 | (typeof HTML === 'string' ? 64 | hyper.wire(null, HTML) : 65 | ('raw' in HTML ? 66 | content('html')(HTML) : 67 | ('nodeType' in HTML ? 68 | hyper.bind(HTML) : 69 | weakly(HTML, 'html') 70 | ) 71 | ) 72 | )) : 73 | ('raw' in HTML ? 74 | content('html') : hyper.wire 75 | ).apply(null, arguments); 76 | } 77 | Object.defineProperty(exports, '__esModule', {value: true}).default = hyper 78 | -------------------------------------------------------------------------------- /cjs/objects/Intent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const attributes = {}; 3 | const intents = {}; 4 | const keys = []; 5 | const hasOwnProperty = intents.hasOwnProperty; 6 | 7 | let length = 0; 8 | 9 | Object.defineProperty(exports, '__esModule', {value: true}).default = { 10 | 11 | // used to invoke right away hyper:attributes 12 | attributes, 13 | 14 | // hyperHTML.define('intent', (object, update) => {...}) 15 | // can be used to define a third parts update mechanism 16 | // when every other known mechanism failed. 17 | // hyper.define('user', info => info.name); 18 | // hyper(node)`

${{user}}

`; 19 | define: (intent, callback) => { 20 | if (intent.indexOf('-') < 0) { 21 | if (!(intent in intents)) { 22 | length = keys.push(intent); 23 | } 24 | intents[intent] = callback; 25 | } else { 26 | attributes[intent] = callback; 27 | } 28 | }, 29 | 30 | // this method is used internally as last resort 31 | // to retrieve a value out of an object 32 | invoke: (object, callback) => { 33 | for (let i = 0; i < length; i++) { 34 | let key = keys[i]; 35 | if (hasOwnProperty.call(object, key)) { 36 | return intents[key](object[key], callback); 37 | } 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type":"commonjs"} -------------------------------------------------------------------------------- /cjs/shared/constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Node.CONSTANTS 3 | // 'cause some engine has no global Node defined 4 | // (i.e. Node, NativeScript, basicHTML ... ) 5 | const ELEMENT_NODE = 1; 6 | exports.ELEMENT_NODE = ELEMENT_NODE; 7 | const DOCUMENT_FRAGMENT_NODE = 11; 8 | exports.DOCUMENT_FRAGMENT_NODE = DOCUMENT_FRAGMENT_NODE; 9 | 10 | // SVG related constants 11 | const OWNER_SVG_ELEMENT = 'ownerSVGElement'; 12 | exports.OWNER_SVG_ELEMENT = OWNER_SVG_ELEMENT; 13 | 14 | // Custom Elements / MutationObserver constants 15 | const CONNECTED = 'connected'; 16 | exports.CONNECTED = CONNECTED; 17 | const DISCONNECTED = 'dis' + CONNECTED; 18 | exports.DISCONNECTED = DISCONNECTED; 19 | -------------------------------------------------------------------------------- /cjs/shared/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | -------------------------------------------------------------------------------- /esm/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "globals": { 4 | "setTimeout": true, 5 | "clearTimeout": true, 6 | "MutationObserver": true, 7 | "Promise": true, 8 | "document": true 9 | }, 10 | "parserOptions": { 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "no-case-declarations": 0, 15 | "no-fallthrough": 0 16 | } 17 | } -------------------------------------------------------------------------------- /esm/classes/Component.js: -------------------------------------------------------------------------------- 1 | import CustomEvent from '@ungap/custom-event'; 2 | import Map from '@ungap/essential-map'; 3 | import WeakMap from '@ungap/weakmap'; 4 | 5 | // hyperHTML.Component is a very basic class 6 | // able to create Custom Elements like components 7 | // including the ability to listen to connect/disconnect 8 | // events via onconnect/ondisconnect attributes 9 | // Components can be created imperatively or declaratively. 10 | // The main difference is that declared components 11 | // will not automatically render on setState(...) 12 | // to simplify state handling on render. 13 | export default function Component() { 14 | return this; // this is needed in Edge !!! 15 | } 16 | 17 | // Component is lazily setup because it needs 18 | // wire mechanism as lazy content 19 | export function setup(content) { 20 | // there are various weakly referenced variables in here 21 | // and mostly are to use Component.for(...) static method. 22 | const children = new WeakMap; 23 | const create = Object.create; 24 | const createEntry = (wm, id, component) => { 25 | wm.set(id, component); 26 | return component; 27 | }; 28 | const get = (Class, info, context, id) => { 29 | const relation = info.get(Class) || relate(Class, info); 30 | switch (typeof id) { 31 | case 'object': 32 | case 'function': 33 | const wm = relation.w || (relation.w = new WeakMap); 34 | return wm.get(id) || createEntry(wm, id, new Class(context)); 35 | default: 36 | const sm = relation.p || (relation.p = create(null)); 37 | return sm[id] || (sm[id] = new Class(context)); 38 | } 39 | }; 40 | const relate = (Class, info) => { 41 | const relation = {w: null, p: null}; 42 | info.set(Class, relation); 43 | return relation; 44 | }; 45 | const set = context => { 46 | const info = new Map; 47 | children.set(context, info); 48 | return info; 49 | }; 50 | // The Component Class 51 | Object.defineProperties( 52 | Component, 53 | { 54 | // Component.for(context[, id]) is a convenient way 55 | // to automatically relate data/context to children components 56 | // If not created yet, the new Component(context) is weakly stored 57 | // and after that same instance would always be returned. 58 | for: { 59 | configurable: true, 60 | value(context, id) { 61 | return get( 62 | this, 63 | children.get(context) || set(context), 64 | context, 65 | id == null ? 66 | 'default' : id 67 | ); 68 | } 69 | } 70 | } 71 | ); 72 | Object.defineProperties( 73 | Component.prototype, 74 | { 75 | // all events are handled with the component as context 76 | handleEvent: {value(e) { 77 | const ct = e.currentTarget; 78 | this[ 79 | ('getAttribute' in ct && ct.getAttribute('data-call')) || 80 | ('on' + e.type) 81 | ](e); 82 | }}, 83 | // components will lazily define html or svg properties 84 | // as soon as these are invoked within the .render() method 85 | // Such render() method is not provided by the base class 86 | // but it must be available through the Component extend. 87 | // Declared components could implement a 88 | // render(props) method too and use props as needed. 89 | html: lazyGetter('html', content), 90 | svg: lazyGetter('svg', content), 91 | // the state is a very basic/simple mechanism inspired by Preact 92 | state: lazyGetter('state', function () { return this.defaultState; }), 93 | // it is possible to define a default state that'd be always an object otherwise 94 | defaultState: {get() { return {}; }}, 95 | // dispatch a bubbling, cancelable, custom event 96 | // through the first known/available node 97 | dispatch: {value(type, detail) { 98 | const {_wire$} = this; 99 | if (_wire$) { 100 | const event = new CustomEvent(type, { 101 | bubbles: true, 102 | cancelable: true, 103 | detail 104 | }); 105 | event.component = this; 106 | return (_wire$.dispatchEvent ? 107 | _wire$ : 108 | _wire$.firstChild 109 | ).dispatchEvent(event); 110 | } 111 | return false; 112 | }}, 113 | // setting some property state through a new object 114 | // or a callback, triggers also automatically a render 115 | // unless explicitly specified to not do so (render === false) 116 | setState: {value(state, render) { 117 | const target = this.state; 118 | const source = typeof state === 'function' ? state.call(this, target) : state; 119 | for (const key in source) target[key] = source[key]; 120 | if (render !== false) 121 | this.render(); 122 | return this; 123 | }} 124 | } 125 | ); 126 | } 127 | 128 | // instead of a secret key I could've used a WeakMap 129 | // However, attaching a property directly will result 130 | // into better performance with thousands of components 131 | // hanging around, and less memory pressure caused by the WeakMap 132 | const lazyGetter = (type, fn) => { 133 | const secret = '_' + type + '$'; 134 | return { 135 | get() { 136 | return this[secret] || setValue(this, secret, fn.call(this, type)); 137 | }, 138 | set(value) { 139 | setValue(this, secret, value); 140 | } 141 | }; 142 | }; 143 | 144 | // shortcut to set value on get or set(value) 145 | const setValue = (self, secret, value) => 146 | Object.defineProperty(self, secret, { 147 | configurable: true, 148 | value: typeof value === 'function' ? 149 | function () { 150 | return (self._wire$ = value.apply(this, arguments)); 151 | } : 152 | value 153 | })[secret] 154 | ; 155 | 156 | Object.defineProperties( 157 | Component.prototype, 158 | { 159 | // used to distinguish better than instanceof 160 | ELEMENT_NODE: {value: 1}, 161 | nodeType: {value: -1} 162 | } 163 | ); 164 | -------------------------------------------------------------------------------- /esm/hyper/render.js: -------------------------------------------------------------------------------- 1 | import WeakMap from '@ungap/weakmap'; 2 | import tta from '@ungap/template-tag-arguments'; 3 | 4 | import {OWNER_SVG_ELEMENT} from '../shared/constants.js'; 5 | import {Tagger} from '../objects/Updates.js'; 6 | 7 | // a weak collection of contexts that 8 | // are already known to hyperHTML 9 | const bewitched = new WeakMap; 10 | 11 | // better known as hyper.bind(node), the render is 12 | // the main tag function in charge of fully upgrading 13 | // or simply updating, contexts used as hyperHTML targets. 14 | // The `this` context is either a regular DOM node or a fragment. 15 | function render() { 16 | const wicked = bewitched.get(this); 17 | const args = tta.apply(null, arguments); 18 | if (wicked && wicked.template === args[0]) { 19 | wicked.tagger.apply(null, args); 20 | } else { 21 | upgrade.apply(this, args); 22 | } 23 | return this; 24 | } 25 | 26 | // an upgrade is in charge of collecting template info, 27 | // parse it once, if unknown, to map all interpolations 28 | // as single DOM callbacks, relate such template 29 | // to the current context, and render it after cleaning the context up 30 | function upgrade(template) { 31 | const type = OWNER_SVG_ELEMENT in this ? 'svg' : 'html'; 32 | const tagger = new Tagger(type); 33 | bewitched.set(this, {tagger, template: template}); 34 | this.textContent = ''; 35 | this.appendChild(tagger.apply(null, arguments)); 36 | } 37 | 38 | export default render; 39 | -------------------------------------------------------------------------------- /esm/hyper/wire.js: -------------------------------------------------------------------------------- 1 | import WeakMap from '@ungap/weakmap'; 2 | import tta from '@ungap/template-tag-arguments'; 3 | 4 | import Wire from 'hyperhtml-wire'; 5 | 6 | import {Tagger} from '../objects/Updates.js'; 7 | 8 | // all wires used per each context 9 | const wires = new WeakMap; 10 | 11 | // A wire is a callback used as tag function 12 | // to lazily relate a generic object to a template literal. 13 | // hyper.wire(user)`
${user.name}
`; => the div#user 14 | // This provides the ability to have a unique DOM structure 15 | // related to a unique JS object through a reusable template literal. 16 | // A wire can specify a type, as svg or html, and also an id 17 | // via html:id or :id convention. Such :id allows same JS objects 18 | // to be associated to different DOM structures accordingly with 19 | // the used template literal without losing previously rendered parts. 20 | const wire = (obj, type) => obj == null ? 21 | content(type || 'html') : 22 | weakly(obj, type || 'html'); 23 | 24 | // A wire content is a virtual reference to one or more nodes. 25 | // It's represented by either a DOM node, or an Array. 26 | // In both cases, the wire content role is to simply update 27 | // all nodes through the list of related callbacks. 28 | // In few words, a wire content is like an invisible parent node 29 | // in charge of updating its content like a bound element would do. 30 | const content = type => { 31 | let wire, tagger, template; 32 | return function () { 33 | const args = tta.apply(null, arguments); 34 | if (template !== args[0]) { 35 | template = args[0]; 36 | tagger = new Tagger(type); 37 | wire = wireContent(tagger.apply(tagger, args)); 38 | } else { 39 | tagger.apply(tagger, args); 40 | } 41 | return wire; 42 | }; 43 | }; 44 | 45 | // wires are weakly created through objects. 46 | // Each object can have multiple wires associated 47 | // and this is thanks to the type + :id feature. 48 | const weakly = (obj, type) => { 49 | const i = type.indexOf(':'); 50 | let wire = wires.get(obj); 51 | let id = type; 52 | if (-1 < i) { 53 | id = type.slice(i + 1); 54 | type = type.slice(0, i) || 'html'; 55 | } 56 | if (!wire) 57 | wires.set(obj, wire = {}); 58 | return wire[id] || (wire[id] = content(type)); 59 | }; 60 | 61 | // A document fragment loses its nodes 62 | // as soon as it is appended into another node. 63 | // This has the undesired effect of losing wired content 64 | // on a second render call, because (by then) the fragment would be empty: 65 | // no longer providing access to those sub-nodes that ultimately need to 66 | // stay associated with the original interpolation. 67 | // To prevent hyperHTML from forgetting about a fragment's sub-nodes, 68 | // fragments are instead returned as an Array of nodes or, if there's only one entry, 69 | // as a single referenced node which, unlike fragments, will indeed persist 70 | // wire content throughout multiple renderings. 71 | // The initial fragment, at this point, would be used as unique reference to this 72 | // array of nodes or to this single referenced node. 73 | const wireContent = node => { 74 | const childNodes = node.childNodes; 75 | const {length} = childNodes; 76 | return length === 1 ? 77 | childNodes[0] : 78 | (length ? new Wire(childNodes) : node); 79 | }; 80 | 81 | export { content, weakly }; 82 | export default wire; 83 | -------------------------------------------------------------------------------- /esm/index.d.ts: -------------------------------------------------------------------------------- 1 | import hyper from ".."; 2 | export * from '..'; 3 | export default hyper; 4 | -------------------------------------------------------------------------------- /esm/index.js: -------------------------------------------------------------------------------- 1 | /*! (c) Andrea Giammarchi (ISC) */ 2 | import WeakMap from '@ungap/weakmap'; 3 | import WeakSet from '@ungap/essential-weakset'; 4 | 5 | import diff from 'domdiff'; 6 | import Component, {setup} from './classes/Component.js'; 7 | import Intent from './objects/Intent.js'; 8 | import {observe, Tagger} from './objects/Updates.js'; 9 | import wire, {content, weakly} from './hyper/wire.js'; 10 | import render from './hyper/render.js'; 11 | 12 | // all functions are self bound to the right context 13 | // you can do the following 14 | // const {bind, wire} = hyperHTML; 15 | // and use them right away: bind(node)`hello!`; 16 | const bind = context => render.bind(context); 17 | const define = Intent.define; 18 | const tagger = Tagger.prototype; 19 | 20 | hyper.Component = Component; 21 | hyper.bind = bind; 22 | hyper.define = define; 23 | hyper.diff = diff; 24 | hyper.hyper = hyper; 25 | hyper.observe = observe; 26 | hyper.tagger = tagger; 27 | hyper.wire = wire; 28 | 29 | // exported as shared utils 30 | // for projects based on hyperHTML 31 | // that don't necessarily need upfront polyfills 32 | // i.e. those still targeting IE 33 | hyper._ = { 34 | WeakMap, 35 | WeakSet 36 | }; 37 | 38 | // the wire content is the lazy defined 39 | // html or svg property of each hyper.Component 40 | setup(content); 41 | 42 | // everything is exported directly or through the 43 | // hyperHTML callback, when used as top level script 44 | export {Component, bind, define, diff, hyper, observe, tagger, wire}; 45 | 46 | // by default, hyperHTML is a smart function 47 | // that "magically" understands what's the best 48 | // thing to do with passed arguments 49 | export default function hyper(HTML) { 50 | return arguments.length < 2 ? 51 | (HTML == null ? 52 | content('html') : 53 | (typeof HTML === 'string' ? 54 | hyper.wire(null, HTML) : 55 | ('raw' in HTML ? 56 | content('html')(HTML) : 57 | ('nodeType' in HTML ? 58 | hyper.bind(HTML) : 59 | weakly(HTML, 'html') 60 | ) 61 | ) 62 | )) : 63 | ('raw' in HTML ? 64 | content('html') : hyper.wire 65 | ).apply(null, arguments); 66 | } 67 | -------------------------------------------------------------------------------- /esm/objects/Intent.js: -------------------------------------------------------------------------------- 1 | const attributes = {}; 2 | const intents = {}; 3 | const keys = []; 4 | const hasOwnProperty = intents.hasOwnProperty; 5 | 6 | let length = 0; 7 | 8 | export default { 9 | 10 | // used to invoke right away hyper:attributes 11 | attributes, 12 | 13 | // hyperHTML.define('intent', (object, update) => {...}) 14 | // can be used to define a third parts update mechanism 15 | // when every other known mechanism failed. 16 | // hyper.define('user', info => info.name); 17 | // hyper(node)`

${{user}}

`; 18 | define: (intent, callback) => { 19 | if (intent.indexOf('-') < 0) { 20 | if (!(intent in intents)) { 21 | length = keys.push(intent); 22 | } 23 | intents[intent] = callback; 24 | } else { 25 | attributes[intent] = callback; 26 | } 27 | }, 28 | 29 | // this method is used internally as last resort 30 | // to retrieve a value out of an object 31 | invoke: (object, callback) => { 32 | for (let i = 0; i < length; i++) { 33 | let key = keys[i]; 34 | if (hasOwnProperty.call(object, key)) { 35 | return intents[key](object[key], callback); 36 | } 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /esm/shared/constants.js: -------------------------------------------------------------------------------- 1 | // Node.CONSTANTS 2 | // 'cause some engine has no global Node defined 3 | // (i.e. Node, NativeScript, basicHTML ... ) 4 | export const ELEMENT_NODE = 1; 5 | export const DOCUMENT_FRAGMENT_NODE = 11; 6 | 7 | // SVG related constants 8 | export const OWNER_SVG_ELEMENT = 'ownerSVGElement'; 9 | 10 | // Custom Elements / MutationObserver constants 11 | export const CONNECTED = 'connected'; 12 | export const DISCONNECTED = 'dis' + CONNECTED; 13 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | type TemplateFunction = (template: TemplateStringsArray, ...values: any[]) => T; 2 | export type BoundTemplateFunction = TemplateFunction; 3 | export type WiredTemplateFunction = TemplateFunction; 4 | 5 | export declare class Component { 6 | static for(this: new() => TComponent, context: object, identity?: any): TComponent; 7 | handleEvent(e: Event): void; 8 | html: WiredTemplateFunction; 9 | svg: WiredTemplateFunction; 10 | state: T; 11 | get defaultState(): T; 12 | setState(state: Partial | ((this: this, state: T) => Partial), render?: boolean): this; 13 | dispatch(type: string, detail?: any): boolean; 14 | } 15 | 16 | export declare function bind(element: T): BoundTemplateFunction; 17 | 18 | export declare function define(intent: string, callback: Function): void; 19 | 20 | export declare function wire(identity?: object | null, type?: 'html' | 'svg'): WiredTemplateFunction; 21 | export declare function wire(identity?: object | null, type_id?: string): WiredTemplateFunction; 22 | 23 | export declare const hyper: { 24 | Component: typeof Component; 25 | bind: typeof bind; 26 | define: typeof define; 27 | hyper: typeof hyper; 28 | wire: typeof wire; 29 | 30 | // hyper(null, 'html')`HTML` 31 | (identity: null | undefined, type?: 'html' | 'svg'): WiredTemplateFunction; 32 | 33 | // hyper('html')`HTML` 34 | (type: 'html' | 'svg'): WiredTemplateFunction; 35 | 36 | // hyper(element)`HTML` 37 | (element: T): BoundTemplateFunction; 38 | 39 | // hyper`HTML` 40 | (template: TemplateStringsArray, ...values: any[]): any; 41 | 42 | // hyper(obj, 'html:id')`HTML` 43 | // hyper(obj)`HTML` 44 | (identity: object, type?: 'html' | 'svg'): WiredTemplateFunction; 45 | (identity: object, type_id?: string): WiredTemplateFunction; 46 | 47 | // hyper()`HTML` 48 | (): WiredTemplateFunction; 49 | }; 50 | 51 | export default hyper; 52 | -------------------------------------------------------------------------------- /logo.txt: -------------------------------------------------------------------------------- 1 | .,,. 2 | .;srrrrr: 3 | .si, .;S; 4 | ,;;rrS; .is 5 | :ss;,,,ri;...,ri: 6 | .ri;. :rrrrr;. 7 | ;ir. 8 | .. ,:;;;si: .. 9 | .,:;rr: ,ss;::;ii, :rr;:,. 10 | ;ssr:,. .is. .ii. .,:rss; 11 | :;rr;:,. .is. .ii. .,:;rr;: 12 | .,:;r; ,is;::;ss, ;r;:,. 13 | :is;;;:, 14 | .ri;. 15 | .;rrrrr: ;ir. 16 | :ir,...;ir,,,;ss: 17 | si. ;Ssr;;, 18 | ;S;. ,is. 19 | :rrrrrs;. 20 | .,,. 21 | -------------------------------------------------------------------------------- /logo/basichtml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/logo/basichtml.png -------------------------------------------------------------------------------- /logo/basichtml.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 21 | 24 | 28 | 29 | 32 | 36 | 41 | 45 | 46 | 47 | 50 | 54 | 59 | 63 | 64 | 65 | 66 | 67 | 69 | 70 | 72 | image/svg+xml 73 | 75 | 76 | 77 | 78 | 79 | 82 | 84 | 91 | 94 | 99 | 102 | 105 | 109 | 113 | 114 | 115 | 116 | 120 | 124 | 125 | 129 | 133 | 134 | 138 | 142 | 143 | 147 | 151 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /logo/creepy-html.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/logo/creepy-html.jpg -------------------------------------------------------------------------------- /logo/hyperhtml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/logo/hyperhtml.png -------------------------------------------------------------------------------- /logo/hyperhtml.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 21 | 24 | 28 | 29 | 32 | 36 | 41 | 45 | 46 | 47 | 50 | 54 | 59 | 63 | 64 | 65 | 66 | 67 | 69 | 70 | 72 | image/svg+xml 73 | 75 | 76 | 77 | 78 | 79 | 82 | 84 | 91 | 94 | 99 | 102 | 105 | 109 | 113 | 114 | 115 | 116 | 120 | 124 | 125 | 129 | 133 | 134 | 138 | 142 | 143 | 147 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /logo/nativehtml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/logo/nativehtml.png -------------------------------------------------------------------------------- /logo/viper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/logo/viper.png -------------------------------------------------------------------------------- /logo/viperhtml.inkscape.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 30 | 33 | 38 | 39 | 42 | 47 | 52 | 57 | 58 | 59 | 62 | 67 | 72 | 77 | 78 | 79 | 80 | 81 | 100 | 102 | 103 | 105 | image/svg+xml 106 | 108 | 109 | 110 | 111 | 112 | 117 | 122 | 127 | 128 | 134 | 137 | 144 | 147 | 151 | 156 | 157 | 161 | 166 | 167 | 171 | 176 | 177 | 183 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /logo/viperhtml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/logo/viperhtml.png -------------------------------------------------------------------------------- /logo/viperhtml.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 21 | 24 | 28 | 29 | 32 | 36 | 41 | 45 | 46 | 47 | 50 | 54 | 59 | 63 | 64 | 65 | 66 | 67 | 69 | 70 | 72 | image/svg+xml 73 | 75 | 76 | 77 | 78 | 79 | 82 | 87 | 92 | 93 | 97 | 100 | 107 | 110 | 114 | 118 | 119 | 123 | 127 | 128 | 132 | 136 | 137 | 141 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /no.js: -------------------------------------------------------------------------------- 1 | (function (hyperHTML) { 2 | /*! (c) Andrea Giammarchi (ISC) */ 3 | var uid = new Date * Math.random(); 4 | var templates = Object.create(null); 5 | fix('bind'); 6 | fix('wire'); 7 | function fix(name) { 8 | var method = hyperHTML[name]; 9 | hyperHTML[name] = function () { 10 | var fn = method.apply(null, arguments); 11 | return function () { 12 | return fn.apply(null, tagArguments.apply(null, arguments)); 13 | }; 14 | }; 15 | } 16 | function tagArguments() { 17 | var length = arguments.length; 18 | var template = []; 19 | var args = [template]; 20 | if (length) { 21 | var i = 0; 22 | var mod = typeof arguments[i] === 'string' ? 0 : 1; 23 | if (mod) template.push(''); 24 | while (i < length) { 25 | ((i + mod) % 2 ? args : template).push(arguments[i]); 26 | i++; 27 | } 28 | if (template.length < args.length) template.push(''); 29 | var key = template.join(uid); 30 | if (key in templates) { 31 | args[0] = templates[key]; 32 | } else { 33 | (templates[key] = template).raw = template; 34 | } 35 | } 36 | return args; 37 | } 38 | }(hyperHTML)); 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperhtml", 3 | "version": "2.34.2", 4 | "description": "A Fast & Light Virtual DOM Alternative", 5 | "homepage": "https://viperhtml.js.org/hyper.html", 6 | "unpkg": "min.js", 7 | "main": "cjs/index.js", 8 | "module": "esm/index.js", 9 | "types": "index.d.ts", 10 | "scripts": { 11 | "$": "npm-dollar", 12 | "build": "npm run $ build", 13 | "bundle": "npm run $ bundle", 14 | "test": "npm run $ test" 15 | }, 16 | "$": { 17 | "bundle": { 18 | "max": [ 19 | "rollup --config rollup.config.js", 20 | "$ clean.esm", 21 | "drop-babel-typeof index.js" 22 | ], 23 | "min": [ 24 | [ 25 | "echo \"/*! (c) Andrea Giammarchi (ISC) */$(", 26 | "uglifyjs index.js -c -m", 27 | ")\" > min.js" 28 | ] 29 | ], 30 | "ie": [ 31 | [ 32 | "rollup --config rollup.ie.js" 33 | ] 34 | ], 35 | "umd": [ 36 | [ 37 | "echo \"(function(A,G){if(typeof define=='function'&&define.amd)define([],G);else", 38 | "if(typeof module=='object'&&module.exports)module.exports=G();else", 39 | "A.hyperHTML=G()}(typeof self!='undefined'?self:this,function(){\">umd.js;cat", 40 | "min.js>>umd.js;echo \"return hyperHTML}));\">>umd.js" 41 | ] 42 | ], 43 | "esm": [ 44 | "cp min.js esm.js", 45 | "echo 'export default hyperHTML;' >> esm.js", 46 | "echo 'export const {Component, bind, define, diff, hyper, wire} = hyperHTML;' >> esm.js" 47 | ] 48 | }, 49 | "build": [ 50 | "$ cjs", 51 | "$ clean.cjs index.js", 52 | "$ clean.cjs classes/Component.js", 53 | "$ clean.cjs hyper/render.js", 54 | "$ clean.cjs objects/Updates.js", 55 | "$ clean.cjs shared/utils.js", 56 | "$ clean.cjs hyper/wire.js", 57 | "$ bundle.max", 58 | "$ bundle.min", 59 | "$ bundle.umd", 60 | "$ bundle.esm", 61 | "$ bundle.ie", 62 | "$ test", 63 | "$ size" 64 | ], 65 | "clean": { 66 | "cjs": "sed -i.bck 's/m.default : m/\\/* istanbul ignore next *\\/ m.default : \\/* istanbul ignore next *\\/ m/g' cjs/$1 && rm -f cjs/$1.bck", 67 | "esm": [ 68 | [ 69 | "cat index.js |", 70 | "sed 's/(exports)/(document)/' |", 71 | "sed 's/return exports;/return hyper;/' |", 72 | "sed -e 's/exports.*;//g' |", 73 | "sed '/var createContent$1 =/,/(document);/d' |", 74 | "sed 's/createContent$1/createContent/' |", 75 | "sed 's/({})/(document)/' |", 76 | "sed -e 's/var isNoOp =.*/var isNoOp = false;/' > index.clean" 77 | ], 78 | "mv index.clean index.js" 79 | ] 80 | }, 81 | "cjs": [ 82 | "ascjs ./esm ./cjs" 83 | ], 84 | "size": { 85 | "gzip": [ 86 | [ 87 | "cat index.js |", 88 | "wc -c;cat min.js |", 89 | "wc -c;gzip -c9 min.js |", 90 | "wc -c" 91 | ] 92 | ], 93 | "brotli": "cat min.js | brotli | wc -c" 94 | }, 95 | "test": [ 96 | "istanbul cover test/runner.js" 97 | ] 98 | }, 99 | "repository": { 100 | "type": "git", 101 | "url": "git+https://github.com/WebReflection/hyperhtml.git" 102 | }, 103 | "keywords": [ 104 | "dom", 105 | "diff", 106 | "performance", 107 | "template", 108 | "literals", 109 | "lightweight", 110 | "fast", 111 | "react", 112 | "virtual", 113 | "lit-html", 114 | "alternative" 115 | ], 116 | "author": "Andrea Giammarchi", 117 | "license": "ISC", 118 | "bugs": { 119 | "url": "https://github.com/WebReflection/hyperhtml/issues" 120 | }, 121 | "devDependencies": { 122 | "@babel/core": "7.10.3", 123 | "@babel/preset-env": "7.10.3", 124 | "ascjs": "4.0.1", 125 | "basichtml": "2.3.0", 126 | "drop-babel-typeof": "1.0.3", 127 | "istanbul": "0.4.5", 128 | "npm-dollar": "2.2.1", 129 | "rollup": "2.18.1", 130 | "rollup-plugin-babel": "4.4.0", 131 | "rollup-plugin-node-resolve": "5.2.0", 132 | "tressa": "0.3.1", 133 | "uglify-js": "3.10.0" 134 | }, 135 | "greenkeeper": { 136 | "ignore": [ 137 | "uglify-js", 138 | "rollup", 139 | "rollup-plugin-babel", 140 | "rollup-plugin-node-resolve" 141 | ] 142 | }, 143 | "dependencies": { 144 | "@ungap/create-content": "0.2.0", 145 | "@ungap/custom-event": "0.3.1", 146 | "@ungap/essential-map": "0.3.2", 147 | "@ungap/essential-weakset": "0.2.1", 148 | "@ungap/is-array": "0.2.0", 149 | "@ungap/template-tag-arguments": "0.5.0", 150 | "@ungap/weakmap": "0.2.1", 151 | "disconnected": "0.2.1", 152 | "domdiff": "2.2.2", 153 | "domtagger": "0.7.0", 154 | "hyperhtml-style": "0.1.2", 155 | "hyperhtml-wire": "2.1.1" 156 | }, 157 | "collective": { 158 | "type": "opencollective", 159 | "url": "https://opencollective.com/hyperhtml" 160 | }, 161 | "funding": { 162 | "type": "opencollective", 163 | "url": "https://opencollective.com/hyperhtml" 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import babel from 'rollup-plugin-babel'; 3 | 4 | export default { 5 | input: 'esm/index.js', 6 | plugins: [ 7 | resolve({module: true}), 8 | babel({presets: ["@babel/preset-env"]}) 9 | ], 10 | context: 'null', 11 | moduleContext: 'null', 12 | output: { 13 | exports: 'named', 14 | file: 'index.js', 15 | format: 'iife', 16 | name: 'hyperHTML' 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /rollup.ie.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import babel from 'rollup-plugin-babel'; 3 | 4 | export default { 5 | input: 'test/test.js', 6 | plugins: [ 7 | resolve({module: true}), 8 | babel({presets: ["@babel/preset-env"]}) 9 | ], 10 | context: 'null', 11 | moduleContext: 'null', 12 | output: { 13 | file: 'test/ie/test/test.js', 14 | format: 'iife' 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/OpenExchangeRates.js: -------------------------------------------------------------------------------- 1 | var OpenExchangeRates = function () { 2 | /*! (c) Andrea Giammarchi */ 3 | return function OpenExchangeRates(APP_ID) { 4 | var cache = {}; 5 | return { 6 | format: (amount, currency) => 7 | format(amount, currency), 8 | clear: (base) => 9 | delete cache[(base || 'USD').toUpperCase()], 10 | convert: (amount, from, to) => 11 | latest(from).then(info => 12 | format(info.rates[to.toUpperCase()] * amount, to)), 13 | currencies: () => 14 | cache.currencies || (cache.currencies = new Promise((res, rej) => { 15 | load(urlFor('currencies'), res, rej); 16 | })), 17 | latest: (base) => { 18 | base = (base || 'USD').toUpperCase(); 19 | return cache[base] || (cache[base] = new Promise((res, rej) => { 20 | load(`${urlFor('latest')}&base=${base}`, res, rej); 21 | })); 22 | } 23 | }; 24 | function urlFor(key) { 25 | return `https://openexchangerates.org/api/${key}.json?app_id=${APP_ID}`; 26 | } 27 | function latest(base) { 28 | base = (base || 'USD').toUpperCase(); 29 | return cache[base] || (cache[base] = new Promise((res, rej) => { 30 | load(`${urlFor('latest')}&base=${base}`, res, rej); 31 | })); 32 | } 33 | 34 | }; 35 | function format(amount, currency) { 36 | return new Intl.NumberFormat('en-US', { 37 | style: 'currency', 38 | currency: currency, 39 | minimumFractionDigits: 2, 40 | maximumFractionDigits: 8 41 | }) 42 | .format(amount) 43 | .replace(/^NaN$/, '') 44 | .replace(/^(\D+)/, '$1 ') 45 | .replace('BTC', '₿'); 46 | } 47 | function load(url, res, rej) { 48 | var xhr = new XMLHttpRequest(); 49 | xhr.open('get', url, true); 50 | xhr.onload = () => res(JSON.parse(xhr.responseText)); 51 | xhr.onerror = rej; 52 | xhr.send(null); 53 | } 54 | }(); -------------------------------------------------------------------------------- /test/abstracts.md: -------------------------------------------------------------------------------- 1 | # Abstract 2 | 3 | The following content represents ideal steps 4 | performed by hyperHTML to grant performance, 5 | recycle everything it can, and update layouts. 6 | 7 | It's rather a specification than current implementation, 8 | but the goal is to optimize such specification as much as possible 9 | and achieve best performance on top of that. 10 | 11 | 12 | 13 | ### Definitions 14 | 15 | `hyperHTML` is a function tag for template literals. 16 | 17 | Its unique `templateObject` argument is generated once 18 | implicitly invoked as template literal function-tag. 19 | http://www.ecma-international.org/ecma-262/6.0/#sec-gettemplateobject 20 | 21 | Its `context` MUST be a DOM Element. 22 | 23 | `hyperHTML.bind(context)` creates a new function-tag for such `context`. 24 | 25 | ```js 26 | const render = hyperHTML.bind(context); 27 | render` 28 | 29 | `; 30 | ``` 31 | 32 | A list of `paths` is an array of instructions used to address a specific target node. 33 | Each instruction also includes the kind of update operation to be performed on the target node. 34 | 35 | ```js 36 | // paths example 37 | [ 38 | {type: 'attr', path: ['children', 1, 'attributes', 2]}, 39 | {type: 'text', path: ['children', 1, 'childNodes', 0]}, 40 | {type: 'any', path: ['children', 5]}, 41 | {type: 'virtual', path: ['children', 1, 'children', 3, 'childNodes', 2]} 42 | ] 43 | ``` 44 | 45 | Retrieving a node through a path can be done by reducing its values. 46 | 47 | Updates are similarly defined through the following procedure. 48 | 49 | ```js 50 | paths.forEach((info, i) => { 51 | const target = info.path.reduce( 52 | (node, accessor) => node[accessor], 53 | fragment 54 | ); 55 | switch (info.type) { 56 | case 'attr': updates[i] = setAttribute(target); break; 57 | case 'any': updates[i] = setAnyContent(target); break; 58 | case 'text': updates[i] = setText(target); break; 59 | case 'virtual': updates[i] = setVirtualContent(target); break; 60 | } 61 | }); 62 | ``` 63 | 64 | The list of `updates` is an array containing functions in charge of updating each path through interpolations. 65 | ```js 66 | // updates through interpolated values 67 | interpolations.forEach((value, i) => updates[i](value)); 68 | ``` 69 | 70 | 71 | ### hyperHTML operations 72 | ``` 73 | hyperHTML(templateObject, ...interpolations) 74 | │ 75 | └▶ is current `context` known ? 76 | │ 77 | ├▶ YES 78 | │ 79 | │ is current `context` representing `templateObject` ? 80 | │ │ 81 | │ ├▶ YES ┌───────────────────────────────────────────────┐ 82 | │ │ ▼ │ 83 | │ │ invoke `updates` through `interpolations` │ 84 | │ │ │ 85 | │ │ return the current `context` │ 86 | │ │ │ 87 | └───┴▶ NO │ 88 | │ 89 | is `templateObject` known ? │ 90 | │ │ 91 | ┌───┼▶ YES │ 92 | │ │ │ 93 | │ │ clone deeply the `templateObject` `fragment` │ 94 | │ │ │ 95 | │ │ replace `context` content with the `fragment` │ 96 | │ │ │ 97 | │ │ retrieve via `paths` the nodes to update │ 98 | │ │ │ 99 | │ │ create a list of `updates` │ 100 | │ │ │ 101 | │ │ relate `templateObject` to current `context` │ 102 | │ │ │ 103 | │ │ relate `updates` to current `context` ──────────┘ 104 | │ │ 105 | │ └▶ NO 106 | │ 107 | │ create a `fragment` through `templateObject` 108 | │ 109 | │ create a list of `paths` to find out nodes 110 | │ 111 | │ relate `fragment` to current `templateObject` 112 | │ 113 | │ relate `paths` to current `templateObject` ──┐ 114 | │ │ 115 | └─────────────────────────────────────────────────────┘ 116 | ``` 117 | 118 | ### Weakly referenced 119 | 120 | ``` 121 | context 122 | ├▶ templateObject 123 | └▶ updates 124 | 125 | any 126 | └▶ wires 127 | ``` 128 | 129 | ### Strongly referenced 130 | 131 | Template literals are _forever_, there's no reason to create extra GC pressure. 132 | 133 | ``` 134 | templateObject 135 | ├▶ fragment 136 | └▶ paths 137 | ``` 138 | -------------------------------------------------------------------------------- /test/adopt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 |
before 9 |
    10 |
  • lonely
  • 11 |
NO
12 |
13 | 14 | 42 | -------------------------------------------------------------------------------- /test/adopt.js: -------------------------------------------------------------------------------- 1 | var wrap = document.body.appendChild(document.createElement('div')); 2 | wrap = document.createElement('div'); 3 | wrap.innerHTML = '
before
  • lonely
NO
'; 4 | 5 | var div = wrap.firstElementChild; 6 | var text = div.firstChild; 7 | var ul = div.firstElementChild; 8 | var hr = div.lastElementChild; 9 | var render = hyperHTML.adopt(wrap); 10 | var model = { 11 | test: 'after', 12 | text: 'after', 13 | list: [ 14 | {name: 'first'}, 15 | {name: 'second'} 16 | ], 17 | inBetween: 'OK' 18 | }; 19 | 20 | function update(render, model) { 21 | render` 22 |
23 | ${model.text} 24 |
    ${ 25 | model.list.map(item => `
  • ${item.name}
  • `) 26 | }
${ 27 | model.inBetween 28 | }
29 |
30 | `; 31 | } 32 | 33 | console.log('-----------------------------------------------------------------'); 34 | 35 | setTimeout(() => { 36 | update(render, model); 37 | }, 1000); -------------------------------------------------------------------------------- /test/article.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 38 | 39 | -------------------------------------------------------------------------------- /test/attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Attributes Order 5 | 6 | 7 | 52 | 53 | 54 |

55 | some content, also an input. 56 | 63 |

64 | 65 | 66 | -------------------------------------------------------------------------------- /test/basic-table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 56 | -------------------------------------------------------------------------------- /test/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/boot-speed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 |

Boot speed:

15 |
16 | 17 |
18 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /test/bundle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/cars-mithril.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Recycling benchmark (uber-inspired) 5 | 29 | 30 | 31 | 32 | 33 | 34 | 50 | 51 | -------------------------------------------------------------------------------- /test/cars-wired.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Recycling benchmark (uber-inspired) 5 | 30 | 31 | 32 | 33 | 34 | 35 | 72 | 73 | -------------------------------------------------------------------------------- /test/cars.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Recycling benchmark (uber-inspired) 5 | 6 | 32 | 33 | 34 | 35 | 36 | 73 | 74 | -------------------------------------------------------------------------------- /test/ce.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /test/connected.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | onconnected 8 | 9 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /test/dbmonster-bench.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 113 | 114 | 115 |
116 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /test/dbmonster.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #333; 3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | font-size: 14px; 5 | line-height: 1.42857143; 6 | margin: 0; 7 | } 8 | 9 | label { 10 | display: inline-block; 11 | font-weight: 700; 12 | margin-bottom: 5px; 13 | } 14 | 15 | #range { 16 | display: flex; 17 | } 18 | 19 | input[type=range] { 20 | display: block; 21 | width: 100%; 22 | margin-bottom: 10px; 23 | margin-top: 5px; 24 | } 25 | 26 | table { 27 | border-collapse: collapse; 28 | border-spacing: 0; 29 | } 30 | 31 | :before, 32 | :after { 33 | box-sizing: border-box; 34 | } 35 | 36 | .table > thead > tr > th, 37 | .table > tbody > tr > th, 38 | .table > tfoot > tr > th, 39 | .table > thead > tr > td, 40 | .table > tbody > tr > td, 41 | .table > tfoot > tr > td { 42 | border-top: 1px solid #ddd; 43 | line-height: 1.42857143; 44 | padding: 8px; 45 | vertical-align: top; 46 | } 47 | 48 | .table { 49 | width: 100%; 50 | } 51 | 52 | .table-striped > tbody > tr:nth-child(odd) > td, 53 | .table-striped > tbody > tr:nth-child(odd) > th { 54 | background: #f9f9f9; 55 | } 56 | 57 | .label { 58 | border-radius: .25em; 59 | color: #fff; 60 | display: inline; 61 | font-size: 75%; 62 | font-weight: 700; 63 | line-height: 1; 64 | padding: .2em .6em .3em; 65 | text-align: center; 66 | vertical-align: baseline; 67 | white-space: nowrap; 68 | } 69 | 70 | .label-success { 71 | background-color: #5cb85c; 72 | } 73 | 74 | .label-warning { 75 | background-color: #f0ad4e; 76 | } 77 | 78 | .popover { 79 | background-color: #fff; 80 | background-clip: padding-box; 81 | border: 1px solid #ccc; 82 | border: 1px solid rgba(0, 0, 0, .2); 83 | border-radius: 6px; 84 | box-shadow: 0 5px 10px rgba(0, 0, 0, .2); 85 | display: none; 86 | left: 0; 87 | max-width: 276px; 88 | padding: 1px; 89 | position: absolute; 90 | text-align: left; 91 | top: 0; 92 | white-space: normal; 93 | z-index: 1010; 94 | } 95 | 96 | .popover>.arrow:after { 97 | border-width: 10px; 98 | content: ""; 99 | } 100 | 101 | .popover.left { 102 | margin-left: -10px; 103 | } 104 | 105 | .popover.left > .arrow { 106 | border-right-width: 0; 107 | border-left-color: rgba(0, 0, 0, .25); 108 | margin-top: -11px; 109 | right: -11px; 110 | top: 50%; 111 | } 112 | 113 | .popover.left > .arrow:after { 114 | border-left-color: #fff; 115 | border-right-width: 0; 116 | bottom: -10px; 117 | content: " "; 118 | right: 1px; 119 | } 120 | 121 | .popover > .arrow { 122 | border-width: 11px; 123 | } 124 | 125 | .popover > .arrow, 126 | .popover>.arrow:after { 127 | border-color: transparent; 128 | border-style: solid; 129 | display: block; 130 | height: 0; 131 | position: absolute; 132 | width: 0; 133 | } 134 | 135 | .popover-content { 136 | padding: 9px 14px; 137 | } 138 | 139 | .Query { 140 | position: relative; 141 | } 142 | 143 | .Query:hover .popover { 144 | display: block; 145 | left: -100%; 146 | width: 100%; 147 | } 148 | -------------------------------------------------------------------------------- /test/dbmonster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 89 | 90 | 91 |
92 |
93 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /test/dbmonster.js: -------------------------------------------------------------------------------- 1 | var ENV = function () {'use strict'; 2 | var counter = 0; 3 | var data; 4 | var _base; 5 | if (!(_base = String.prototype).lpad) 6 | _base.lpad = function (padding, toLength) { 7 | return padding.repeat((toLength - this.length) / padding.length).concat(this); 8 | }; 9 | 10 | function formatElapsed(value) { 11 | var str = parseFloat(value).toFixed(2); 12 | if (value > 60) { 13 | var minutes = Math.floor(value / 60); 14 | var comps = (value % 60).toFixed(2).split('.'); 15 | var seconds = comps[0].lpad('0', 2); 16 | var ms = comps[1]; 17 | str = minutes + ":" + seconds + "." + ms; 18 | } 19 | return str; 20 | } 21 | 22 | function getElapsedClassName(elapsed) { 23 | var className = 'Query elapsed'; 24 | if (elapsed >= 10.0) { 25 | className += ' warn_long'; 26 | } 27 | else if (elapsed >= 1.0) { 28 | className += ' warn'; 29 | } 30 | else { 31 | className += ' short'; 32 | } 33 | return className; 34 | } 35 | 36 | function countClassName(queries) { 37 | var countClassName = "label"; 38 | if (queries >= 20) { 39 | countClassName += " label-important"; 40 | } 41 | else if (queries >= 10) { 42 | countClassName += " label-warning"; 43 | } 44 | else { 45 | countClassName += " label-success"; 46 | } 47 | return countClassName; 48 | } 49 | 50 | function updateQuery(object) { 51 | if (!object) { 52 | object = {}; 53 | } 54 | var elapsed = Math.random() * 15; 55 | object.elapsed = elapsed; 56 | object.formatElapsed = formatElapsed(elapsed); 57 | object.elapsedClassName = getElapsedClassName(elapsed); 58 | object.query = "SELECT blah FROM something"; 59 | object.waiting = Math.random() < 0.5; 60 | if (Math.random() < 0.2) { 61 | object.query = " in transaction"; 62 | } 63 | if (Math.random() < 0.1) { 64 | object.query = "vacuum"; 65 | } 66 | return object; 67 | } 68 | 69 | function cleanQuery(value) { 70 | if (value) { 71 | value.formatElapsed = ""; 72 | value.elapsedClassName = ""; 73 | value.query = ""; 74 | value.elapsed = null; 75 | value.waiting = null; 76 | } else { 77 | return { 78 | query: "***", 79 | formatElapsed: "", 80 | elapsedClassName: "" 81 | }; 82 | } 83 | } 84 | 85 | function generateRow(object, keepIdentity, counter) { 86 | var nbQueries = Math.floor((Math.random() * 10) + 1); 87 | if (!object) { 88 | object = {}; 89 | } 90 | object.lastMutationId = counter; 91 | object.nbQueries = nbQueries; 92 | if (!object.lastSample) { 93 | object.lastSample = {}; 94 | } 95 | if (!object.lastSample.topFiveQueries) { 96 | object.lastSample.topFiveQueries = []; 97 | } 98 | if (keepIdentity) { 99 | // for Angular optimization 100 | if (!object.lastSample.queries) { 101 | object.lastSample.queries = []; 102 | for (var l = 0; l < 12; l++) { 103 | object.lastSample.queries[l] = cleanQuery(); 104 | } 105 | } 106 | for (var j in object.lastSample.queries) { 107 | var value = object.lastSample.queries[j]; 108 | if (j <= nbQueries) { 109 | updateQuery(value); 110 | } else { 111 | cleanQuery(value); 112 | } 113 | } 114 | } else { 115 | object.lastSample.queries = []; 116 | for (var j = 0; j < 12; j++) { 117 | if (j < nbQueries) { 118 | var value = updateQuery(cleanQuery()); 119 | object.lastSample.queries.push(value); 120 | } else { 121 | object.lastSample.queries.push(cleanQuery()); 122 | } 123 | } 124 | } 125 | for (var i = 0; i < 5; i++) { 126 | var source = object.lastSample.queries[i]; 127 | object.lastSample.topFiveQueries[i] = source; 128 | } 129 | object.lastSample.nbQueries = nbQueries; 130 | object.lastSample.countClassName = countClassName(nbQueries); 131 | return object; 132 | } 133 | 134 | function getData(keepIdentity) { 135 | var oldData = data; 136 | if (!keepIdentity) { // reset for each tick when !keepIdentity 137 | data = []; 138 | for (var i = 1; i <= ENV.rows; i++) { 139 | data.push({ dbname: 'cluster' + i, query: "", formatElapsed: "", elapsedClassName: "" }); 140 | data.push({ dbname: 'cluster' + i + ' slave', query: "", formatElapsed: "", elapsedClassName: "" }); 141 | } 142 | } 143 | if (!data) { // first init when keepIdentity 144 | data = []; 145 | for (var i = 1; i <= ENV.rows; i++) { 146 | data.push({ dbname: 'cluster' + i }); 147 | data.push({ dbname: 'cluster' + i + ' slave' }); 148 | } 149 | oldData = data; 150 | } 151 | for (var i in data) { 152 | var row = data[i]; 153 | if (!keepIdentity && oldData && oldData[i]) { 154 | row.lastSample = oldData[i].lastSample; 155 | } 156 | if (!row.lastSample || Math.random() < ENV.mutations()) { 157 | counter = counter + 1; 158 | if (!keepIdentity) { 159 | row.lastSample = null; 160 | } 161 | generateRow(row, keepIdentity, counter); 162 | } else { 163 | data[i] = oldData[i]; 164 | } 165 | } 166 | return { 167 | toArray: function () { 168 | return data; 169 | } 170 | }; 171 | } 172 | 173 | var mutationsValue = 0.5; 174 | 175 | function mutations(value) { 176 | if (value) { 177 | mutationsValue = value; 178 | return mutationsValue; 179 | } else { 180 | return mutationsValue; 181 | } 182 | } 183 | 184 | var body = document.querySelector('body'); 185 | var theFirstChild = body.firstChild; 186 | 187 | var sliderContainer = document.createElement('div'); 188 | sliderContainer.style.cssText = "display: flex"; 189 | var slider = document.createElement('input'); 190 | var text = document.createElement('label'); 191 | text.innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%'; 192 | text.id = "ratioval"; 193 | slider.setAttribute("type", "range"); 194 | slider.style.cssText = 'margin-bottom: 10px; margin-top: 5px'; 195 | slider.addEventListener('change', function (e) { 196 | ENV.mutations(e.target.value / 100); 197 | document.querySelector('#ratioval').innerHTML = 'mutations : ' + (ENV.mutations() * 100).toFixed(0) + '%'; 198 | }); 199 | sliderContainer.appendChild(text); 200 | sliderContainer.appendChild(slider); 201 | body.insertBefore(sliderContainer, theFirstChild); 202 | 203 | return { 204 | generateData: getData, 205 | rows: 50, 206 | timeout: 0, 207 | mutations: mutations 208 | }; 209 | }(); -------------------------------------------------------------------------------- /test/dbmonster100.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var ENV = { 4 | rows: 100, 5 | timeout: 0 6 | }; 7 | 8 | var data = null; 9 | var data2 = null; 10 | 11 | function getTopFiveQueries(db) { 12 | var arr = db.samples[db.samples.length - 1].queries.slice(0, 5); 13 | while (arr.length < 5) { 14 | arr[arr.length] = { query: '', elapsed: 0 }; 15 | } 16 | return arr; 17 | } 18 | 19 | function assignDb(dbname) { 20 | var info = data.databases[dbname]; 21 | var r = Math.floor((Math.random() * 10) + 1); 22 | 23 | for (var i = 0; i < r; i++) { 24 | var q = { 25 | canvas_action: null, 26 | canvas_context_id: null, 27 | canvas_controller: null, 28 | canvas_hostname: null, 29 | canvas_job_tag: null, 30 | canvas_pid: null, 31 | elapsed: Math.random() * 15, 32 | query: "SELECT blah FROM something", 33 | waiting: Math.random() < 0.5 34 | }; 35 | 36 | if (Math.random() < 0.2) { 37 | q.query = " in transaction"; 38 | } 39 | 40 | if (Math.random() < 0.1) { 41 | q.query = "vacuum"; 42 | } 43 | 44 | info.queries[info.queries.length] = q; 45 | } 46 | 47 | info.queries = info.queries.sort(function(a, b) { 48 | return formatElapsed(b.elapsed - a.elapsed); 49 | }); 50 | 51 | var samples = []; 52 | 53 | samples[samples.length] = { 54 | time: data.start_at, 55 | queries: info.queries 56 | }; 57 | 58 | if (samples.length > 5) { 59 | samples.splice(0, samples.length - 5); 60 | } 61 | 62 | var db = { 63 | name: dbname, 64 | queries: info.queries, 65 | samples: samples, 66 | } 67 | 68 | db.topFiveQueries = getTopFiveQueries(db); 69 | 70 | data2[data2.length] = db; 71 | }; 72 | 73 | function getData() { 74 | // generate some dummy data 75 | 76 | data = { 77 | start_at: new Date().getTime() / 1000, 78 | databases: {} 79 | }; 80 | 81 | for (var i = 1; i <= ENV.rows; i++) { 82 | data.databases["cluster" + i] = { 83 | queries: [] 84 | }; 85 | 86 | data.databases["cluster" + i + "slave"] = { 87 | queries: [] 88 | }; 89 | } 90 | 91 | data2 = []; 92 | 93 | Object.keys(data.databases).forEach(assignDb); 94 | 95 | return data2; 96 | } 97 | 98 | 99 | function getLastSample(db) { 100 | return db.samples[db.samples.length - 1]; 101 | } 102 | 103 | function getCountClassName(db) { 104 | var count = getLastSample(db).queries.length; 105 | var className = 'label'; 106 | if (count >= 20) { 107 | className += ' label-important'; 108 | } 109 | else if (count >= 10) { 110 | className += ' label-warning'; 111 | } 112 | else { 113 | className += ' label-success'; 114 | } 115 | return className; 116 | } 117 | 118 | function elapsedClassName(elapsed) { 119 | var className = 'Query elapsed'; 120 | if (elapsed >= 10.0) { 121 | className += ' warn_long'; 122 | } 123 | else if (elapsed >= 1.0) { 124 | className += ' warn'; 125 | } 126 | else { 127 | className += ' short'; 128 | } 129 | return className; 130 | } 131 | 132 | function formatElapsed(value) { 133 | if (!value) return ''; 134 | var str = parseFloat(value).toFixed(2); 135 | if (value > 60) { 136 | var minutes = Math.floor(value / 60); 137 | var comps = (value % 60).toFixed(2).split('.'); 138 | var seconds = comps[0].lpad('0', 2); 139 | var ms = comps[1]; 140 | str = minutes + ":" + seconds + "." + ms; 141 | } 142 | return str; 143 | } -------------------------------------------------------------------------------- /test/diff.html: -------------------------------------------------------------------------------- 1 | 2 | 20 |
21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/dist/hyper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 4 | 5 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6 | 7 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8 | 9 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 10 | 11 | // A Basic Hyper Class 12 | var Hyper = function (_HTMLElement) { 13 | _inherits(Hyper, _HTMLElement); 14 | 15 | function Hyper() { 16 | _classCallCheck(this, Hyper); 17 | 18 | return _possibleConstructorReturn(this, (Hyper.__proto__ || Object.getPrototypeOf(Hyper)).apply(this, arguments)); 19 | } 20 | 21 | _createClass(Hyper, [{ 22 | key: '_initHyper', 23 | value: function _initHyper() { 24 | if ('hyper' in this) return; 25 | this.hyper = hyperHTML.wire(); 26 | this.appendChild(this.render()); 27 | } 28 | }, { 29 | key: 'attributeChangedCallback', 30 | value: function attributeChangedCallback() { 31 | this._initHyper(); 32 | } 33 | }, { 34 | key: 'connectedCallback', 35 | value: function connectedCallback() { 36 | this._initHyper(); 37 | } 38 | }]); 39 | 40 | return Hyper; 41 | }(HTMLElement); -------------------------------------------------------------------------------- /test/dist/ie.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _templateObject = _taggedTemplateLiteral(['\n \n

', '

\n List of ', ' paragraphs:\n
    ', '
\n \n '], ['\n \n

', '

\n List of ', ' paragraphs:\n
    ', '
\n \n ']), 4 | _templateObject2 = _taggedTemplateLiteral(['
  • ', '
  • '], ['
  • ', '
  • ']), 5 | _templateObject3 = _taggedTemplateLiteral(['\n \n even\n IE\n \n \n \n can do partial ', '\n \n \n '], ['\n \n even\n IE\n \n \n \n can do partial ', '\n \n \n ']); 6 | 7 | function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } 8 | 9 | window.onload = function () { 10 | 11 | var articleElement = document.querySelector('article'); 12 | 13 | var update = function update(render, state) { 14 | return render(_templateObject, state.magic, state.id, state.class, state.title, state.paragraphs.length, function (e) { 15 | return alert(e.target.innerHTML); 16 | }, state.paragraphs.map(function (p) { 17 | return hyperHTML.wire(p)(_templateObject2, p.title); 18 | })); 19 | }; 20 | 21 | update(hyperHTML.bind(articleElement), { 22 | id: 'completely-random', 23 | class: 'it-does-not-matter', 24 | title: 'True story', 25 | magic: true, 26 | paragraphs: [{ title: 'touching' }, { title: 'incredible' }, { title: 'doge' }] 27 | }); 28 | 29 | var tbody = hyperHTML.bind(document.body.appendChild(document.createElement('table')).appendChild(document.createElement('tbody'))); 30 | 31 | tbody(_templateObject3, ''); 32 | }; -------------------------------------------------------------------------------- /test/dom-splicer.js: -------------------------------------------------------------------------------- 1 | var splicer = new DOMSplicer({ 2 | target: document.documentElement 3 | }); 4 | 5 | var a = document.createTextNode('a'); 6 | var b = document.createTextNode('b'); 7 | var c = document.createTextNode('c'); 8 | var d = document.createTextNode('d'); 9 | var e = document.createTextNode('e'); 10 | var f = document.createTextNode('f'); 11 | var g = document.createTextNode('g'); 12 | var h = document.createTextNode('h'); 13 | var i = document.createTextNode('i'); 14 | 15 | console.time(); 16 | splicer.splice(0, 0, a, b, c, d, e); 17 | console.timeEnd(); 18 | verify(splicer); 19 | 20 | console.time(); 21 | splicer.splice(2, 1, g); 22 | console.timeEnd(); 23 | verify(splicer); 24 | 25 | splicer = new DOMSplicer({ 26 | target: document.createElement('div'), 27 | childNodes: [] 28 | }); 29 | 30 | console.time(); 31 | splicer.splice(0, 0, a, b, c, d, e); 32 | console.timeEnd(); 33 | verify(splicer); 34 | 35 | console.time(); 36 | splicer.splice(2, 1, g); 37 | console.timeEnd(); 38 | verify(splicer); 39 | 40 | console.time(); 41 | splicer.splice(); 42 | console.timeEnd(); 43 | verify(splicer); 44 | 45 | console.time(); 46 | splicer.splice(1, 1); 47 | console.timeEnd(); 48 | verify(splicer); 49 | 50 | console.time(); 51 | splicer.splice(10); 52 | console.timeEnd(); 53 | verify(splicer); 54 | 55 | console.time(); 56 | splicer.splice(-1); 57 | console.timeEnd(); 58 | verify(splicer); 59 | 60 | const fragment = document.createDocumentFragment(); 61 | fragment.appendChild(document.createComment('placeholder')); 62 | splicer = new DOMSplicer({ 63 | before: fragment.childNodes[0] 64 | }); 65 | splicer.splice(0, 0, a, b, c, d, e); 66 | 67 | splicer = new DOMSplicer({ 68 | target: fragment, 69 | childNodes: {length: 0} 70 | }); 71 | splicer.splice(0, 100); 72 | 73 | function verify(splicer) { 74 | console.assert( 75 | [].slice.call(splicer.childNodes).every( 76 | function (node, i) { 77 | return splicer.target.childNodes[i] === node; 78 | } 79 | ), 80 | 'splicer.childNodes same as splicer.target.childNodes' 81 | ); 82 | console.assert( 83 | [].slice.call(splicer.target.childNodes).every( 84 | function (node, i) { 85 | return splicer.childNodes[i] === node; 86 | } 87 | ), 88 | 'splicer.target.childNodes same as splicer.childNodes' 89 | ); 90 | } -------------------------------------------------------------------------------- /test/double-rainbow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hyperHTML Double Rainbow 5 | 6 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /test/e-icon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 71 | -------------------------------------------------------------------------------- /test/edge.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Edge test 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/esm.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/exchange.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenExchangeRates hyperHTML Demo 7 | 8 | 9 | 10 | 74 | 88 | 89 | 90 | 91 | 92 |

    93 | powered by hyper(HTML) 94 |

    95 |
    96 |
    97 |
    98 | 100 | 101 | -------------------------------------------------------------------------------- /test/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hyperHTML Basic Form Example 5 | 6 | 11 | 12 | 13 | 14 | 63 | 64 | -------------------------------------------------------------------------------- /test/fuzzysort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /test/haunted.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | hyperHTML & haunted 8 | 9 | 10 | 11 | 26 | 27 | -------------------------------------------------------------------------------- /test/hello-world-ce.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/ie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
    10 | 11 | -------------------------------------------------------------------------------- /test/ie/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 15 | 21 | 22 | 23 | 24 | 25 |

    results in console

    26 | 27 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /test/incremental.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hyperHTML Incremental Form Example 5 | 6 | 22 | 23 | 24 | 25 | 49 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 |

    results in console

    17 | 18 | 19 | 20 | 28 | -------------------------------------------------------------------------------- /test/infinite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Infinite Scroll 7 | 8 | 45 | 79 | 80 | 81 |
    82 | 83 | -------------------------------------------------------------------------------- /test/js/double-rainbow.js: -------------------------------------------------------------------------------- 1 | class Demo { 2 | 3 | constructor(numPoints) { 4 | this.numPoints = numPoints; 5 | this.update = this.update.bind(this); 6 | this.vd = new VizDemo(numPoints, false); 7 | } 8 | 9 | update(e) { 10 | const prop = e.currentTarget.name; 11 | this[prop] = e.currentTarget[prop === 'async' ? 'checked' : 'value']; 12 | this.vd.update(+this.numPoints, this.async); 13 | this.render(); 14 | } 15 | 16 | render() { 17 | return hyperHTML.wire(this)` 18 |
    ${ 19 | this.vd.render() 20 | }
    21 | # Points 22 | 23 | ${this.numPoints} 24 | 28 |
    29 |
    30 | Demo by Andrea Giammarchi, 31 | based on the Preact demo by Jason Miller, 32 | based on the Glimmer demo by Michael Lange. 33 |
    34 |
    `; 35 | } 36 | } 37 | 38 | const Layout = { 39 | PHYLLOTAXIS: 0, 40 | GRID: 1, 41 | WAVE: 2, 42 | SPIRAL: 3 43 | }; 44 | 45 | const LAYOUT_ORDER = [ 46 | Layout.PHYLLOTAXIS, 47 | Layout.SPIRAL, 48 | Layout.PHYLLOTAXIS, 49 | Layout.GRID, 50 | Layout.WAVE 51 | ]; 52 | 53 | class VizDemo { 54 | 55 | constructor(count, async) { 56 | 57 | this.layout = 0; 58 | this.step = 0; 59 | this.numSteps = 60 * 2; 60 | 61 | this.points = []; 62 | 63 | this.update(count, async); 64 | 65 | (this.next = this.next.bind(this))(); 66 | } 67 | 68 | next() { 69 | 70 | requestAnimationFrame(this.next); 71 | 72 | this.step = (this.step + 1) % this.numSteps; 73 | 74 | if (this.step === 0) { 75 | this.layout = (this.layout + 1) % LAYOUT_ORDER.length; 76 | } 77 | 78 | // Clamp the linear interpolation at 80% for a pause at each finished layout state 79 | const pct = Math.min(1, this.step / (this.numSteps * 0.8)); 80 | 81 | const currentLayout = LAYOUT_ORDER[this.layout]; 82 | const nextLayout = LAYOUT_ORDER[(this.layout + 1) % LAYOUT_ORDER.length]; 83 | 84 | // Keep these redundant computations out of the loop 85 | const pxProp = xForLayout(currentLayout); 86 | const nxProp = xForLayout(nextLayout); 87 | const pyProp = yForLayout(currentLayout); 88 | const nyProp = yForLayout(nextLayout); 89 | 90 | this.points.forEach(point => { 91 | point.x = lerp(point, pct, pxProp, nxProp); 92 | point.y = lerp(point, pct, pyProp, nyProp); 93 | }); 94 | 95 | if (this.async) { 96 | if (!this.ric) this.ric = requestIdleCallback( 97 | () => { this.ric = 0; this.render(); }, 98 | {timeout: 50} 99 | ); 100 | } else { 101 | this.render(); 102 | } 103 | } 104 | 105 | makePoints() { 106 | const newPoints = []; 107 | for (var i = 0; i < this.count; i++) { 108 | newPoints.push({ 109 | x: 0, 110 | y: 0, 111 | color: d3.interpolateViridis(i / this.count), 112 | }); 113 | } 114 | this.points = newPoints; 115 | this.setAnchors(); 116 | } 117 | 118 | setAnchors() { 119 | this.points.forEach((p, index) => { 120 | const [ gx, gy ] = project(this.grid(index)); 121 | const [ wx, wy ] = project(this.wave(index)); 122 | const [ sx, sy ] = project(this.spiral(index)); 123 | const [ px, py ] = project(this.phyllotaxis(index)); 124 | Object.assign(p, { gx, gy, wx, wy, sx, sy, px, py }); 125 | }); 126 | } 127 | 128 | update(count, async) { 129 | this.count = count; 130 | this.async = async; 131 | 132 | this.phyllotaxis = genPhyllotaxis(count); 133 | this.grid = genGrid(count); 134 | this.wave = genWave(count); 135 | this.spiral = genSpiral(count); 136 | 137 | this.makePoints(); 138 | } 139 | 140 | render() { 141 | return hyperHTML.wire(this)` 142 | 143 | ${ 144 | this.points.map(renderPoint) 145 | } 146 | `; 147 | } 148 | } 149 | 150 | // utilities 151 | 152 | function renderPoint(point) { 153 | return hyperHTML.wire(point, 'svg')` 154 | `; 159 | } 160 | 161 | const theta = Math.PI * (3 - Math.sqrt(5)); 162 | 163 | function xForLayout(layout) { 164 | switch (layout) { 165 | case Layout.PHYLLOTAXIS: 166 | return 'px'; 167 | case Layout.GRID: 168 | return 'gx'; 169 | case Layout.WAVE: 170 | return 'wx'; 171 | case Layout.SPIRAL: 172 | return 'sx'; 173 | } 174 | } 175 | 176 | function yForLayout(layout) { 177 | switch (layout) { 178 | case Layout.PHYLLOTAXIS: 179 | return 'py'; 180 | case Layout.GRID: 181 | return 'gy'; 182 | case Layout.WAVE: 183 | return 'wy'; 184 | case Layout.SPIRAL: 185 | return 'sy'; 186 | } 187 | } 188 | 189 | function lerp(obj, percent, startProp, endProp) { 190 | let px = obj[startProp]; 191 | return px + (obj[endProp] - px) * percent; 192 | } 193 | 194 | function genPhyllotaxis(n) { 195 | return i => { 196 | let r = Math.sqrt(i / n); 197 | let th = i * theta; 198 | return [r * Math.cos(th), r * Math.sin(th)]; 199 | }; 200 | } 201 | 202 | function genGrid(n) { 203 | let rowLength = Math.round(Math.sqrt(n)); 204 | return i => [ 205 | -0.8 + 1.6 / rowLength * (i % rowLength), 206 | -0.8 + 1.6 / rowLength * Math.floor(i / rowLength), 207 | ]; 208 | } 209 | 210 | function genWave(n) { 211 | let xScale = 2 / (n - 1); 212 | return i => { 213 | let x = -1 + i * xScale; 214 | return [x, Math.sin(x * Math.PI * 3) * 0.3]; 215 | }; 216 | } 217 | 218 | function genSpiral(n) { 219 | return i => { 220 | let t = Math.sqrt(i / (n - 1)), 221 | phi = t * Math.PI * 10; 222 | return [t * Math.cos(phi), t * Math.sin(phi)]; 223 | }; 224 | } 225 | 226 | function scale(magnitude, vector) { 227 | return vector.map(p => p * magnitude); 228 | } 229 | 230 | function translate(translation, vector) { 231 | return vector.map((p, i) => p + translation[i]); 232 | } 233 | 234 | function project(vector) { 235 | const wh = window.innerHeight / 2; 236 | const ww = window.innerWidth / 2; 237 | return translate([ ww, wh ], scale(Math.min(wh, ww), vector)); 238 | } 239 | 240 | document.body.appendChild(new Demo(1000).render()); 241 | -------------------------------------------------------------------------------- /test/js/hyper.js: -------------------------------------------------------------------------------- 1 | 2 | // A Basic Hyper Class 3 | class Hyper extends HTMLElement { 4 | _initHyper() { 5 | if ('hyper' in this) return; 6 | this.hyper = hyperHTML.wire(); 7 | this.appendChild(this.render()); 8 | } 9 | attributeChangedCallback() { this._initHyper(); } 10 | connectedCallback() { this._initHyper(); } 11 | } 12 | -------------------------------------------------------------------------------- /test/js/ie.js: -------------------------------------------------------------------------------- 1 | window.onload = () => { 2 | 3 | const articleElement = document.querySelector('article'); 4 | 5 | const update = (render, state) => render` 6 |
    12 |

    ${state.title}

    13 | List of ${state.paragraphs.length} paragraphs: 14 |
      ${ 15 | state.paragraphs 16 | .map(p => hyperHTML.wire(p)`
    • ${p.title}
    • `) 17 | }
    18 |
    19 | `; 20 | 21 | update( 22 | hyperHTML.bind(articleElement), 23 | { 24 | id: 'completely-random', 25 | class: 'it-does-not-matter', 26 | title: 'True story', 27 | magic: true, 28 | paragraphs: [ 29 | {title: 'touching'}, 30 | {title: 'incredible'}, 31 | {title: 'doge'} 32 | ] 33 | } 34 | ); 35 | 36 | const tbody = hyperHTML.bind(document.body.appendChild( 37 | document.createElement('table') 38 | ).appendChild( 39 | document.createElement('tbody') 40 | )); 41 | 42 | tbody` 43 | 44 | even 45 | IE 46 | 47 | 48 | 49 | can do partial ${''} 50 | 51 | 52 | `; 53 | 54 | }; -------------------------------------------------------------------------------- /test/lib.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/many-rows.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

    Boot speed:

    10 |
    11 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /test/mjs.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/multi-wire.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/mutations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | hyperHTML Smart Diffing 6 | 22 | 23 | 24 | 25 | 26 | 27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    33 |
    34 |
    35 | 36 | 170 | -------------------------------------------------------------------------------- /test/my-button-ns.js: -------------------------------------------------------------------------------- 1 | fetch('my-button.css').then(t => t.text()).then(css => { 2 | 3 | document.head.appendChild( 4 | document.createElement('style') 5 | ).textContent = css.replace(/^(\S)/gm, 'my-button-ns $1'); 6 | 7 | class MyButtonNS extends HTMLElement { 8 | 9 | // bind once the component Shadow DOM 10 | constructor(self) { 11 | self = super(self); 12 | self.html = hyperHTML.bind(self); 13 | return self; 14 | } 15 | 16 | // render once connected 17 | connectedCallback() { 18 | this.render(); 19 | } 20 | 21 | // do something on click 22 | onclick(event) { 23 | event.preventDefault(); 24 | alert('clicked'); 25 | } 26 | 27 | // render whenever it's necessary 28 | render() { 29 | this.html` 30 | `; 33 | } 34 | 35 | } 36 | 37 | // register the component 38 | customElements.define('my-button-ns', MyButtonNS); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /test/my-button.css: -------------------------------------------------------------------------------- 1 | button { 2 | cursor: pointer; 3 | outline: none; 4 | font-family: sans-serif; 5 | font-weight: bold; 6 | } -------------------------------------------------------------------------------- /test/my-button.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom Elements meet hyperHTML 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /test/my-button.js: -------------------------------------------------------------------------------- 1 | fetch('my-button.css').then(t => t.text()).then(css => { 2 | 3 | class MyButton extends HTMLElement { 4 | 5 | // bind once the component Shadow DOM 6 | constructor() { 7 | super(); 8 | this.html = hyperHTML.bind( 9 | this.attachShadow({mode: 'closed'}) 10 | ); 11 | } 12 | 13 | // render once connected 14 | connectedCallback() { 15 | this.render(); 16 | } 17 | 18 | // do something on click 19 | onclick(event) { 20 | event.preventDefault(); 21 | alert('clicked'); 22 | } 23 | 24 | // render whenever it's necessary 25 | render() { 26 | this.html` 27 | 28 | `; 31 | } 32 | 33 | } 34 | 35 | // register the component 36 | customElements.define('my-button', MyButton); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /test/repl.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/hyperHTML/9580a066963012abf3658b568691261c60d33c9e/test/repl.html -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | function usableAfter(object, property, times) { 2 | if (!times) times = 0; 3 | var original = object[property], i = 0; 4 | Object.defineProperty(object, property, { 5 | get() { 6 | return i++ < times ? void 0 : original; 7 | } 8 | }); 9 | } 10 | 11 | const $WeakMap = global.WeakMap; 12 | 13 | global.tressa = require('tressa'); 14 | 15 | const {CustomEvent, Document, HTMLElement} = require('basichtml'); 16 | global.window = global; 17 | global.document = new Document(); 18 | global.customElements = document.customElements; 19 | global.CustomEvent = CustomEvent; 20 | 21 | document.importNode = function (node, deep) { 22 | return node.cloneNode(deep); 23 | }; 24 | 25 | customElements.define('input', class extends HTMLElement { 26 | get name() { return this.getAttribute('name'); } 27 | set name(text) { this.setAttribute('name', text); } 28 | get value() { return this.getAttribute('value'); } 29 | set value(text) { this.setAttribute('value', text); } 30 | }); 31 | 32 | global.hyperHTML = require('../cjs').default; 33 | 34 | require('./test.js'); 35 | 36 | setTimeout(function () { 37 | return; 38 | 39 | delete require.cache[require.resolve('../cjs')]; 40 | delete require.cache[require.resolve('./test.js')]; 41 | 42 | usableAfter(Array, 'isArray', 1); 43 | usableAfter(String.prototype, 'trim', 1); 44 | 45 | global.navigator = {userAgent: 'Firefox/54'}; 46 | 47 | /* 48 | const propertyIsEnumerable = {}.propertyIsEnumerable; 49 | Object.prototype.propertyIsEnumerable = function (key) { 50 | return key === 'raw' ? true : propertyIsEnumerable.call(this, key); 51 | }; 52 | */ 53 | 54 | delete global.Int32Array; 55 | delete document.importNode; 56 | delete Object.getPrototypeOf(document.constructor.prototype).importNode; 57 | delete Object.getPrototypeOf(document.createDocumentFragment().constructor.prototype).append; 58 | 59 | var createElement = document.createElement; 60 | document.createElement = function (name) { 61 | return createElement.call(this, name === 'template' ? 'div' : name); 62 | }; 63 | 64 | var createDocumentFragment = document.createDocumentFragment, cDF = 0; 65 | document.createDocumentFragment = function () { 66 | return cDF++ === 0 ? 67 | { 68 | ownerDocument: document, 69 | appendChild: Object, 70 | cloneNode: function () { 71 | return {childNodes: {length: 1}}; 72 | } 73 | } : 74 | createDocumentFragment.call(document); 75 | }; 76 | 77 | global.Event = function (type) { 78 | var e = global.document.createEvent('Event'); 79 | e.initEvent(type, false, false); 80 | return e; 81 | }; 82 | global.MutationObserver = function (fn) { 83 | return {observe: function (document) { 84 | document.addEventListener('DOMNodeInserted', function (e) { 85 | fn([{ 86 | addedNodes: [e.target], 87 | removedNodes: [] 88 | }]); 89 | }, false); 90 | document.addEventListener('DOMNodeRemoved', function (e) { 91 | fn([{ 92 | addedNodes: [], 93 | removedNodes: [e.target] 94 | }]); 95 | }, false); 96 | }}; 97 | }; 98 | 99 | window.hyperHTML = require('../cjs').default; 100 | require('./domdiff.js'); 101 | require('./test.js'); 102 | 103 | if ($WeakMap) setTimeout(() => { 104 | delete require.cache[require.resolve('../cjs')]; 105 | delete require.cache[require.resolve('./test.js')]; 106 | global.WeakMap = function () { 107 | const wm = new $WeakMap; 108 | wm.get = () => false; 109 | return wm; 110 | }; 111 | require('../cjs'); 112 | require('./test.js'); 113 | }, 2000); 114 | }, 2000); -------------------------------------------------------------------------------- /test/select.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 60 | 61 | -------------------------------------------------------------------------------- /test/shared/main.js: -------------------------------------------------------------------------------- 1 | const {Document} = require('basichtml'); 2 | global.document = new Document(); 3 | 4 | const ed = require('../../cjs/shared/easy-dom'); 5 | console.assert(ed.create(document, 'P').nodeName === 'P', 'easy-dom.create'); 6 | console.assert(ed.fragment(document).nodeName === '#document-fragment', 'easy-dom.fragment'); 7 | console.assert(ed.text(document, 'hello').textContent === 'hello', 'easy-dom.text'); 8 | 9 | //const utils = require('../../cjs/shared/utils'); 10 | 11 | delete global.Event; 12 | delete global.Map; 13 | delete global.WeakMap; 14 | delete global.WeakSet; 15 | delete String.prototype.trim; 16 | const isArray = Array.isArray; 17 | 18 | let i = 0; 19 | Object.defineProperty(Array, 'isArray', { 20 | configurable: true, 21 | get() { return i++ ? isArray : null; } 22 | }); 23 | const pf = require('../../cjs/shared/poorlyfills.js'); 24 | 25 | console.assert(pf.isArray([]) && !pf.isArray({}), 'poorlyfilled isArray'); 26 | 27 | i = 0; 28 | document.addEventListener('test', () => i++, {once: true}); 29 | document.dispatchEvent(new pf.Event('test')); 30 | console.assert(i === 1, 'poorlyfilled Event'); 31 | 32 | let m = new pf.Map; 33 | console.assert(m.get(pf) == null, 'poorlyfilled Map.get'); 34 | console.assert(!m.set(pf, 1), 'poorlyfilled Map.set'); 35 | console.assert(m.get(pf) === 1, 'poorlyfilled Map.get(1)'); 36 | 37 | let wm = new pf.WeakMap; 38 | console.assert(wm.has(pf) == false, 'poorlyfilled WeakMap.has'); 39 | console.assert(wm.get(pf) == null, 'poorlyfilled WeakMap.get'); 40 | console.assert(!wm.set(pf, 1), 'poorlyfilled WeakMap.set'); 41 | console.assert(wm.has(pf) == true, 'poorlyfilled WeakMap.has(1)'); 42 | console.assert(wm.get(pf) === 1, 'poorlyfilled WeakMap.get(1)'); 43 | console.assert(!wm.delete(pf), 'poorlyfilled WeakMap.delete(1)'); 44 | 45 | let ws = new pf.WeakSet; 46 | console.assert(ws.has(pf) === false, 'poorlyfilled WeakSet.has'); 47 | console.assert(!ws.add(pf), 'poorlyfilled WeakSet.add(1)'); 48 | console.assert(ws.has(pf) === true, 'poorlyfilled WeakSet.has(1)'); 49 | 50 | console.assert(pf.trim.call(' test ') === 'test', 'poorlyfilled trim'); -------------------------------------------------------------------------------- /test/svgclass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 25 | 26 | -------------------------------------------------------------------------------- /test/tabindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /test/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | hyperHTML table 8 | 9 | 10 | 11 |
    12 | 240 | 241 | -------------------------------------------------------------------------------- /test/test-attribute-value.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Attribute Nodes Shenanigans 5 | 41 | 42 | 43 |
    44 | 45 | -------------------------------------------------------------------------------- /test/tick.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /test/tick/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 27 | -------------------------------------------------------------------------------- /test/todo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 71 | -------------------------------------------------------------------------------- /test/value.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 35 | -------------------------------------------------------------------------------- /test/virtual.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /test/webkit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 17 | -------------------------------------------------------------------------------- /umd.d.ts: -------------------------------------------------------------------------------- 1 | import hyper from '.'; 2 | export * from '.'; 3 | export default hyper; 4 | --------------------------------------------------------------------------------