From cf2a65660de3f3caa8303e37d8ccbdc7e07dfcda Mon Sep 17 00:00:00 2001 From: Ryan Haskell-Glatz Date: Tue, 9 Feb 2021 01:20:05 -0600 Subject: [PATCH] fix highlighting, add a bunch of docs --- docs/public/content/guide.md | 67 ++--- docs/public/content/guide/cli.md | 175 +++++------ docs/public/content/guide/pages.md | 223 ++++++++++++-- docs/public/content/guide/requests.md | 24 +- docs/public/highlight.pack.js | 406 -------------------------- docs/public/index.html | 4 +- docs/public/main.js | 9 +- docs/public/style.css | 9 +- docs/public/vendor/prism.css | 5 + docs/public/vendor/prism.js | 3 + docs/src/Main.elm | 18 +- docs/src/Shared.elm | 1 - docs/src/UI.elm | 2 +- docs/src/UI/Layout.elm | 2 +- docs/src/Utils/String.elm | 2 +- 15 files changed, 341 insertions(+), 609 deletions(-) delete mode 100644 docs/public/highlight.pack.js create mode 100644 docs/public/vendor/prism.css create mode 100644 docs/public/vendor/prism.js diff --git a/docs/public/content/guide.md b/docs/public/content/guide.md index 2b688f8..762c7ec 100644 --- a/docs/public/content/guide.md +++ b/docs/public/content/guide.md @@ -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 . . . . . . . . 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 . . . . . . . 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) \ No newline at end of file +__Ready for more?__ + +Let's check out [the CLI](/guide/cli) to learn more about those five commands! diff --git a/docs/public/content/guide/cli.md b/docs/public/content/guide/cli.md index 902d90e..5437dee 100644 --- a/docs/public/content/guide/cli.md +++ b/docs/public/content/guide/cli.md @@ -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 . . . . . . . . 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! \ No newline at end of file +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)! \ No newline at end of file diff --git a/docs/public/content/guide/pages.md b/docs/public/content/guide/pages.md index 6a2ccde..691e328 100644 --- a/docs/public/content/guide/pages.md +++ b/docs/public/content/guide/pages.md @@ -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 } -``` \ No newline at end of file +``` + +### 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) \ No newline at end of file diff --git a/docs/public/content/guide/requests.md b/docs/public/content/guide/requests.md index d091f64..6f9d853 100644 --- a/docs/public/content/guide/requests.md +++ b/docs/public/content/guide/requests.md @@ -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. \ No newline at end of file diff --git a/docs/public/highlight.pack.js b/docs/public/highlight.pack.js deleted file mode 100644 index f3d630b..0000000 --- a/docs/public/highlight.pack.js +++ /dev/null @@ -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,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") -}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" "+e.nodeName+'="'+r(e.value)+'"')).join("")+">" -}function u(e){a+=""}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+="")}value(){return this.buffer}span(e){ -this.buffer+=``}}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;a0&&(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||"")+'"') -;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?"
":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(//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:/`]+/}]}]}]};return{name:"HTML, XML", -aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], -case_insensitive:!0,contains:[{className:"meta",begin:"", -relevance:10,contains:[c,g,l,r,{begin:"\\[",end:"\\]",contains:[{ -className:"meta",begin:"",contains:[c,r,g,l]}]}] -},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"",relevance:10},i,{className:"meta",begin:/<\?xml/,end:/\?>/, -relevance:10},{className:"tag",begin:")",end:">",keywords:{ -name:"style"},contains:[m],starts:{end:"",returnEnd:!0, -subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">", -keywords:{name:"script"},contains:[m],starts:{end:/<\/script>/,returnEnd:!0, -subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/ -},{className:"tag",begin:a(//,/>/,/\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="", -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:/;/}}})()); \ No newline at end of file diff --git a/docs/public/index.html b/docs/public/index.html index f29a0c2..df40b75 100644 --- a/docs/public/index.html +++ b/docs/public/index.html @@ -5,14 +5,14 @@ - + - + diff --git a/docs/public/main.js b/docs/public/main.js index 2008682..e5098af 100644 --- a/docs/public/main.js +++ b/docs/public/main.js @@ -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) } }) diff --git a/docs/public/style.css b/docs/public/style.css index 72d691a..b0f91db 100644 --- a/docs/public/style.css +++ b/docs/public/style.css @@ -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; } diff --git a/docs/public/vendor/prism.css b/docs/public/vendor/prism.css new file mode 100644 index 0000000..ceb3337 --- /dev/null +++ b/docs/public/vendor/prism.css @@ -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} \ No newline at end of file diff --git a/docs/public/vendor/prism.js b/docs/public/vendor/prism.js new file mode 100644 index 0000000..58fe3b3 --- /dev/null +++ b/docs/public/vendor/prism.js @@ -0,0 +1,3 @@ +/* PrismJS 1.23.0 - https://prismjs.com/download.html#themes=prism-tomorrow&languages=elm */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);y+=m.value.length,m=m.next){var k=m.value;if(r.length>n.length)return;if(!(k instanceof W)){var b,x=1;if(h){if(!(b=z(p,y,n,f)))break;var w=b.index,A=b.index+b[0].length,P=y;for(P+=m.value.length;P<=w;)m=m.next,P+=m.value.length;if(P-=m.value.length,y=P,m.value instanceof W)continue;for(var S=m;S!==r.tail&&(Pl.reach&&(l.reach=N);var j=m.prev;O&&(j=I(r,j,O),y+=O.length),q(r,j,x);var C=new W(o,g?M.tokenize(E,g):E,d,E);if(m=I(r,j,C),L&&I(r,m,L),1l.reach&&(l.reach=_.reach)}}}}}}(e,a,n,a.head,0),function(e){var n=[],r=e.head.next;for(;r!==e.tail;)n.push(r.value),r=r.next;return n}(a)},hooks:{all:{},add:function(e,n){var r=M.hooks.all;r[e]=r[e]||[],r[e].push(n)},run:function(e,n){var r=M.hooks.all[e];if(r&&r.length)for(var t,a=0;t=r[a++];)t(n)}},Token:W};function W(e,n,r,t){this.type=e,this.content=n,this.alias=r,this.length=0|(t||"").length}function z(e,n,r,t){e.lastIndex=n;var a=e.exec(r);if(a&&t&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function i(){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}function I(e,n,r){var t=n.next,a={value:r,prev:n,next:t};return n.next=a,t.prev=a,e.length++,a}function q(e,n,r){for(var t=n.next,a=0;a"+a.content+""},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,t=n.code,a=n.immediateClose;u.postMessage(M.highlight(t,M.languages[r],r)),a&&u.close()},!1)),M;var e=M.util.currentScript();function r(){M.manual||M.highlightAll()}if(e&&(M.filename=e.src,e.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var t=document.readyState;"loading"===t||"interactive"===t&&e&&e.defer?document.addEventListener("DOMContentLoaded",r):window.requestAnimationFrame?window.requestAnimationFrame(r):window.setTimeout(r,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.elm={comment:/--.*|{-[\s\S]*?-}/,char:{pattern:/'(?:[^\\'\r\n]|\\(?:[abfnrtv\\']|\d+|x[0-9a-fA-F]+))'/,greedy:!0},string:[{pattern:/"""[\s\S]*?"""/,greedy:!0},{pattern:/"(?:[^\\"\r\n]|\\.)*"/,greedy:!0}],"import-statement":{pattern:/^\s*import\s+[A-Z]\w*(?:\.[A-Z]\w*)*(?:\s+as\s+(?:[A-Z]\w*)(?:\.[A-Z]\w*)*)?(?:\s+exposing\s+)?/m,inside:{keyword:/\b(?:import|as|exposing)\b/}},keyword:/\b(?:alias|as|case|else|exposing|if|in|infixl|infixr|let|module|of|then|type)\b/,builtin:/\b(?:abs|acos|always|asin|atan|atan2|ceiling|clamp|compare|cos|curry|degrees|e|flip|floor|fromPolar|identity|isInfinite|isNaN|logBase|max|min|negate|never|not|pi|radians|rem|round|sin|sqrt|tan|toFloat|toPolar|toString|truncate|turns|uncurry|xor)\b/,number:/\b(?:\d+(?:\.\d+)?(?:e[+-]?\d+)?|0x[0-9a-f]+)\b/i,operator:/\s\.\s|[+\-/*=.$<>:&|^?%#@~!]{2,}|[+\-/*=$<>:&|^?%#@~!]/,hvariable:/\b(?:[A-Z]\w*\.)*[a-z]\w*\b/,constant:/\b(?:[A-Z]\w*\.)*[A-Z]\w*\b/,punctuation:/[{}[\]|(),.:]/}; \ No newline at end of file diff --git a/docs/src/Main.elm b/docs/src/Main.elm index c94f09c..918e0df 100644 --- a/docs/src/Main.elm +++ b/docs/src/Main.elm @@ -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 ) diff --git a/docs/src/Shared.elm b/docs/src/Shared.elm index 1fa7aeb..80f1475 100644 --- a/docs/src/Shared.elm +++ b/docs/src/Shared.elm @@ -21,7 +21,6 @@ type alias Flags = type alias Model = { index : Index - , token : Maybe Token } diff --git a/docs/src/UI.elm b/docs/src/UI.elm index 55e25f0..5d81083 100644 --- a/docs/src/UI.elm +++ b/docs/src/UI.elm @@ -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") ] diff --git a/docs/src/UI/Layout.elm b/docs/src/UI/Layout.elm index f28f93e..01d466a 100644 --- a/docs/src/UI/Layout.elm +++ b/docs/src/UI/Layout.elm @@ -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 diff --git a/docs/src/Utils/String.elm b/docs/src/Utils/String.elm index 6450952..6dbc828 100644 --- a/docs/src/Utils/String.elm +++ b/docs/src/Utils/String.elm @@ -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 "-"