chore: format code
This commit is contained in:
parent
ae2df5096d
commit
833e835f65
51 changed files with 3547 additions and 1684 deletions
6
.github/workflows/eslint.yml
vendored
6
.github/workflows/eslint.yml
vendored
|
@ -2,12 +2,12 @@ name: ESLint
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
branches: ['main']
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["main"]
|
||||
branches: ['main']
|
||||
schedule:
|
||||
- cron: "36 3 * * 2"
|
||||
- cron: '36 3 * * 2'
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
|
|
152
README.md
152
README.md
|
@ -14,10 +14,12 @@ When you extend the `WebComponent` class for your component, you only have to de
|
|||
The result is a reactive UI on property changes.
|
||||
|
||||
Links:
|
||||
|
||||
- [Read a blog explaining the reactivity](https://ayos.blog/reactive-custom-elements-with-html-dataset/)
|
||||
- [View demo on CodePen](https://codepen.io/ayoayco-the-styleful/pen/ZEwoNOz?editors=1010)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Project Status](#project-status)
|
||||
1. [Installation](#installation)
|
||||
1. [Import via CDN](#import-via-cdn)
|
||||
|
@ -44,6 +46,7 @@ Links:
|
|||
1. [Library Size](#library-size)
|
||||
|
||||
## Project Status
|
||||
|
||||
It is ready for many cases we see people use custom elements for. If you have a cool project built on **WebComponent.io** we'd love to know! :)
|
||||
|
||||
For building some advanced interactions, we have a few issues that are still open: [#24 smart diffing](https://github.com/ayoayco/web-component-base/issues/24) & [#4 attachEffect improvements](https://github.com/ayoayco/web-component-base/issues/4)
|
||||
|
@ -53,6 +56,7 @@ In the mean time, if you have some complex needs, we recommend using the `WebCom
|
|||
...or you can even [use just parts](#just-the-templating) of it for your own base class.
|
||||
|
||||
## Installation
|
||||
|
||||
The library is distributed as complete ECMAScript Modules (ESM) and published on [NPM](https://ayco.io/n/web-component-base). Please file an issue in our [issue tracker](https://ayco.io/gh/web-component-base/issues) for problems or requests regarding our distribution.
|
||||
|
||||
### Import via CDN
|
||||
|
@ -62,10 +66,11 @@ It is possible to import directly using a CDN like [esm.sh](https://esm.sh/web-c
|
|||
Additionally, we use `@latest` in the rest of our [usage examples](#usage) here for simplicity, but take note that this incurs additional resolution steps for CDNs to find the actual latest published version. You may replace the `@latest` in the URL with specific versions as shown in our CodePen examples, and this will typically be better for performance.
|
||||
|
||||
```js
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js"
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
```
|
||||
|
||||
### Installation via npm
|
||||
|
||||
Usable for projects with bundlers or using import maps pointing to the specific files downloaded in `node_modules/web-component-base`.
|
||||
|
||||
```bash
|
||||
|
@ -81,31 +86,37 @@ You can import everything separately, or in a single file each for the main expo
|
|||
```js
|
||||
// all in a single file
|
||||
|
||||
import { WebComponent, html, attachEffect } from "web-component-base";
|
||||
import { WebComponent, html, attachEffect } from 'web-component-base'
|
||||
|
||||
// in separate files
|
||||
|
||||
import { WebComponent } from "web-component-base/WebComponent.js";
|
||||
import { WebComponent } from 'web-component-base/WebComponent.js'
|
||||
|
||||
import { html } from "web-component-base/html.js";
|
||||
import { html } from 'web-component-base/html.js'
|
||||
|
||||
import { attachEffect } from "web-component-base/attach-effect.js";
|
||||
import { attachEffect } from 'web-component-base/attach-effect.js'
|
||||
```
|
||||
|
||||
### Utilities
|
||||
|
||||
```js
|
||||
// in a single file
|
||||
|
||||
import { serialize, deserialize, getCamelCase, getKebabCase, createElement } from "web-component-base/utils";
|
||||
import {
|
||||
serialize,
|
||||
deserialize,
|
||||
getCamelCase,
|
||||
getKebabCase,
|
||||
createElement,
|
||||
} from 'web-component-base/utils'
|
||||
|
||||
// or separate files
|
||||
|
||||
import { serialize } from "web-component-base/utils/serialize.js";
|
||||
import { serialize } from 'web-component-base/utils/serialize.js'
|
||||
|
||||
import { createElement } from "web-component-base/utils/create-element.js";
|
||||
import { createElement } from 'web-component-base/utils/create-element.js'
|
||||
|
||||
// etc...
|
||||
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
@ -114,21 +125,21 @@ In your component class:
|
|||
|
||||
```js
|
||||
// HelloWorld.mjs
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
|
||||
class HelloWorld extends WebComponent {
|
||||
static props ={
|
||||
static props = {
|
||||
myName: 'World',
|
||||
emotion: 'sad'
|
||||
emotion: 'sad',
|
||||
}
|
||||
get template() {
|
||||
return `
|
||||
<h1>Hello ${this.props.myName}${this.props.emotion === "sad" ? ". 😭" : "! 🙌"}</h1>
|
||||
`;
|
||||
<h1>Hello ${this.props.myName}${this.props.emotion === 'sad' ? '. 😭' : '! 🙌'}</h1>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('hello-world', HelloWorld);
|
||||
customElements.define('hello-world', HelloWorld)
|
||||
```
|
||||
|
||||
In your HTML page:
|
||||
|
@ -163,39 +174,41 @@ A simple app that allows adding / completing tasks:
|
|||
Here is an example of using a custom element in a single .html file.
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>WC Base Test</title>
|
||||
<script type="module">
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
|
||||
class HelloWorld extends WebComponent {
|
||||
static props = {
|
||||
myName: 'World'
|
||||
myName: 'World',
|
||||
}
|
||||
get template() {
|
||||
return `<h1>Hello ${this.props.myName}!</h1>`;
|
||||
return `<h1>Hello ${this.props.myName}!</h1>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hello-world", HelloWorld);
|
||||
customElements.define('hello-world', HelloWorld)
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<hello-world my-name="Ayo"></hello-world>
|
||||
<script>
|
||||
const helloWorld = document.querySelector('hello-world');
|
||||
const helloWorld = document.querySelector('hello-world')
|
||||
setTimeout(() => {
|
||||
helloWorld.props.myName = 'Ayo zzzZzzz';
|
||||
}, 2500);
|
||||
helloWorld.props.myName = 'Ayo zzzZzzz'
|
||||
}, 2500)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 3. Feature Demos
|
||||
|
||||
Some feature-specific demos:
|
||||
|
||||
1. [Context-Aware Post-Apocalyptic Human](https://codepen.io/ayoayco-the-styleful/pen/WNqJMNG?editors=1010)
|
||||
1. [Simple reactive property](https://codepen.io/ayoayco-the-styleful/pen/ZEwoNOz?editors=1010)
|
||||
1. [Counter & Toggle](https://codepen.io/ayoayco-the-styleful/pen/PoVegBK?editors=1010)
|
||||
|
@ -209,25 +222,23 @@ Some feature-specific demos:
|
|||
|
||||
This mental model attempts to reduce the cognitive complexity of authoring components:
|
||||
|
||||
1. The `template` is a read-only property (initialized with a `get` keyword) that represents *how* the component view is rendered.
|
||||
1. The `template` is a read-only property (initialized with a `get` keyword) that represents _how_ the component view is rendered.
|
||||
1. There is a `render()` method that triggers a view render.
|
||||
1. This `render()` method is *automatically* called under the hood every time an attribute value changed.
|
||||
1. You can *optionally* call this `render()` method at any point to trigger a render if you need (eg, if you have private unobserved properties that need to manually trigger a render)
|
||||
1. This `render()` method is _automatically_ called under the hood every time an attribute value changed.
|
||||
1. You can _optionally_ call this `render()` method at any point to trigger a render if you need (eg, if you have private unobserved properties that need to manually trigger a render)
|
||||
1. Overriding the `render()` function for handling a custom `template` is also possible. Here's an example of using `lit-html`: [View on CodePen ↗](https://codepen.io/ayoayco-the-styleful/pen/ZEwNJBR?editors=1010)
|
||||
|
||||
## Prop Access
|
||||
|
||||
The `props` property of the `WebComponent` interface is provided for easy read/write access to a camelCase counterpart of *any* observed attribute.
|
||||
The `props` property of the `WebComponent` interface is provided for easy read/write access to a camelCase counterpart of _any_ observed attribute.
|
||||
|
||||
```js
|
||||
class HelloWorld extends WebComponent {
|
||||
static props = {
|
||||
myProp: 'World'
|
||||
myProp: 'World',
|
||||
}
|
||||
get template() {
|
||||
return html`
|
||||
<h1>Hello ${this.props.myProp}</h1>
|
||||
`;
|
||||
return html` <h1>Hello ${this.props.myProp}</h1> `
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -235,11 +246,13 @@ class HelloWorld extends WebComponent {
|
|||
Assigning a value to the `props.camelCase` counterpart of an observed attribute will trigger an "attribute change" hook.
|
||||
|
||||
For example, assigning a value like so:
|
||||
|
||||
```
|
||||
this.props.myName = 'hello'
|
||||
```
|
||||
|
||||
...is like calling the following:
|
||||
|
||||
```
|
||||
this.setAttribute('my-name','hello');
|
||||
```
|
||||
|
@ -253,6 +266,7 @@ Therefore, this will tell the browser that the UI needs a render if the attribut
|
|||
### Alternatives
|
||||
|
||||
The current alternatives are using what `HTMLElement` provides out-of-the-box, which are:
|
||||
|
||||
1. `HTMLElement.dataset` for attributes prefixed with `data-*`. Read more about this [on MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset).
|
||||
1. Methods for reading/writing attribute values: `setAttribute(...)` and `getAttribute(...)`; note that managing the attribute names as strings can be difficult as the code grows.
|
||||
|
||||
|
@ -261,56 +275,58 @@ The current alternatives are using what `HTMLElement` provides out-of-the-box, w
|
|||
When using the built-in `html` function for tagged templates, a style object of type `Partial<CSSStyleDeclaration>` can be passed to any element's `style` attribute. This allows for calculated and conditional styles. Read more on style objects [on MDN](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration).
|
||||
|
||||
Try it now with this [example on CodePen ↗](https://codepen.io/ayoayco-the-styleful/pen/bGzXjwQ?editors=1010)
|
||||
|
||||
```js
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
|
||||
class StyledElements extends WebComponent {
|
||||
static props = {
|
||||
emphasize: false,
|
||||
type: "warn",
|
||||
};
|
||||
type: 'warn',
|
||||
}
|
||||
|
||||
#typeStyles = {
|
||||
warn: {
|
||||
backgroundColor: "yellow",
|
||||
border: "1px solid orange",
|
||||
backgroundColor: 'yellow',
|
||||
border: '1px solid orange',
|
||||
},
|
||||
error: {
|
||||
backgroundColor: "orange",
|
||||
border: "1px solid red",
|
||||
backgroundColor: 'orange',
|
||||
border: '1px solid red',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
get template() {
|
||||
return html`
|
||||
<div
|
||||
style=${{
|
||||
...this.#typeStyles[this.props.type],
|
||||
padding: "1em",
|
||||
padding: '1em',
|
||||
}}
|
||||
>
|
||||
<p style=${{ fontStyle: this.props.emphasize && "italic" }}>Wow!</p>
|
||||
<p style=${{ fontStyle: this.props.emphasize && 'italic' }}>Wow!</p>
|
||||
</div>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("styled-elements", StyledElements);
|
||||
customElements.define('styled-elements', StyledElements)
|
||||
```
|
||||
|
||||
## Shadow DOM Opt-In
|
||||
|
||||
Add a static property `shadowRootInit` with object value of type `ShadowRootInit` (see [options on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow#options)) to opt-in to using shadow dom for the whole component.
|
||||
|
||||
Try it now [on CodePen ↗](https://codepen.io/ayoayco-the-styleful/pen/VwRYVPv?editors=1010)
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
static shadowRootInit = {
|
||||
mode: "closed",
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Just the Templating
|
||||
|
||||
You don't have to extend the whole base class just to use some features. All internals are exposed and usable separately so you can practically build the behavior on your own classes.
|
||||
|
@ -318,105 +334,107 @@ You don't have to extend the whole base class just to use some features. All int
|
|||
Here's an example of using the `html` tag template on a class that extends from vanilla `HTMLElement`... also [View on CodePen ↗](https://codepen.io/ayoayco-the-styleful/pen/bGzJQJg?editors=1010).
|
||||
|
||||
```js
|
||||
import {html} from 'https://unpkg.com/web-component-base/html'
|
||||
import {createElement} from 'https://unpkg.com/web-component-base/utils'
|
||||
import { html } from 'https://unpkg.com/web-component-base/html'
|
||||
import { createElement } from 'https://unpkg.com/web-component-base/utils'
|
||||
|
||||
class MyQuote extends HTMLElement {
|
||||
connectedCallback() {
|
||||
const el = createElement(html`
|
||||
<button onClick=${() => alert('hey')}>
|
||||
hey
|
||||
</button>`);
|
||||
const el = createElement(
|
||||
html` <button onClick=${() => alert('hey')}>hey</button>`
|
||||
)
|
||||
this.appendChild(el)
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('my-quote', MyQuote)
|
||||
```
|
||||
|
||||
## Life-Cycle Hooks
|
||||
|
||||
Define behavior when certain events in the component's life cycle is triggered by providing hook methods
|
||||
|
||||
### onInit()
|
||||
|
||||
- Triggered when the component is connected to the DOM
|
||||
- Best for setting up the component
|
||||
|
||||
```js
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
|
||||
class ClickableText extends WebComponent {
|
||||
// gets called when the component is used in an HTML document
|
||||
onInit() {
|
||||
this.onclick = () => console.log(">>> click!");
|
||||
this.onclick = () => console.log('>>> click!')
|
||||
}
|
||||
|
||||
get template() {
|
||||
return `<span style="cursor:pointer">Click me!</span>`;
|
||||
return `<span style="cursor:pointer">Click me!</span>`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### afterViewInit()
|
||||
- Triggered after the view is first initialized
|
||||
|
||||
- Triggered after the view is first initialized
|
||||
|
||||
```js
|
||||
class ClickableText extends WebComponent {
|
||||
// gets called when the component's innerHTML is first filled
|
||||
afterViewInit() {
|
||||
const footer = this.querySelector('footer');
|
||||
const footer = this.querySelector('footer')
|
||||
// do stuff to footer after view is initialized
|
||||
}
|
||||
|
||||
get template() {
|
||||
return `<footer>Awesome site © 2023</footer>`;
|
||||
return `<footer>Awesome site © 2023</footer>`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### onDestroy()
|
||||
|
||||
- Triggered when the component is disconnected from the DOM
|
||||
- best for undoing any setup done in `onInit()`
|
||||
|
||||
```js
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
|
||||
class ClickableText extends WebComponent {
|
||||
|
||||
clickCallback() {
|
||||
console.log(">>> click!");
|
||||
console.log('>>> click!')
|
||||
}
|
||||
|
||||
onInit() {
|
||||
this.onclick = this.clickCallback;
|
||||
this.onclick = this.clickCallback
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
console.log(">>> removing event listener");
|
||||
this.removeEventListener("click", this.clickCallback);
|
||||
console.log('>>> removing event listener')
|
||||
this.removeEventListener('click', this.clickCallback)
|
||||
}
|
||||
|
||||
get template() {
|
||||
return `<span style="cursor:pointer">Click me!</span>`;
|
||||
return `<span style="cursor:pointer">Click me!</span>`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### onChanges()
|
||||
|
||||
- Triggered when an attribute value changed
|
||||
|
||||
```js
|
||||
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
|
||||
import { WebComponent } from 'https://unpkg.com/web-component-base@latest/index.js'
|
||||
|
||||
class ClickableText extends WebComponent {
|
||||
// gets called when an attribute value changes
|
||||
onChanges(changes) {
|
||||
const {property, previousValue, currentValue} = changes;
|
||||
console.log('>>> ', {property, previousValue, currentValue})
|
||||
const { property, previousValue, currentValue } = changes
|
||||
console.log('>>> ', { property, previousValue, currentValue })
|
||||
}
|
||||
|
||||
get template() {
|
||||
return `<span style="cursor:pointer">Click me!</span>`;
|
||||
return `<span style="cursor:pointer">Click me!</span>`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import globals from "globals";
|
||||
import pluginJs from "@eslint/js";
|
||||
import globals from 'globals'
|
||||
import pluginJs from '@eslint/js'
|
||||
|
||||
/** @type {import('eslint').Linter.Config[]} */
|
||||
export default [
|
||||
|
@ -7,10 +7,10 @@ export default [
|
|||
pluginJs.configs.recommended,
|
||||
{
|
||||
rules: {
|
||||
"no-unused-vars": "warn",
|
||||
'no-unused-vars': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ["site/*"],
|
||||
ignores: ['site/*'],
|
||||
},
|
||||
];
|
||||
]
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
// @ts-check
|
||||
import { WebComponent, attachEffect, html } from "../../src/index.js";
|
||||
import { WebComponent, attachEffect, html } from '../../src/index.js'
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
|
||||
onInit() {
|
||||
attachEffect(this.props.count, (count) => console.log(count));
|
||||
attachEffect(this.props.count, (count) => console.log(count))
|
||||
}
|
||||
|
||||
afterViewInit() {
|
||||
attachEffect(this.props.count, (count) => console.log(count + 100));
|
||||
attachEffect(this.props.count, (count) => console.log(count + 100))
|
||||
}
|
||||
|
||||
get template() {
|
||||
return html`<button onclick=${() => ++this.props.count} id="btn">${this.props.count}</button>`;
|
||||
return html`<button onclick=${() => ++this.props.count} id="btn">
|
||||
${this.props.count}
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define('my-counter', Counter)
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
// @ts-check
|
||||
import { WebComponent, attachEffect, html } from "../../src/index.js";
|
||||
import { WebComponent, attachEffect, html } from '../../src/index.js'
|
||||
|
||||
export class Decrease extends WebComponent {
|
||||
static props = {
|
||||
count: 999,
|
||||
};
|
||||
}
|
||||
|
||||
onInit() {
|
||||
attachEffect(this.props.count, (count) => console.log(count));
|
||||
attachEffect(this.props.count, (count) => console.log(count))
|
||||
}
|
||||
|
||||
afterViewInit() {
|
||||
attachEffect(this.props.count, (count) => console.log(count + 100));
|
||||
attachEffect(this.props.count, (count) => console.log(count + 100))
|
||||
}
|
||||
|
||||
get template() {
|
||||
return html`<button onclick=${() => --this.props.count} id="btn">${this.props.count}</button>`;
|
||||
return html`<button onclick=${() => --this.props.count} id="btn">
|
||||
${this.props.count}
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-decrement", Decrease);
|
||||
customElements.define('my-decrement', Decrease)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class BooleanPropTest extends WebComponent {
|
||||
static props = {
|
||||
isInline: false,
|
||||
anotherone: false,
|
||||
};
|
||||
}
|
||||
|
||||
get template() {
|
||||
return html`
|
||||
<p>is-inline: ${this.props.isInline}</p><p>another-one: ${this.props.anotherone}</p>
|
||||
`;
|
||||
<p>is-inline: ${this.props.isInline}</p>
|
||||
<p>another-one: ${this.props.anotherone}</p>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("boolean-prop-test", BooleanPropTest);
|
||||
customElements.define('boolean-prop-test', BooleanPropTest)
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// @ts-check
|
||||
import { WebComponent, html } from "../../src/index.js";
|
||||
import { WebComponent, html } from '../../src/index.js'
|
||||
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
return html`
|
||||
<button onClick=${() => ++this.props.count} id="btn">
|
||||
${this.props.count}
|
||||
</button>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define('my-counter', Counter)
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
// @ts-check
|
||||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class HelloWorld extends WebComponent {
|
||||
static props = {
|
||||
count: 0,
|
||||
emotion: "sad",
|
||||
};
|
||||
emotion: 'sad',
|
||||
}
|
||||
|
||||
onInit() {
|
||||
this.props.count = 0;
|
||||
this.props.count = 0
|
||||
}
|
||||
|
||||
get template() {
|
||||
const label = this.props.count ? `Clicked ${this.props.count}` : "World";
|
||||
const emote = this.props.emotion === "sad" ? ". 😭" : "! 🙌";
|
||||
const label = this.props.count ? `Clicked ${this.props.count}` : 'World'
|
||||
const emote = this.props.emotion === 'sad' ? '. 😭' : '! 🙌'
|
||||
|
||||
return html`
|
||||
<button onclick=${() => ++this.props.count}>Hello ${label}${emote}</button>
|
||||
<button onclick=${() => ++this.props.count}>
|
||||
Hello ${label}${emote}
|
||||
</button>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hello-world", HelloWorld);
|
||||
customElements.define('hello-world', HelloWorld)
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
// @ts-check
|
||||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
class SimpleText extends WebComponent {
|
||||
clickCallback() {
|
||||
console.log(">>> click!");
|
||||
console.log('>>> click!')
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
console.log(">>> removing event listener");
|
||||
this.removeEventListener("click", this.clickCallback);
|
||||
console.log('>>> removing event listener')
|
||||
this.removeEventListener('click', this.clickCallback)
|
||||
}
|
||||
|
||||
get template() {
|
||||
return html`<span onclick=${this.clickCallback} style="cursor:pointer">Click me!</span>`;
|
||||
return html`<span onclick=${this.clickCallback} style="cursor:pointer"
|
||||
>Click me!</span
|
||||
>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("simple-text", SimpleText);
|
||||
customElements.define('simple-text', SimpleText)
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { WebComponent, html } from "../../src/index.js";
|
||||
import { WebComponent, html } from '../../src/index.js'
|
||||
|
||||
class Toggle extends WebComponent {
|
||||
static props = {
|
||||
toggle: false,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
return html`
|
||||
<button onClick=${() => (this.props.toggle = !this.props.toggle)}>
|
||||
${this.props.toggle}
|
||||
</button>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-toggle", Toggle);
|
||||
customElements.define('my-toggle', Toggle)
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
</p>
|
||||
|
||||
<script type="module">
|
||||
const helloWorld = document.querySelector("hello-world");
|
||||
const helloWorld = document.querySelector('hello-world')
|
||||
setTimeout(() => {
|
||||
helloWorld.props.emotion = "excited";
|
||||
}, 2500);
|
||||
helloWorld.props.emotion = 'excited'
|
||||
}, 2500)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -13,33 +13,35 @@
|
|||
import {
|
||||
WebComponent,
|
||||
html,
|
||||
} from "https://esm.sh/web-component-base@latest";
|
||||
} from 'https://esm.sh/web-component-base@latest'
|
||||
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
return html`<button onClick=${() => ++this.props.count}>
|
||||
${this.props.count}
|
||||
</button>`;
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
||||
class Toggle extends WebComponent {
|
||||
static props = {
|
||||
toggle: false,
|
||||
};
|
||||
}
|
||||
|
||||
clickFn = () => (this.props.toggle = !this.props.toggle);
|
||||
clickFn = () => (this.props.toggle = !this.props.toggle)
|
||||
|
||||
get template() {
|
||||
return html`<button onclick=${this.clickFn}>${this.props.toggle ? "On" : "Off"}</button>`;
|
||||
return html`<button onclick=${this.clickFn}>
|
||||
${this.props.toggle ? 'On' : 'Off'}
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define("my-toggle", Toggle);
|
||||
customElements.define('my-counter', Counter)
|
||||
customElements.define('my-toggle', Toggle)
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class HelloWorld extends WebComponent {
|
||||
static props = {
|
||||
myName: "World",
|
||||
};
|
||||
myName: 'World',
|
||||
}
|
||||
get template() {
|
||||
return html`<p>Hello ${this.props.myName}</p>`;
|
||||
return html`<p>Hello ${this.props.myName}</p>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hello-world", HelloWorld);
|
||||
customElements.define('hello-world', HelloWorld)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 123,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
return html`<button onclick=${() => ++this.props.count} id="btn">${this.props.count}</button>`;
|
||||
return html`<button onclick=${() => ++this.props.count} id="btn">
|
||||
${this.props.count}
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define('my-counter', Counter)
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
// @ts-check
|
||||
import { WebComponent, html } from "../../src/index.js";
|
||||
import { WebComponent, html } from '../../src/index.js'
|
||||
|
||||
class StyledElements extends WebComponent {
|
||||
static props = {
|
||||
condition: false,
|
||||
type: "info",
|
||||
};
|
||||
type: 'info',
|
||||
}
|
||||
|
||||
#typeStyles = {
|
||||
info: {
|
||||
backgroundColor: "blue",
|
||||
border: "1px solid green",
|
||||
backgroundColor: 'blue',
|
||||
border: '1px solid green',
|
||||
},
|
||||
warn: {
|
||||
backgroundColor: "yellow",
|
||||
border: "1px solid orange",
|
||||
backgroundColor: 'yellow',
|
||||
border: '1px solid orange',
|
||||
},
|
||||
error: {
|
||||
backgroundColor: "orange",
|
||||
border: "1px solid red",
|
||||
backgroundColor: 'orange',
|
||||
border: '1px solid red',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
get template() {
|
||||
return html`
|
||||
<div
|
||||
style=${{
|
||||
...this.#typeStyles[this.props.type],
|
||||
padding: "1em",
|
||||
padding: '1em',
|
||||
}}
|
||||
>
|
||||
<p style=${{ fontStyle: this.props.condition && "italic" }}>Wow!</p>
|
||||
<p style=${{ fontStyle: this.props.condition && 'italic' }}>Wow!</p>
|
||||
</div>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("styled-elements", StyledElements);
|
||||
customElements.define('styled-elements', StyledElements)
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// @ts-check
|
||||
import { WebComponent, html } from "../../src/index.js";
|
||||
import { WebComponent, html } from '../../src/index.js'
|
||||
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 123,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
const list = ["a", "b", "c", "what"];
|
||||
const list = ['a', 'b', 'c', 'what']
|
||||
const links = [
|
||||
{
|
||||
url: "https://ayco.io",
|
||||
text: "Ayo Ayco",
|
||||
url: 'https://ayco.io',
|
||||
text: 'Ayo Ayco',
|
||||
},
|
||||
{
|
||||
url: "https://ayco.io/gh/McFly",
|
||||
text: "McFly",
|
||||
url: 'https://ayco.io/gh/McFly',
|
||||
text: 'McFly',
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
return html`
|
||||
<button
|
||||
|
@ -39,11 +39,11 @@ export class Counter extends WebComponent {
|
|||
<ul>
|
||||
${links.map(
|
||||
(link) =>
|
||||
html`<li><a href=${link.url} target="_blank">${link.text}</a></li>`,
|
||||
html`<li><a href=${link.url} target="_blank">${link.text}</a></li>`
|
||||
)}
|
||||
</ul>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define('my-counter', Counter)
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
import { WebComponent } from "../../src/index.js";
|
||||
import { WebComponent } from '../../src/index.js'
|
||||
import {
|
||||
html,
|
||||
render as lit,
|
||||
} from "https://unpkg.com/lit-html@3.1.0/lit-html.js";
|
||||
} from 'https://unpkg.com/lit-html@3.1.0/lit-html.js'
|
||||
|
||||
export class LitCounter extends WebComponent {
|
||||
static props = {
|
||||
count: 123,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
const list = ["a", "b", "c", "what"];
|
||||
const list = ['a', 'b', 'c', 'what']
|
||||
const links = [
|
||||
{
|
||||
url: "https://ayco.io",
|
||||
text: "Ayo Ayco",
|
||||
url: 'https://ayco.io',
|
||||
text: 'Ayo Ayco',
|
||||
},
|
||||
{
|
||||
url: "https://ayco.io/gh/McFly",
|
||||
text: "McFly",
|
||||
url: 'https://ayco.io/gh/McFly',
|
||||
text: 'McFly',
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
return html`
|
||||
<button
|
||||
|
@ -42,14 +42,14 @@ export class LitCounter extends WebComponent {
|
|||
<ul>
|
||||
${links.map(
|
||||
(link) =>
|
||||
html`<li><a href=${link.url} target="_blank">${link.text}</a></li>`,
|
||||
html`<li><a href=${link.url} target="_blank">${link.text}</a></li>`
|
||||
)}
|
||||
</ul>
|
||||
`;
|
||||
`
|
||||
}
|
||||
render() {
|
||||
lit(this.template, this);
|
||||
lit(this.template, this)
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("lit-counter", LitCounter);
|
||||
customElements.define('lit-counter', LitCounter)
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
// @ts-check
|
||||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 1,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
return html`<button onclick=${() => ++this.props.count}>${this.props.count}</button>`;
|
||||
return html`<button onclick=${() => ++this.props.count}>
|
||||
${this.props.count}
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define('my-counter', Counter)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class HelloWorld extends WebComponent {
|
||||
static props = {
|
||||
name: "a",
|
||||
};
|
||||
addA = () => (this.props.name += "a");
|
||||
name: 'a',
|
||||
}
|
||||
addA = () => (this.props.name += 'a')
|
||||
|
||||
get template() {
|
||||
return html`<button onclick=${this.addA}>W${this.props.name}h!</button>`;
|
||||
return html`<button onclick=${this.addA}>W${this.props.name}h!</button>`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-hello-world", HelloWorld);
|
||||
customElements.define('my-hello-world', HelloWorld)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
/**
|
||||
* TODO: rendering currently wipes all children so focus gets removed on fields
|
||||
|
@ -6,45 +6,43 @@ import { html, WebComponent } from "../../src/index.js";
|
|||
export class ObjectText extends WebComponent {
|
||||
static props = {
|
||||
object: {
|
||||
hello: "worldzz",
|
||||
hello: 'worldzz',
|
||||
age: 2,
|
||||
},
|
||||
};
|
||||
}
|
||||
onChanges() {
|
||||
console.log(">>> object", this.props.object);
|
||||
console.log('>>> object', this.props.object)
|
||||
}
|
||||
get template() {
|
||||
return html`
|
||||
<form>
|
||||
<label for="greeting-field">Hello</label>
|
||||
<textarea
|
||||
onkeyup=${
|
||||
(event) => {
|
||||
onkeyup=${(event) => {
|
||||
this.props.object = {
|
||||
...this.props.object,
|
||||
hello: event.target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
id="greeting-field">
|
||||
}}
|
||||
id="greeting-field"
|
||||
>
|
||||
${this.props.object.hello}
|
||||
</textarea>
|
||||
</textarea
|
||||
>
|
||||
<label for="age-field">Age</label>
|
||||
<input
|
||||
onkeyup=${
|
||||
(event) => {
|
||||
onkeyup=${(event) => {
|
||||
this.props.object = {
|
||||
...this.props.object,
|
||||
age: event.target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
id="age-field" value=${this.props.object.age} />
|
||||
}}
|
||||
id="age-field"
|
||||
value=${this.props.object.age}
|
||||
/>
|
||||
</form>
|
||||
`;
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
customElements.define("my-object", ObjectText);
|
||||
customElements.define('my-object', ObjectText)
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
// @ts-check
|
||||
import { html, WebComponent } from "../../src/index.js";
|
||||
import { html, WebComponent } from '../../src/index.js'
|
||||
|
||||
export class Toggle extends WebComponent {
|
||||
static props = {
|
||||
toggle: false,
|
||||
};
|
||||
}
|
||||
handleToggle() {
|
||||
this.props.toggle = !this.props.toggle;
|
||||
this.props.toggle = !this.props.toggle
|
||||
}
|
||||
get template() {
|
||||
return html`
|
||||
<button onclick=${() => this.handleToggle()} id="toggle">${this.props.toggle ? "On" : "Off"}</button>
|
||||
`;
|
||||
<button onclick=${() => this.handleToggle()} id="toggle">
|
||||
${this.props.toggle ? 'On' : 'Off'}
|
||||
</button>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-toggle", Toggle);
|
||||
customElements.define('my-toggle', Toggle)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
@ -10,33 +10,27 @@
|
|||
<script type="module" src="./Object.mjs"></script>
|
||||
<style>
|
||||
* {
|
||||
font-size: larger
|
||||
font-size: larger;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Counter: <my-counter></my-counter>
|
||||
</div>
|
||||
<div>
|
||||
Toggle: <my-toggle></my-toggle>
|
||||
</div>
|
||||
<div>
|
||||
String: <my-hello-world></my-hello-world>
|
||||
</div>
|
||||
<div>Counter: <my-counter></my-counter></div>
|
||||
<div>Toggle: <my-toggle></my-toggle></div>
|
||||
<div>String: <my-hello-world></my-hello-world></div>
|
||||
<div>
|
||||
<my-object></my-object>
|
||||
<p id="display-panel"></p>
|
||||
</div>
|
||||
<script type="module">
|
||||
import { attachEffect } from "../../src/index.js";
|
||||
const myObjectEl = document.querySelector('my-object');
|
||||
const objectProp = myObjectEl.props.object;
|
||||
const displayPanelEl = document.querySelector('#display-panel');
|
||||
displayPanelEl.textContent = JSON.stringify(objectProp);
|
||||
import { attachEffect } from '../../src/index.js'
|
||||
const myObjectEl = document.querySelector('my-object')
|
||||
const objectProp = myObjectEl.props.object
|
||||
const displayPanelEl = document.querySelector('#display-panel')
|
||||
displayPanelEl.textContent = JSON.stringify(objectProp)
|
||||
attachEffect(objectProp, (object) => {
|
||||
displayPanelEl.textContent = JSON.stringify(object);
|
||||
});
|
||||
displayPanelEl.textContent = JSON.stringify(object)
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
// @ts-check
|
||||
import { WebComponent, html } from "../../src/index.js";
|
||||
import { WebComponent, html } from '../../src/index.js'
|
||||
|
||||
export class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 123,
|
||||
};
|
||||
}
|
||||
static shadowRootInit = {
|
||||
mode: "open",
|
||||
};
|
||||
mode: 'open',
|
||||
}
|
||||
|
||||
get template() {
|
||||
const list = ["a", "b", "c", "what"];
|
||||
const list = ['a', 'b', 'c', 'what']
|
||||
const links = [
|
||||
{
|
||||
url: "https://ayco.io",
|
||||
text: "Ayo Ayco",
|
||||
url: 'https://ayco.io',
|
||||
text: 'Ayo Ayco',
|
||||
},
|
||||
{
|
||||
url: "https://ayco.io/gh/McFly",
|
||||
text: "McFly",
|
||||
url: 'https://ayco.io/gh/McFly',
|
||||
text: 'McFly',
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
return html`
|
||||
<button
|
||||
class="hey"
|
||||
id="btn"
|
||||
onClick=${() => ++this.props.count}
|
||||
style=${{ backgroundColor: "green", color: "white" }}
|
||||
style=${{ backgroundColor: 'green', color: 'white' }}
|
||||
about="Elephant"
|
||||
data-name="thing"
|
||||
aria-name="thingz"
|
||||
|
@ -48,8 +48,8 @@ export class Counter extends WebComponent {
|
|||
</li>`
|
||||
)}
|
||||
</ul>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("my-counter", Counter);
|
||||
customElements.define('my-counter', Counter)
|
||||
|
|
3566
pnpm-lock.yaml
3566
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,3 @@
|
|||
packages:
|
||||
# include packages in subfolders (e.g. apps/ and packages/)
|
||||
- "site/**"
|
||||
- 'site/**'
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# McFly Starter Project
|
||||
|
||||
|
||||
|
||||
## Background
|
||||
|
||||
This project was generated from the basic template for **McFly** -- a no-framework framework that assists in leveraging the web platform.
|
||||
|
||||

|
||||
|
@ -10,6 +9,7 @@ This project was generated from the basic template for **McFly** -- a no-framewo
|
|||
It contains example files to get you started using vanilla web technologies in a modern way. See the [Special Directories](#special-directories) section for more information.
|
||||
|
||||
## Features
|
||||
|
||||
The time has come for vanilla Web tech. 🎉
|
||||
|
||||
✅ Create web apps with vanilla custom elements<br>
|
||||
|
@ -19,17 +19,21 @@ The time has come for vanilla Web tech. 🎉
|
|||
✅ Deploy anywhere<br>
|
||||
|
||||
## Special directories
|
||||
|
||||
**1. `./src/pages/`**
|
||||
|
||||
- file-based routing for `.html` files
|
||||
- directly use custom elements & static fragments (no imports or registry maintenance needed)
|
||||
- use `<script server:setup>` to define logic that runs on the server, which then gets stripped away
|
||||
|
||||
**2. `./src/components/`**
|
||||
|
||||
- custom element constructor files (only `.js` files for now)
|
||||
- all components are automatically registered using their file names; a `hello-world.js` component can be used as `<hello-world>`
|
||||
- static `.html` fragments; a `my-header.html` fragment can be directly used as `<my-header>`
|
||||
|
||||
**3. `./routes/api/`**
|
||||
|
||||
- file-based routing for REST API endpoints
|
||||
- e.g., `./routes/api/users.ts` can be accessed via `http://<domain>/api/users`
|
||||
- TypeScript or JavaScript welcome!
|
||||
|
@ -39,25 +43,25 @@ The time has come for vanilla Web tech. 🎉
|
|||
To tell McFly you want to use components, pass the mode (only `"js"` for now) to the `components` prop mcfly.config.ts
|
||||
|
||||
```js
|
||||
import defineConfig from "./packages/define-config";
|
||||
import defineConfig from './packages/define-config'
|
||||
|
||||
export default defineConfig({
|
||||
components: "js",
|
||||
});
|
||||
|
||||
components: 'js',
|
||||
})
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
The following commands are available to you on this project. Add more, or modify them as needed in your `./package.json` file.
|
||||
|
||||
| Command | Action |
|
||||
| --- | --- |
|
||||
| --------------- | ---------------------------------------------------- |
|
||||
| npm start | Start the development server |
|
||||
| npm run prepare | Prepare the workspace |
|
||||
| npm run build | Locally generate the app's build files to `./output` |
|
||||
| npm run preview | Preview the built app locally |
|
||||
|
||||
|
||||
---
|
||||
*Just keep building*<br />
|
||||
*A project by [Ayo Ayco](https://ayco.io)*
|
||||
|
||||
_Just keep building_<br />
|
||||
_A project by [Ayo Ayco](https://ayco.io)_
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { defineMcFlyConfig } from "#imports";
|
||||
import { defineMcFlyConfig } from '#imports'
|
||||
export default defineMcFlyConfig({
|
||||
components: "js",
|
||||
});
|
||||
components: 'js',
|
||||
})
|
||||
|
|
|
@ -1 +1 @@
|
|||
export default defineNitroConfig({ extends: "@mcflyjs/config" });
|
||||
export default defineNitroConfig({ extends: '@mcflyjs/config' })
|
||||
|
|
|
@ -1,3 +1,118 @@
|
|||
/* PrismJS 1.29.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
color: #000;
|
||||
background: 0 0;
|
||||
text-shadow: 0 1px #fff;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
code[class*='language-'] ::-moz-selection,
|
||||
code[class*='language-']::-moz-selection,
|
||||
pre[class*='language-'] ::-moz-selection,
|
||||
pre[class*='language-']::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
code[class*='language-'] ::selection,
|
||||
code[class*='language-']::selection,
|
||||
pre[class*='language-'] ::selection,
|
||||
pre[class*='language-']::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
@media print {
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
pre[class*='language-'] {
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
:not(pre) > code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
:not(pre) > code[class*='language-'] {
|
||||
padding: 0.1em;
|
||||
border-radius: 0.3em;
|
||||
white-space: normal;
|
||||
}
|
||||
.token.cdata,
|
||||
.token.comment,
|
||||
.token.doctype,
|
||||
.token.prolog {
|
||||
color: #708090;
|
||||
}
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
.token.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.token.boolean,
|
||||
.token.constant,
|
||||
.token.deleted,
|
||||
.token.number,
|
||||
.token.property,
|
||||
.token.symbol,
|
||||
.token.tag {
|
||||
color: #905;
|
||||
}
|
||||
.token.attr-name,
|
||||
.token.builtin,
|
||||
.token.char,
|
||||
.token.inserted,
|
||||
.token.selector,
|
||||
.token.string {
|
||||
color: #690;
|
||||
}
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.entity,
|
||||
.token.operator,
|
||||
.token.url {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, 0.5);
|
||||
}
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
.token.class-name,
|
||||
.token.function {
|
||||
color: #dd4a68;
|
||||
}
|
||||
.token.important,
|
||||
.token.regex,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
.token.bold,
|
||||
.token.important {
|
||||
font-weight: 700;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/* PrismJS 1.29.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
var _self =
|
||||
"undefined" != typeof window
|
||||
'undefined' != typeof window
|
||||
? window
|
||||
: "undefined" != typeof WorkerGlobalScope &&
|
||||
: 'undefined' != typeof WorkerGlobalScope &&
|
||||
self instanceof WorkerGlobalScope
|
||||
? self
|
||||
: {},
|
||||
|
@ -22,27 +22,27 @@ var _self =
|
|||
: Array.isArray(n)
|
||||
? n.map(e)
|
||||
: n
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/\u00a0/g, " ");
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/\u00a0/g, ' ')
|
||||
},
|
||||
type: function (e) {
|
||||
return Object.prototype.toString.call(e).slice(8, -1);
|
||||
return Object.prototype.toString.call(e).slice(8, -1)
|
||||
},
|
||||
objId: function (e) {
|
||||
return (
|
||||
e.__id || Object.defineProperty(e, "__id", { value: ++t }), e.__id
|
||||
);
|
||||
e.__id || Object.defineProperty(e, '__id', { value: ++t }), e.__id
|
||||
)
|
||||
},
|
||||
clone: function e(n, t) {
|
||||
var r, i;
|
||||
var r, i
|
||||
switch (((t = t || {}), a.util.type(n))) {
|
||||
case "Object":
|
||||
if (((i = a.util.objId(n)), t[i])) return t[i];
|
||||
case 'Object':
|
||||
if (((i = a.util.objId(n)), t[i])) return t[i]
|
||||
for (var l in ((r = {}), (t[i] = r), n))
|
||||
n.hasOwnProperty(l) && (r[l] = e(n[l], t));
|
||||
return r;
|
||||
case "Array":
|
||||
n.hasOwnProperty(l) && (r[l] = e(n[l], t))
|
||||
return r
|
||||
case 'Array':
|
||||
return (
|
||||
(i = a.util.objId(n)),
|
||||
t[i]
|
||||
|
@ -50,49 +50,49 @@ var _self =
|
|||
: ((r = []),
|
||||
(t[i] = r),
|
||||
n.forEach(function (n, a) {
|
||||
r[a] = e(n, t);
|
||||
r[a] = e(n, t)
|
||||
}),
|
||||
r)
|
||||
);
|
||||
)
|
||||
default:
|
||||
return n;
|
||||
return n
|
||||
}
|
||||
},
|
||||
getLanguage: function (e) {
|
||||
for (; e; ) {
|
||||
var t = n.exec(e.className);
|
||||
if (t) return t[1].toLowerCase();
|
||||
e = e.parentElement;
|
||||
var t = n.exec(e.className)
|
||||
if (t) return t[1].toLowerCase()
|
||||
e = e.parentElement
|
||||
}
|
||||
return "none";
|
||||
return 'none'
|
||||
},
|
||||
setLanguage: function (e, t) {
|
||||
(e.className = e.className.replace(RegExp(n, "gi"), "")),
|
||||
e.classList.add("language-" + t);
|
||||
;(e.className = e.className.replace(RegExp(n, 'gi'), '')),
|
||||
e.classList.add('language-' + t)
|
||||
},
|
||||
currentScript: function () {
|
||||
if ("undefined" == typeof document) return null;
|
||||
if ("currentScript" in document) return document.currentScript;
|
||||
if ('undefined' == typeof document) return null
|
||||
if ('currentScript' in document) return document.currentScript
|
||||
try {
|
||||
throw new Error();
|
||||
throw new Error()
|
||||
} catch (r) {
|
||||
var e = (/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack) ||
|
||||
[])[1];
|
||||
[])[1]
|
||||
if (e) {
|
||||
var n = document.getElementsByTagName("script");
|
||||
for (var t in n) if (n[t].src == e) return n[t];
|
||||
var n = document.getElementsByTagName('script')
|
||||
for (var t in n) if (n[t].src == e) return n[t]
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
},
|
||||
isActive: function (e, n, t) {
|
||||
for (var r = "no-" + n; e; ) {
|
||||
var a = e.classList;
|
||||
if (a.contains(n)) return !0;
|
||||
if (a.contains(r)) return !1;
|
||||
e = e.parentElement;
|
||||
for (var r = 'no-' + n; e; ) {
|
||||
var a = e.classList
|
||||
if (a.contains(n)) return !0
|
||||
if (a.contains(r)) return !1
|
||||
e = e.parentElement
|
||||
}
|
||||
return !!t;
|
||||
return !!t
|
||||
},
|
||||
},
|
||||
languages: {
|
||||
|
@ -101,45 +101,45 @@ var _self =
|
|||
text: r,
|
||||
txt: r,
|
||||
extend: function (e, n) {
|
||||
var t = a.util.clone(a.languages[e]);
|
||||
for (var r in n) t[r] = n[r];
|
||||
return t;
|
||||
var t = a.util.clone(a.languages[e])
|
||||
for (var r in n) t[r] = n[r]
|
||||
return t
|
||||
},
|
||||
insertBefore: function (e, n, t, r) {
|
||||
var i = (r = r || a.languages)[e],
|
||||
l = {};
|
||||
l = {}
|
||||
for (var o in i)
|
||||
if (i.hasOwnProperty(o)) {
|
||||
if (o == n)
|
||||
for (var s in t) t.hasOwnProperty(s) && (l[s] = t[s]);
|
||||
t.hasOwnProperty(o) || (l[o] = i[o]);
|
||||
for (var s in t) t.hasOwnProperty(s) && (l[s] = t[s])
|
||||
t.hasOwnProperty(o) || (l[o] = i[o])
|
||||
}
|
||||
var u = r[e];
|
||||
var u = r[e]
|
||||
return (
|
||||
(r[e] = l),
|
||||
a.languages.DFS(a.languages, function (n, t) {
|
||||
t === u && n != e && (this[n] = l);
|
||||
t === u && n != e && (this[n] = l)
|
||||
}),
|
||||
l
|
||||
);
|
||||
)
|
||||
},
|
||||
DFS: function e(n, t, r, i) {
|
||||
i = i || {};
|
||||
var l = a.util.objId;
|
||||
i = i || {}
|
||||
var l = a.util.objId
|
||||
for (var o in n)
|
||||
if (n.hasOwnProperty(o)) {
|
||||
t.call(n, o, n[o], r || o);
|
||||
t.call(n, o, n[o], r || o)
|
||||
var s = n[o],
|
||||
u = a.util.type(s);
|
||||
"Object" !== u || i[l(s)]
|
||||
? "Array" !== u || i[l(s)] || ((i[l(s)] = !0), e(s, t, o, i))
|
||||
: ((i[l(s)] = !0), e(s, t, null, i));
|
||||
u = a.util.type(s)
|
||||
'Object' !== u || i[l(s)]
|
||||
? 'Array' !== u || i[l(s)] || ((i[l(s)] = !0), e(s, t, o, i))
|
||||
: ((i[l(s)] = !0), e(s, t, null, i))
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: {},
|
||||
highlightAll: function (e, n) {
|
||||
a.highlightAllUnder(document, e, n);
|
||||
a.highlightAllUnder(document, e, n)
|
||||
},
|
||||
highlightAllUnder: function (e, n, t) {
|
||||
var r = {
|
||||
|
@ -147,162 +147,160 @@ var _self =
|
|||
container: e,
|
||||
selector:
|
||||
'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code',
|
||||
};
|
||||
a.hooks.run("before-highlightall", r),
|
||||
}
|
||||
a.hooks.run('before-highlightall', r),
|
||||
(r.elements = Array.prototype.slice.apply(
|
||||
r.container.querySelectorAll(r.selector),
|
||||
r.container.querySelectorAll(r.selector)
|
||||
)),
|
||||
a.hooks.run("before-all-elements-highlight", r);
|
||||
a.hooks.run('before-all-elements-highlight', r)
|
||||
for (var i, l = 0; (i = r.elements[l++]); )
|
||||
a.highlightElement(i, !0 === n, r.callback);
|
||||
a.highlightElement(i, !0 === n, r.callback)
|
||||
},
|
||||
highlightElement: function (n, t, r) {
|
||||
var i = a.util.getLanguage(n),
|
||||
l = a.languages[i];
|
||||
a.util.setLanguage(n, i);
|
||||
var o = n.parentElement;
|
||||
o && "pre" === o.nodeName.toLowerCase() && a.util.setLanguage(o, i);
|
||||
var s = { element: n, language: i, grammar: l, code: n.textContent };
|
||||
l = a.languages[i]
|
||||
a.util.setLanguage(n, i)
|
||||
var o = n.parentElement
|
||||
o && 'pre' === o.nodeName.toLowerCase() && a.util.setLanguage(o, i)
|
||||
var s = { element: n, language: i, grammar: l, code: n.textContent }
|
||||
function u(e) {
|
||||
(s.highlightedCode = e),
|
||||
a.hooks.run("before-insert", s),
|
||||
;(s.highlightedCode = e),
|
||||
a.hooks.run('before-insert', s),
|
||||
(s.element.innerHTML = s.highlightedCode),
|
||||
a.hooks.run("after-highlight", s),
|
||||
a.hooks.run("complete", s),
|
||||
r && r.call(s.element);
|
||||
a.hooks.run('after-highlight', s),
|
||||
a.hooks.run('complete', s),
|
||||
r && r.call(s.element)
|
||||
}
|
||||
if (
|
||||
(a.hooks.run("before-sanity-check", s),
|
||||
(a.hooks.run('before-sanity-check', s),
|
||||
(o = s.element.parentElement) &&
|
||||
"pre" === o.nodeName.toLowerCase() &&
|
||||
!o.hasAttribute("tabindex") &&
|
||||
o.setAttribute("tabindex", "0"),
|
||||
'pre' === o.nodeName.toLowerCase() &&
|
||||
!o.hasAttribute('tabindex') &&
|
||||
o.setAttribute('tabindex', '0'),
|
||||
!s.code)
|
||||
)
|
||||
return a.hooks.run("complete", s), void (r && r.call(s.element));
|
||||
if ((a.hooks.run("before-highlight", s), s.grammar))
|
||||
return a.hooks.run('complete', s), void (r && r.call(s.element))
|
||||
if ((a.hooks.run('before-highlight', s), s.grammar))
|
||||
if (t && e.Worker) {
|
||||
var c = new Worker(a.filename);
|
||||
(c.onmessage = function (e) {
|
||||
u(e.data);
|
||||
var c = new Worker(a.filename)
|
||||
;(c.onmessage = function (e) {
|
||||
u(e.data)
|
||||
}),
|
||||
c.postMessage(
|
||||
JSON.stringify({
|
||||
language: s.language,
|
||||
code: s.code,
|
||||
immediateClose: !0,
|
||||
}),
|
||||
);
|
||||
} else u(a.highlight(s.code, s.grammar, s.language));
|
||||
else u(a.util.encode(s.code));
|
||||
})
|
||||
)
|
||||
} else u(a.highlight(s.code, s.grammar, s.language))
|
||||
else u(a.util.encode(s.code))
|
||||
},
|
||||
highlight: function (e, n, t) {
|
||||
var r = { code: e, grammar: n, language: t };
|
||||
if ((a.hooks.run("before-tokenize", r), !r.grammar))
|
||||
throw new Error(
|
||||
'The language "' + r.language + '" has no grammar.',
|
||||
);
|
||||
var r = { code: e, grammar: n, language: t }
|
||||
if ((a.hooks.run('before-tokenize', r), !r.grammar))
|
||||
throw new Error('The language "' + r.language + '" has no grammar.')
|
||||
return (
|
||||
(r.tokens = a.tokenize(r.code, r.grammar)),
|
||||
a.hooks.run("after-tokenize", r),
|
||||
a.hooks.run('after-tokenize', r),
|
||||
i.stringify(a.util.encode(r.tokens), r.language)
|
||||
);
|
||||
)
|
||||
},
|
||||
tokenize: function (e, n) {
|
||||
var t = n.rest;
|
||||
var t = n.rest
|
||||
if (t) {
|
||||
for (var r in t) n[r] = t[r];
|
||||
delete n.rest;
|
||||
for (var r in t) n[r] = t[r]
|
||||
delete n.rest
|
||||
}
|
||||
var a = new s();
|
||||
var a = new s()
|
||||
return (
|
||||
u(a, a.head, e),
|
||||
o(e, a, n, a.head, 0),
|
||||
(function (e) {
|
||||
for (var n = [], t = e.head.next; t !== e.tail; )
|
||||
n.push(t.value), (t = t.next);
|
||||
return n;
|
||||
n.push(t.value), (t = t.next)
|
||||
return n
|
||||
})(a)
|
||||
);
|
||||
)
|
||||
},
|
||||
hooks: {
|
||||
all: {},
|
||||
add: function (e, n) {
|
||||
var t = a.hooks.all;
|
||||
(t[e] = t[e] || []), t[e].push(n);
|
||||
var t = a.hooks.all
|
||||
;(t[e] = t[e] || []), t[e].push(n)
|
||||
},
|
||||
run: function (e, n) {
|
||||
var t = a.hooks.all[e];
|
||||
if (t && t.length) for (var r, i = 0; (r = t[i++]); ) r(n);
|
||||
var t = a.hooks.all[e]
|
||||
if (t && t.length) for (var r, i = 0; (r = t[i++]); ) r(n)
|
||||
},
|
||||
},
|
||||
Token: i,
|
||||
};
|
||||
}
|
||||
function i(e, n, t, r) {
|
||||
(this.type = e),
|
||||
;(this.type = e),
|
||||
(this.content = n),
|
||||
(this.alias = t),
|
||||
(this.length = 0 | (r || "").length);
|
||||
(this.length = 0 | (r || '').length)
|
||||
}
|
||||
function l(e, n, t, r) {
|
||||
e.lastIndex = n;
|
||||
var a = e.exec(t);
|
||||
e.lastIndex = n
|
||||
var a = e.exec(t)
|
||||
if (a && r && a[1]) {
|
||||
var i = a[1].length;
|
||||
(a.index += i), (a[0] = a[0].slice(i));
|
||||
var i = a[1].length
|
||||
;(a.index += i), (a[0] = a[0].slice(i))
|
||||
}
|
||||
return a;
|
||||
return a
|
||||
}
|
||||
function o(e, n, t, r, s, g) {
|
||||
for (var f in t)
|
||||
if (t.hasOwnProperty(f) && t[f]) {
|
||||
var h = t[f];
|
||||
h = Array.isArray(h) ? h : [h];
|
||||
var h = t[f]
|
||||
h = Array.isArray(h) ? h : [h]
|
||||
for (var d = 0; d < h.length; ++d) {
|
||||
if (g && g.cause == f + "," + d) return;
|
||||
if (g && g.cause == f + ',' + d) return
|
||||
var v = h[d],
|
||||
p = v.inside,
|
||||
m = !!v.lookbehind,
|
||||
y = !!v.greedy,
|
||||
k = v.alias;
|
||||
k = v.alias
|
||||
if (y && !v.pattern.global) {
|
||||
var x = v.pattern.toString().match(/[imsuy]*$/)[0];
|
||||
v.pattern = RegExp(v.pattern.source, x + "g");
|
||||
var x = v.pattern.toString().match(/[imsuy]*$/)[0]
|
||||
v.pattern = RegExp(v.pattern.source, x + 'g')
|
||||
}
|
||||
for (
|
||||
var b = v.pattern || v, w = r.next, A = s;
|
||||
w !== n.tail && !(g && A >= g.reach);
|
||||
A += w.value.length, w = w.next
|
||||
) {
|
||||
var E = w.value;
|
||||
if (n.length > e.length) return;
|
||||
var E = w.value
|
||||
if (n.length > e.length) return
|
||||
if (!(E instanceof i)) {
|
||||
var P,
|
||||
L = 1;
|
||||
L = 1
|
||||
if (y) {
|
||||
if (!(P = l(b, A, e, m)) || P.index >= e.length) break;
|
||||
if (!(P = l(b, A, e, m)) || P.index >= e.length) break
|
||||
var S = P.index,
|
||||
O = P.index + P[0].length,
|
||||
j = A;
|
||||
j = A
|
||||
for (j += w.value.length; S >= j; )
|
||||
j += (w = w.next).value.length;
|
||||
j += (w = w.next).value.length
|
||||
if (((A = j -= w.value.length), w.value instanceof i))
|
||||
continue;
|
||||
continue
|
||||
for (
|
||||
var C = w;
|
||||
C !== n.tail && (j < O || "string" == typeof C.value);
|
||||
C !== n.tail && (j < O || 'string' == typeof C.value);
|
||||
C = C.next
|
||||
)
|
||||
L++, (j += C.value.length);
|
||||
L--, (E = e.slice(A, j)), (P.index -= A);
|
||||
} else if (!(P = l(b, 0, E, m))) continue;
|
||||
S = P.index;
|
||||
L++, (j += C.value.length)
|
||||
L--, (E = e.slice(A, j)), (P.index -= A)
|
||||
} else if (!(P = l(b, 0, E, m))) continue
|
||||
S = P.index
|
||||
var N = P[0],
|
||||
_ = E.slice(0, S),
|
||||
M = E.slice(S + N.length),
|
||||
W = A + E.length;
|
||||
g && W > g.reach && (g.reach = W);
|
||||
var z = w.prev;
|
||||
W = A + E.length
|
||||
g && W > g.reach && (g.reach = W)
|
||||
var z = w.prev
|
||||
if (
|
||||
(_ && ((z = u(n, z, _)), (A += _.length)),
|
||||
c(n, z, L),
|
||||
|
@ -310,9 +308,9 @@ var _self =
|
|||
M && u(n, w, M),
|
||||
L > 1)
|
||||
) {
|
||||
var I = { cause: f + "," + d, reach: W };
|
||||
var I = { cause: f + ',' + d, reach: W }
|
||||
o(e, n, t, w.prev, A, I),
|
||||
g && I.reach > g.reach && (g.reach = I.reach);
|
||||
g && I.reach > g.reach && (g.reach = I.reach)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -321,107 +319,106 @@ var _self =
|
|||
}
|
||||
function s() {
|
||||
var e = { value: null, prev: null, next: null },
|
||||
n = { value: null, prev: e, next: null };
|
||||
(e.next = n), (this.head = e), (this.tail = n), (this.length = 0);
|
||||
n = { value: null, prev: e, next: null }
|
||||
;(e.next = n), (this.head = e), (this.tail = n), (this.length = 0)
|
||||
}
|
||||
function u(e, n, t) {
|
||||
var r = n.next,
|
||||
a = { value: t, prev: n, next: r };
|
||||
return (n.next = a), (r.prev = a), e.length++, a;
|
||||
a = { value: t, prev: n, next: r }
|
||||
return (n.next = a), (r.prev = a), e.length++, a
|
||||
}
|
||||
function c(e, n, t) {
|
||||
for (var r = n.next, a = 0; a < t && r !== e.tail; a++) r = r.next;
|
||||
(n.next = r), (r.prev = n), (e.length -= a);
|
||||
for (var r = n.next, a = 0; a < t && r !== e.tail; a++) r = r.next
|
||||
;(n.next = r), (r.prev = n), (e.length -= a)
|
||||
}
|
||||
if (
|
||||
((e.Prism = a),
|
||||
(i.stringify = function e(n, t) {
|
||||
if ("string" == typeof n) return n;
|
||||
if ('string' == typeof n) return n
|
||||
if (Array.isArray(n)) {
|
||||
var r = "";
|
||||
var r = ''
|
||||
return (
|
||||
n.forEach(function (n) {
|
||||
r += e(n, t);
|
||||
r += e(n, t)
|
||||
}),
|
||||
r
|
||||
);
|
||||
)
|
||||
}
|
||||
var i = {
|
||||
type: n.type,
|
||||
content: e(n.content, t),
|
||||
tag: "span",
|
||||
classes: ["token", n.type],
|
||||
tag: 'span',
|
||||
classes: ['token', n.type],
|
||||
attributes: {},
|
||||
language: t,
|
||||
},
|
||||
l = n.alias;
|
||||
l = n.alias
|
||||
l &&
|
||||
(Array.isArray(l)
|
||||
? Array.prototype.push.apply(i.classes, l)
|
||||
: i.classes.push(l)),
|
||||
a.hooks.run("wrap", i);
|
||||
var o = "";
|
||||
a.hooks.run('wrap', i)
|
||||
var o = ''
|
||||
for (var s in i.attributes)
|
||||
o +=
|
||||
" " +
|
||||
' ' +
|
||||
s +
|
||||
'="' +
|
||||
(i.attributes[s] || "").replace(/"/g, """) +
|
||||
'"';
|
||||
(i.attributes[s] || '').replace(/"/g, '"') +
|
||||
'"'
|
||||
return (
|
||||
"<" +
|
||||
'<' +
|
||||
i.tag +
|
||||
' class="' +
|
||||
i.classes.join(" ") +
|
||||
i.classes.join(' ') +
|
||||
'"' +
|
||||
o +
|
||||
">" +
|
||||
'>' +
|
||||
i.content +
|
||||
"</" +
|
||||
'</' +
|
||||
i.tag +
|
||||
">"
|
||||
);
|
||||
'>'
|
||||
)
|
||||
}),
|
||||
!e.document)
|
||||
)
|
||||
return e.addEventListener
|
||||
? (a.disableWorkerMessageHandler ||
|
||||
e.addEventListener(
|
||||
"message",
|
||||
'message',
|
||||
function (n) {
|
||||
var t = JSON.parse(n.data),
|
||||
r = t.language,
|
||||
i = t.code,
|
||||
l = t.immediateClose;
|
||||
e.postMessage(a.highlight(i, a.languages[r], r)),
|
||||
l && e.close();
|
||||
l = t.immediateClose
|
||||
e.postMessage(a.highlight(i, a.languages[r], r)), l && e.close()
|
||||
},
|
||||
!1,
|
||||
!1
|
||||
),
|
||||
a)
|
||||
: a;
|
||||
var g = a.util.currentScript();
|
||||
: a
|
||||
var g = a.util.currentScript()
|
||||
function f() {
|
||||
a.manual || a.highlightAll();
|
||||
a.manual || a.highlightAll()
|
||||
}
|
||||
if (
|
||||
(g &&
|
||||
((a.filename = g.src),
|
||||
g.hasAttribute("data-manual") && (a.manual = !0)),
|
||||
g.hasAttribute('data-manual') && (a.manual = !0)),
|
||||
!a.manual)
|
||||
) {
|
||||
var h = document.readyState;
|
||||
"loading" === h || ("interactive" === h && g && g.defer)
|
||||
? document.addEventListener("DOMContentLoaded", f)
|
||||
var h = document.readyState
|
||||
'loading' === h || ('interactive' === h && g && g.defer)
|
||||
? document.addEventListener('DOMContentLoaded', f)
|
||||
: window.requestAnimationFrame
|
||||
? window.requestAnimationFrame(f)
|
||||
: window.setTimeout(f, 16);
|
||||
: window.setTimeout(f, 16)
|
||||
}
|
||||
return a;
|
||||
})(_self);
|
||||
"undefined" != typeof module && module.exports && (module.exports = Prism),
|
||||
"undefined" != typeof global && (global.Prism = Prism);
|
||||
(Prism.languages.markup = {
|
||||
return a
|
||||
})(_self)
|
||||
'undefined' != typeof module && module.exports && (module.exports = Prism),
|
||||
'undefined' != typeof global && (global.Prism = Prism)
|
||||
;(Prism.languages.markup = {
|
||||
comment: { pattern: /<!--(?:(?!<!--)[\s\S])*?-->/, greedy: !0 },
|
||||
prolog: { pattern: /<\?[\s\S]+?\?>/, greedy: !0 },
|
||||
doctype: {
|
||||
|
@ -429,7 +426,7 @@ var _self =
|
|||
/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,
|
||||
greedy: !0,
|
||||
inside: {
|
||||
"internal-subset": {
|
||||
'internal-subset': {
|
||||
pattern: /(^[^\[]*\[)[\s\S]+(?=\]>$)/,
|
||||
lookbehind: !0,
|
||||
greedy: !0,
|
||||
|
@ -437,7 +434,7 @@ var _self =
|
|||
},
|
||||
string: { pattern: /"[^"]*"|'[^']*'/, greedy: !0 },
|
||||
punctuation: /^<!|>$|[[\]]/,
|
||||
"doctype-tag": /^DOCTYPE/i,
|
||||
'doctype-tag': /^DOCTYPE/i,
|
||||
name: /[^\s<>'"]+/,
|
||||
},
|
||||
},
|
||||
|
@ -451,120 +448,118 @@ var _self =
|
|||
pattern: /^<\/?[^\s>\/]+/,
|
||||
inside: { punctuation: /^<\/?/, namespace: /^[^\s>\/:]+:/ },
|
||||
},
|
||||
"special-attr": [],
|
||||
"attr-value": {
|
||||
'special-attr': [],
|
||||
'attr-value': {
|
||||
pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,
|
||||
inside: {
|
||||
punctuation: [
|
||||
{ pattern: /^=/, alias: "attr-equals" },
|
||||
{ pattern: /^=/, alias: 'attr-equals' },
|
||||
{ pattern: /^(\s*)["']|["']$/, lookbehind: !0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
punctuation: /\/?>/,
|
||||
"attr-name": {
|
||||
'attr-name': {
|
||||
pattern: /[^\s>\/]+/,
|
||||
inside: { namespace: /^[^\s>\/:]+:/ },
|
||||
},
|
||||
},
|
||||
},
|
||||
entity: [
|
||||
{ pattern: /&[\da-z]{1,8};/i, alias: "named-entity" },
|
||||
{ pattern: /&[\da-z]{1,8};/i, alias: 'named-entity' },
|
||||
/&#x?[\da-f]{1,8};/i,
|
||||
],
|
||||
}),
|
||||
(Prism.languages.markup.tag.inside["attr-value"].inside.entity =
|
||||
(Prism.languages.markup.tag.inside['attr-value'].inside.entity =
|
||||
Prism.languages.markup.entity),
|
||||
(Prism.languages.markup.doctype.inside["internal-subset"].inside =
|
||||
(Prism.languages.markup.doctype.inside['internal-subset'].inside =
|
||||
Prism.languages.markup),
|
||||
Prism.hooks.add("wrap", function (a) {
|
||||
"entity" === a.type &&
|
||||
(a.attributes.title = a.content.replace(/&/, "&"));
|
||||
Prism.hooks.add('wrap', function (a) {
|
||||
'entity' === a.type &&
|
||||
(a.attributes.title = a.content.replace(/&/, '&'))
|
||||
}),
|
||||
Object.defineProperty(Prism.languages.markup.tag, "addInlined", {
|
||||
Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
|
||||
value: function (a, e) {
|
||||
var s = {};
|
||||
(s["language-" + e] = {
|
||||
var s = {}
|
||||
;(s['language-' + e] = {
|
||||
pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
|
||||
lookbehind: !0,
|
||||
inside: Prism.languages[e],
|
||||
}),
|
||||
(s.cdata = /^<!\[CDATA\[|\]\]>$/i);
|
||||
(s.cdata = /^<!\[CDATA\[|\]\]>$/i)
|
||||
var t = {
|
||||
"included-cdata": { pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, inside: s },
|
||||
};
|
||||
t["language-" + e] = { pattern: /[\s\S]+/, inside: Prism.languages[e] };
|
||||
var n = {};
|
||||
(n[a] = {
|
||||
'included-cdata': { pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i, inside: s },
|
||||
}
|
||||
t['language-' + e] = { pattern: /[\s\S]+/, inside: Prism.languages[e] }
|
||||
var n = {}
|
||||
;(n[a] = {
|
||||
pattern: RegExp(
|
||||
"(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)".replace(
|
||||
'(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)'.replace(
|
||||
/__/g,
|
||||
function () {
|
||||
return a;
|
||||
},
|
||||
return a
|
||||
}
|
||||
),
|
||||
"i",
|
||||
'i'
|
||||
),
|
||||
lookbehind: !0,
|
||||
greedy: !0,
|
||||
inside: t,
|
||||
}),
|
||||
Prism.languages.insertBefore("markup", "cdata", n);
|
||||
Prism.languages.insertBefore('markup', 'cdata', n)
|
||||
},
|
||||
}),
|
||||
Object.defineProperty(Prism.languages.markup.tag, "addAttribute", {
|
||||
Object.defineProperty(Prism.languages.markup.tag, 'addAttribute', {
|
||||
value: function (a, e) {
|
||||
Prism.languages.markup.tag.inside["special-attr"].push({
|
||||
Prism.languages.markup.tag.inside['special-attr'].push({
|
||||
pattern: RegExp(
|
||||
"(^|[\"'\\s])(?:" +
|
||||
'(^|["\'\\s])(?:' +
|
||||
a +
|
||||
")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))",
|
||||
"i",
|
||||
')\\s*=\\s*(?:"[^"]*"|\'[^\']*\'|[^\\s\'">=]+(?=[\\s>]))',
|
||||
'i'
|
||||
),
|
||||
lookbehind: !0,
|
||||
inside: {
|
||||
"attr-name": /^[^\s=]+/,
|
||||
"attr-value": {
|
||||
'attr-name': /^[^\s=]+/,
|
||||
'attr-value': {
|
||||
pattern: /=[\s\S]+/,
|
||||
inside: {
|
||||
value: {
|
||||
pattern: /(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,
|
||||
lookbehind: !0,
|
||||
alias: [e, "language-" + e],
|
||||
alias: [e, 'language-' + e],
|
||||
inside: Prism.languages[e],
|
||||
},
|
||||
punctuation: [{ pattern: /^=/, alias: "attr-equals" }, /"|'/],
|
||||
punctuation: [{ pattern: /^=/, alias: 'attr-equals' }, /"|'/],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
},
|
||||
}),
|
||||
(Prism.languages.html = Prism.languages.markup),
|
||||
(Prism.languages.mathml = Prism.languages.markup),
|
||||
(Prism.languages.svg = Prism.languages.markup),
|
||||
(Prism.languages.xml = Prism.languages.extend("markup", {})),
|
||||
(Prism.languages.xml = Prism.languages.extend('markup', {})),
|
||||
(Prism.languages.ssml = Prism.languages.xml),
|
||||
(Prism.languages.atom = Prism.languages.xml),
|
||||
(Prism.languages.rss = Prism.languages.xml);
|
||||
(Prism.languages.rss = Prism.languages.xml)
|
||||
!(function (s) {
|
||||
var e =
|
||||
/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;
|
||||
(s.languages.css = {
|
||||
/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/
|
||||
;(s.languages.css = {
|
||||
comment: /\/\*[\s\S]*?\*\//,
|
||||
atrule: {
|
||||
pattern: RegExp(
|
||||
"@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|" +
|
||||
e.source +
|
||||
")*?(?:;|(?=\\s*\\{))",
|
||||
'@[\\w-](?:[^;{\\s"\']|\\s+(?!\\s)|' + e.source + ')*?(?:;|(?=\\s*\\{))'
|
||||
),
|
||||
inside: {
|
||||
rule: /^@[\w-]+/,
|
||||
"selector-function-argument": {
|
||||
'selector-function-argument': {
|
||||
pattern:
|
||||
/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,
|
||||
lookbehind: !0,
|
||||
alias: "selector",
|
||||
alias: 'selector',
|
||||
},
|
||||
keyword: {
|
||||
pattern: /(^|[^\w-])(?:and|not|only|or)(?![\w-])/,
|
||||
|
@ -574,21 +569,21 @@ var _self =
|
|||
},
|
||||
url: {
|
||||
pattern: RegExp(
|
||||
"\\burl\\((?:" + e.source + "|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)",
|
||||
"i",
|
||||
'\\burl\\((?:' + e.source + '|(?:[^\\\\\r\n()"\']|\\\\[^])*)\\)',
|
||||
'i'
|
||||
),
|
||||
greedy: !0,
|
||||
inside: {
|
||||
function: /^url/i,
|
||||
punctuation: /^\(|\)$/,
|
||||
string: { pattern: RegExp("^" + e.source + "$"), alias: "url" },
|
||||
string: { pattern: RegExp('^' + e.source + '$'), alias: 'url' },
|
||||
},
|
||||
},
|
||||
selector: {
|
||||
pattern: RegExp(
|
||||
"(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|" +
|
||||
'(^|[{}\\s])[^{}\\s](?:[^{};"\'\\s]|\\s+(?![\\s{])|' +
|
||||
e.source +
|
||||
")*(?=\\s*\\{)",
|
||||
')*(?=\\s*\\{)'
|
||||
),
|
||||
lookbehind: !0,
|
||||
},
|
||||
|
@ -602,10 +597,10 @@ var _self =
|
|||
function: { pattern: /(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i, lookbehind: !0 },
|
||||
punctuation: /[(){};:,]/,
|
||||
}),
|
||||
(s.languages.css.atrule.inside.rest = s.languages.css);
|
||||
var t = s.languages.markup;
|
||||
t && (t.tag.addInlined("style", "css"), t.tag.addAttribute("style", "css"));
|
||||
})(Prism);
|
||||
(s.languages.css.atrule.inside.rest = s.languages.css)
|
||||
var t = s.languages.markup
|
||||
t && (t.tag.addInlined('style', 'css'), t.tag.addAttribute('style', 'css'))
|
||||
})(Prism)
|
||||
Prism.languages.clike = {
|
||||
comment: [
|
||||
{ pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, lookbehind: !0, greedy: !0 },
|
||||
|
@ -615,7 +610,7 @@ Prism.languages.clike = {
|
|||
pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
|
||||
greedy: !0,
|
||||
},
|
||||
"class-name": {
|
||||
'class-name': {
|
||||
pattern:
|
||||
/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,
|
||||
lookbehind: !0,
|
||||
|
@ -628,10 +623,10 @@ Prism.languages.clike = {
|
|||
number: /\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,
|
||||
operator: /[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,
|
||||
punctuation: /[{}[\];(),.:]/,
|
||||
};
|
||||
(Prism.languages.javascript = Prism.languages.extend("clike", {
|
||||
"class-name": [
|
||||
Prism.languages.clike["class-name"],
|
||||
}
|
||||
;(Prism.languages.javascript = Prism.languages.extend('clike', {
|
||||
'class-name': [
|
||||
Prism.languages.clike['class-name'],
|
||||
{
|
||||
pattern:
|
||||
/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,
|
||||
|
@ -650,37 +645,37 @@ Prism.languages.clike = {
|
|||
/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
|
||||
number: {
|
||||
pattern: RegExp(
|
||||
"(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])",
|
||||
'(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])'
|
||||
),
|
||||
lookbehind: !0,
|
||||
},
|
||||
operator:
|
||||
/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/,
|
||||
})),
|
||||
(Prism.languages.javascript["class-name"][0].pattern =
|
||||
(Prism.languages.javascript['class-name'][0].pattern =
|
||||
/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/),
|
||||
Prism.languages.insertBefore("javascript", "keyword", {
|
||||
Prism.languages.insertBefore('javascript', 'keyword', {
|
||||
regex: {
|
||||
pattern: RegExp(
|
||||
"((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))",
|
||||
'((?:^|[^$\\w\\xA0-\\uFFFF."\'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))'
|
||||
),
|
||||
lookbehind: !0,
|
||||
greedy: !0,
|
||||
inside: {
|
||||
"regex-source": {
|
||||
'regex-source': {
|
||||
pattern: /^(\/)[\s\S]+(?=\/[a-z]*$)/,
|
||||
lookbehind: !0,
|
||||
alias: "language-regex",
|
||||
alias: 'language-regex',
|
||||
inside: Prism.languages.regex,
|
||||
},
|
||||
"regex-delimiter": /^\/|\/$/,
|
||||
"regex-flags": /^[a-z]+$/,
|
||||
'regex-delimiter': /^\/|\/$/,
|
||||
'regex-flags': /^[a-z]+$/,
|
||||
},
|
||||
},
|
||||
"function-variable": {
|
||||
'function-variable': {
|
||||
pattern:
|
||||
/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,
|
||||
alias: "function",
|
||||
alias: 'function',
|
||||
},
|
||||
parameter: [
|
||||
{
|
||||
|
@ -710,22 +705,22 @@ Prism.languages.clike = {
|
|||
],
|
||||
constant: /\b[A-Z](?:[A-Z_]|\dx?)*\b/,
|
||||
}),
|
||||
Prism.languages.insertBefore("javascript", "string", {
|
||||
hashbang: { pattern: /^#!.*/, greedy: !0, alias: "comment" },
|
||||
"template-string": {
|
||||
Prism.languages.insertBefore('javascript', 'string', {
|
||||
hashbang: { pattern: /^#!.*/, greedy: !0, alias: 'comment' },
|
||||
'template-string': {
|
||||
pattern:
|
||||
/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,
|
||||
greedy: !0,
|
||||
inside: {
|
||||
"template-punctuation": { pattern: /^`|`$/, alias: "string" },
|
||||
'template-punctuation': { pattern: /^`|`$/, alias: 'string' },
|
||||
interpolation: {
|
||||
pattern:
|
||||
/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,
|
||||
lookbehind: !0,
|
||||
inside: {
|
||||
"interpolation-punctuation": {
|
||||
'interpolation-punctuation': {
|
||||
pattern: /^\$\{|\}$/,
|
||||
alias: "punctuation",
|
||||
alias: 'punctuation',
|
||||
},
|
||||
rest: Prism.languages.javascript,
|
||||
},
|
||||
|
@ -733,26 +728,26 @@ Prism.languages.clike = {
|
|||
string: /[\s\S]+/,
|
||||
},
|
||||
},
|
||||
"string-property": {
|
||||
'string-property': {
|
||||
pattern:
|
||||
/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,
|
||||
lookbehind: !0,
|
||||
greedy: !0,
|
||||
alias: "property",
|
||||
alias: 'property',
|
||||
},
|
||||
}),
|
||||
Prism.languages.insertBefore("javascript", "operator", {
|
||||
"literal-property": {
|
||||
Prism.languages.insertBefore('javascript', 'operator', {
|
||||
'literal-property': {
|
||||
pattern:
|
||||
/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,
|
||||
lookbehind: !0,
|
||||
alias: "property",
|
||||
alias: 'property',
|
||||
},
|
||||
}),
|
||||
Prism.languages.markup &&
|
||||
(Prism.languages.markup.tag.addInlined("script", "javascript"),
|
||||
(Prism.languages.markup.tag.addInlined('script', 'javascript'),
|
||||
Prism.languages.markup.tag.addAttribute(
|
||||
"on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)",
|
||||
"javascript",
|
||||
'on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)',
|
||||
'javascript'
|
||||
)),
|
||||
(Prism.languages.js = Prism.languages.javascript);
|
||||
(Prism.languages.js = Prism.languages.javascript)
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
* ...reusable code are in ./src/components
|
||||
* @see https://ayco.io/gh/McFly#special-directories
|
||||
*/
|
||||
import config from "../mcfly.config.mjs";
|
||||
export default useMcFlyRoute({ config, storage: useStorage() });
|
||||
import config from '../mcfly.config.mjs'
|
||||
export default useMcFlyRoute({ config, storage: useStorage() })
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
class CodeBlockComponent extends HTMLElement {
|
||||
connectedCallback() {
|
||||
const trimmed = this.innerHTML.trim();
|
||||
const lang = this.getAttribute("language");
|
||||
const inline = this.getAttribute("inline") !== null;
|
||||
const trimmed = this.innerHTML.trim()
|
||||
const lang = this.getAttribute('language')
|
||||
const inline = this.getAttribute('inline') !== null
|
||||
|
||||
this.innerHTML = `
|
||||
<pre><code id="code">${trimmed}</code></pre>
|
||||
`;
|
||||
`
|
||||
|
||||
/**
|
||||
* @type {HTMLPreElement}
|
||||
*/
|
||||
const pre = this.querySelector("pre");
|
||||
const pre = this.querySelector('pre')
|
||||
|
||||
if (lang) {
|
||||
pre.className = `language-${lang}`;
|
||||
pre.className = `language-${lang}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Partial<CSSStyleDeclaration>}
|
||||
*/
|
||||
const style = {
|
||||
background: "#f5f2f0",
|
||||
padding: "1em",
|
||||
margin: "1em 0",
|
||||
fontSize: "large",
|
||||
overflow: "auto",
|
||||
borderRadius: "5px",
|
||||
};
|
||||
background: '#f5f2f0',
|
||||
padding: '1em',
|
||||
margin: '1em 0',
|
||||
fontSize: 'large',
|
||||
overflow: 'auto',
|
||||
borderRadius: '5px',
|
||||
}
|
||||
|
||||
if (inline) {
|
||||
style.display = "inline";
|
||||
style.padding = "0.25em 0.3em";
|
||||
style.display = 'inline'
|
||||
style.padding = '0.25em 0.3em'
|
||||
}
|
||||
|
||||
Object.keys(style).forEach((rule) => {
|
||||
pre.style[rule] = style[rule];
|
||||
});
|
||||
pre.style[rule] = style[rule]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
class FeatureSet extends WebComponent {
|
||||
#features = [
|
||||
{
|
||||
icon: "️🔄",
|
||||
title: "Reactive.",
|
||||
icon: '️🔄',
|
||||
title: 'Reactive.',
|
||||
description:
|
||||
"A robust API for synchronizing your component's UI and properties",
|
||||
},
|
||||
{
|
||||
icon: "️🤏",
|
||||
title: "Tiny.",
|
||||
icon: '️🤏',
|
||||
title: 'Tiny.',
|
||||
description:
|
||||
"~1 kB base class (minified, compressed) with versatile utilities",
|
||||
'~1 kB base class (minified, compressed) with versatile utilities',
|
||||
},
|
||||
{
|
||||
icon: "😌",
|
||||
title: "Easy.",
|
||||
description: "Sensible life-cycle hooks that you understand and remember",
|
||||
url: "",
|
||||
icon: '😌',
|
||||
title: 'Easy.',
|
||||
description: 'Sensible life-cycle hooks that you understand and remember',
|
||||
url: '',
|
||||
},
|
||||
{
|
||||
icon: "️💡",
|
||||
title: "Familiar.",
|
||||
icon: '️💡',
|
||||
title: 'Familiar.',
|
||||
description:
|
||||
"Use the built-in JSX-like syntax or bring your own custom templating",
|
||||
url: "https://codepen.io/ayoayco-the-styleful/pen/ZEwNJBR?editors=1010",
|
||||
'Use the built-in JSX-like syntax or bring your own custom templating',
|
||||
url: 'https://codepen.io/ayoayco-the-styleful/pen/ZEwNJBR?editors=1010',
|
||||
},
|
||||
{
|
||||
icon: "️🛜",
|
||||
title: "Powerful.",
|
||||
icon: '️🛜',
|
||||
title: 'Powerful.',
|
||||
description:
|
||||
"Attach 'side effects' that gets triggered on property value changes",
|
||||
url: "",
|
||||
url: '',
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
/**
|
||||
* @type {Array<HTMLArticleElement>}
|
||||
*/
|
||||
get articleEl() {
|
||||
return this.querySelectorAll("article");
|
||||
return this.querySelectorAll('article')
|
||||
}
|
||||
|
||||
afterViewInit() {
|
||||
|
@ -46,29 +46,29 @@ class FeatureSet extends WebComponent {
|
|||
* @type {Partial<CSSStyleDeclaration>}
|
||||
*/
|
||||
const articleStyles = {
|
||||
border: "1px solid #ccc",
|
||||
borderRadius: "5px",
|
||||
padding: "30px",
|
||||
margin: "0 auto 1em",
|
||||
boxShadow: "5px 25px 10px -25px rgba(34, 34, 34, 0.15)",
|
||||
};
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '5px',
|
||||
padding: '30px',
|
||||
margin: '0 auto 1em',
|
||||
boxShadow: '5px 25px 10px -25px rgba(34, 34, 34, 0.15)',
|
||||
}
|
||||
Object.keys(articleStyles).forEach((rule) =>
|
||||
this.articleEl.forEach((el) => (el.style[rule] = articleStyles[rule])),
|
||||
);
|
||||
this.articleEl.forEach((el) => (el.style[rule] = articleStyles[rule]))
|
||||
)
|
||||
|
||||
/**
|
||||
* @type {Partial<CSSStyleDeclaration>}
|
||||
*/
|
||||
const ftrStyles = {
|
||||
maxWidth: "800px",
|
||||
margin: "0 auto",
|
||||
padding: "30px",
|
||||
gap: "1em",
|
||||
};
|
||||
const featureWrapper = this.querySelector(".feature-wrapper");
|
||||
maxWidth: '800px',
|
||||
margin: '0 auto',
|
||||
padding: '30px',
|
||||
gap: '1em',
|
||||
}
|
||||
const featureWrapper = this.querySelector('.feature-wrapper')
|
||||
Object.keys(ftrStyles).forEach(
|
||||
(rule) => (featureWrapper.style[rule] = ftrStyles[rule]),
|
||||
);
|
||||
(rule) => (featureWrapper.style[rule] = ftrStyles[rule])
|
||||
)
|
||||
}
|
||||
|
||||
get template() {
|
||||
|
@ -84,9 +84,9 @@ class FeatureSet extends WebComponent {
|
|||
${feature.description}
|
||||
</p>
|
||||
</article>
|
||||
`,
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
class Counter extends WebComponent {
|
||||
static props = {
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
get template() {
|
||||
return html`<button onClick=${() => ++this.props.count}>
|
||||
${this.props.count}
|
||||
</button>`;
|
||||
</button>`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
--color-fade: #416fff;
|
||||
}
|
||||
body {
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
class HelloWorld extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
return ["my-name"];
|
||||
return ['my-name']
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
let count = 0;
|
||||
const currentName = this.getAttribute("my-name");
|
||||
let count = 0
|
||||
const currentName = this.getAttribute('my-name')
|
||||
|
||||
if (!currentName) {
|
||||
this.setAttribute("my-name", "World");
|
||||
this.setAttribute('my-name', 'World')
|
||||
}
|
||||
|
||||
this.onclick = () => this.setAttribute("my-name", `Clicked ${++count}x`);
|
||||
this.onclick = () => this.setAttribute('my-name', `Clicked ${++count}x`)
|
||||
}
|
||||
|
||||
attributeChangedCallback(property, previousValue, currentValue) {
|
||||
if (property === "my-name" && previousValue !== currentValue) {
|
||||
this.innerHTML = `<button style="cursor:pointer">Hello ${currentValue}!</button>`;
|
||||
if (property === 'my-name' && previousValue !== currentValue) {
|
||||
this.innerHTML = `<button style="cursor:pointer">Hello ${currentValue}!</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,20 +10,20 @@
|
|||
<script src="prism.js" defer></script>
|
||||
<script server:setup>
|
||||
const project = {
|
||||
name: "WebComponent.io",
|
||||
description: "A simple reactivity system for web components",
|
||||
};
|
||||
name: 'WebComponent.io',
|
||||
description: 'A simple reactivity system for web components',
|
||||
}
|
||||
const author = {
|
||||
name: "Ayo Ayco",
|
||||
url: "https://ayco.io",
|
||||
year: "2023",
|
||||
};
|
||||
name: 'Ayo Ayco',
|
||||
url: 'https://ayco.io',
|
||||
year: '2023',
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@counter-style publish-icons {
|
||||
system: cyclic;
|
||||
symbols: "️✅";
|
||||
suffix: " ";
|
||||
symbols: '️✅';
|
||||
suffix: ' ';
|
||||
}
|
||||
main {
|
||||
font-size: large;
|
||||
|
|
|
@ -9,31 +9,31 @@ import {
|
|||
getCamelCase,
|
||||
serialize,
|
||||
deserialize,
|
||||
} from "./utils/index.js";
|
||||
} from './utils/index.js'
|
||||
|
||||
/**
|
||||
* A minimal base class to reduce the complexity of creating reactive custom elements
|
||||
* @see https://WebComponent.io
|
||||
*/
|
||||
export class WebComponent extends HTMLElement {
|
||||
#host;
|
||||
#prevDOM;
|
||||
#props;
|
||||
#typeMap = {};
|
||||
#effectsMap = {};
|
||||
#host
|
||||
#prevDOM
|
||||
#props
|
||||
#typeMap = {}
|
||||
#effectsMap = {}
|
||||
|
||||
/**
|
||||
* Array of strings that tells the browsers which attributes will cause a render
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
static properties = [];
|
||||
static properties = []
|
||||
|
||||
/**
|
||||
* Blueprint for the Proxy props
|
||||
* @typedef {{[name: string]: any}} PropStringMap
|
||||
* @type {PropStringMap}
|
||||
*/
|
||||
static props;
|
||||
static props
|
||||
|
||||
/**
|
||||
* Read-only string property that represents how the component will be rendered
|
||||
|
@ -41,14 +41,14 @@ export class WebComponent extends HTMLElement {
|
|||
* @see https://www.npmjs.com/package/web-component-base#template-vs-render
|
||||
*/
|
||||
get template() {
|
||||
return "";
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow root initialization options
|
||||
* @type {ShadowRootInit}
|
||||
*/
|
||||
static shadowRootInit;
|
||||
static shadowRootInit
|
||||
|
||||
/**
|
||||
* Read-only property containing camelCase counterparts of observed attributes.
|
||||
|
@ -57,7 +57,7 @@ export class WebComponent extends HTMLElement {
|
|||
* @type {PropStringMap}
|
||||
*/
|
||||
get props() {
|
||||
return this.#props;
|
||||
return this.#props
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,126 +87,126 @@ export class WebComponent extends HTMLElement {
|
|||
onChanges(changes) {}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#initializeProps();
|
||||
this.#initializeHost();
|
||||
super()
|
||||
this.#initializeProps()
|
||||
this.#initializeHost()
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
const propKeys = this.props
|
||||
? Object.keys(this.props).map((camelCase) => getKebabCase(camelCase))
|
||||
: [];
|
||||
: []
|
||||
|
||||
return [...new Set([...this.properties, ...propKeys])];
|
||||
return [...new Set([...this.properties, ...propKeys])]
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.onInit();
|
||||
this.render();
|
||||
this.afterViewInit();
|
||||
this.onInit()
|
||||
this.render()
|
||||
this.afterViewInit()
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.onDestroy();
|
||||
this.onDestroy()
|
||||
}
|
||||
|
||||
attributeChangedCallback(property, previousValue, currentValue) {
|
||||
const camelCaps = getCamelCase(property);
|
||||
const camelCaps = getCamelCase(property)
|
||||
|
||||
if (previousValue !== currentValue) {
|
||||
this[property] = currentValue === "" || currentValue;
|
||||
this[camelCaps] = this[property];
|
||||
this[property] = currentValue === '' || currentValue
|
||||
this[camelCaps] = this[property]
|
||||
|
||||
this.#handleUpdateProp(camelCaps, this[property]);
|
||||
this.#handleUpdateProp(camelCaps, this[property])
|
||||
|
||||
this.render();
|
||||
this.onChanges({ property, previousValue, currentValue });
|
||||
this.render()
|
||||
this.onChanges({ property, previousValue, currentValue })
|
||||
}
|
||||
}
|
||||
|
||||
#handleUpdateProp(key, stringifiedValue) {
|
||||
const restored = deserialize(stringifiedValue, this.#typeMap[key]);
|
||||
if (restored !== this.props[key]) this.props[key] = restored;
|
||||
const restored = deserialize(stringifiedValue, this.#typeMap[key])
|
||||
if (restored !== this.props[key]) this.props[key] = restored
|
||||
}
|
||||
|
||||
#handler(setter, meta) {
|
||||
const effectsMap = meta.#effectsMap;
|
||||
const typeMap = meta.#typeMap;
|
||||
const effectsMap = meta.#effectsMap
|
||||
const typeMap = meta.#typeMap
|
||||
|
||||
return {
|
||||
set(obj, prop, value) {
|
||||
const oldValue = obj[prop];
|
||||
const oldValue = obj[prop]
|
||||
|
||||
if (!(prop in typeMap)) {
|
||||
typeMap[prop] = typeof value;
|
||||
typeMap[prop] = typeof value
|
||||
}
|
||||
|
||||
if (value.attach === "effect") {
|
||||
if (value.attach === 'effect') {
|
||||
if (!effectsMap[prop]) {
|
||||
effectsMap[prop] = [];
|
||||
effectsMap[prop] = []
|
||||
}
|
||||
effectsMap[prop].push(value.callback);
|
||||
effectsMap[prop].push(value.callback)
|
||||
} else if (typeMap[prop] !== typeof value) {
|
||||
throw TypeError(
|
||||
`Cannot assign ${typeof value} to ${
|
||||
typeMap[prop]
|
||||
} property (setting '${prop}' of ${meta.constructor.name})`
|
||||
);
|
||||
)
|
||||
} else if (oldValue !== value) {
|
||||
obj[prop] = value;
|
||||
effectsMap[prop]?.forEach((f) => f(value));
|
||||
const kebab = getKebabCase(prop);
|
||||
setter(kebab, serialize(value));
|
||||
obj[prop] = value
|
||||
effectsMap[prop]?.forEach((f) => f(value))
|
||||
const kebab = getKebabCase(prop)
|
||||
setter(kebab, serialize(value))
|
||||
}
|
||||
|
||||
return true;
|
||||
return true
|
||||
},
|
||||
get(obj, prop) {
|
||||
// TODO: handle non-objects
|
||||
if (obj[prop] !== null && obj[prop] !== undefined) {
|
||||
Object.getPrototypeOf(obj[prop]).proxy = meta.#props;
|
||||
Object.getPrototypeOf(obj[prop]).prop = prop;
|
||||
Object.getPrototypeOf(obj[prop]).proxy = meta.#props
|
||||
Object.getPrototypeOf(obj[prop]).prop = prop
|
||||
}
|
||||
return obj[prop];
|
||||
return obj[prop]
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#initializeProps() {
|
||||
let initialProps = structuredClone(this.constructor.props) ?? {};
|
||||
let initialProps = structuredClone(this.constructor.props) ?? {}
|
||||
Object.keys(initialProps).forEach((camelCase) => {
|
||||
const value = initialProps[camelCase];
|
||||
this.#typeMap[camelCase] = typeof value;
|
||||
this.setAttribute(getKebabCase(camelCase), serialize(value));
|
||||
});
|
||||
const value = initialProps[camelCase]
|
||||
this.#typeMap[camelCase] = typeof value
|
||||
this.setAttribute(getKebabCase(camelCase), serialize(value))
|
||||
})
|
||||
if (!this.#props) {
|
||||
this.#props = new Proxy(
|
||||
initialProps,
|
||||
this.#handler((key, value) => this.setAttribute(key, value), this)
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
#initializeHost() {
|
||||
this.#host = this;
|
||||
this.#host = this
|
||||
if (this.constructor.shadowRootInit) {
|
||||
this.#host = this.attachShadow(this.constructor.shadowRootInit);
|
||||
this.#host = this.attachShadow(this.constructor.shadowRootInit)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (typeof this.template === "string") {
|
||||
this.innerHTML = this.template;
|
||||
} else if (typeof this.template === "object") {
|
||||
const tree = this.template;
|
||||
if (typeof this.template === 'string') {
|
||||
this.innerHTML = this.template
|
||||
} else if (typeof this.template === 'object') {
|
||||
const tree = this.template
|
||||
|
||||
// TODO: smart diffing
|
||||
if (JSON.stringify(this.#prevDOM) !== JSON.stringify(tree)) {
|
||||
const el = createElement(tree);
|
||||
const el = createElement(tree)
|
||||
if (el) {
|
||||
if (Array.isArray(el)) this.#host.replaceChildren(...el);
|
||||
else this.#host.replaceChildren(el);
|
||||
if (Array.isArray(el)) this.#host.replaceChildren(...el)
|
||||
else this.#host.replaceChildren(el)
|
||||
}
|
||||
this.#prevDOM = tree;
|
||||
this.#prevDOM = tree
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* @param {(newValue: any) => void} callback
|
||||
*/
|
||||
export function attachEffect(obj, callback) {
|
||||
const { proxy, prop } = Object.getPrototypeOf(obj);
|
||||
const { proxy, prop } = Object.getPrototypeOf(obj)
|
||||
|
||||
proxy[prop] = {
|
||||
attach: "effect",
|
||||
attach: 'effect',
|
||||
callback,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
42
src/html.js
42
src/html.js
|
@ -6,15 +6,15 @@ const htm =
|
|||
l,
|
||||
s = arguments,
|
||||
t = 1,
|
||||
u = "",
|
||||
r = "",
|
||||
u = '',
|
||||
r = '',
|
||||
o = [0],
|
||||
f = function (n) {
|
||||
1 === t && (n || (u = u.replace(/^\s*\n\s*|\s*\n\s*$/g, "")))
|
||||
1 === t && (n || (u = u.replace(/^\s*\n\s*|\s*\n\s*$/g, '')))
|
||||
? o.push(n ? s[n] : u)
|
||||
: 3 === t && (n || u)
|
||||
? ((o[1] = n ? s[n] : u), (t = 2))
|
||||
: 2 === t && "..." === u && n
|
||||
: 2 === t && '...' === u && n
|
||||
? (o[2] = Object.assign(o[2] || {}, s[n]))
|
||||
: 2 === t && u && !n
|
||||
? ((o[2] = o[2] || {})[u] = !0)
|
||||
|
@ -27,50 +27,50 @@ const htm =
|
|||
: u),
|
||||
(t = 6))
|
||||
: (n || u) && (o[2][l] += n ? u + s[n] : u)),
|
||||
(u = "");
|
||||
(u = '')
|
||||
},
|
||||
i = 0;
|
||||
i < n.length;
|
||||
i++
|
||||
) {
|
||||
i && (1 === t && f(), f(i));
|
||||
i && (1 === t && f(), f(i))
|
||||
for (var p = 0; p < n[i].length; p++)
|
||||
(e = n[i][p]),
|
||||
1 === t
|
||||
? "<" === e
|
||||
? (f(), (o = [o, "", null]), (t = 3))
|
||||
? '<' === e
|
||||
? (f(), (o = [o, '', null]), (t = 3))
|
||||
: (u += e)
|
||||
: 4 === t
|
||||
? "--" === u && ">" === e
|
||||
? ((t = 1), (u = ""))
|
||||
? '--' === u && '>' === e
|
||||
? ((t = 1), (u = ''))
|
||||
: (u = e + u[0])
|
||||
: r
|
||||
? e === r
|
||||
? (r = "")
|
||||
? (r = '')
|
||||
: (u += e)
|
||||
: '"' === e || "'" === e
|
||||
? (r = e)
|
||||
: ">" === e
|
||||
: '>' === e
|
||||
? (f(), (t = 1))
|
||||
: t &&
|
||||
("=" === e
|
||||
? ((t = 5), (l = u), (u = ""))
|
||||
: "/" === e && (t < 5 || ">" === n[i][p + 1])
|
||||
('=' === e
|
||||
? ((t = 5), (l = u), (u = ''))
|
||||
: '/' === e && (t < 5 || '>' === n[i][p + 1])
|
||||
? (f(),
|
||||
3 === t && (o = o[0]),
|
||||
(t = o),
|
||||
(o = o[0]).push(this.apply(null, t.slice(1))),
|
||||
(t = 0))
|
||||
: " " === e || "\t" === e || "\n" === e || "\r" === e
|
||||
: ' ' === e || '\t' === e || '\n' === e || '\r' === e
|
||||
? (f(), (t = 2))
|
||||
: (u += e)),
|
||||
3 === t && "!--" === u && ((t = 4), (o = o[0]));
|
||||
3 === t && '!--' === u && ((t = 4), (o = o[0]))
|
||||
}
|
||||
return f(), o.length > 2 ? o.slice(1) : o[1];
|
||||
});
|
||||
return f(), o.length > 2 ? o.slice(1) : o[1]
|
||||
})
|
||||
|
||||
function h(type, props, ...children) {
|
||||
return { type, props, children };
|
||||
return { type, props, children }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,4 +78,4 @@ function h(type, props, ...children) {
|
|||
* @license Apache <https://www.apache.org/licenses/LICENSE-2.0>
|
||||
* @author Jason Miller <jason@developit.ca>
|
||||
*/
|
||||
export const html = htm.bind(h);
|
||||
export const html = htm.bind(h)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export { attachEffect } from "./attach-effect.js";
|
||||
export { WebComponent } from "./WebComponent.js";
|
||||
export { html } from "./html.js";
|
||||
export { attachEffect } from './attach-effect.js'
|
||||
export { WebComponent } from './WebComponent.js'
|
||||
export { html } from './html.js'
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
import { serialize } from "./serialize.mjs";
|
||||
import { serialize } from './serialize.mjs'
|
||||
export function createElement(tree) {
|
||||
if (!tree.type) {
|
||||
if (Array.isArray(tree)) {
|
||||
const frag = document.createDocumentFragment();
|
||||
frag.replaceChildren(...tree.map((leaf) => createElement(leaf)));
|
||||
return frag;
|
||||
const frag = document.createDocumentFragment()
|
||||
frag.replaceChildren(...tree.map((leaf) => createElement(leaf)))
|
||||
return frag
|
||||
}
|
||||
return document.createTextNode(tree);
|
||||
return document.createTextNode(tree)
|
||||
} else {
|
||||
const el = document.createElement(tree.type);
|
||||
const el = document.createElement(tree.type)
|
||||
/**
|
||||
* handle props
|
||||
*/
|
||||
if (tree.props) {
|
||||
Object.entries(tree.props).forEach(([prop, value]) => {
|
||||
const domProp = prop.toLowerCase();
|
||||
if (domProp === "style" && typeof value === "object" && !!value) {
|
||||
applyStyles(el, value);
|
||||
const domProp = prop.toLowerCase()
|
||||
if (domProp === 'style' && typeof value === 'object' && !!value) {
|
||||
applyStyles(el, value)
|
||||
} else if (prop in el) {
|
||||
el[prop] = value;
|
||||
el[prop] = value
|
||||
} else if (domProp in el) {
|
||||
el[domProp] = value;
|
||||
el[domProp] = value
|
||||
} else {
|
||||
el.setAttribute(prop, serialize(value));
|
||||
el.setAttribute(prop, serialize(value))
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
/**
|
||||
* handle children
|
||||
*/
|
||||
tree.children?.forEach((child) => {
|
||||
const childEl = createElement(child);
|
||||
const childEl = createElement(child)
|
||||
if (childEl instanceof Node) {
|
||||
el.appendChild(childEl);
|
||||
el.appendChild(childEl)
|
||||
}
|
||||
});
|
||||
return el;
|
||||
})
|
||||
return el
|
||||
}
|
||||
}
|
||||
|
||||
function applyStyles(el, styleObj) {
|
||||
Object.entries(styleObj).forEach(([rule, value]) => {
|
||||
if (rule in el.style && value) el.style[rule] = value;
|
||||
});
|
||||
if (rule in el.style && value) el.style[rule] = value
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
export function deserialize(value, type) {
|
||||
switch (type) {
|
||||
case "number":
|
||||
case "boolean":
|
||||
case "object":
|
||||
case "undefined":
|
||||
return JSON.parse(value);
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
case 'undefined':
|
||||
return JSON.parse(value)
|
||||
default:
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export function getCamelCase(kebab) {
|
||||
return kebab.replace(/-./g, (x) => x[1].toUpperCase());
|
||||
return kebab.replace(/-./g, (x) => x[1].toUpperCase())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export function getKebabCase(str) {
|
||||
return str.replace(
|
||||
/[A-Z]+(?![a-z])|[A-Z]/g,
|
||||
($, ofs) => (ofs ? "-" : "") + $.toLowerCase(),
|
||||
);
|
||||
($, ofs) => (ofs ? '-' : '') + $.toLowerCase()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export { serialize } from "./serialize.mjs";
|
||||
export { deserialize } from "./deserialize.mjs";
|
||||
export { getCamelCase } from "./get-camel-case.mjs";
|
||||
export { getKebabCase } from "./get-kebab-case.mjs";
|
||||
export { createElement } from "./create-element.mjs";
|
||||
export { serialize } from './serialize.mjs'
|
||||
export { deserialize } from './deserialize.mjs'
|
||||
export { getCamelCase } from './get-camel-case.mjs'
|
||||
export { getKebabCase } from './get-kebab-case.mjs'
|
||||
export { createElement } from './create-element.mjs'
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
export function serialize(value) {
|
||||
switch (typeof value) {
|
||||
case "number":
|
||||
case "boolean":
|
||||
case "object":
|
||||
return JSON.stringify(value);
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
return JSON.stringify(value)
|
||||
default:
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import { describe, expect, test } from "vitest";
|
||||
import { serialize } from "../src/utils/serialize.mjs";
|
||||
import { describe, expect, test } from 'vitest'
|
||||
import { serialize } from '../src/utils/serialize.mjs'
|
||||
|
||||
describe("serialize", () => {
|
||||
test("should stringify number", () => {
|
||||
const result = serialize(3);
|
||||
expect(result).toBeTypeOf("string");
|
||||
expect(result).toEqual("3");
|
||||
});
|
||||
describe('serialize', () => {
|
||||
test('should stringify number', () => {
|
||||
const result = serialize(3)
|
||||
expect(result).toBeTypeOf('string')
|
||||
expect(result).toEqual('3')
|
||||
})
|
||||
|
||||
test("should stringify boolean", () => {
|
||||
const result = serialize(false);
|
||||
expect(result).toBeTypeOf("string");
|
||||
expect(result).toEqual("false");
|
||||
});
|
||||
test('should stringify boolean', () => {
|
||||
const result = serialize(false)
|
||||
expect(result).toBeTypeOf('string')
|
||||
expect(result).toEqual('false')
|
||||
})
|
||||
|
||||
test("should stringify object", () => {
|
||||
const result = serialize({ hello: "world" });
|
||||
expect(result).toBeTypeOf("string");
|
||||
expect(result).toEqual('{"hello":"world"}');
|
||||
});
|
||||
test('should stringify object', () => {
|
||||
const result = serialize({ hello: 'world' })
|
||||
expect(result).toBeTypeOf('string')
|
||||
expect(result).toEqual('{"hello":"world"}')
|
||||
})
|
||||
|
||||
test("should return undefined", () => {
|
||||
const result = serialize(undefined);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
test('should return undefined', () => {
|
||||
const result = serialize(undefined)
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue