fix highlighting, add a bunch of docs

This commit is contained in:
Ryan Haskell-Glatz 2021-02-09 01:20:05 -06:00
parent 8b7e741e50
commit cf2a65660d
15 changed files with 341 additions and 609 deletions

View File

@ -4,71 +4,50 @@ Welcome to __elm-spa__, a framework for building web applications with [Elm](htt
If you are new to Elm, you should check out [the official guide](https://guide.elm-lang.org), which
is a great introduction to the language.
The goal of _this_ guide is to help you solve common problems you might run into with real world single-page web applications.
The goal of this guide is to help you solve any problems you might run into when building real world single-page web applications.
## Features
Here are a few benefits to using elm-spa:
Here are some of the benefits for using __elm-spa__:
1. __Automatic routing__ - routes for your web app are automatically generated based on file names. No need to maintain URL routing logic or wire pages together manually.
1. __Simple shared state__ - comes with a straightforward way to share data within and between pages. You can also make pages as simple or complex as you need!
1. __User authentication__ - provides an easy way to guarantee certain pages are only visible to signed-in users. Checkout the [user authentication](/examples/user-authentication) section for more details!
1. __Zero configuration__ - Includes a hot-reloading dev server, build tool, and everything you need in one CLI tool! No need for webpack, uglify, or other NPM packages.
## Quickstart
### Creating your first project
You can create a new __elm-spa__ project from scratch my creating a new folder:
```terminal
mkdir my-new-project && cd my-new-project
```
And then running this command in your terminal:
If you already have [NodeJS](https://nodejs.org) installed, getting started with __elm-spa__ is easy:
```terminal
mkdir our-cool-app
cd our-cool-app
npx elm-spa new
```
This will create a brand new project in the `my-new-project` folder! It should only contain these three files:
This will create a new project in the `our-cool-app` folder! It will only create __three__ new files:
```bash
my-new-project/
- .gitignore # folders to ignore in git
- elm.json # project dependencies
- src/
- public/
- index.html # entrypoint to your application
our-cool-app/
- elm.json # project dependencies
- src/Pages/Home_.elm # our homepage
- public/index.html # entrypoint to your application
```
### Running the dev server
Running this command will start a development server at `http://localhost:1234`:
Let's run it at `http://localhost:1234`:
```terminal
npx elm-spa server
```
### Adding your first page
To add a homepage, run the `elm-spa add` command:
```terminal
npx elm-spa add /
```
This will create a new page at `./src/Pages/Home_.elm`. Try editing the text in that file's `view` function, it will automatically change in the browser!
## Installation
So far, we've been using the [npx command](https://www.npmjs.com/package/npx) built into Node.js to run the `elm-spa` CLI. If we would rather use the CLI without this prefix, we can install __elm-spa__ globally:
So far, we've used [npx](https://www.npmjs.com/package/npx) so we don't need to install __elm-spa__ directly. If you'd like to run commands from the terminal, without the `npx` prefix, you can install __elm-spa__ like this:
```terminal
npm install -g elm-spa@latest
npm install -g elm-spa@next
```
This will ensure we have the latest version of elm-spa available in our terminal. You can make sure it works by calling `elm-spa` directly:
You can verify the install succeeded by running `elm-spa help` from your terminal:
```terminal
elm-spa help
@ -76,15 +55,17 @@ elm-spa help
elm-spa version 6.0.0
Commands:
elm-spa new . . . . . . . . . create a new project
elm-spa add <url> . . . . . . . . create a new page
elm-spa build . . . . . . one-time production build
elm-spa watch . . . . . . . runs build as you code
elm-spa server . . . . . . start a live dev server
elm-spa new . . . . . . . . create a new project
elm-spa add <url> . . . . . . . create a new page
elm-spa build . . . . . one-time production build
elm-spa watch . . . . . . runs build as you code
elm-spa server . . . . . start a live dev server
Visit https://next.elm-spa.dev for more!
```
If you see this message, you can run all the CLI commands without needing to prefix them with `npx`!
---
__Next up:__ [The CLI](/guide/cli)
__Ready for more?__
Let's check out [the CLI](/guide/cli) to learn more about those five commands!

View File

@ -1,138 +1,111 @@
# The CLI
The [official __elm-spa__ CLI tool](https://npmjs.org/elm-spa) has a few commands to help you build single page applications. As we saw in [the previous section](/guide/overview), you can use the CLI from your terminal by running:
At the end of the last section, we installed the __elm-spa__ CLI using [npm](https://npmjs.org) like this:
```terminal
npm install -g elm-spa@beta
npm install -g elm-spa@next
```
At any time running `elm-spa` or `elm-spa help` will show you the available commands:
This gave us the ability to run a few commands:
```
elm-spa new . . . . . . . . . create a new project
elm-spa add <url> . . . . . . . . create a new page
elm-spa build . . . . . . one-time production build
elm-spa watch . . . . . . . runs build as you code
elm-spa server . . . . . . start a live dev server
```
1. __[`elm-spa new`](#elm-spa-new)__ - creates a new project
1. __[`elm-spa server`](#elm-spa-server)__ - runs a dev server as you code
1. __[`elm-spa watch`](#elm-spa-watch)__ - builds as you code
1. __[`elm-spa build`](#elm-spa-build)__ - one-time production build
1. __[`elm-spa add`](#elm-spa-add)__ - adds a page to an existing project
## Creating new projects
What do these do? This section of the guide dives into more detail on each command!
The `new` command creates a new project in the current folder:
## elm-spa new
When you want to create a new project, you can use the `elm-spa new` command. This creates a new project in the current folder:
```terminal
elm-spa new
```
This command will only create a few files, so don't worry about getting overwhelmed with new files in your repo! Other than a `.gitignore`, there are only 3 new files created.
```bash
New project created in:
/Users/ryan/code/my-new-app
```
File | Description
This command only creates __three__ files:
Filename | Description
--- | ---
`elm.json` | Your project's dependencies.
`src/Pages/Home_.elm` | The homepage.
`public/index.html` | The entrypoint to your application.
`elm.json` | Keeps track of [Elm packages](https://package.elm-lang.org).
`src/Pages/Home_.elm` | The project's homepage.
`public/index.html` | The HTML entrypoint to the app.
```
your-project/
|- elm.json
|- src/
| |- Pages/
| |- Home_.elm
|- public/
|- index.html
```
## elm-spa server
The `public` folder is a place for static assets! For example, a file at `./public/style.css` will be available at `/style.css` in your web browser.
## Adding pages
The next section will dive into deeper detail, but __elm-spa__ directly maps file names to URLs.
URL | File Location
--- | ---
`/` | `src/Pages/Home_.elm`
`/about-us` | `src/Pages/AboutUs.elm`
`/people/ryan` | `src/Pages/People/Ryan.elm`
The `elm-spa add` command makes it easy to scaffold out new pages in your application!
### Adding a homepage
Here's how you can add a homepage with the `elm-spa add` command:
```terminal
elm-spa add /
```
### Adding static pages
You can add [static routes](/guide/basics/routing#static-routes) with the add command also:
```terminal
elm-spa add /settings
```
This command will create a new page at `src/Pages/Settings.elm`, and be available at `/settings`.
### Adding dynamic pages
In the [next section](/guide/basics/routing), you'll learn more about static and dynamic pages, which can handle dynamic URL parameters to make life easy. For example, if we wanted a "Person Detail" page, we could do something like this:
```terminal
elm-spa add /people/:name
```
This creates a new page at `src/Pages/People/Name_.elm`. The underscore (`_`) at the end of the filename indicates a __dynamic__ route! This dynamic route handles requests to pages like these:
URL | Params
--- | ---
`/people/ryan` | `{ name = "ryan" }`
`/people/erik` | `{ name = "erik" }`
`/people/alexa` | `{ name = "alexa" }`
The name of the file (`Name_.elm`) determines the variable name.
### Removing pages
Removing pages with __elm-spa__ is as simple as __deleting the file__
```terminal
rm src/Pages/Settings.elm
```
You can do this however you prefer, but there isn't an `elm-spa remove` command in the CLI.
## Developing locally
The __elm-spa__ CLI comes with a hot-reloading development server built in. As you save files in the `src` and `public` folders, your local site will automatically refresh.
The first thing you'll want to do after creating a new project is try it out in the browser! The `elm-spa server` is all you need to see your app in action:
```terminal
elm-spa server
```
By default, the server will start on port 1234. You can specify a different port with the `PORT` environment variable:
This will start a development server for your project at [http://localhost:1234](http://localhost:1234).
```terminal
PORT=8000 elm-spa server
```
When we edit our code, `elm-spa server` automatically compiles your application.
__Note:__ The `server` command is not designed for production use! To
## elm-spa watch
### Prefer webpack or parcel?
You can use the `watch` command to build assets without running the development server. This will build your application, and allow you to use something like [Parcel](https://parceljs.org/elm.html) or [webpack](https://github.com/elm-community/elm-webpack-loader).
If you want the automatic compilation on change, but don't need a HTTP server, you can use the `elm-spa watch` command:
```terminal
elm-spa watch
```
## Building for production
This will automatically generate and compile on save, but without the server. This is a great choice when combining __elm-spa__ with another tool like [Parcel](https://parceljs.org/elm.html)
When you are ready you ship your application, the `build` command will create a minified and optimized JS file for production.
## elm-spa build
The `server` and `watch` commands are great for development, but for __production__, we'll want the `elm-spa build` command.
```terminal
elm-spa build
```
For more information about deployments and hosting, you should check out the [Hosting & Development](/guide/hosting) section!
This compiles your app into __an optimized and minified JS file__. This makes it great for serving your application in the real world!
## elm-spa add
You can add new pages to your app with the `elm-spa add` command:
```terminal
elm-spa add /contact
```
This creates a new file at `src/Pages/Contact.elm`. If you visit [http://localhost:1234/contact](http://localhost:1234/contact) in the browser, you'll see a new page with the text `"Contact"` displayed.
### adding other pages
Here are a few examples of other routes you can create with the add command
```bash
elm-spa add / # src/Pages/Home_.elm
elm-spa add /settings # src/Pages/Settings.elm
elm-spa add /people/:id # src/Pages/People/Id_.elm
```
We'll cover this in more detail in the [routing section](./routing)
### using page templates
The `elm-spa add` command also accepts an optional `template` argument to for common
pages you might create.
```bash
elm-spa add /example static
elm-spa add /example sandbox
elm-spa add /example element
```
---
__So, what's a page?__
Let's answer that next in the [pages section](./pages)!

View File

@ -1,32 +1,76 @@
# Pages
Every route in your Elm application will be connected to a `Page` file. These files
all have the same general shape:
In __elm-spa__, every URL connects to a single page. Let's take a closer look at the homepage we created earlier with the `elm-spa new` command:
```elm
module Pages._____ exposing (Model, Msg, page)
module Pages.Home_ exposing (view)
page : Shared.Model -> Request Params -> Page Model Msg
```
import Html
This section of the guide will introduce you to the __four__ kinds of pages you might
need to use in any Elm application.
> It's important that _every_ page exposes `Model`, `Msg`, and `page`. If any of these three are missing or renamed, the generated code will not work.
## Static pages
If you only need to render some HTML on the page, use `Page.static`:
```elm
Page.static
{ view : View Msg
view =
{ title = "Homepage"
, body = [ Html.text "Hello, world!" ]
}
```
## Sandbox pages
This homepage renders the tab `title`, and a HTML `body` onto the page. This is great when you have a static page that just needs to render some elements.
Need to keep track of local state, like the current tab? Check out `Page.sandbox`!
Because the file is named `Home_.elm`, we know it's the homepage. These 8 lines of code are all we need to tell __elm-spa__ we'd like to render this when users visit the homepage.
For real world applications, we'll need pages that can do more. That's where the `Page` module comes in handy.
### Upgrading "Hello World!"
Let's start by introducing the `page` function, marking the start of our journey from "Hello world!" to the real world:
```elm
module Pages.Home_ exposing (page)
-- our other imports
import Page exposing (Page)
page :
Shared.Model
-> Request Params
-> Page () Never
page shared req =
Page.static
{ view = view
}
view =
{ title = "Homepage"
, body = [ Html.text "Hello, world!" ]
}
```
Here, our code hasn't changed very much- except now we have this new `page` function that:
1. Takes in two inputs: `Shared.Model` and `Request Params`
2. Returns a `Page () Never` value.
3. Is exposed at the top of our `Pages.Home_` module.
> Without exposing `page`, __elm-spa__ will not understand how to compile your application. Make sure to _always_ expose `page` from modules within the `src/Pages` folder.
This new `page` will always get the latest `Shared.Model` and URL information, which means you don't have to worry about tracking that stuff yourself.
This is great, but there is still more that our `page` function can do other than render a view!
### Beyond static pages
The right page to use is the __simplest page__ that can support what you need! As we move from `Page.static` to `Page.advanced`, we'll have more capabilities, but at the cost of more code.
This section of the guide will introduce you to the functions exposed by the `Page` module, so you have all the information you need.
__[Page.static](#pagestatic)__ - for pages that only render a view.
```elm
Page.static
{ view : View Never
}
```
__[Page.sandbox](#pagesandbox)__ - for pages that need to keep track of state.
```elm
Page.sandbox
@ -36,9 +80,7 @@ Page.sandbox
}
```
## Element pages
If you want to send [HTTP requests](https://guide.elm-lang.org/effects/http.html) or subscribe to other external events, you're ready for `Page.element`:
__[Page.element](#pageelement)__ - for pages that send HTTP requests or continually listen for events from the browser or user.
```elm
Page.element
@ -49,15 +91,138 @@ Page.element
}
```
## Shared pages
When it comes time to update the global state shared from page to page, you can upgrade to `Page.shared`:
__[Page.advanced](#pageadvanced)__ - For pages that need to sign in a user or work with other details that should persist between page navigation.
```elm
Page.shared
{ init : ( Model, Cmd Msg, List Shared.Msg )
, update : Msg -> Model -> ( Model, Cmd Msg, List Shared.Msg )
Page.advanced
{ init : ( Model, Effect Msg )
, update : Msg -> Model -> ( Model, Effect Msg )
, view : Model -> View Msg
, subscriptions : Model -> Sub Msg
}
```
```
### Working with pages
The `page` function allows us to pass `Shared.Model` and `Request` information to any inner function that needs it.
```elm
page shared req =
Page.sandbox
{ init = init shared -- pass init "shared"
, update = update req -- pass update "req"
, view = view
}
```
Imagine your `init` function needs access to `shared` data, and your `update` function needs URL information from the current `req`.
Because `page` is a function, you can pass those values in where you see fit. This means the type annotations of those inner functions should also update:
__Before__
```elm
page shared req =
Page.sandbox
{ init = init
, update = update
, view = view
}
init : Model
update : Msg -> Model -> Model
view : Model -> View Msg
```
__After__
```elm
page shared req =
Page.sandbox
{ init = init shared
, update = update req
, view = view
}
init : Shared.Model -> Model
update : Request Params -> Msg -> Model -> Model
view : Model -> View Msg
```
Notice how the type annotations of `init` and `update` changed to accept their input? (The `view` function didn't change because it didn't get any new values)
## Page.static
```elm
Page.static
{ view : View Never
}
```
( video introducing concept )
## Page.sandbox
```elm
Page.sandbox
{ init : Model
, update : Msg -> Model -> Model
, view : Model -> View Msg
}
```
( video introducing concept )
## Page.element
```elm
Page.element
{ init : ( Model, Cmd Msg )
, update : Msg -> Model -> ( Model, Cmd Msg )
, view : Model -> View Msg
, subscriptions : Model -> Sub Msg
}
```
( video introducing concept )
## Page.advanced
```elm
Page.advanced
{ init : ( Model, Effect Msg )
, update : Msg -> Model -> ( Model, Effect Msg )
, view : Model -> View Msg
, subscriptions : Model -> Sub Msg
}
```
( video introducing concept )
## Page.protected
Each of the four page types also have a "protected" version, that is guaranteed to receive a `User` or redirect if no user is signed in.
```elm
Page.protected.static
{ view : User -> View Never
}
Page.protected.sandbox
{ init : User -> Model
, update : User -> Msg -> Model -> Model
, view : User -> Model -> View Msg
}
-- Page.protected.element
-- Page.protected.advanced
```
When you are ready, you can learn more about this in the [user authentication example](/examples/authentication).
---
__What's next?__
Let me introduce you to the `Request Params` type we pass into our pages in the [next section on requests](./requests)

View File

@ -10,6 +10,7 @@ type alias Request params =
{ params : params
, query : Dict String String
, url : Url
, route : Route
, key : Nav.Key
}
```
@ -39,7 +40,7 @@ __Note:__ When working with [shared state](/guide/shared-state), all requests ar
## Query Parameters
For convenience, query parameters are automatically turned into a `Dict String String`, making it easy to handle URLs like this:
For convenience, query parameters are automatically turned into a `Dict String String`, making it easy to handle common query URL parameters like these:
```
/people?team=design&ascending
@ -70,6 +71,21 @@ type alias Url =
This is less common than `req.params` and `req.query`, but can be useful for getting the `hash` at the end of a URL too!
## Getting the current route
The `Request` type also has access to the `Route` value, so you can easily do comparisons agains the current route!
```elm
-- "/"
req.route == Gen.Route.Home_
-- "/about-us"
req.route == Gen.Route.AboutUs
-- "/people/ryan"
req.route == Gen.Route.People_ { name = "ryan" }
```
## Programmatic Navigation
Most of the time, navigation in Elm is as easy as giving an `href` attribute to an anchor tag:
@ -80,7 +96,7 @@ a [ href "/guide" ] [ text "elm-spa guide" ]
Other times, you'll want to do __programmatic navigation__ navigating to another page after some event completes. Maybe you want to __redirect__ to a sign in page, or head to the __dashboard after signing in successfully__.
In that case you'll need access to `req.key` in order to use `Nav.pushUrl` or `Nav.replaceUrl`. Here's a quick example of what that looks like:
In that case we store `req.key` in order to use `Request.pushRoute` or `Request.replaceRoute`. Here's a quick example of what that looks like:
```elm
type Msg = SignedIn User
@ -90,8 +106,8 @@ update req msg model =
case msg of
SignedIn user ->
( model
, Nav.pushUrl req.key "/dashboard"
, Request.pushRoute Gen.Route.Dashboard req
)
```
When the `SignedIn` message is fired, this code will redirect the user to the dashboard. Feel free to check out the [elm/browser](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation) package docs for more in-depth examples.
When the `SignedIn` message is fired, this code will redirect the user to the `Dashboard` route.

View File

@ -1,406 +0,0 @@
/*
Highlight.js 10.4.1 (e96b915a)
License: BSD-3-Clause
Copyright (c) 2006-2020, Ivan Sagalaev
*/
var hljs=function(){"use strict";function e(t){
return t instanceof Map?t.clear=t.delete=t.set=()=>{
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
throw Error("set is read-only")
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{var s=t[n]
;"object"!=typeof s||Object.isFrozen(s)||e(s)})),t}var t=e,n=e;t.default=n
;class s{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}
ignoreMatch(){this.ignore=!0}}function r(e){
return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")
}function a(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}function i(e){
return e.nodeName.toLowerCase()}var o=Object.freeze({__proto__:null,
escapeHTML:r,inherit:a,nodeStream:e=>{const t=[];return function e(n,s){
for(let r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?s+=r.nodeValue.length:1===r.nodeType&&(t.push({
event:"start",offset:s,node:r}),s=e(r,s),i(r).match(/br|hr|img|input/)||t.push({
event:"stop",offset:s,node:r}));return s}(e,0),t},mergeStreams:(e,t,n)=>{
let s=0,a="";const o=[];function l(){
return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset<t[0].offset?e:t:"start"===t[0].event?e:t:e.length?e:t
}function c(e){
a+="<"+i(e)+[].map.call(e.attributes,(e=>" "+e.nodeName+'="'+r(e.value)+'"')).join("")+">"
}function u(e){a+="</"+i(e)+">"}function g(e){("start"===e.event?c:u)(e.node)}
for(;e.length||t.length;){let t=l()
;if(a+=r(n.substring(s,t[0].offset)),s=t[0].offset,t===e){o.reverse().forEach(u)
;do{g(t.splice(0,1)[0]),t=l()}while(t===e&&t.length&&t[0].offset===s)
;o.reverse().forEach(c)
}else"start"===t[0].event?o.push(t[0].node):o.pop(),g(t.splice(0,1)[0])}
return a+r(n.substr(s))}});const l=e=>!!e.kind;class c{constructor(e,t){
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
this.buffer+=r(e)}openNode(e){if(!l(e))return;let t=e.kind
;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){
l(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
this.buffer+=`<span class="${e}">`}}class u{constructor(){this.rootNode={
children:[]},this.stack=[this.rootNode]}get top(){
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
this.top.children.push(e)}openNode(e){const t={kind:e,children:[]}
;this.add(t),this.stack.push(t)}closeNode(){
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
u._collapse(e)})))}}class g extends u{constructor(e){super(),this.options=e}
addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}
addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root
;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){
return new c(this,this.options).value()}finalize(){return!0}}function d(e){
return e?"string"==typeof e?e:e.source:null}
const h="[a-zA-Z]\\w*",f="[a-zA-Z_]\\w*",p="\\b\\d+(\\.\\d+)?",m="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",b="\\b(0b[01]+)",x={
begin:"\\\\[\\s\\S]",relevance:0},E={className:"string",begin:"'",end:"'",
illegal:"\\n",contains:[x]},v={className:"string",begin:'"',end:'"',
illegal:"\\n",contains:[x]},_={
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
},w=(e,t,n={})=>{const s=a({className:"comment",begin:e,end:t,contains:[]},n)
;return s.contains.push(_),s.contains.push({className:"doctag",
begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),s
},N=w("//","$"),y=w("/\\*","\\*/"),R=w("#","$");var k=Object.freeze({
__proto__:null,IDENT_RE:h,UNDERSCORE_IDENT_RE:f,NUMBER_RE:p,C_NUMBER_RE:m,
BINARY_NUMBER_RE:b,
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
SHEBANG:(e={})=>{const t=/^#![ ]*\//
;return e.binary&&(e.begin=((...e)=>e.map((e=>d(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)),
a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{
0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:x,APOS_STRING_MODE:E,
QUOTE_STRING_MODE:v,PHRASAL_WORDS_MODE:_,COMMENT:w,C_LINE_COMMENT_MODE:N,
C_BLOCK_COMMENT_MODE:y,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number",
begin:p,relevance:0},C_NUMBER_MODE:{className:"number",begin:m,relevance:0},
BINARY_NUMBER_MODE:{className:"number",begin:b,relevance:0},CSS_NUMBER_MODE:{
className:"number",
begin:p+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",
relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",
begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[x,{begin:/\[/,end:/\]/,
relevance:0,contains:[x]}]}]},TITLE_MODE:{className:"title",begin:h,relevance:0
},UNDERSCORE_TITLE_MODE:{className:"title",begin:f,relevance:0},METHOD_GUARD:{
begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
t.data._beginMatch!==e[1]&&t.ignoreMatch()}})})
;const M=["of","and","for","in","not","or","if","then","parent","list","value"]
;function O(e){function t(t,n){
return RegExp(d(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class n{
constructor(){
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
addRule(e,t){
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
this.matchAt+=(e=>RegExp(e.toString()+"|").exec("").length-1)(e)+1}compile(){
0===this.regexes.length&&(this.exec=()=>null)
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(((e,t="|")=>{
const n=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;let s=0,r=""
;for(let a=0;a<e.length;a++){s+=1;const i=s;let o=d(e[a])
;for(a>0&&(r+=t),r+="(";o.length>0;){const e=n.exec(o);if(null==e){r+=o;break}
r+=o.substring(0,e.index),
o=o.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+i):(r+=e[0],
"("===e[0]&&s++)}r+=")"}return r})(e),!0),this.lastIndex=0}exec(e){
this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e)
;if(!t)return null
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),s=this.matchIndexes[n]
;return t.splice(0,n),Object.assign(t,s)}}class s{constructor(){
this.rules=[],this.multiRegexes=[],
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
;let n=t.exec(e)
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
return n&&(this.regexIndex+=n.position+1,
this.regexIndex===this.count&&this.considerAll()),n}}function r(e,t){
"."===e.input[e.index-1]&&t.ignoreMatch()}
if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
;return e.classNameAliases=a(e.classNameAliases||{}),function n(i,o){const l=i
;if(i.compiled)return l
;i.compiled=!0,i.__beforeBegin=null,i.keywords=i.keywords||i.beginKeywords
;let c=null
;if("object"==typeof i.keywords&&(c=i.keywords.$pattern,delete i.keywords.$pattern),
i.keywords&&(i.keywords=((e,t)=>{const n={}
;return"string"==typeof e?s("keyword",e):Object.keys(e).forEach((t=>{s(t,e[t])
})),n;function s(e,s){t&&(s=s.toLowerCase()),s.split(" ").forEach((t=>{
const s=t.split("|");n[s[0]]=[e,A(s[0],s[1])]}))}
})(i.keywords,e.case_insensitive)),
i.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ")
;return l.keywordPatternRe=t(i.lexemes||c||/\w+/,!0),
o&&(i.beginKeywords&&(i.begin="\\b("+i.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
i.__beforeBegin=r),
i.begin||(i.begin=/\B|\b/),l.beginRe=t(i.begin),i.endSameAsBegin&&(i.end=i.begin),
i.end||i.endsWithParent||(i.end=/\B|\b/),
i.end&&(l.endRe=t(i.end)),l.terminator_end=d(i.end)||"",
i.endsWithParent&&o.terminator_end&&(l.terminator_end+=(i.end?"|":"")+o.terminator_end)),
i.illegal&&(l.illegalRe=t(i.illegal)),
void 0===i.relevance&&(i.relevance=1),i.contains||(i.contains=[]),
i.contains=[].concat(...i.contains.map((e=>(e=>(e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((t=>a(e,{
variants:null},t)))),e.cached_variants?e.cached_variants:L(e)?a(e,{
starts:e.starts?a(e.starts):null
}):Object.isFrozen(e)?a(e):e))("self"===e?i:e)))),i.contains.forEach((e=>{n(e,l)
})),i.starts&&n(i.starts,o),l.matcher=(e=>{const t=new s
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
}))),e.terminator_end&&t.addRule(e.terminator_end,{type:"end"
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(l),l}(e)}function L(e){
return!!e&&(e.endsWithParent||L(e.starts))}function A(e,t){
return t?Number(t):(e=>M.includes(e.toLowerCase()))(e)?0:1}function j(e){
const t={props:["language","code","autodetect"],data:()=>({detectedLanguage:"",
unknownLanguage:!1}),computed:{className(){
return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){
if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),
this.unknownLanguage=!0,r(this.code);let t
;return this.autoDetect?(t=e.highlightAuto(this.code),
this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals),
this.detectedLanguage=this.language),t.value},autoDetect(){
return!(this.language&&(e=this.autodetect,!e&&""!==e));var e},
ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{
class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{
Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}
const I=r,S=a,{nodeStream:T,mergeStreams:B}=o,P=Symbol("nomatch");return(e=>{
const n=[],r=Object.create(null),a=Object.create(null),i=[];let o=!0
;const l=/(^(<[^>]+>|\t|)+|\n)/gm,c="Could not find the language '{}', did you forget to load/include a language module?",u={
disableAutodetect:!0,name:"Plain text",contains:[]};let d={
noHighlightRe:/^(no-?highlight)$/i,
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
tabReplace:null,useBR:!1,languages:null,__emitter:g};function h(e){
return d.noHighlightRe.test(e)}function f(e,t,n,s){const r={code:t,language:e}
;N("before:highlight",r);const a=r.result?r.result:p(r.language,r.code,n,s)
;return a.code=r.code,N("after:highlight",a),a}function p(e,t,n,a){const i=t
;function l(e,t){const n=_.case_insensitive?t[0].toLowerCase():t[0]
;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]}
function u(){null!=y.subLanguage?(()=>{if(""===M)return;let e=null
;if("string"==typeof y.subLanguage){
if(!r[y.subLanguage])return void k.addText(M)
;e=p(y.subLanguage,M,!0,R[y.subLanguage]),R[y.subLanguage]=e.top
}else e=m(M,y.subLanguage.length?y.subLanguage:null)
;y.relevance>0&&(L+=e.relevance),k.addSublanguage(e.emitter,e.language)
})():(()=>{if(!y.keywords)return void k.addText(M);let e=0
;y.keywordPatternRe.lastIndex=0;let t=y.keywordPatternRe.exec(M),n="";for(;t;){
n+=M.substring(e,t.index);const s=l(y,t);if(s){const[e,r]=s
;k.addText(n),n="",L+=r;const a=_.classNameAliases[e]||e;k.addKeyword(t[0],a)
}else n+=t[0];e=y.keywordPatternRe.lastIndex,t=y.keywordPatternRe.exec(M)}
n+=M.substr(e),k.addText(n)})(),M=""}function g(e){
return e.className&&k.openNode(_.classNameAliases[e.className]||e.className),
y=Object.create(e,{parent:{value:y}}),y}function h(e,t,n){let r=((e,t)=>{
const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){
const n=new s(e);e["on:end"](t,n),n.ignore&&(r=!1)}if(r){
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
if(e.endsWithParent)return h(e.parent,t,n)}function f(e){
return 0===y.matcher.regexIndex?(M+=e[0],1):(S=!0,0)}function b(e){
const t=e[0],n=i.substr(e.index),s=h(y,e,n);if(!s)return P;const r=y
;r.skip?M+=t:(r.returnEnd||r.excludeEnd||(M+=t),u(),r.excludeEnd&&(M=t));do{
y.className&&k.closeNode(),y.skip||y.subLanguage||(L+=y.relevance),y=y.parent
}while(y!==s.parent)
;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe),
g(s.starts)),r.returnEnd?0:t.length}let x={};function E(t,r){const a=r&&r[0]
;if(M+=t,null==a)return u(),0
;if("begin"===x.type&&"end"===r.type&&x.index===r.index&&""===a){
if(M+=i.slice(r.index,r.index+1),!o){const t=Error("0 width match regex")
;throw t.languageName=e,t.badRule=x.rule,t}return 1}
if(x=r,"begin"===r.type)return function(e){
const t=e[0],n=e.rule,r=new s(n),a=[n.__beforeBegin,n["on:begin"]]
;for(const n of a)if(n&&(n(e,r),r.ignore))return f(t)
;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),
n.skip?M+=t:(n.excludeBegin&&(M+=t),
u(),n.returnBegin||n.excludeBegin||(M=t)),g(n),n.returnBegin?0:t.length}(r)
;if("illegal"===r.type&&!n){
const e=Error('Illegal lexeme "'+a+'" for mode "'+(y.className||"<unnamed>")+'"')
;throw e.mode=y,e}if("end"===r.type){const e=b(r);if(e!==P)return e}
if("illegal"===r.type&&""===a)return 1
;if(j>1e5&&j>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
;return M+=a,a.length}const _=v(e);if(!_)throw console.error(c.replace("{}",e)),
Error('Unknown language: "'+e+'"');const w=O(_);let N="",y=a||w
;const R={},k=new d.__emitter(d);(()=>{const e=[]
;for(let t=y;t!==_;t=t.parent)t.className&&e.unshift(t.className)
;e.forEach((e=>k.openNode(e)))})();let M="",L=0,A=0,j=0,S=!1;try{
for(y.matcher.considerAll();;){
j++,S?S=!1:y.matcher.considerAll(),y.matcher.lastIndex=A
;const e=y.matcher.exec(i);if(!e)break;const t=E(i.substring(A,e.index),e)
;A=e.index+t}return E(i.substr(A)),k.closeAllNodes(),k.finalize(),N=k.toHTML(),{
relevance:L,value:N,language:e,illegal:!1,emitter:k,top:y}}catch(t){
if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{
msg:t.message,context:i.slice(A-100,A+100),mode:t.mode},sofar:N,relevance:0,
value:I(i),emitter:k};if(o)return{illegal:!1,relevance:0,value:I(i),emitter:k,
language:e,top:y,errorRaised:t};throw t}}function m(e,t){
t=t||d.languages||Object.keys(r);const n=(e=>{const t={relevance:0,
emitter:new d.__emitter(d),value:I(e),illegal:!1,top:u}
;return t.emitter.addText(e),t})(e),s=t.filter(v).filter(w).map((t=>p(t,e,!1)))
;s.unshift(n);const a=s.sort(((e,t)=>{
if(e.relevance!==t.relevance)return t.relevance-e.relevance
;if(e.language&&t.language){if(v(e.language).supersetOf===t.language)return 1
;if(v(t.language).supersetOf===e.language)return-1}return 0})),[i,o]=a,l=i
;return l.second_best=o,l}function b(e){
return d.tabReplace||d.useBR?e.replace(l,(e=>"\n"===e?d.useBR?"<br>":e:d.tabReplace?e.replace(/\t/g,d.tabReplace):e)):e
}function x(e){let t=null;const n=(e=>{let t=e.className+" "
;t+=e.parentNode?e.parentNode.className:"";const n=d.languageDetectRe.exec(t)
;if(n){const t=v(n[1])
;return t||(console.warn(c.replace("{}",n[1])),console.warn("Falling back to no-highlight mode for this block.",e)),
t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>h(e)||v(e)))})(e)
;if(h(n))return;N("before:highlightBlock",{block:e,language:n
}),d.useBR?(t=document.createElement("div"),
t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ /]*>/g,"\n")):t=e
;const s=t.textContent,r=n?f(n,s,!0):m(s),i=T(t);if(i.length){
const e=document.createElement("div");e.innerHTML=r.value,r.value=B(i,T(e),s)}
r.value=b(r.value),N("after:highlightBlock",{block:e,result:r
}),e.innerHTML=r.value,e.className=((e,t,n)=>{const s=t?a[t]:n,r=[e.trim()]
;return e.match(/\bhljs\b/)||r.push("hljs"),
e.includes(s)||r.push(s),r.join(" ").trim()
})(e.className,n,r.language),e.result={language:r.language,re:r.relevance,
relavance:r.relevance},r.second_best&&(e.second_best={
language:r.second_best.language,re:r.second_best.relevance,
relavance:r.second_best.relevance})}const E=()=>{if(E.called)return;E.called=!0
;const e=document.querySelectorAll("pre code");n.forEach.call(e,x)}
;function v(e){return e=(e||"").toLowerCase(),r[e]||r[a[e]]}
function _(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{a[e]=t
}))}function w(e){const t=v(e);return t&&!t.disableAutodetect}function N(e,t){
const n=e;i.forEach((e=>{e[n]&&e[n](t)}))}Object.assign(e,{highlight:f,
highlightAuto:m,
fixMarkup:e=>(console.warn("fixMarkup is deprecated and will be removed entirely in v11.0"),
console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2534"),
b(e)),highlightBlock:x,configure:e=>{
e.useBR&&(console.warn("'useBR' option is deprecated and will be removed entirely in v11.0"),
console.warn("Please see https://github.com/highlightjs/highlight.js/issues/2559")),
d=S(d,e)},initHighlighting:E,initHighlightingOnLoad:()=>{
window.addEventListener("DOMContentLoaded",E,!1)},registerLanguage:(t,n)=>{
let s=null;try{s=n(e)}catch(e){
if(console.error("Language definition for '{}' could not be registered.".replace("{}",t)),
!o)throw e;console.error(e),s=u}
s.name||(s.name=t),r[t]=s,s.rawDefinition=n.bind(null,e),
s.aliases&&_(s.aliases,{languageName:t})},listLanguages:()=>Object.keys(r),
getLanguage:v,registerAliases:_,requireLanguage:e=>{
console.warn("requireLanguage is deprecated and will be removed entirely in the future."),
console.warn("Please see https://github.com/highlightjs/highlight.js/pull/2844")
;const t=v(e);if(t)return t
;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},
autoDetection:w,inherit:S,addPlugin:e=>{i.push(e)},vuePlugin:j(e).VuePlugin
}),e.debugMode=()=>{o=!1},e.safeMode=()=>{o=!0},e.versionString="10.4.1"
;for(const e in k)"object"==typeof k[e]&&t(k[e]);return Object.assign(e,k),e
})({})}()
;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("xml",(()=>{"use strict";function e(e){
return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")}
function a(...n){return n.map((n=>e(n))).join("")}function s(...n){
return"("+n.map((n=>e(n))).join("|")+")"}return e=>{
const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]+:/,")?"),/[A-Z0-9_.-]*/),i={
className:"symbol",begin:"&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;"},c={begin:"\\s",
contains:[{className:"meta-keyword",begin:"#?[a-z_][a-z1-9_-]+",illegal:"\\n"}]
},r=e.inherit(c,{begin:"\\(",end:"\\)"}),l=e.inherit(e.APOS_STRING_MODE,{
className:"meta-string"}),g=e.inherit(e.QUOTE_STRING_MODE,{
className:"meta-string"}),m={endsWithParent:!0,illegal:/</,relevance:0,
contains:[{className:"attr",begin:"[A-Za-z0-9\\._:-]+",relevance:0},{
begin:/=\s*/,relevance:0,contains:[{className:"string",endsParent:!0,variants:[{
begin:/"/,end:/"/,contains:[i]},{begin:/'/,end:/'/,contains:[i]},{
begin:/[^\s"'=<>`]+/}]}]}]};return{name:"HTML, XML",
aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],
case_insensitive:!0,contains:[{className:"meta",begin:"<![a-z]",end:">",
relevance:10,contains:[c,g,l,r,{begin:"\\[",end:"\\]",contains:[{
className:"meta",begin:"<![a-z]",end:">",contains:[c,r,g,l]}]}]
},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<!\\[CDATA\\[",
end:"\\]\\]>",relevance:10},i,{className:"meta",begin:/<\?xml/,end:/\?>/,
relevance:10},{className:"tag",begin:"<style(?=\\s|>)",end:">",keywords:{
name:"style"},contains:[m],starts:{end:"</style>",returnEnd:!0,
subLanguage:["css","xml"]}},{className:"tag",begin:"<script(?=\\s|>)",end:">",
keywords:{name:"script"},contains:[m],starts:{end:/<\/script>/,returnEnd:!0,
subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/
},{className:"tag",begin:a(/</,n(a(t,s(/\/>/,/>/,/\s/)))),end:/\/?>/,contains:[{
className:"name",begin:t,relevance:0,starts:m}]},{className:"tag",
begin:a(/<\//,n(a(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{
begin:/>/,relevance:0}]}]}}})());hljs.registerLanguage("css",(()=>{"use strict";return e=>{
var n="[a-zA-Z-][a-zA-Z0-9_-]*",a={
begin:/([*]\s?)?(?:[A-Z_.\-\\]+|--[a-zA-Z0-9_-]+)\s*(\/\*\*\/)?:/,
returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",
begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,
contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",
begin:/[\w-]+/},{begin:/\(/,end:/\)/,
contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]
},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{
className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]
}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,
contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",
begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:"\\."+n},{
className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",
contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",
begin:/:(:)?[a-zA-Z0-9_+()"'.-]+/},{begin:"@(page|font-face)",
lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",
illegal:/:/,returnBegin:!0,contains:[{className:"keyword",
begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,
relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,
className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]
}]},{className:"selector-tag",begin:n,relevance:0},{begin:/\{/,end:/\}/,
illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,{begin:/;/},a]}]}}})());hljs.registerLanguage("javascript",(()=>{"use strict"
;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"])
;function r(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{
return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return t=>{
const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,
isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,s=e.input[a]
;"<"!==s?">"===s&&(((e,{after:n})=>{const a="</"+e[0].slice(1)
;return-1!==e.input.indexOf(a,n)})(e,{after:a
})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n.join(" "),
literal:a.join(" "),built_in:s.join(" ")
},b="\\.([0-9](_?[0-9])*)",g="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",d={
className:"number",variants:[{
begin:`(\\b(${g})((${b})|\\.)?|(${b}))[eE][+-]?([0-9](_?[0-9])*)\\b`},{
begin:`\\b(${g})\\b((${b})\\b|\\.)?|(${b})\\b`},{
begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{
begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{
begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
begin:"\\b0[0-7]+n?\\b"}],relevance:0},E={className:"subst",begin:"\\$\\{",
end:"\\}",keywords:l,contains:[]},u={begin:"html`",end:"",starts:{end:"`",
returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},_={
begin:"css`",end:"",starts:{end:"`",returnEnd:!1,
contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},m={className:"string",
begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]},N={className:"comment",
variants:[t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",
begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0
},{className:"variable",begin:c+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{
begin:/(?=[^\n])\s/,relevance:0}]}]
}),t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]
},y=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,u,_,m,d,t.REGEXP_MODE]
;E.contains=y.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(y)
});const f=[].concat(N,E.contains),A=f.concat([{begin:/\(/,end:/\)/,keywords:l,
contains:["self"].concat(f)}]),p={className:"params",begin:/\(/,end:/\)/,
excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A};return{name:"Javascript",
aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:A},
illegal:/#(?![$_A-z])/,contains:[t.SHEBANG({label:"shebang",binary:"node",
relevance:5}),{label:"use_strict",className:"meta",relevance:10,
begin:/^\s*['"]use (strict|asm)['"]/
},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,u,_,m,N,d,{
begin:i(/[{,\n]\s*/,r(i(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))),
relevance:0,contains:[{className:"attr",begin:c+r("\\s*:"),relevance:0}]},{
begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
keywords:"return throw case",contains:[N,t.REGEXP_MODE,{className:"function",
begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",
returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{
begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0
},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A}]}]
},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{
variants:[{begin:"<>",end:"</>"},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,
end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,
contains:["self"]}]}],relevance:0},{className:"function",
beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l,
contains:["self",t.inherit(t.TITLE_MODE,{begin:c}),p],illegal:/%/},{
beginKeywords:"while if switch catch for"},{className:"function",
begin:t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
returnBegin:!0,contains:[p,t.inherit(t.TITLE_MODE,{begin:c})]},{variants:[{
begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class",
beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{
beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,
end:/[{;]/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:c}),"self",p]
},{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set",
contains:[t.inherit(t.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}]
}}})());hljs.registerLanguage("elm",(()=>{"use strict";return e=>{const n={
variants:[e.COMMENT("--","$"),e.COMMENT(/\{-/,/-\}/,{contains:["self"]})]},i={
className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},s={begin:"\\(",end:"\\)",
illegal:'"',contains:[{className:"type",
begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},n]};return{name:"Elm",
keywords:"let in if then else case of where module import exposing type alias as infix infixl infixr port effect command subscription",
contains:[{beginKeywords:"port effect module",end:"exposing",
keywords:"port effect module where command subscription exposing",
contains:[s,n],illegal:"\\W\\.|;"},{begin:"import",end:"$",
keywords:"import as exposing",contains:[s,n],illegal:"\\W\\.|;"},{begin:"type",
end:"$",keywords:"type alias",contains:[i,s,{begin:/\{/,end:/\}/,
contains:s.contains},n]},{beginKeywords:"infix infixl infixr",end:"$",
contains:[e.C_NUMBER_MODE,n]},{begin:"port",end:"$",keywords:"port",contains:[n]
},{className:"string",begin:"'\\\\?.",end:"'",illegal:"."
},e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,i,e.inherit(e.TITLE_MODE,{
begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}],illegal:/;/}}})());

View File

@ -5,14 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&family=Nunito:ital,wght@0,300;0,600;0,800;1,300&family=Nunito+Sans:wght@800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.5.0/build/styles/a11y-dark.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<link rel="stylesheet" href="https://nope.rhg.dev/dist/3.0.0/core.min.css">
<link rel="stylesheet" href="/vendor/prism.css">
<link rel="stylesheet" href="/style.css">
<link rel="shortcut icon" href="/favicon.png" type="image/x-png">
</head>
<body>
<script src="/highlight.pack.js"></script>
<script src="/vendor/prism.js" data-manual></script>
<script src="/dist/elm.js"></script>
<script src="/dist/flags.js"></script>
<script src="/main.js"></script>

View File

@ -27,19 +27,16 @@ window.addEventListener('keypress', (e) => {
})
// HighlightJS custom element
customElements.define('highlight-js', class HighlightJS extends HTMLElement {
customElements.define('prism-js', class HighlightJS extends HTMLElement {
constructor () { super() }
connectedCallback () {
const pre = document.createElement('pre')
const code = document.createElement('code')
pre.className = `language-elm`
code.innerText = this.body
pre.appendChild(code)
pre.textContent = this.body
this.appendChild(pre)
window.hljs.highlightBlock(pre)
window.Prism.highlightElement(pre)
}
})

View File

@ -28,7 +28,6 @@
--shadow-dark: 0 0.5em 2em rgb(0, 0, 0, 0.2);
}
/* Resets */
@media screen and (min-width: 1920px ) {
html { font-size: 20px; }
@ -137,12 +136,16 @@ hr { border: 0; }
max-width: 36em;
}
.markdown hr {
margin-top: 2em;
}
.markdown > h1:not(:first-child),
.markdown > h2:not(:first-child),
.markdown > h3:not(:first-child) {
.markdown > h2:not(:first-child) {
padding-top: 2rem;
}
.markdown > h3:not(:first-child),
.markdown > h4:not(:first-child) {
padding-top: 1rem;
}

5
docs/public/vendor/prism.css vendored Normal file
View File

@ -0,0 +1,5 @@
code[class*=language-elm],pre[class*=language-elm]{color:#ccc;background:0 0;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}pre[class*=language-elm]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-elm],pre[class*=language-elm]{background:#2d2d2d}:not(pre)>code[class*=language-elm]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}

3
docs/public/vendor/prism.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,6 +2,7 @@ module Main exposing (main)
import Browser
import Browser.Navigation as Nav exposing (Key)
import Effect
import Gen.Pages as Pages
import Gen.Route as Route
import Ports
@ -41,14 +42,13 @@ init flags url key =
( shared, sharedCmd ) =
Shared.init (request { url = url, key = key }) flags
( page, pageCmd, sharedPageCmd ) =
( page, effect ) =
Pages.init (Route.fromUrl url) shared url key
in
( Model url key shared page
, Cmd.batch
[ Cmd.map Shared sharedCmd
, Cmd.map Shared sharedPageCmd
, Cmd.map Page pageCmd
, Effect.toCmd ( Shared, Page ) effect
]
)
@ -89,13 +89,12 @@ update msg model =
else
let
( page, pageCmd, sharedPageCmd ) =
( page, effect ) =
Pages.init (Route.fromUrl url) model.shared url model.key
in
( { model | url = url, page = page }
, Cmd.batch
[ Cmd.map Page pageCmd
, Cmd.map Shared sharedPageCmd
[ Effect.toCmd ( Shared, Page ) effect
, Ports.onUrlChange ()
]
)
@ -111,14 +110,11 @@ update msg model =
Page pageMsg ->
let
( page, pageCmd, sharedPageCmd ) =
( page, effect ) =
Pages.update pageMsg model.page model.shared model.url model.key
in
( { model | page = page }
, Cmd.batch
[ Cmd.map Page pageCmd
, Cmd.map Shared sharedPageCmd
]
, Effect.toCmd ( Shared, Page ) effect
)

View File

@ -21,7 +21,6 @@ type alias Flags =
type alias Model =
{ index : Index
, token : Maybe Token
}

View File

@ -164,7 +164,7 @@ markdown options str =
Html.Keyed.node "div"
[]
[ ( body
, Html.node "highlight-js"
, Html.node "prism-js"
[ Attr.property "body" (Json.string body)
, Attr.property "language" (Json.string "elm")
]

View File

@ -15,7 +15,7 @@ module UI.Layout exposing
import Html exposing (Html)
import Html.Attributes as Attr
import Page exposing (Page, shared)
import Page exposing (Page)
import Request exposing (Request)
import Shared
import UI

View File

@ -16,7 +16,7 @@ toId : String -> String
toId =
String.toLower
>> String.words
>> List.map (String.filter Char.isAlphaNum)
>> List.map (String.filter (\c -> c == '-' || Char.isAlphaNum c))
>> String.join "-"