From 3e28d4b6e17875583a4011464e089cdd88a7a6b4 Mon Sep 17 00:00:00 2001 From: Bad Date: Tue, 20 Oct 2020 20:22:13 +0200 Subject: [PATCH 01/92] Style the login form --- package-lock.json | 2 +- spec.js | 57 ++++++++++++++++++++++++-------------------- src/login.pug | 36 +++++++++++++++++----------- src/sass/login.sass | 58 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 41 deletions(-) create mode 100644 src/sass/login.sass diff --git a/package-lock.json b/package-lock.json index e5806d8..94cc52b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "cosc212-assignment-1", + "name": "carbon", "version": "1.0.0", "lockfileVersion": 1, "requires": true, diff --git a/spec.js b/spec.js index 38bb96e..600af14 100644 --- a/spec.js +++ b/spec.js @@ -2,121 +2,126 @@ module.exports = [ { type: "file", source: "/assets/fonts/whitney-500.woff", - target: "/static/whitney-500.woff" + target: "/static/whitney-500.woff", }, { type: "file", source: "/assets/fonts/whitney-400.woff", - target: "/static/whitney-400.woff" + target: "/static/whitney-400.woff", }, { type: "js", source: "/js/basic.js", - target: "/static/basic.js" + target: "/static/basic.js", }, { type: "js", source: "/js/groups.js", - target: "/static/groups.js" + target: "/static/groups.js", }, { type: "js", source: "/js/chat-input.js", - target: "/static/chat-input.js" + target: "/static/chat-input.js", }, { type: "js", source: "/js/room-picker.js", - target: "/static/room-picker.js" + target: "/static/room-picker.js", }, { type: "js", source: "/js/store/store.js", - target: "/static/store/store.js" + target: "/static/store/store.js", }, { type: "js", source: "/js/store/Subscribable.js", - target: "/static/store/Subscribable.js" + target: "/static/store/Subscribable.js", }, { type: "js", source: "/js/store/SubscribeValue.js", - target: "/static/store/SubscribeValue.js" + target: "/static/store/SubscribeValue.js", }, { type: "js", source: "/js/store/SubscribeMapList.js", - target: "/static/store/SubscribeMapList.js" + target: "/static/store/SubscribeMapList.js", }, { type: "js", source: "/js/store/SubscribeSet.js", - target: "/static/store/SubscribeSet.js" + target: "/static/store/SubscribeSet.js", }, { type: "js", source: "/js/sync/sync.js", - target: "/static/sync/sync.js" + target: "/static/sync/sync.js", }, { type: "js", source: "/js/lsm.js", - target: "/static/lsm.js" + target: "/static/lsm.js", }, { type: "js", source: "/js/Timeline.js", - target: "/static/Timeline.js" + target: "/static/Timeline.js", }, { type: "js", source: "/js/Anchor.js", - target: "/static/Anchor.js" + target: "/static/Anchor.js", }, { type: "js", source: "/js/chat.js", - target: "/static/chat.js" + target: "/static/chat.js", }, { type: "js", source: "/js/functions.js", - target: "/static/functions.js" + target: "/static/functions.js", }, { type: "file", source: "/assets/fonts/whitney-500.woff", - target: "/static/whitney-500.woff" + target: "/static/whitney-500.woff", }, { type: "file", source: "/assets/icons/directs.svg", - target: "/static/directs.svg" + target: "/static/directs.svg", }, { type: "file", source: "/assets/icons/channels.svg", - target: "/static/channels.svg" + target: "/static/channels.svg", }, { type: "file", source: "/assets/icons/join-event.svg", - target: "/static/join-event.svg" + target: "/static/join-event.svg", }, { type: "sass", source: "/sass/main.sass", - target: "/static/main.css" + target: "/static/main.css", + }, + { + type: "sass", + source: "/sass/login.sass", + target: "/static/login.css", }, { type: "pug", source: "/home.pug", - target: "/index.html" + target: "/index.html", }, { type: "pug", source: "/login.pug", - target: "/login.html" - } -] + target: "/login.html", + }, +]; diff --git a/src/login.pug b/src/login.pug index 105b6bd..954728c 100644 --- a/src/login.pug +++ b/src/login.pug @@ -2,20 +2,28 @@ doctype html html head meta(charset="utf-8") - link(rel="stylesheet" type="text/css" href=getStatic("/sass/main.sass")) + link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) title Carbon + meta(name="viewport" content= "width=device-width, initial-scale=1.0") + body main.main - form - div - label(for="login") Username - input(type="text" name="login" autocomplete="username" placeholder="example:matrix.org" required)#login - div - label(for="password") Password - input(type="text" name="password" autocomplete="current-password" required)#password - div - - label(for="homeserver") Homeserver - input(type="text" name="homeserver" value="matrix.org" required)#homeserver - div - input(type="submit" value="Login") + div.center-login-container + h1 Welcome to the Matrix! + form.login-form + div.data-input + div.form-input-container + label(for="login") Username + input(type="text" name="login" autocomplete="username" required)#login + div.form-input-container + + label(for="password") Password + input(name="password" autocomplete="current-password" type="password" required)#password + div.form-input-container + + + label(for="homeserver") Homeserver + input(type="text" name="homeserver" value="matrix.org" required)#homeserver + div.form-input-container + + input(type="submit" value="Login") diff --git a/src/sass/login.sass b/src/sass/login.sass new file mode 100644 index 0000000..4040453 --- /dev/null +++ b/src/sass/login.sass @@ -0,0 +1,58 @@ +@use "./base" +@use "./colors.sass" as c + +.main + justify-content: center + align-items: center + +.center-login-container + display: flex + flex-flow: column + justify-content: center + align-items: center + width: min(100vw, 30rem) + padding: max(1rem,3vw) 2rem + height: 27rem + box-shadow: -2px 2px 10px c.$darkest + background-color: c.$darker + border-radius: 5px + +.login-form + align-items: center + flex: 1 1 auto + width: 100% + display: flex + justify-content: space-around + flex-flow: column + +.data-input + width: 100% + + +.form-input-container + width: 100% + display: flex + flex-direction: column + margin: 1em 0 + +input + width: 100% + height: 2.4rem + border-radius: 5px + box-sizing: border-box + transition: background-color 1s, border-color 1s + padding: 0px 1ch + border: 0px + +input[type="text"],input[type="password"] + border: 3px solid transparent + margin: 0.4em 0px + &:hover + border-color: c.$mild + +input[type="submit"]:hover + background-color: c.$mild + +label + font-size: 1.2em + From c21ab3b90fc18a627fcc30d78a4bfb24e40b52d1 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 18:17:01 +1300 Subject: [PATCH 02/92] Adjust group list internal sizing --- src/js/Timeline.js | 1 - src/sass/components/groups.sass | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/Timeline.js b/src/js/Timeline.js index fa8f2e5..94e77bf 100644 --- a/src/js/Timeline.js +++ b/src/js/Timeline.js @@ -282,7 +282,6 @@ class Timeline extends Subscribable { if (eventData.type === "m.room.message" && eventData.content["m.relates_to"] && eventData.content["m.relates_to"].rel_type === "m.replace") { const replaces = eventData.content["m.relates_to"].event_id if (this.map.has(replaces)) { - console.log(eventData) const event = this.map.get(replaces) event.data.content = eventData.content["m.new_content"] event.setEdited(eventData.origin_server_ts) diff --git a/src/sass/components/groups.sass b/src/sass/components/groups.sass index 44685d5..e91f4cc 100644 --- a/src/sass/components/groups.sass +++ b/src/sass/components/groups.sass @@ -33,6 +33,7 @@ $out-width: $base-width + rooms.$list-width &__container width: $out-width padding: $icon-padding + box-sizing: border-box .c-group display: flex From 3fc8104bdd52334cfc98e8cbce66cc840b528d62 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 18:23:44 +1300 Subject: [PATCH 03/92] Format Bad's code --- src/login.pug | 51 ++++++++++++++++++++++----------------------- src/sass/login.sass | 4 +--- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/login.pug b/src/login.pug index 954728c..13acefd 100644 --- a/src/login.pug +++ b/src/login.pug @@ -1,29 +1,28 @@ doctype html html - head - meta(charset="utf-8") - link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) - title Carbon - meta(name="viewport" content= "width=device-width, initial-scale=1.0") + head + meta(charset="utf-8") + link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) + title Carbon + meta(name="viewport" content="width=device-width, initial-scale=1") - body - main.main - div.center-login-container - h1 Welcome to the Matrix! - form.login-form - div.data-input - div.form-input-container - label(for="login") Username - input(type="text" name="login" autocomplete="username" required)#login - div.form-input-container - - label(for="password") Password - input(name="password" autocomplete="current-password" type="password" required)#password - div.form-input-container - - - label(for="homeserver") Homeserver - input(type="text" name="homeserver" value="matrix.org" required)#homeserver - div.form-input-container - - input(type="submit" value="Login") + body + main.main + .center-login-container + h1 Welcome to the Matrix! + form.login-form + .data-input + .form-input-container + label(for="login") Username + input(type="text" name="login" autocomplete="username" required)#login + + .form-input-container + label(for="password") Password + input(name="password" autocomplete="current-password" type="password" required)#password + + .form-input-container + label(for="homeserver") Homeserver + input(type="text" name="homeserver" value="matrix.org" required)#homeserver + + .form-input-container + input(type="submit" value="Login") diff --git a/src/sass/login.sass b/src/sass/login.sass index 4040453..92f443f 100644 --- a/src/sass/login.sass +++ b/src/sass/login.sass @@ -27,8 +27,7 @@ .data-input width: 100% - - + .form-input-container width: 100% display: flex @@ -55,4 +54,3 @@ input[type="submit"]:hover label font-size: 1.2em - From 265d774b4f149f0fafe6f6e77b6e384967710c98 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 19:33:36 +1300 Subject: [PATCH 04/92] Some login functionality --- spec.js | 5 ++ src/js/login.js | 114 ++++++++++++++++++++++++++++++++++++++++++++ src/login.pug | 13 ++--- src/sass/login.sass | 31 +++++++----- 4 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 src/js/login.js diff --git a/spec.js b/spec.js index 600af14..a488e5e 100644 --- a/spec.js +++ b/spec.js @@ -84,6 +84,11 @@ module.exports = [ source: "/js/functions.js", target: "/static/functions.js", }, + { + type: "js", + source: "/js/login.js", + target: "/static/login.js", + }, { type: "file", source: "/assets/fonts/whitney-500.woff", diff --git a/src/js/login.js b/src/js/login.js new file mode 100644 index 0000000..d3baf3a --- /dev/null +++ b/src/js/login.js @@ -0,0 +1,114 @@ +import {q, ElemJS} from $to_relative "/js/basic.js" + +const password = q("#password") +const homeserver = q("#homeserver") + +class Username extends ElemJS { + constructor() { + super(q("#username")) + + this.on("change", this.updateServer.bind(this)) + } + + isValid() { + return !!this.element.value.match(/^@?[a-z0-9._=\/-]+(?::[a-zA-Z0-9.:\[\]-]+)?$/) + } + + getUsername() { + return this.element.value.match(/^@?([a-z0-9._=\/-]+)/)[1] + } + + getServer() { + const server = this.element.value.match(/^@?[a-z0-9._=\?-]+:([a-zA-Z0-9.:\[\]-]+)$/) + if (server && server[1]) return server[1] + else return null + } + + updateServer() { + if (!this.isValid()) return + if (this.getServer()) homeserver.value = this.getServer() + } +} + +const username = new Username() + +class Form extends ElemJS { + constructor() { + super(q("#form")) + + this.processing = false + + this.on("submit", this.submit.bind(this)) + } + + async submit() { + if (this.processing) return + this.processing = true + if (!username.isValid()) return this.cancel("Username is not valid.") + + // Resolve homeserver address + let currentAddress = homeserver.value + let ok = false + while (!ok) { + if (!currentAddress.match(/^https?:\/\//)) currentAddress = "https://" + currentAddress + currentAddress = currentAddress.replace(/\/*$/, "") + this.status(`Looking up homeserver... trying ${currentAddress}`) + try { + // check if we found the actual matrix server + const exists = await fetch(`${currentAddress}/_matrix/client/r0`).then(res => res.json()) + if (exists.errcode === "M_UNRECOGNIZED") { + ok = true + break + } + // find the next matrix server in the chain + const root = await fetch(`${currentAddress}/.well-known/matrix/client`).then(res => res.json()) + let nextAddress = root["m.homeserver"].base_url + nextAddress = nextAddress.replace(/\/*$/, "") + if (currentAddress === nextAddress) { + ok = true + } + currentAddress = nextAddress + } catch (e) { + return this.cancel(`Failed to look up server ${currentAddress}`) + } + } + + // Request access token + this.status("Logging in...") + const root = await fetch(`${currentAddress}/_matrix/client/r0/login`, { + method: "POST", + body: JSON.stringify({ + type: "m.login.password", + user: username.getUsername(), + password: password.value + }) + }).then(res => res.json()) + + if (!root.access_token) { + if (root.error) { + this.cancel(`Server said: ${root.error}`) + } else { + this.cancel("Login mysteriously failed.") + console.error(root) + } + return + } + + localStorage.setItem("mx_user_id", root.user_id) + localStorage.setItem("domain", currentAddress) + localStorage.setItem("access_token", root.access_token) + + location.assign("./") + } + + status(message) { + //TODO: display the message + } + + cancel(message) { + this.processing = false + //TODO: display the message + } +} + +const form = new Form() diff --git a/src/login.pug b/src/login.pug index 13acefd..4e86f06 100644 --- a/src/login.pug +++ b/src/login.pug @@ -2,19 +2,20 @@ doctype html html head meta(charset="utf-8") - link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) title Carbon meta(name="viewport" content="width=device-width, initial-scale=1") + link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) + script(type="module" src=getStatic("/js/login.js")) body main.main .center-login-container h1 Welcome to the Matrix! - form.login-form + form.login-form(method="post" onsubmit="return false")#form .data-input .form-input-container - label(for="login") Username - input(type="text" name="login" autocomplete="username" required)#login + label(for="username") Username + input(type="text" name="username" autocomplete="username" placeholder="@username:server.tld" pattern="^@?[a-z0-9._=/-]+(?::[a-zA-Z0-9.:\\[\\]-]+)?$" required)#username .form-input-container label(for="password") Password @@ -22,7 +23,7 @@ html .form-input-container label(for="homeserver") Homeserver - input(type="text" name="homeserver" value="matrix.org" required)#homeserver + input(type="text" name="homeserver" value="matrix.org" placeholder="matrix.org" required)#homeserver .form-input-container - input(type="submit" value="Login") + input(type="submit" value="Log in")#submit diff --git a/src/sass/login.sass b/src/sass/login.sass index 92f443f..77144bc 100644 --- a/src/sass/login.sass +++ b/src/sass/login.sass @@ -10,10 +10,10 @@ flex-flow: column justify-content: center align-items: center - width: min(100vw, 30rem) + width: min(100vw, 450px) padding: max(1rem,3vw) 2rem - height: 27rem - box-shadow: -2px 2px 10px c.$darkest + margin: 8px + box-shadow: 0px 2px 10px c.$darkest background-color: c.$darker border-radius: 5px @@ -34,23 +34,30 @@ flex-direction: column margin: 1em 0 -input +input, button + font-family: inherit + font-size: 17px + background-color: c.$mild + color: #eee width: 100% - height: 2.4rem border-radius: 5px box-sizing: border-box - transition: background-color 1s, border-color 1s - padding: 0px 1ch + transition: background-color 0.15s ease-out, border-color 0.15s ease-out + padding: 4px 9px border: 0px input[type="text"],input[type="password"] border: 3px solid transparent margin: 0.4em 0px - &:hover - border-color: c.$mild -input[type="submit"]:hover - background-color: c.$mild + &:hover, &:focus + border-color: c.$milder + +button, input[type="submit"] + padding: 7px + + &:hover + background-color: c.$milder label - font-size: 1.2em + font-size: 18px From 735ca360c8dc24dd14597f12db5e50c846a5afd8 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 20:23:51 +1300 Subject: [PATCH 05/92] Login form feedback --- src/js/login.js | 50 ++++++++++++++++++++++++++++++++++++------- src/js/room-picker.js | 8 ++++--- src/login.pug | 3 +++ src/sass/login.sass | 18 ++++++++++++++++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index d3baf3a..de32932 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -1,4 +1,4 @@ -import {q, ElemJS} from $to_relative "/js/basic.js" +import {q, ElemJS, ejs} from $to_relative "/js/basic.js" const password = q("#password") const homeserver = q("#homeserver") @@ -32,6 +32,36 @@ class Username extends ElemJS { const username = new Username() +class Feedback extends ElemJS { + constructor() { + super(q("#feedback")) + this.loading = false + this.loadingIcon = ejs("span").class("loading-icon") + this.messageSpan = ejs("span") + this.child(this.messageSpan) + } + + setLoading(state) { + if (this.loading && !state) { + this.loadingIcon.remove() + } else if (!this.loading && state) { + this.childAt(0, this.loadingIcon) + } + this.loading = state + } + + message(content) { + if (content) { + this.class("form-feedback") + } else { + this.removeClass("form-feedback") + } + this.messageSpan.text(content) + } +} + +const feedback = new Feedback() + class Form extends ElemJS { constructor() { super(q("#form")) @@ -55,11 +85,13 @@ class Form extends ElemJS { this.status(`Looking up homeserver... trying ${currentAddress}`) try { // check if we found the actual matrix server - const exists = await fetch(`${currentAddress}/_matrix/client/r0`).then(res => res.json()) - if (exists.errcode === "M_UNRECOGNIZED") { - ok = true - break - } + try { + const versions = await fetch(`${currentAddress}/_matrix/client/versions`).then(res => res.json()) + if (Array.isArray(versions.versions)) { + ok = true + break + } + } catch (e) {} // find the next matrix server in the chain const root = await fetch(`${currentAddress}/.well-known/matrix/client`).then(res => res.json()) let nextAddress = root["m.homeserver"].base_url @@ -102,12 +134,14 @@ class Form extends ElemJS { } status(message) { - //TODO: display the message + feedback.setLoading(true) + feedback.message(message) } cancel(message) { this.processing = false - //TODO: display the message + feedback.setLoading(false) + feedback.message(message) } } diff --git a/src/js/room-picker.js b/src/js/room-picker.js index 8e696de..9fbbece 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -108,10 +108,12 @@ class Room extends ElemJS { getIcon() { const avatar = this.data.state.events.find(e => e.type === "m.room.avatar") if (avatar) { - return resolveMxc(avatar.content.url || avatar.content.avatar_url, 32, "crop") - } else { - return null + const url = avatar.content.url || avatar.content.avatar_url + if (url) { + return resolveMxc(url, 32, "crop") + } } + return null } isDirect() { diff --git a/src/login.pug b/src/login.pug index 4e86f06..85d4485 100644 --- a/src/login.pug +++ b/src/login.pug @@ -25,5 +25,8 @@ html label(for="homeserver") Homeserver input(type="text" name="homeserver" value="matrix.org" placeholder="matrix.org" required)#homeserver + #feedback + .form-input-container input(type="submit" value="Log in")#submit + diff --git a/src/sass/login.sass b/src/sass/login.sass index 77144bc..e4820ee 100644 --- a/src/sass/login.sass +++ b/src/sass/login.sass @@ -34,6 +34,24 @@ flex-direction: column margin: 1em 0 +.form-feedback + width: 100% + margin: -0.5em 0 -0.8em + +@keyframes spin + 0% + transform: rotate(0deg) + 100% + transform: rotate(180deg) + +.loading-icon + display: inline-block + background-color: #ccc + width: 12px + height: 12px + margin-right: 6px + animation: spin 0.7s infinite + input, button font-family: inherit font-size: 17px From 988dd1050ba2dff0c92e8dfe2d538b0b19e40d53 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 20:27:22 +1300 Subject: [PATCH 06/92] Add note about encrypted messages --- src/js/Timeline.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/Timeline.js b/src/js/Timeline.js index 94e77bf..3e2a0b6 100644 --- a/src/js/Timeline.js +++ b/src/js/Timeline.js @@ -81,6 +81,8 @@ class Event extends ElemJS { } else { this.child(ejs("i").text("left the room")) } + } else if (this.data.type === "m.room.encrypted") { + this.child(ejs("i").text("Carbon does not yet support encrypted messages.")) } else { this.child(ejs("i").text(`Unsupported event type ${this.data.type}`)) } From 184c876fb96af0bc4ae9bb21b541ac07f47bb0d5 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 20:34:09 +1300 Subject: [PATCH 07/92] Redirect to login page if not logged in --- spec.js | 7 ++++++- src/home.pug | 10 +++------- src/js/main.js | 9 +++++++++ 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 src/js/main.js diff --git a/spec.js b/spec.js index a488e5e..450f7cc 100644 --- a/spec.js +++ b/spec.js @@ -9,6 +9,11 @@ module.exports = [ source: "/assets/fonts/whitney-400.woff", target: "/static/whitney-400.woff", }, + { + type: "js", + source: "/js/main.js", + target: "/static/main.js", + }, { type: "js", source: "/js/basic.js", @@ -127,6 +132,6 @@ module.exports = [ { type: "pug", source: "/login.pug", - target: "/login.html", + target: "/login/index.html", }, ]; diff --git a/src/home.pug b/src/home.pug index 68bbd21..09a00c7 100644 --- a/src/home.pug +++ b/src/home.pug @@ -33,18 +33,14 @@ doctype html html head meta(charset="utf-8") + title Carbon // var static = !{JSON.stringify([...static.entries()].reduce((a, c) => (a[c[0]] = getRelative(c[1]), a), {}))} script | var staticFiles = new Map( != JSON.stringify([...static.keys()].map(k => [k, getStatic(k)])) | ) link(rel="stylesheet" type="text/css" href=getStatic("/sass/main.sass")) - script(type="module" src=getStatic("/js/groups.js")) - script(type="module" src=getStatic("/js/chat-input.js")) - script(type="module" src=getStatic("/js/room-picker.js")) - script(type="module" src=getStatic("/js/sync/sync.js")) - script(type="module" src=getStatic("/js/chat.js")) - title Carbon + script(type="module" src=getStatic("/js/main.js")) body main.main .c-groups @@ -56,4 +52,4 @@ html .c-chat__messages#c-chat-messages .c-chat__inner#c-chat .c-chat-input - textarea(placeholder="Send a message..." autocomplete="off").c-chat-input__textarea#c-chat-textarea \ No newline at end of file + textarea(placeholder="Send a message..." autocomplete="off").c-chat-input__textarea#c-chat-textarea diff --git a/src/js/main.js b/src/js/main.js new file mode 100644 index 0000000..98dd38b --- /dev/null +++ b/src/js/main.js @@ -0,0 +1,9 @@ +import $to_relative "/js/groups.js" +import $to_relative "/js/chat-input.js" +import $to_relative "/js/room-picker.js" +import $to_relative "/js/sync/sync.js" +import $to_relative "/js/chat.js" + +if (!localStorage.getItem("access_token")) { + location.assign("./login") +} From b9df147db5adb119d0f1f25e0abbf0608c3285ec Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 20:40:24 +1300 Subject: [PATCH 08/92] Branding --- src/login.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/login.pug b/src/login.pug index 85d4485..470ee82 100644 --- a/src/login.pug +++ b/src/login.pug @@ -10,7 +10,7 @@ html body main.main .center-login-container - h1 Welcome to the Matrix! + h1 Welcome to Carbon! form.login-form(method="post" onsubmit="return false")#form .data-input .form-input-container From 253ccbadc2ded29d1436b0b074605d1d3d1b4f64 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 20:56:36 +1300 Subject: [PATCH 09/92] Only enter sync loop if logged in --- src/js/sync/sync.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js index bbd6b11..f338e4d 100644 --- a/src/js/sync/sync.js +++ b/src/js/sync/sync.js @@ -121,4 +121,6 @@ function syncLoop() { store.activeGroup.set(store.groups.get("directs").value()) -syncLoop() +if (lsm.get("access_token")) { + syncLoop() +} From 4869a31ec2ddc92d7817e28329261d1bffe79d08 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 21:57:38 +1300 Subject: [PATCH 10/92] Update README, remove login GUI from todo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75b3ef3..9d48a04 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ Carbon is currently _technically_ usable as a chat app, but is very early in development. These important features still need to be implemented: -- Login GUI - Unreads - Chat history +- Typing indicators - Formatting - Emojis - Reactions From 64c3e1878884b35a3cf7be8e883356c65d063b8c Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 22:10:58 +1300 Subject: [PATCH 11/92] Don't break on rooms with a redacted name --- src/js/room-picker.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/js/room-picker.js b/src/js/room-picker.js index 9fbbece..d2e79fa 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -94,15 +94,20 @@ class Room extends ElemJS { } getName() { + // if the room has a name let name = this.data.state.events.find(e => e.type === "m.room.name") - if (name) { - name = name.content.name - } else { - const users = this.data.summary["m.heroes"] - const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u) - name = usernames.join(", ") + if (name && name.content.name) { + return name.content.name } - return name + // if the room has no name, use its canonical alias + let canonicalAlias = this.data.state.events.find(e => e.type === "m.room.canonical_alias") + if (canonicalAlias && canonicalAlias.content.alias) { + return canonicalAlias.content.alias + } + // if the room has no alias, use the names of its members ("heroes") + const users = this.data.summary["m.heroes"] + const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u) + return usernames.join(", ") } getIcon() { From a56c42311fe3b6bd100d8c4b24a8d2be1a694091 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 22:21:21 +1300 Subject: [PATCH 12/92] Use sh shell instead of fish for watch script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d32107..454c3cb 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "build.js", "scripts": { "build": "node build.js", - "watch": "fish -c 'while true; echo -n \"Build started at \"; date; npm run build; inotifywait (find src -type f) build.js -e close_write -qq; end'", + "watch": "sh -c 'while true; do echo -n \"Build started at \"; date; npm run build; inotifywait $(find src -type f) build.js spec.js package.json -e close_write -qq; done'", "rebuild": "rm build -rf && node build.js" }, "keywords": [], From ff427d0354320fe0ec4781f2a7916a0d7460ca39 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 22:53:47 +1300 Subject: [PATCH 13/92] Update readme - add bug report instructions --- README.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f2006b..4536623 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,31 @@ Carbon is the Matrix client for Discord and Guilded refugees. +Visit the hosted instance on +[https://carbon.chat](https://carbon.chat). + +## Report bugs and suggest features + +Please briefly check this README and the issues page first to make +sure that the issue/feature is not already known! + +- If you already have an account on Gitdab, use the issues page. +- If you don't have an account, and don't wish to create one, you can +send an email to the [mailing list]. + +If something in the interface isn't working as you think it should, +please provide a screenshot of any messages from the browser devtools +console. If using the mailing list, attachments aren't supported, so +you'll have to upload to some image host and post the link. + +[mailing list]: https://lists.sr.ht/~cadence/carbon-discuss + ## The dream Carbon's planned features, compared to Discord and Guilded: - End to end encryption -- Free of charge per-account custom emojis and custom emoji packs +- Free of charge, per-account, custom emojis and custom emoji packs - No limit to number of groups you can join at a time - Uses the open Matrix and Mumble systems - Much better IRC layout @@ -41,6 +60,7 @@ implemented: - Formatting - Emojis - Reactions +- Encryption - Groups v2 - Group management - Pinned channels @@ -48,7 +68,13 @@ implemented: ## The code -### Building +### Downloading a CI build + +Visit [drone CI](https://drone.badat.dev/cadence/Carbon/branches), +select the branch you want to use, select `b2` on the left, scroll +down, and open the URL on the last line to download the build. + +### Building yourself npm install -D npm run rebuild @@ -64,3 +90,8 @@ no cache-control header to everything else. npm run watch Files will be rebuilt as you save them. + +Use `python3 -m http.server -d build` to serve the build on +[http://localhost:8000](http://localhost:8000). + +(Avoid `npx http-server`, since this applies too much caching.) From cf2c691585072c4aa2de7bf97261b1a2bf3004a3 Mon Sep 17 00:00:00 2001 From: Bad Date: Wed, 21 Oct 2020 19:22:23 +0200 Subject: [PATCH 14/92] Make font feedback red on error --- src/js/login.js | 14 +++++++------- src/sass/login.sass | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index de32932..c49ad98 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -50,12 +50,12 @@ class Feedback extends ElemJS { this.loading = state } - message(content) { - if (content) { - this.class("form-feedback") - } else { - this.removeClass("form-feedback") - } + message(content, isError) { + this.removeClass("form-feedback") + this.removeClass("form-error") + if (content) this.class("form-feedback") + if(isError) this.class("form-error") + this.messageSpan.text(content) } } @@ -141,7 +141,7 @@ class Form extends ElemJS { cancel(message) { this.processing = false feedback.setLoading(false) - feedback.message(message) + feedback.message(message, true) } } diff --git a/src/sass/login.sass b/src/sass/login.sass index e4820ee..235fad4 100644 --- a/src/sass/login.sass +++ b/src/sass/login.sass @@ -38,6 +38,9 @@ width: 100% margin: -0.5em 0 -0.8em +.form-error + color: red + @keyframes spin 0% transform: rotate(0deg) From c8351e8f0c52ea9bb43e7975920ecf036798b8d3 Mon Sep 17 00:00:00 2001 From: Bad Date: Wed, 21 Oct 2020 19:43:21 +0200 Subject: [PATCH 15/92] Warn when assuming https --- src/js/login.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/login.js b/src/js/login.js index c49ad98..33d2006 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -80,7 +80,10 @@ class Form extends ElemJS { let currentAddress = homeserver.value let ok = false while (!ok) { - if (!currentAddress.match(/^https?:\/\//)) currentAddress = "https://" + currentAddress + if (!currentAddress.match(/^https?:\/\//)) { + console.warn(`${currentAddress} doesn't specify the protocol, assuming https`) + currentAddress = "https://" + currentAddress + } currentAddress = currentAddress.replace(/\/*$/, "") this.status(`Looking up homeserver... trying ${currentAddress}`) try { From f1b75f5e10b48b1f6fbf801cacf2bfe6a38c2f49 Mon Sep 17 00:00:00 2001 From: Bad Date: Wed, 21 Oct 2020 20:03:34 +0200 Subject: [PATCH 16/92] Fix login redirecting to an incorrect address --- src/js/login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/login.js b/src/js/login.js index 33d2006..55caa66 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -133,7 +133,7 @@ class Form extends ElemJS { localStorage.setItem("domain", currentAddress) localStorage.setItem("access_token", root.access_token) - location.assign("./") + location.assign("../") } status(message) { From 6f67ddbce57d6c80263d1a5d6741e06ddbeed6c1 Mon Sep 17 00:00:00 2001 From: Bad Date: Wed, 21 Oct 2020 21:11:45 +0200 Subject: [PATCH 17/92] Refactor homeserver lookup code --- src/js/login.js | 82 ++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index 55caa66..c285898 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -71,46 +71,23 @@ class Form extends ElemJS { this.on("submit", this.submit.bind(this)) } + async submit() { if (this.processing) return this.processing = true if (!username.isValid()) return this.cancel("Username is not valid.") // Resolve homeserver address - let currentAddress = homeserver.value - let ok = false - while (!ok) { - if (!currentAddress.match(/^https?:\/\//)) { - console.warn(`${currentAddress} doesn't specify the protocol, assuming https`) - currentAddress = "https://" + currentAddress - } - currentAddress = currentAddress.replace(/\/*$/, "") - this.status(`Looking up homeserver... trying ${currentAddress}`) - try { - // check if we found the actual matrix server - try { - const versions = await fetch(`${currentAddress}/_matrix/client/versions`).then(res => res.json()) - if (Array.isArray(versions.versions)) { - ok = true - break - } - } catch (e) {} - // find the next matrix server in the chain - const root = await fetch(`${currentAddress}/.well-known/matrix/client`).then(res => res.json()) - let nextAddress = root["m.homeserver"].base_url - nextAddress = nextAddress.replace(/\/*$/, "") - if (currentAddress === nextAddress) { - ok = true - } - currentAddress = nextAddress - } catch (e) { - return this.cancel(`Failed to look up server ${currentAddress}`) - } + let domain + try { + domain = await this.findHomeserver(homeserver.value) + } catch(e) { + return this.cancel(e.message) } // Request access token this.status("Logging in...") - const root = await fetch(`${currentAddress}/_matrix/client/r0/login`, { + const root = await fetch(`${domain}/_matrix/client/r0/login`, { method: "POST", body: JSON.stringify({ type: "m.login.password", @@ -130,12 +107,52 @@ class Form extends ElemJS { } localStorage.setItem("mx_user_id", root.user_id) - localStorage.setItem("domain", currentAddress) + localStorage.setItem("domain", domain) localStorage.setItem("access_token", root.access_token) location.assign("../") } + async findHomeserver(address, maxDepth = 5) { + + //Protects us from servers sending us on a redirect loop + maxDepth-- + if(maxDepth<=0) throw new Error(`Failed to look up homeserver, maximum search depth reached`) + + if (!address.match(/^https?:\/\//)) { + console.warn(`${address} doesn't specify the protocol, assuming https`) + address = "https://" + address + } + + address = address.replace(/\/*$/, "") + this.status(`Looking up homeserver... trying ${address}`) + + // check if we found the actual matrix server + const versionsReq = await fetch(`${address}/_matrix/client/versions`).catch(()=>{}); + if(versionsReq?.ok) { + const versions = await versionsReq.json().catch(()=>{}) + if (Array.isArray(versions.versions)) { + return address + } + } + + // find the next matrix server in the chain + const root = await fetch(`${address}/.well-known/matrix/client`).then(res => res.json()).catch(e => { + console.error(e) + throw new Error(`Failed to look up server ${address}`) + }) + + let nextAddress = root["m.homeserver"].base_url + nextAddress = nextAddress.replace(/\/*$/, "") + + if (address === nextAddress) { + throw new Error(`Failed to look up server ${address}, /.well-known/matrix/client found a redirect loop`); + } + + return this.findHomeserver(nextAddress, maxDepth) + } + + status(message) { feedback.setLoading(true) feedback.message(message) @@ -149,3 +166,6 @@ class Form extends ElemJS { } const form = new Form() + + + From 0c3c06bc0af1ab93232e7cdd02751181fbcd7765 Mon Sep 17 00:00:00 2001 From: Bad Date: Wed, 21 Oct 2020 21:16:45 +0200 Subject: [PATCH 18/92] oops --- src/js/login.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index c285898..52ec151 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -128,13 +128,15 @@ class Form extends ElemJS { this.status(`Looking up homeserver... trying ${address}`) // check if we found the actual matrix server - const versionsReq = await fetch(`${address}/_matrix/client/versions`).catch(()=>{}); - if(versionsReq?.ok) { - const versions = await versionsReq.json().catch(()=>{}) - if (Array.isArray(versions.versions)) { - return address + try { + const versionsReq = await fetch(`${address}/_matrix/client/versions`) + if(versionsReq.ok) { + const versions = await versionsReq.json().catch(()=>{}) + if (Array.isArray(versions.versions)) { + return address + } } - } + } catch(e) {} // find the next matrix server in the chain const root = await fetch(`${address}/.well-known/matrix/client`).then(res => res.json()).catch(e => { From 61cc4a19f3a420c93ec3d411b776fbc4ae331c65 Mon Sep 17 00:00:00 2001 From: Bad Date: Wed, 21 Oct 2020 21:19:17 +0200 Subject: [PATCH 19/92] Small cosmetic fixes --- src/js/login.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index 52ec151..725ef4e 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -119,19 +119,20 @@ class Form extends ElemJS { maxDepth-- if(maxDepth<=0) throw new Error(`Failed to look up homeserver, maximum search depth reached`) + //Preprocess the address if (!address.match(/^https?:\/\//)) { console.warn(`${address} doesn't specify the protocol, assuming https`) address = "https://" + address } - address = address.replace(/\/*$/, "") + this.status(`Looking up homeserver... trying ${address}`) - // check if we found the actual matrix server + // Check if we found the actual matrix server try { const versionsReq = await fetch(`${address}/_matrix/client/versions`) if(versionsReq.ok) { - const versions = await versionsReq.json().catch(()=>{}) + const versions = await versionsReq.json() if (Array.isArray(versions.versions)) { return address } From 9e71336c5be03b262f69f26086e2a4f3d406d670 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 22 Oct 2020 17:30:11 +1300 Subject: [PATCH 20/92] Update initial login redirect --- src/js/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/main.js b/src/js/main.js index 98dd38b..5fc0fe6 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -5,5 +5,5 @@ import $to_relative "/js/sync/sync.js" import $to_relative "/js/chat.js" if (!localStorage.getItem("access_token")) { - location.assign("./login") + location.assign("./login/") } From 2ff43ea801822b914117710751d90d808e26e1fd Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 22 Oct 2020 17:35:48 +1300 Subject: [PATCH 21/92] Style cleanup --- src/js/login.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index 725ef4e..3bf23d0 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -71,7 +71,6 @@ class Form extends ElemJS { this.on("submit", this.submit.bind(this)) } - async submit() { if (this.processing) return this.processing = true @@ -114,32 +113,31 @@ class Form extends ElemJS { } async findHomeserver(address, maxDepth = 5) { - - //Protects us from servers sending us on a redirect loop + // Protects us from servers sending us on a redirect loop maxDepth-- - if(maxDepth<=0) throw new Error(`Failed to look up homeserver, maximum search depth reached`) - - //Preprocess the address + if (maxDepth <= 0) throw new Error(`Failed to look up homeserver, maximum search depth reached`) + + // Normalise the address if (!address.match(/^https?:\/\//)) { console.warn(`${address} doesn't specify the protocol, assuming https`) address = "https://" + address } address = address.replace(/\/*$/, "") - + this.status(`Looking up homeserver... trying ${address}`) - + // Check if we found the actual matrix server try { const versionsReq = await fetch(`${address}/_matrix/client/versions`) - if(versionsReq.ok) { + if (versionsReq.ok) { const versions = await versionsReq.json() if (Array.isArray(versions.versions)) { return address } } } catch(e) {} - - // find the next matrix server in the chain + + // Find the next matrix server in the chain const root = await fetch(`${address}/.well-known/matrix/client`).then(res => res.json()).catch(e => { console.error(e) throw new Error(`Failed to look up server ${address}`) @@ -155,7 +153,6 @@ class Form extends ElemJS { return this.findHomeserver(nextAddress, maxDepth) } - status(message) { feedback.setLoading(true) feedback.message(message) @@ -169,6 +166,3 @@ class Form extends ElemJS { } const form = new Form() - - - From 1b97351ca063a332fd92f93fff64da5253c2994e Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 22 Oct 2020 07:46:32 +0200 Subject: [PATCH 22/92] Switch to using .catch(()=>{}) instead of try catch --- src/js/login.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index 725ef4e..83353cd 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -129,15 +129,15 @@ class Form extends ElemJS { this.status(`Looking up homeserver... trying ${address}`) // Check if we found the actual matrix server - try { - const versionsReq = await fetch(`${address}/_matrix/client/versions`) - if(versionsReq.ok) { - const versions = await versionsReq.json() - if (Array.isArray(versions.versions)) { - return address - } - } - } catch(e) {} + const versionsReq = await fetch(`${address}/_matrix/client/versions`).catch(()=>{}) + /* jshint ignore:start */ + //JsHint doesn't support optional chaining + // https://github.com/jshint/jshint/issues/3448 + if(versionsReq?.ok) { + const versions = await versionsReq.json().catch(()=>{}) + if (Array.isArray(versions?.versions)) return address + } + /* jshint ignore:end */ // find the next matrix server in the chain const root = await fetch(`${address}/.well-known/matrix/client`).then(res => res.json()).catch(e => { From 16de7edd19d864045b5af9f2e5195edd86ad7bfd Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 22 Oct 2020 09:14:58 +0200 Subject: [PATCH 23/92] Style fixes --- src/js/login.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index 83353cd..68d4683 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -71,7 +71,6 @@ class Form extends ElemJS { this.on("submit", this.submit.bind(this)) } - async submit() { if (this.processing) return this.processing = true @@ -114,12 +113,12 @@ class Form extends ElemJS { } async findHomeserver(address, maxDepth = 5) { - - //Protects us from servers sending us on a redirect loop + + //Protects from servers sending us on a redirect loop maxDepth-- - if(maxDepth<=0) throw new Error(`Failed to look up homeserver, maximum search depth reached`) + if (maxDepth <= 0) throw new Error(`Failed to look up homeserver, maximum search depth reached`) - //Preprocess the address + //Normalise the address if (!address.match(/^https?:\/\//)) { console.warn(`${address} doesn't specify the protocol, assuming https`) address = "https://" + address @@ -133,13 +132,13 @@ class Form extends ElemJS { /* jshint ignore:start */ //JsHint doesn't support optional chaining // https://github.com/jshint/jshint/issues/3448 - if(versionsReq?.ok) { + if (versionsReq?.ok) { const versions = await versionsReq.json().catch(()=>{}) if (Array.isArray(versions?.versions)) return address } /* jshint ignore:end */ - // find the next matrix server in the chain + // Find the next matrix server in the chain const root = await fetch(`${address}/.well-known/matrix/client`).then(res => res.json()).catch(e => { console.error(e) throw new Error(`Failed to look up server ${address}`) @@ -155,7 +154,6 @@ class Form extends ElemJS { return this.findHomeserver(nextAddress, maxDepth) } - status(message) { feedback.setLoading(true) feedback.message(message) @@ -169,6 +167,3 @@ class Form extends ElemJS { } const form = new Form() - - - From e18c8c77aef6a5ac36b2a9c22daa8f3fb8efb7cb Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 22 Oct 2020 20:56:27 +1300 Subject: [PATCH 24/92] Fallback to room name "empty room" --- src/js/room-picker.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/room-picker.js b/src/js/room-picker.js index d2e79fa..5a5340c 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -106,8 +106,12 @@ class Room extends ElemJS { } // if the room has no alias, use the names of its members ("heroes") const users = this.data.summary["m.heroes"] - const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u) - return usernames.join(", ") + if (users && users.length) { + const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u) + return usernames.join(", ") + } + // the room is empty + return "Empty room" } getIcon() { From 6499cd4ff6ee7e7d1af0a4af8bbd8ce2f08771f1 Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Thu, 22 Oct 2020 11:46:46 +0200 Subject: [PATCH 25/92] add better .gitignore --- .gitignore | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 292 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 5cac20b..75e7d32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,293 @@ -# Generated files -node_modules -build +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig -# Editor artifacts -.vscode -*~ -\#*# -.#* -._* +# Created by https://www.toptal.com/developers/gitignore/api/node,vscode,webstorm,webstorm+all +# Edit at https://www.toptal.com/developers/gitignore?templates=node,vscode,webstorm,webstorm+all + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +### vscode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### WebStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### WebStorm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### WebStorm+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +# End of https://www.toptal.com/developers/gitignore/api/node,vscode,webstorm,webstorm+all + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + +/build/ From 36f204624f3aebc221813c220ca8e82af8556647 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 22 Oct 2020 23:02:50 +1300 Subject: [PATCH 26/92] Refuse to send empty messages --- src/js/chat-input.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/chat-input.js b/src/js/chat-input.js index de0eec7..9b703c1 100644 --- a/src/js/chat-input.js +++ b/src/js/chat-input.js @@ -33,5 +33,6 @@ function fixHeight() { function send(body) { if (!store.activeRoom.exists()) return + if (!body.trim().length) return return store.activeRoom.value().timeline.send(body) } From 2f5955b0431b9ca3a87accdf1ee0722c028a8107 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 22 Oct 2020 23:03:26 +1300 Subject: [PATCH 27/92] Be able to load past room state (no UI yet) --- src/js/Timeline.js | 80 ++++++++++++++++++++++++++++++++++++--------- src/js/sync/sync.js | 1 + 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/js/Timeline.js b/src/js/Timeline.js index 3e2a0b6..f4e6d09 100644 --- a/src/js/Timeline.js +++ b/src/js/Timeline.js @@ -5,6 +5,10 @@ import {Anchor} from $to_relative "/js/Anchor.js" import * as lsm from $to_relative "/js/lsm.js" import {resolveMxc} from $to_relative "/js/functions.js" +let debug = false + +const NO_MAX = Symbol("NO_MAX") + const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", day: "numeric", month: "short", year: "numeric"}) let sentIndex = 0 @@ -13,10 +17,10 @@ function getTxnId() { return Date.now() + (sentIndex++) } -function eventSearch(list, event, min = 0, max = -1) { +function eventSearch(list, event, min = 0, max = NO_MAX) { if (list.length === 0) return {success: false, i: 0} - if (max === -1) max = list.length - 1 + if (max === NO_MAX) max = list.length - 1 let mid = Math.floor((max + min) / 2) // success condition if (list[mid] && list[mid].data.event_id === event.data.event_id) return {success: true, i: mid} @@ -78,8 +82,12 @@ class Event extends ElemJS { } else if (this.data.type === "m.room.member") { if (this.data.content.membership === "join") { this.child(ejs("i").text("joined the room")) - } else { + } else if (this.data.content.membership === "invite") { + this.child(ejs("i").text(`invited ${this.data.content.displayname} to the room`)) + } else if (this.data.content.membership === "leave") { this.child(ejs("i").text("left the room")) + } else { + this.child(ejs("i").text("unknown membership event")) } } else if (this.data.type === "m.room.encrypted") { this.child(ejs("i").text("Carbon does not yet support encrypted messages.")) @@ -178,10 +186,21 @@ class ReactiveTimeline extends ElemJS { } addEvent(event) { + // if (debug) console.log("running search", this.list, event) + // if (debug) debugger; const search = eventSearch(this.list, event) // console.log(search, this.list.map(l => l.data.sender), event.data) - if (!search.success && search.i >= 1) this.tryAddGroups(event, [search.i-1, search.i]) - else this.tryAddGroups(event, [search.i]) + if (!search.success) { + if (search.i >= 1) { + // add at end + this.tryAddGroups(event, [search.i-1, search.i]) + } else { + // add at start + this.tryAddGroups(event, [0, -1]) + } + } else { + this.tryAddGroups(event, [search.i]) + } } tryAddGroups(event, indices) { @@ -189,6 +208,10 @@ class ReactiveTimeline extends ElemJS { if (!this.list[i]) { // if (printed++ < 100) console.log("tryadd success, created group") const group = new EventGroup(this, [event]) + if (i === -1) { + // here, -1 means at the start, before the first group + i = 0 // jank but it does the trick + } this.list.splice(i, 0, group) this.childAt(i, group) event.setGroup(group) @@ -234,6 +257,8 @@ class Timeline extends Subscribable { this.reactiveTimeline = new ReactiveTimeline(this.id, []) this.latest = 0 this.pending = new Set() + this.pendingEdits = [] + this.from = null } updateStateEvents(events) { @@ -282,17 +307,8 @@ class Timeline extends Subscribable { } // handle edits if (eventData.type === "m.room.message" && eventData.content["m.relates_to"] && eventData.content["m.relates_to"].rel_type === "m.replace") { - const replaces = eventData.content["m.relates_to"].event_id - if (this.map.has(replaces)) { - const event = this.map.get(replaces) - event.data.content = eventData.content["m.new_content"] - event.setEdited(eventData.origin_server_ts) - event.update(event.data) - continue - } else { - // uhhhhhhh - console.error(`want to replace event ${replaces} with ${eventData.id} but replaced event not found`) - } + this.pendingEdits.push(eventData) + continue } // add new event const event = new Event(eventData) @@ -300,6 +316,19 @@ class Timeline extends Subscribable { this.reactiveTimeline.addEvent(event) } } + // apply edits + this.pendingEdits = this.pendingEdits.filter(eventData => { + const replaces = eventData.content["m.relates_to"].event_id + if (this.map.has(replaces)) { + const event = this.map.get(replaces) + event.data.content = eventData.content["m.new_content"] + event.setEdited(eventData.origin_server_ts) + event.update(event.data) + return false // handled; remove from list + } else { + return true // we don't have the event it edits yet; keep in list + } + }) this.broadcast("afterChange") } @@ -313,6 +342,25 @@ class Timeline extends Subscribable { return this.reactiveTimeline } + async loadScrollback() { + debug = true + if (!this.from) throw new Error("Can't load scrollback, no from token") + const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/rooms/${this.id}/messages`) + url.searchParams.set("access_token", lsm.get("access_token")) + url.searchParams.set("from", this.from) + url.searchParams.set("dir", "b") + url.searchParams.set("limit", 10) + const filter = { + lazy_load_members: true + } + url.searchParams.set("filter", JSON.stringify(filter)) + const root = await fetch(url.toString()).then(res => res.json()) + this.from = root.end + console.log(this.updateEvents, root.chunk) + if (root.state) this.updateStateEvents(root.state) + this.updateEvents(root.chunk) + } + send(body) { const tx = getTxnId() const id = `pending$${tx}` diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js index f338e4d..195af6e 100644 --- a/src/js/sync/sync.js +++ b/src/js/sync/sync.js @@ -53,6 +53,7 @@ function manageSync(root) { } const room = store.rooms.get(id).value() const timeline = room.timeline + if (!timeline.from) timeline.from = data.timeline.prev_batch if (data.timeline.events.length) newEvents = true timeline.updateStateEvents(data.state.events) timeline.updateEvents(data.timeline.events) From aa12cd68e67a2ecbf0ef591abdbff152242b08de Mon Sep 17 00:00:00 2001 From: Bad Date: Fri, 23 Oct 2020 10:51:04 +0200 Subject: [PATCH 28/92] Clean up code --- src/js/login.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/js/login.js b/src/js/login.js index 68d4683..4754bef 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -128,15 +128,13 @@ class Form extends ElemJS { this.status(`Looking up homeserver... trying ${address}`) // Check if we found the actual matrix server - const versionsReq = await fetch(`${address}/_matrix/client/versions`).catch(()=>{}) - /* jshint ignore:start */ - //JsHint doesn't support optional chaining - // https://github.com/jshint/jshint/issues/3448 - if (versionsReq?.ok) { - const versions = await versionsReq.json().catch(()=>{}) - if (Array.isArray(versions?.versions)) return address - } - /* jshint ignore:end */ + try { + const versionsReq = await fetch(`${address}/_matrix/client/versions`) + if (versionsReq.ok) { + const versions = await versionsReq.json() + if (Array.isArray(versions.versions)) return address + } + } catch(e) {} // Find the next matrix server in the chain const root = await fetch(`${address}/.well-known/matrix/client`).then(res => res.json()).catch(e => { From 51905ab3f2ef5d54dd4d1d39243a69541181f3d7 Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Fri, 23 Oct 2020 16:15:14 +0200 Subject: [PATCH 29/92] add browserify --- build.js | 67 +- package-lock.json | 1604 ++++++++++++++++- package.json | 4 +- spec.js | 167 +- src/js/{Anchor.js => anchor.js} | 4 +- src/js/basic.js | 2 +- src/js/chat-input.js | 8 +- src/js/chat.js | 6 +- src/js/functions.js | 4 +- src/js/groups.js | 2 +- src/js/login.js | 2 +- src/js/lsm.js | 2 +- src/js/main.js | 10 +- src/js/room-picker.js | 14 +- src/js/store/store.js | 10 +- .../{Subscribable.js => subscribable.js} | 2 +- .../{SubscribeMap.js => subscribe_map.js} | 6 +- ...scribeMapList.js => subscribe_map_list.js} | 6 +- .../{SubscribeSet.js => subscribe_set.js} | 4 +- .../{SubscribeValue.js => subscribe_value.js} | 4 +- src/js/sync/sync.js | 6 +- src/js/{Timeline.js => timeline.js} | 14 +- 22 files changed, 1743 insertions(+), 205 deletions(-) rename src/js/{Anchor.js => anchor.js} (74%) rename src/js/store/{Subscribable.js => subscribable.js} (96%) rename src/js/store/{SubscribeMap.js => subscribe_map.js} (79%) rename src/js/store/{SubscribeMapList.js => subscribe_map_list.js} (90%) rename src/js/store/{SubscribeSet.js => subscribe_set.js} (88%) rename src/js/store/{SubscribeValue.js => subscribe_value.js} (88%) rename src/js/{Timeline.js => timeline.js} (97%) diff --git a/build.js b/build.js index 4932101..c6e0671 100644 --- a/build.js +++ b/build.js @@ -1,6 +1,6 @@ const pug = require("pug") const sass = require("sass") -const fs = require("fs").promises +const fs = require("fs") const os = require("os") const crypto = require("crypto") const path = require("path") @@ -9,6 +9,7 @@ const babel = require("@babel/core") const fetch = require("node-fetch") const chalk = require("chalk") const hint = require("jshint").JSHINT +const browserify = require('browserify') process.chdir(pj(__dirname, "src")) @@ -16,10 +17,10 @@ const buildDir = "../build" const validationQueue = [] const validationHost = os.hostname() === "future" ? "http://localhost:8888/" : "http://validator.w3.org/nu/" -const static = new Map() +const static_files = new Map() const links = new Map() const sources = new Map() -const pugLocals = {static, links} +const pugLocals = {static: static_files, links} const spec = require("./spec.js") @@ -125,26 +126,34 @@ function runHint(filename, source) { } async function addFile(sourcePath, targetPath) { - const contents = await fs.readFile(pj(".", sourcePath), {encoding: null}) - static.set(sourcePath, `${targetPath}?static=${hash(contents)}`) - fs.writeFile(pj(buildDir, targetPath), contents) + const contents = await fs.promises.readFile(pj(".", sourcePath), {encoding: null}); + static_files.set(sourcePath, `${targetPath}?static=${hash(contents)}`) + await fs.promises.writeFile(pj(buildDir, targetPath), contents) } async function loadJS(sourcePath, targetPath) { - let content = await fs.readFile(pj(".", sourcePath), {encoding: "utf8"}) - sources.set(sourcePath, content) - static.set(sourcePath, `${targetPath}?static=${hash(content)}`) + let content = await fs.promises.readFile(pj(".", sourcePath), {encoding: "utf8"}) + sources.set(sourcePath, content); + static_files.set(sourcePath, `${targetPath}?static=${hash(content)}`) } async function addJS(sourcePath, targetPath) { let content = sources.get(sourcePath) // resolve imports to hashed paths content = content.replace(/\$to_relative "([^"]+)"/g, function(_, file) { - if (!static.get(file)) throw new Error(`Tried to relative import ${file} from ${sourcePath}, but import not found`) - return '"' + getRelative(targetPath, static.get(file)) + '"' + if (!static_files.get(file)) throw new Error(`Tried to relative import ${file} from ${sourcePath}, but import not found`) + return '"' + getRelative(targetPath, static_files.get(file)) + '"' }) runHint(sourcePath, content) - fs.writeFile(pj(buildDir, targetPath), content) + await fs.promises.writeFile(pj(buildDir, targetPath), content) +} + +async function addBundle(sourcePath, targetPath) { + await browserify() + .add(pj(".", sourcePath)) + .bundle() + .pipe(fs.createWriteStream(pj(buildDir, targetPath))); + static_files.set(sourcePath, targetPath) } async function addSass(sourcePath, targetPath) { @@ -158,7 +167,7 @@ async function addSass(sourcePath, targetPath) { if (!(name instanceof sass.types.String)) { throw "$name: expected a string" } - const result = getRelative(targetPath, static.get(name.getValue())) + const result = getRelative(targetPath, static_files.get(name.getValue())) if (typeof result === "string") { return new sass.types.String(result) } else { @@ -166,10 +175,10 @@ async function addSass(sourcePath, targetPath) { } } } - }).css - static.set(sourcePath, `${targetPath}?static=${hash(renderedCSS)}`) - validate(sourcePath, renderedCSS, "css") - await fs.writeFile(pj(buildDir, targetPath), renderedCSS) + }).css; + static_files.set(sourcePath, `${targetPath}?static=${hash(renderedCSS)}`) + await validate(sourcePath, renderedCSS, "css") + await fs.promises.writeFile(pj(buildDir, targetPath), renderedCSS) } async function addPug(sourcePath, targetPath) { @@ -177,22 +186,22 @@ async function addPug(sourcePath, targetPath) { return getRelative(targetPath, staticTarget) } function getStatic(target) { - return getRelativeHere(static.get(target)) + return getRelativeHere(static_files.get(target)) } function getStaticName(target) { - return getRelativeHere(static.get(target)).replace(/\?.*$/, "") + return getRelativeHere(static_files.get(target)).replace(/\?.*$/, "") } function getLink(target) { return getRelativeHere(links.get(target)) } const renderedHTML = pug.compileFile(pj(".", sourcePath), {pretty: true})({getStatic, getStaticName, getLink, ...pugLocals}) let renderedWithoutPHP = renderedHTML.replace(/<\?(?:php|=).*?\?>/gsm, "") - validate(sourcePath, renderedWithoutPHP, "html") - await fs.writeFile(pj(buildDir, targetPath), renderedHTML) + await validate(sourcePath, renderedWithoutPHP, "html") + await fs.promises.writeFile(pj(buildDir, targetPath), renderedHTML) } async function addBabel(sourcePath, targetPath) { - const originalCode = await fs.readFile(pj(".", sourcePath), "utf8") + const originalCode = await fs.promises.readFile(pj(".", sourcePath), "utf8") const compiled = babel.transformSync(originalCode, { sourceMaps: false, @@ -213,14 +222,14 @@ async function addBabel(sourcePath, targetPath) { } }) - const filenameWithQuery = `${targetPath}?static=${hash(compiled.code)}` + const filenameWithQuery = `${targetPath}?static=${hash(compiled.code)}`; - static.set(sourcePath, filenameWithQuery) + static_files.set(sourcePath, filenameWithQuery) await Promise.all([ - fs.writeFile(pj(buildDir, targetPath), originalCode), - fs.writeFile(pj(buildDir, minFilename), compiled.code), - fs.writeFile(pj(buildDir, mapFilename), JSON.stringify(compiled.map)) + fs.promises.writeFile(pj(buildDir, targetPath), originalCode), + fs.promises.writeFile(pj(buildDir, minFilename), compiled.code), + fs.promises.writeFile(pj(buildDir, mapFilename), JSON.stringify(compiled.map)) ]) } @@ -241,7 +250,7 @@ async function addBabel(sourcePath, targetPath) { // Stage 3: Create dirs const dirs = [...new Set(spec.map(item => path.dirname(item.target))).values()] - await Promise.all(dirs.map(d => fs.mkdir(pj(buildDir, d), {recursive: true}))) + await Promise.all(dirs.map(d => fs.promises.mkdir(pj(buildDir, d), {recursive: true}))) // Stage 4: Build for (const item of spec) { @@ -255,6 +264,8 @@ async function addBabel(sourcePath, targetPath) { await addBabel(item.source, item.target) } else if (item.type === "pug") { await addPug(item.source, item.target) + } else if (item.type === "bundle") { + await addBundle(item.source, item.target) } else { throw new Error("Unknown item type: "+item.type) } diff --git a/package-lock.json b/package-lock.json index 94cc52b..228e7ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1101,11 +1101,34 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "acorn": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, "ansi-styles": { "version": "4.2.1", @@ -1127,12 +1150,59 @@ "picomatch": "^2.0.4" } }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", "dev": true }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, "assert-never": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", @@ -1148,6 +1218,14 @@ "lodash": "^4.17.14" } }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "requires": { + "array-filter": "^1.0.0" + } + }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -1169,8 +1247,12 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "basic-auth": { "version": "1.1.0", @@ -1184,11 +1266,15 @@ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1203,6 +1289,232 @@ "fill-range": "^7.0.1" } }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "requires": { + "JSONStream": "^1.0.3", + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", + "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "requires": { + "resolve": "^1.17.0" + } + }, + "browserify": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", + "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "requires": { + "JSONStream": "^1.0.3", + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^2.0.0", + "browserify-zlib": "~0.2.0", + "buffer": "~5.2.1", + "cached-path-relative": "^1.0.0", + "concat-stream": "^1.6.0", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.1", + "domain-browser": "^1.2.0", + "duplexer2": "~0.1.2", + "events": "^3.0.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "^1.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.2.1", + "labeled-stream-splicer": "^2.0.0", + "mkdirp-classic": "^0.5.2", + "module-deps": "^6.2.3", + "os-browserify": "~0.3.0", + "parents": "^1.0.1", + "path-browserify": "^1.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum-object": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^3.0.0", + "stream-http": "^3.0.0", + "string_decoder": "^1.1.1", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "0.0.1", + "url": "~0.11.0", + "util": "~0.12.0", + "vm-browserify": "^1.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, "browserslist": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", @@ -1215,6 +1527,35 @@ "node-releases": "^1.1.60" } }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==" + }, "caniuse-lite": { "version": "1.0.30001112", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001112.tgz", @@ -1256,6 +1597,15 @@ "readdirp": "~3.4.0" } }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "cli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", @@ -1287,17 +1637,73 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + }, + "dependencies": { + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=" + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } }, "console-browserify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, "requires": { "date-now": "^0.1.4" } @@ -1312,6 +1718,11 @@ "@babel/types": "^7.6.1" } }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -1342,8 +1753,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "corser": { "version": "2.0.1", @@ -1351,11 +1761,74 @@ "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", "dev": true }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==" + }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" }, "debug": { "version": "3.2.6", @@ -1370,11 +1843,62 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "deps-sort": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", + "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "requires": { + "JSONStream": "^1.0.3", + "shasum-object": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", @@ -1405,6 +1929,11 @@ } } }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", @@ -1430,6 +1959,43 @@ "domelementtype": "1" } }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "ecstatic": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", @@ -1448,12 +2014,100 @@ "integrity": "sha512-ZUvklIBkfXQyA6IeiEss1nfKRICcdB5afAGZAaPGaExdfrkpUu/WWVO+X7QpNnphaVMllXnAcvKnVPdyM+DCPQ==", "dev": true }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, "entities": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escalade": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", @@ -1478,12 +2132,31 @@ "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", "dev": true }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1499,11 +2172,15 @@ "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", "dev": true }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.1.3", @@ -1515,8 +2192,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gensync": { "version": "1.0.0-beta.1", @@ -1524,11 +2200,15 @@ "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", "dev": true }, + "get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==" + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1553,6 +2233,14 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1562,8 +2250,51 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } }, "he": { "version": "1.2.0", @@ -1571,6 +2302,21 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=" + }, "htmlparser2": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", @@ -1613,11 +2359,20 @@ "union": "~0.5.0" } }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -1626,8 +2381,32 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "requires": { + "source-map": "~0.5.3" + } + }, + "insert-module-globals": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", + "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "requires": { + "JSONStream": "^1.0.3", + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + } }, "invariant": { "version": "2.2.4", @@ -1638,6 +2417,11 @@ "loose-envify": "^1.0.0" } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1647,6 +2431,21 @@ "binary-extensions": "^2.0.0" } }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, "is-expression": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", @@ -1663,6 +2462,11 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-generator-function": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==" + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -1672,6 +2476,11 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1693,6 +2502,25 @@ "has-symbols": "^1.0.1" } }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "requires": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -1742,6 +2570,11 @@ "minimist": "^1.2.5" } }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -1752,6 +2585,15 @@ "promise": "^7.0.1" } }, + "labeled-stream-splicer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", + "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "requires": { + "inherits": "^2.0.1", + "stream-splicer": "^2.0.0" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -1773,6 +2615,11 @@ "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -1782,17 +2629,52 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1800,8 +2682,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mkdirp": { "version": "0.5.5", @@ -1812,6 +2693,62 @@ "minimist": "^1.2.5" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "module-deps": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", + "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "requires": { + "JSONStream": "^1.0.3", + "browser-resolve": "^2.0.0", + "cached-path-relative": "^1.0.2", + "concat-stream": "~1.6.0", + "defined": "^1.0.0", + "detective": "^5.2.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.4.0", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1839,14 +2776,17 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { "version": "4.1.0", @@ -1864,7 +2804,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -1875,17 +2814,67 @@ "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", "dev": true }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "requires": { + "path-platform": "~0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=" + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } }, "picomatch": { "version": "2.2.2", @@ -1904,6 +2893,16 @@ "mkdirp": "^0.5.5" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -1913,6 +2912,26 @@ "asap": "~2.0.3" } }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, "pug": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.0.tgz", @@ -2037,12 +3056,81 @@ "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", "dev": true }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, "qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -2141,16 +3229,28 @@ "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { "version": "1.26.10", @@ -2173,17 +3273,287 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shasum-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", + "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "requires": { + "fast-safe-stringify": "^2.0.7" + } + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, "shelljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-http": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", + "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, + "stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", + "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } }, "string_decoder": { "version": "0.10.31", @@ -2197,6 +3567,14 @@ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", "dev": true }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "requires": { + "minimist": "^1.1.0" + } + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -2206,6 +3584,65 @@ "has-flag": "^4.0.0" } }, + "syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "requires": { + "acorn-node": "^1.2.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "requires": { + "process": "~0.11.0" + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2227,6 +3664,33 @@ "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=", "dev": true }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==" + }, + "undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "requires": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -2264,18 +3728,70 @@ "qs": "^6.4.0" } }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, "url-join": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true }, + "util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, "void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=", "dev": true }, + "which-typed-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", + "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "requires": { + "available-typed-arrays": "^1.0.2", + "es-abstract": "^1.17.5", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, "with": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", @@ -2291,8 +3807,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" } } } diff --git a/package.json b/package.json index 454c3cb..8f2ead4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "keywords": [], "author": "", "license": "AGPL-3.0-only", - "dependencies": {}, + "dependencies": { + "browserify": "^17.0.0" + }, "devDependencies": { "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", diff --git a/spec.js b/spec.js index 450f7cc..d525487 100644 --- a/spec.js +++ b/spec.js @@ -10,90 +10,95 @@ module.exports = [ target: "/static/whitney-400.woff", }, { - type: "js", - source: "/js/main.js", - target: "/static/main.js", - }, - { - type: "js", - source: "/js/basic.js", - target: "/static/basic.js", - }, - { - type: "js", - source: "/js/groups.js", - target: "/static/groups.js", - }, - { - type: "js", - source: "/js/chat-input.js", - target: "/static/chat-input.js", - }, - { - type: "js", - source: "/js/room-picker.js", - target: "/static/room-picker.js", - }, - { - type: "js", - source: "/js/store/store.js", - target: "/static/store/store.js", - }, - { - type: "js", - source: "/js/store/Subscribable.js", - target: "/static/store/Subscribable.js", - }, - { - type: "js", - source: "/js/store/SubscribeValue.js", - target: "/static/store/SubscribeValue.js", - }, - { - type: "js", - source: "/js/store/SubscribeMapList.js", - target: "/static/store/SubscribeMapList.js", - }, - { - type: "js", - source: "/js/store/SubscribeSet.js", - target: "/static/store/SubscribeSet.js", - }, - { - type: "js", - source: "/js/sync/sync.js", - target: "/static/sync/sync.js", - }, - { - type: "js", - source: "/js/lsm.js", - target: "/static/lsm.js", - }, - { - type: "js", - source: "/js/Timeline.js", - target: "/static/Timeline.js", - }, - { - type: "js", - source: "/js/Anchor.js", - target: "/static/Anchor.js", - }, - { - type: "js", - source: "/js/chat.js", - target: "/static/chat.js", - }, - { - type: "js", - source: "/js/functions.js", - target: "/static/functions.js", - }, - { - type: "js", + type: "bundle", source: "/js/login.js", target: "/static/login.js", }, + { + type: "bundle", + source: "/js/main.js", + target: "/static/bundle.js" + }, + // { + // type: "js", + // source: "/js/main.js", + // target: "/static/main.js", + // }, + // { + // type: "js", + // source: "/js/basic.js", + // target: "/static/basic.js", + // }, + // { + // type: "js", + // source: "/js/groups.js", + // target: "/static/groups.js", + // }, + // { + // type: "js", + // source: "/js/chat-input.js", + // target: "/static/chat-input.js", + // }, + // { + // type: "js", + // source: "/js/room-picker.js", + // target: "/static/room-picker.js", + // }, + // { + // type: "js", + // source: "/js/store/store.js", + // target: "/static/store/store.js", + // }, + // { + // type: "js", + // source: "/js/store/subscribable.js", + // target: "/static/store/subscribable.js", + // }, + // { + // type: "js", + // source: "/js/store/subscribe_value.js", + // target: "/static/store/subscribe_value.js", + // }, + // { + // type: "js", + // source: "/js/store/subscribe_map_list.js", + // target: "/static/store/subscribe_map_list.js", + // }, + // { + // type: "js", + // source: "/js/store/subscribe_set.js", + // target: "/static/store/subscribe_set.js", + // }, + // { + // type: "js", + // source: "/js/sync/sync.js", + // target: "/static/sync/sync.js", + // }, + // { + // type: "js", + // source: "/js/lsm.js", + // target: "/static/lsm.js", + // }, + // { + // type: "js", + // source: "/js/timeline.js", + // target: "/static/timeline.js", + // }, + // { + // type: "js", + // source: "/js/anchor.js", + // target: "/static/anchor.js", + // }, + // { + // type: "js", + // source: "/js/chat.js", + // target: "/static/chat.js", + // }, + // { + // type: "js", + // source: "/js/functions.js", + // target: "/static/functions.js", + // }, { type: "file", source: "/assets/fonts/whitney-500.woff", diff --git a/src/js/Anchor.js b/src/js/anchor.js similarity index 74% rename from src/js/Anchor.js rename to src/js/anchor.js index 2adbe5f..498a287 100644 --- a/src/js/Anchor.js +++ b/src/js/anchor.js @@ -1,4 +1,4 @@ -import {ElemJS} from $to_relative "/js/basic.js" +const {ElemJS} = require("./basic.js") class Anchor extends ElemJS { constructor() { @@ -12,4 +12,4 @@ class Anchor extends ElemJS { } } -export {Anchor} +module.exports = {Anchor} diff --git a/src/js/basic.js b/src/js/basic.js index 1f3e695..7108662 100644 --- a/src/js/basic.js +++ b/src/js/basic.js @@ -157,4 +157,4 @@ function ejs(tag) { return new ElemJS(tag); } -export {q, qa, ElemJS, ejs} +module.exports = {q, qa, ElemJS, ejs} diff --git a/src/js/chat-input.js b/src/js/chat-input.js index 9b703c1..9558746 100644 --- a/src/js/chat-input.js +++ b/src/js/chat-input.js @@ -1,7 +1,7 @@ -import {q} from $to_relative "/js/basic.js" -import {store} from $to_relative "/js/store/store.js" -import * as lsm from $to_relative "/js/lsm.js" -import {chat} from $to_relative "/js/chat.js" +const {q} = require("./basic.js") +const {store} = require("./store/store.js") +const lsm = require("./lsm.js") +const {chat} = require("./chat.js") const input = q("#c-chat-textarea") diff --git a/src/js/chat.js b/src/js/chat.js index e3df0eb..9f8035f 100644 --- a/src/js/chat.js +++ b/src/js/chat.js @@ -1,5 +1,5 @@ -import {ElemJS, q, ejs} from $to_relative "/js/basic.js" -import {store} from $to_relative "/js/store/store.js" +const {ElemJS, q, ejs} = require("./basic.js") +const {store} = require("./store/store.js") const chatMessages = q("#c-chat-messages") @@ -62,4 +62,4 @@ class Chat extends ElemJS { const chat = new Chat() -export {chat} +module.exports = {chat} diff --git a/src/js/functions.js b/src/js/functions.js index 299d8a9..bf94921 100644 --- a/src/js/functions.js +++ b/src/js/functions.js @@ -1,4 +1,4 @@ -import * as lsm from $to_relative "/js/lsm.js" +const lsm = require("./lsm.js") function resolveMxc(url, size, method) { const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1) @@ -9,4 +9,4 @@ function resolveMxc(url, size, method) { } } -export {resolveMxc} +module.exports = {resolveMxc} diff --git a/src/js/groups.js b/src/js/groups.js index e49ade4..38b6706 100644 --- a/src/js/groups.js +++ b/src/js/groups.js @@ -1,4 +1,4 @@ -import {q} from $to_relative "/js/basic.js" +const {q} = require("./basic.js") let state = "CLOSED" diff --git a/src/js/login.js b/src/js/login.js index 4754bef..4ae9eae 100644 --- a/src/js/login.js +++ b/src/js/login.js @@ -1,4 +1,4 @@ -import {q, ElemJS, ejs} from $to_relative "/js/basic.js" +const {q, ElemJS, ejs} = require("./basic.js") const password = q("#password") const homeserver = q("#homeserver") diff --git a/src/js/lsm.js b/src/js/lsm.js index 7338343..7e9ad4d 100644 --- a/src/js/lsm.js +++ b/src/js/lsm.js @@ -8,4 +8,4 @@ function set(name, value) { window.lsm = {get, set} -export {get, set} +module.exports = {get, set} diff --git a/src/js/main.js b/src/js/main.js index 5fc0fe6..a15bc7f 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,8 +1,8 @@ -import $to_relative "/js/groups.js" -import $to_relative "/js/chat-input.js" -import $to_relative "/js/room-picker.js" -import $to_relative "/js/sync/sync.js" -import $to_relative "/js/chat.js" +const groups = require("./groups.js") +const chat_input = require("./chat-input.js") +const room_picker = require("./room-picker.js") +const sync = require("./sync/sync.js") +const chat = require("./chat.js") if (!localStorage.getItem("access_token")) { location.assign("./login/") diff --git a/src/js/room-picker.js b/src/js/room-picker.js index 5a5340c..a4d7303 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -1,10 +1,10 @@ -import {q, ElemJS, ejs} from $to_relative "/js/basic.js" -import {store} from $to_relative "/js/store/store.js" -import {SubscribeMapList} from $to_relative "/js/store/SubscribeMapList.js" -import {SubscribeValue} from $to_relative "/js/store/SubscribeValue.js" -import {Timeline} from $to_relative "/js/Timeline.js" -import * as lsm from $to_relative "/js/lsm.js" -import {resolveMxc} from $to_relative "/js/functions.js" +const {q, ElemJS, ejs} = require("./basic.js") +const {store} = require("./store/store.js") +const {SubscribeMapList} = require("./store/subscribe_map_list.js") +const {SubscribeValue} = require("./store/subscribe_value.js") +const {Timeline} = require("./timeline.js") +const lsm = require("./lsm.js") +const {resolveMxc} = require("./functions.js") class ActiveGroupMarker extends ElemJS { constructor() { diff --git a/src/js/store/store.js b/src/js/store/store.js index 1c0552d..e11905f 100644 --- a/src/js/store/store.js +++ b/src/js/store/store.js @@ -1,7 +1,7 @@ -import {Subscribable} from $to_relative "/js/store/Subscribable.js" -import {SubscribeMapList} from $to_relative "/js/store/SubscribeMapList.js" -import {SubscribeSet} from $to_relative "/js/store/SubscribeSet.js" -import {SubscribeValue} from $to_relative "/js/store/SubscribeValue.js" +const {Subscribable} = require("./subscribable.js") +const {SubscribeMapList} = require("./subscribe_map_list.js") +const {SubscribeSet} = require("./subscribe_set.js") +const {SubscribeValue} = require("./subscribe_value.js") const store = { groups: new SubscribeMapList(SubscribeValue), @@ -14,4 +14,4 @@ const store = { window.store = store -export {store} +module.exports = {store} diff --git a/src/js/store/Subscribable.js b/src/js/store/subscribable.js similarity index 96% rename from src/js/store/Subscribable.js rename to src/js/store/subscribable.js index 6c7640e..e87bab2 100644 --- a/src/js/store/Subscribable.js +++ b/src/js/store/subscribable.js @@ -35,4 +35,4 @@ class Subscribable { } } -export {Subscribable} +module.exports = {Subscribable} diff --git a/src/js/store/SubscribeMap.js b/src/js/store/subscribe_map.js similarity index 79% rename from src/js/store/SubscribeMap.js rename to src/js/store/subscribe_map.js index 8b0dc0c..9ee3eac 100644 --- a/src/js/store/SubscribeMap.js +++ b/src/js/store/subscribe_map.js @@ -1,5 +1,5 @@ -import {Subscribable} from $to_relative "/js/store/Subscribable.js" -import {SubscribeValue} from $to_relative "/js/store/SubscribeValue.js" +const {Subscribable} = require("./subscribable.js") +const {SubscribeValue} = require("./subscribe_value.js") class SubscribeMap extends Subscribable { constructor() { @@ -38,4 +38,4 @@ class SubscribeMap extends Subscribable { } } -export {SubscribeMap} +module.exports = {SubscribeMap} diff --git a/src/js/store/SubscribeMapList.js b/src/js/store/subscribe_map_list.js similarity index 90% rename from src/js/store/SubscribeMapList.js rename to src/js/store/subscribe_map_list.js index 1883303..28a5ce2 100644 --- a/src/js/store/SubscribeMapList.js +++ b/src/js/store/subscribe_map_list.js @@ -1,5 +1,5 @@ -import {Subscribable} from $to_relative "/js/store/Subscribable.js" -import {SubscribeValue} from $to_relative "/js/store/SubscribeValue.js" +const {Subscribable} = require("./subscribable.js") +const {SubscribeValue} = require("./subscribe_value.js") class SubscribeMapList extends Subscribable { constructor(inner) { @@ -83,4 +83,4 @@ class SubscribeMapList extends Subscribable { } } -export {SubscribeMapList} +module.exports = {SubscribeMapList} diff --git a/src/js/store/SubscribeSet.js b/src/js/store/subscribe_set.js similarity index 88% rename from src/js/store/SubscribeSet.js rename to src/js/store/subscribe_set.js index 789aaaf..32c758c 100644 --- a/src/js/store/SubscribeSet.js +++ b/src/js/store/subscribe_set.js @@ -1,4 +1,4 @@ -import {Subscribable} from $to_relative "/js/store/Subscribable.js" +const {Subscribable} = require("./subscribable.js") class SubscribeSet extends Subscribable { constructor() { @@ -47,4 +47,4 @@ class SubscribeSet extends Subscribable { } } -export {SubscribeSet} +module.exports = {SubscribeSet} diff --git a/src/js/store/SubscribeValue.js b/src/js/store/subscribe_value.js similarity index 88% rename from src/js/store/SubscribeValue.js rename to src/js/store/subscribe_value.js index 6657e27..eaa2cdd 100644 --- a/src/js/store/SubscribeValue.js +++ b/src/js/store/subscribe_value.js @@ -1,4 +1,4 @@ -import {Subscribable} from $to_relative "/js/store/Subscribable.js" +const {Subscribable} = require("./subscribable.js") class SubscribeValue extends Subscribable { constructor() { @@ -44,4 +44,4 @@ class SubscribeValue extends Subscribable { } } -export {SubscribeValue} +module.exports = {SubscribeValue} diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js index 195af6e..c17b0e9 100644 --- a/src/js/sync/sync.js +++ b/src/js/sync/sync.js @@ -1,6 +1,6 @@ -import {store} from $to_relative "/js/store/store.js" -import * as lsm from $to_relative "/js/lsm.js" -import {resolveMxc} from $to_relative "/js/functions.js" +const {store} = require("../store/store.js") +const lsm = require("../lsm.js") +const {resolveMxc} = require("../functions.js") let lastBatch = null diff --git a/src/js/Timeline.js b/src/js/timeline.js similarity index 97% rename from src/js/Timeline.js rename to src/js/timeline.js index f4e6d09..0819ff8 100644 --- a/src/js/Timeline.js +++ b/src/js/timeline.js @@ -1,9 +1,9 @@ -import {ElemJS, ejs} from $to_relative "/js/basic.js" -import {Subscribable} from $to_relative "/js/store/Subscribable.js" -import {store} from $to_relative "/js/store/store.js" -import {Anchor} from $to_relative "/js/Anchor.js" -import * as lsm from $to_relative "/js/lsm.js" -import {resolveMxc} from $to_relative "/js/functions.js" +const {ElemJS, ejs} = require("./basic.js") +const {Subscribable} = require("./store/subscribable.js") +const {store} = require("./store/store.js") +const {Anchor} = require("./anchor.js") +const lsm = require("./lsm.js") +const {resolveMxc} = require("./functions.js") let debug = false @@ -413,4 +413,4 @@ class Timeline extends Subscribable { */ } -export {Timeline} +module.exports = {Timeline} From 6227f6fa84e1402bdd326024cc9dc376d0156a8b Mon Sep 17 00:00:00 2001 From: BadAtNames Date: Sat, 24 Oct 2020 20:56:03 +0200 Subject: [PATCH 30/92] Add scrollback --- src/js/chat.js | 25 +++++++++++++--- src/js/timeline.js | 70 +++++++++++++++++++++++++++++++------------ src/sass/loading.sass | 13 ++++++++ src/sass/login.sass | 15 ++-------- src/sass/main.sass | 1 + 5 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 src/sass/loading.sass diff --git a/src/js/chat.js b/src/js/chat.js index 9f8035f..050ff8b 100644 --- a/src/js/chat.js +++ b/src/js/chat.js @@ -27,7 +27,7 @@ class Chat extends ElemJS { // connect to the new room's timeline updater if (store.activeRoom.exists()) { const timeline = store.activeRoom.value().timeline - const subscription = () => { + const beforeChangeSubscription = () => { // scroll anchor does not work if the timeline is scrolled to the top. // at the start, when there are not enough messages for a full screen, this is the case. // once there are enough messages that scrolling is necessary, we initiate a scroll down to activate the scroll anchor. @@ -40,12 +40,29 @@ class Chat extends ElemJS { } }, 0) } - const name = "beforeChange" - this.removableSubscriptions.push({name, target: timeline, subscription}) - timeline.subscribe(name, subscription) + this.addSubscription("beforeChange", timeline, beforeChangeSubscription) + + //Make sure after loading scrollback we don't move the scroll position + const beforeScrollbackLoadSubscription = () => { + const lastScrollHeight = chatMessages.scrollHeight; + + const afterScrollbackLoadSub = () => { + const scrollDiff = chatMessages.scrollHeight - lastScrollHeight; + chatMessages.scrollTop += scrollDiff; + + timeline.unsubscribe("afterScrollbackLoad", afterScrollbackLoadSub) + } + + timeline.subscribe("afterScrollbackLoad", afterScrollbackLoadSub) + } + this.addSubscription("beforeScrollbackLoad", timeline, beforeScrollbackLoadSubscription) } this.render() } + addSubscription(name, target, subscription) { + this.removableSubscriptions.push({name, target, subscription}) + target.subscribe(name, subscription) + } render() { this.clearChildren() diff --git a/src/js/timeline.js b/src/js/timeline.js index 0819ff8..270c6ca 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -176,12 +176,32 @@ class EventGroup extends ElemJS { } } + +//Displays a spiner and creates an event to notify timeline to load more messages +class LoadMore extends ElemJS { + constructor() { + super("div") + this.html(`
Loading more... `) + const intersection_observer = new IntersectionObserver(e => this.intersectionHandler(e)) + intersection_observer.observe(this.element) + + } + + intersectionHandler(e) { + if (e.some(e => e.isIntersecting)) { + const event = new CustomEvent("LoadMore", {bubbles: true}) + this.element.dispatchEvent(event) + } + } +} + class ReactiveTimeline extends ElemJS { constructor(id, list) { super("div") this.class("c-event-groups") this.id = id this.list = list + this.load_more = new LoadMore() this.render() } @@ -201,6 +221,9 @@ class ReactiveTimeline extends ElemJS { } else { this.tryAddGroups(event, [search.i]) } + this.load_more.remove() + this.load_more = new LoadMore() + this.childAt(0, this.load_more) } tryAddGroups(event, indices) { @@ -233,6 +256,7 @@ class ReactiveTimeline extends ElemJS { render() { this.clearChildren() + this.child(this.load_more) this.list.forEach(group => this.child(group)) this.anchor = new Anchor() this.child(this.anchor) @@ -244,11 +268,15 @@ class Timeline extends Subscribable { super() Object.assign(this.events, { beforeChange: [], - afterChange: [] + afterChange: [], + beforeScrollbackLoad: [], + afterScrollbackLoad: [], }) Object.assign(this.eventDeps, { beforeChange: [], - afterChange: [] + afterChange: [], + beforeScrollbackLoad: [], + afterScrollbackLoad: [], }) this.room = room this.id = this.room.id @@ -259,6 +287,8 @@ class Timeline extends Subscribable { this.pending = new Set() this.pendingEdits = [] this.from = null + + this.reactiveTimeline.element.addEventListener("LoadMore", () => this.loadScrollback()) } updateStateEvents(events) { @@ -343,6 +373,7 @@ class Timeline extends Subscribable { } async loadScrollback() { + this.broadcast("beforeScrollbackLoad") debug = true if (!this.from) throw new Error("Can't load scrollback, no from token") const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/rooms/${this.id}/messages`) @@ -356,9 +387,10 @@ class Timeline extends Subscribable { url.searchParams.set("filter", JSON.stringify(filter)) const root = await fetch(url.toString()).then(res => res.json()) this.from = root.end - console.log(this.updateEvents, root.chunk) + //console.log(this.updateEvents, root.chunk) if (root.state) this.updateStateEvents(root.state) this.updateEvents(root.chunk) + this.broadcast("afterScrollbackLoad") } send(body) { @@ -393,24 +425,24 @@ class Timeline extends Subscribable { this.subscribe("afterChange", subscription) })*/ } -/* - getGroupedEvents() { - let currentSender = Symbol("N/A") - let groups = [] - let currentGroup = [] - for (const event of this.list) { - if (event.sender === currentSender) { - currentGroup.push(event) - } else { - if (currentGroup.length) groups.push(currentGroup) - currentGroup = [event] - currentSender = event.sender + /* + getGroupedEvents() { + let currentSender = Symbol("N/A") + let groups = [] + let currentGroup = [] + for (const event of this.list) { + if (event.sender === currentSender) { + currentGroup.push(event) + } else { + if (currentGroup.length) groups.push(currentGroup) + currentGroup = [event] + currentSender = event.sender + } } + if (currentGroup.length) groups.push(currentGroup) + return groups } - if (currentGroup.length) groups.push(currentGroup) - return groups - } - */ + */ } module.exports = {Timeline} diff --git a/src/sass/loading.sass b/src/sass/loading.sass new file mode 100644 index 0000000..9705bbe --- /dev/null +++ b/src/sass/loading.sass @@ -0,0 +1,13 @@ +@keyframes spin + 0% + transform: rotate(0deg) + 100% + transform: rotate(180deg) + +.loading-icon + display: inline-block + background-color: #ccc + width: 12px + height: 12px + margin-right: 6px + animation: spin 0.7s infinite diff --git a/src/sass/login.sass b/src/sass/login.sass index 235fad4..74d08ac 100644 --- a/src/sass/login.sass +++ b/src/sass/login.sass @@ -1,6 +1,8 @@ @use "./base" +@use "./loading.sass" @use "./colors.sass" as c + .main justify-content: center align-items: center @@ -41,19 +43,6 @@ .form-error color: red -@keyframes spin - 0% - transform: rotate(0deg) - 100% - transform: rotate(180deg) - -.loading-icon - display: inline-block - background-color: #ccc - width: 12px - height: 12px - margin-right: 6px - animation: spin 0.7s infinite input, button font-family: inherit diff --git a/src/sass/main.sass b/src/sass/main.sass index d342bbb..150af73 100644 --- a/src/sass/main.sass +++ b/src/sass/main.sass @@ -5,3 +5,4 @@ @use "./components/chat" @use "./components/chat-input" @use "./components/anchor" +@use "./loading" From c9dffc9d4aabfbde269cfa83b0b3851a2c3b37d7 Mon Sep 17 00:00:00 2001 From: BadAtNames Date: Sat, 24 Oct 2020 23:01:48 +0200 Subject: [PATCH 31/92] Wait for events to load before saving scroll position --- src/js/timeline.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/js/timeline.js b/src/js/timeline.js index 270c6ca..fab0b58 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -33,9 +33,9 @@ function eventSearch(list, event, min = 0, max = NO_MAX) { } } // recurse (below) - if (list[mid].data.origin_server_ts > event.data.origin_server_ts) return eventSearch(list, event, min, mid-1) + if (list[mid].data.origin_server_ts > event.data.origin_server_ts) return eventSearch(list, event, min, mid - 1) // recurse (above) - else return eventSearch(list, event, mid+1, max) + else return eventSearch(list, event, mid + 1, max) } class Event extends ElemJS { @@ -213,7 +213,7 @@ class ReactiveTimeline extends ElemJS { if (!search.success) { if (search.i >= 1) { // add at end - this.tryAddGroups(event, [search.i-1, search.i]) + this.tryAddGroups(event, [search.i - 1, search.i]) } else { // add at start this.tryAddGroups(event, [0, -1]) @@ -373,7 +373,6 @@ class Timeline extends Subscribable { } async loadScrollback() { - this.broadcast("beforeScrollbackLoad") debug = true if (!this.from) throw new Error("Can't load scrollback, no from token") const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/rooms/${this.id}/messages`) @@ -385,7 +384,11 @@ class Timeline extends Subscribable { lazy_load_members: true } url.searchParams.set("filter", JSON.stringify(filter)) + const root = await fetch(url.toString()).then(res => res.json()) + + this.broadcast("beforeScrollbackLoad") + this.from = root.end //console.log(this.updateEvents, root.chunk) if (root.state) this.updateStateEvents(root.state) From 0348fed18db3adefc5d9a537c1952e0cf9233c3a Mon Sep 17 00:00:00 2001 From: BadAtNames Date: Mon, 26 Oct 2020 09:10:02 +0100 Subject: [PATCH 32/92] Initial work on rich messages --- package-lock.json | 27 ++++++++++++++ package.json | 3 +- src/js/dateFormatter.js | 3 ++ src/js/events/encrypted.js | 18 ++++++++++ src/js/events/event.js | 59 +++++++++++++++++++++++++++++++ src/js/events/membership.js | 21 +++++++++++ src/js/events/message.js | 67 +++++++++++++++++++++++++++++++++++ src/js/events/renderEvent.js | 19 ++++++++++ src/js/events/unknown.js | 15 ++++++++ src/js/timeline.js | 68 +++--------------------------------- 10 files changed, 235 insertions(+), 65 deletions(-) create mode 100644 src/js/dateFormatter.js create mode 100644 src/js/events/encrypted.js create mode 100644 src/js/events/event.js create mode 100644 src/js/events/membership.js create mode 100644 src/js/events/message.js create mode 100644 src/js/events/renderEvent.js create mode 100644 src/js/events/unknown.js diff --git a/package-lock.json b/package-lock.json index 228e7ee..40cc6bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1949,6 +1949,11 @@ "domelementtype": "1" } }, + "dompurify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.0.tgz", + "integrity": "sha512-bqFOQ7XRmmozp0VsKdIEe8UwZYxj0yttz7l80GBtBqdVRY48cOpXH2J/CVO7AEkV51qY0EBVXfilec18mdmQ/w==" + }, "domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", @@ -2092,8 +2097,30 @@ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "requires": { "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } } } diff --git a/package.json b/package.json index 8f2ead4..7b1fbfd 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "author": "", "license": "AGPL-3.0-only", "dependencies": { - "browserify": "^17.0.0" + "browserify": "^17.0.0", + "dompurify": "^2.2.0" }, "devDependencies": { "@babel/core": "^7.11.1", diff --git a/src/js/dateFormatter.js b/src/js/dateFormatter.js new file mode 100644 index 0000000..c161d76 --- /dev/null +++ b/src/js/dateFormatter.js @@ -0,0 +1,3 @@ +const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", day: "numeric", month: "short", year: "numeric"}) + +module.exports = {dateFormatter} diff --git a/src/js/events/encrypted.js b/src/js/events/encrypted.js new file mode 100644 index 0000000..32dbe19 --- /dev/null +++ b/src/js/events/encrypted.js @@ -0,0 +1,18 @@ +const {Event} = require("./event") + +class EncryptedMessage extends Event { + render() { + super.render() + return this.text("Carbon cannot render encrypted messages yet") + } + + static canRender(event) { + return event.type == "m.room.encrypted" + } + + canGroup() { + return true + } +} + +module.exports = [EncryptedMessage] diff --git a/src/js/events/event.js b/src/js/events/event.js new file mode 100644 index 0000000..ddb412d --- /dev/null +++ b/src/js/events/event.js @@ -0,0 +1,59 @@ +const {ElemJS, ejs} = require("../basic") +const {dateFormatter} = require("../dateFormatter") + +class Event extends ElemJS { + constructor(data) { + super("div") + this.class("c-message") + this.data = null + this.group = null + this.editedAt = null + this.update(data) + } + + // predicates + + canGroup() { + //return this.data.type === "m.room.message" + return false + } + + // operations + + setGroup(group) { + this.group = group + } + + setEdited(time) { + this.editedAt = time + this.render() + } + + update(data) { + this.data = data + this.render() + } + + removeEvent() { + if (this.group) this.group.removeEvent(this) + else this.remove() + } + + render() { + this.element.classList[this.data.pending ? "add" : "remove"]("c-message--pending") + if (this.editedAt) { + this.child(ejs("span").class("c-message__edited").text("(edited)").attribute("title", "at " + dateFormatter.format(this.editedAt))) + } + } + static canRender(_event) { + return false + } +} + + + +function renderEvent(event) { + return new events.find(e => e.canRender(event))(event) +} + +module.exports = {renderEvent, Event} diff --git a/src/js/events/membership.js b/src/js/events/membership.js new file mode 100644 index 0000000..97daea3 --- /dev/null +++ b/src/js/events/membership.js @@ -0,0 +1,21 @@ +const {Event} = require("./event") + +function createMembershipEvent(membership, text) { + return class extends Event { + render() { + super.render() + return this.text(text(this.data)) + } + + static canRender(event) { + return event.type == "m.room.member" && event.content.membership == membership + } + + } +} + +const JoinedEvent = createMembershipEvent("join", () => "joined the room") +const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) +const LeaveEvent = createMembershipEvent("leave", () => "left the room") + +module.exports = [JoinedEvent, InvitedEvent, LeaveEvent] diff --git a/src/js/events/message.js b/src/js/events/message.js new file mode 100644 index 0000000..0218a60 --- /dev/null +++ b/src/js/events/message.js @@ -0,0 +1,67 @@ +const {ejs} = require("../basic") +const DOMPurify = require("dompurify") +const {resolveMxc} = require("../functions") +const {Event} = require("./event") + +const purifier = DOMPurify() +purifier.addHook("afterSanitizeAttributes", (node, hookevent, config) => { + if (node.tagName == "img") { + let src = node.getAttribute("src") + if (src) src = resolveMxc(src) + + node.setAttribute("src", src) + + } + if (node.tagName = "a") { + node.setAttribute("rel", "noopener") + } + return node + +}) + +function sanitize(html) { + return purifier.sanitize(html, DOMPURIFY_CONFIG) +} +const DOMPURIFY_CONFIG = { + ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris + ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], +}; + +class HTMLMessage extends Event { + render() { + super.render() + let html = this.data.content.formatted_body + const content = ejs("div") + html = sanitize(html) + content.html(html) + this.child(content) + } + + static canRender(event) { + const content = event.content + return event.type == "m.room.message" && content.msgtype == "m.text" && content.format == "org.matrix.custom.html" && content.formatted_body + + } + + canGroup() { + return true + } + +} + +class TextMessage extends Event { + render() { + super.render() + return this.text(this.data.content.body) + } + + static canRender(event) { + return event.type == "m.room.message" + } + + canGroup() { + return true + } +} + +module.exports = [HTMLMessage, TextMessage] diff --git a/src/js/events/renderEvent.js b/src/js/events/renderEvent.js new file mode 100644 index 0000000..e4616a2 --- /dev/null +++ b/src/js/events/renderEvent.js @@ -0,0 +1,19 @@ +const messageEvent = require("./message") +const encryptedEvent = require("./encrypted") +const membershipEvent = require("./membership") +const unknownEvent = require("./unknown") + +const events = [ + ...messageEvent, + ...encryptedEvent, + ...membershipEvent, + ...unknownEvent, +] + + +function renderEvent(eventData) { + const constructor = events.find(e => e.canRender(eventData)) + return new constructor(eventData) +} + +module.exports = {renderEvent} diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js new file mode 100644 index 0000000..d644de7 --- /dev/null +++ b/src/js/events/unknown.js @@ -0,0 +1,15 @@ +const {Event} = require("./event") + +class UnknownEvent extends Event { + render() { + super.render() + this.text("Cannot render event") + } + + static canRender(_event) { + return true + } + +} + +module.exports = [UnknownEvent] diff --git a/src/js/timeline.js b/src/js/timeline.js index 0819ff8..da98aa2 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -4,13 +4,13 @@ const {store} = require("./store/store.js") const {Anchor} = require("./anchor.js") const lsm = require("./lsm.js") const {resolveMxc} = require("./functions.js") +const {renderEvent} = require("./events/renderEvent") +const {dateFormatter} = require("./dateFormatter") let debug = false const NO_MAX = Symbol("NO_MAX") -const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", day: "numeric", month: "short", year: "numeric"}) - let sentIndex = 0 function getTxnId() { @@ -38,67 +38,6 @@ function eventSearch(list, event, min = 0, max = NO_MAX) { else return eventSearch(list, event, mid+1, max) } -class Event extends ElemJS { - constructor(data) { - super("div") - this.class("c-message") - this.data = null - this.group = null - this.editedAt = null - this.update(data) - } - - // predicates - - canGroup() { - return this.data.type === "m.room.message" - } - - // operations - - setGroup(group) { - this.group = group - } - - setEdited(time) { - this.editedAt = time - this.render() - } - - update(data) { - this.data = data - this.render() - } - - removeEvent() { - if (this.group) this.group.removeEvent(this) - else this.remove() - } - - render() { - this.element.classList[this.data.pending ? "add" : "remove"]("c-message--pending") - if (this.data.type === "m.room.message") { - this.text(this.data.content.body) - } else if (this.data.type === "m.room.member") { - if (this.data.content.membership === "join") { - this.child(ejs("i").text("joined the room")) - } else if (this.data.content.membership === "invite") { - this.child(ejs("i").text(`invited ${this.data.content.displayname} to the room`)) - } else if (this.data.content.membership === "leave") { - this.child(ejs("i").text("left the room")) - } else { - this.child(ejs("i").text("unknown membership event")) - } - } else if (this.data.type === "m.room.encrypted") { - this.child(ejs("i").text("Carbon does not yet support encrypted messages.")) - } else { - this.child(ejs("i").text(`Unsupported event type ${this.data.type}`)) - } - if (this.editedAt) { - this.child(ejs("span").class("c-message__edited").text("(edited)").attribute("title", "at " + dateFormatter.format(this.editedAt))) - } - } -} class Sender { constructor(roomID, mxid) { @@ -311,7 +250,7 @@ class Timeline extends Subscribable { continue } // add new event - const event = new Event(eventData) + const event = renderEvent(eventData) this.map.set(id, event) this.reactiveTimeline.addEvent(event) } @@ -393,6 +332,7 @@ class Timeline extends Subscribable { this.subscribe("afterChange", subscription) })*/ } + /* getGroupedEvents() { let currentSender = Symbol("N/A") From 08a0990bc8247527eb5be46e2382f3ca02ac41ff Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 26 Oct 2020 22:30:57 +1300 Subject: [PATCH 33/92] Add back jshint; format --- build.js | 67 ++++++--- package-lock.json | 369 +++++++++++++++++++++++++++++++++++----------- package.json | 5 +- spec.js | 80 ---------- 4 files changed, 331 insertions(+), 190 deletions(-) diff --git a/build.js b/build.js index c6e0671..0d0d7da 100644 --- a/build.js +++ b/build.js @@ -9,7 +9,8 @@ const babel = require("@babel/core") const fetch = require("node-fetch") const chalk = require("chalk") const hint = require("jshint").JSHINT -const browserify = require('browserify') +const browserify = require("browserify") +const {Transform} = require("stream") process.chdir(pj(__dirname, "src")) @@ -17,10 +18,10 @@ const buildDir = "../build" const validationQueue = [] const validationHost = os.hostname() === "future" ? "http://localhost:8888/" : "http://validator.w3.org/nu/" -const static_files = new Map() +const staticFiles = new Map() const links = new Map() const sources = new Map() -const pugLocals = {static: static_files, links} +const pugLocals = {static: staticFiles, links} const spec = require("./spec.js") @@ -95,6 +96,7 @@ function runHint(filename, source) { globals: ["console", "URLSearchParams", "staticFiles"], browser: true, asi: true, + node: true }) const result = hint.data() let problems = 0 @@ -127,33 +129,52 @@ function runHint(filename, source) { async function addFile(sourcePath, targetPath) { const contents = await fs.promises.readFile(pj(".", sourcePath), {encoding: null}); - static_files.set(sourcePath, `${targetPath}?static=${hash(contents)}`) + staticFiles.set(sourcePath, `${targetPath}?static=${hash(contents)}`) await fs.promises.writeFile(pj(buildDir, targetPath), contents) } async function loadJS(sourcePath, targetPath) { let content = await fs.promises.readFile(pj(".", sourcePath), {encoding: "utf8"}) - sources.set(sourcePath, content); - static_files.set(sourcePath, `${targetPath}?static=${hash(content)}`) + sources.set(sourcePath, content) + staticFiles.set(sourcePath, `${targetPath}?static=${hash(content)}`) } async function addJS(sourcePath, targetPath) { let content = sources.get(sourcePath) - // resolve imports to hashed paths - content = content.replace(/\$to_relative "([^"]+)"/g, function(_, file) { - if (!static_files.get(file)) throw new Error(`Tried to relative import ${file} from ${sourcePath}, but import not found`) - return '"' + getRelative(targetPath, static_files.get(file)) + '"' - }) runHint(sourcePath, content) await fs.promises.writeFile(pj(buildDir, targetPath), content) } async function addBundle(sourcePath, targetPath) { - await browserify() - .add(pj(".", sourcePath)) - .bundle() - .pipe(fs.createWriteStream(pj(buildDir, targetPath))); - static_files.set(sourcePath, targetPath) + const content = await new Promise(resolve => { + browserify() + .add(pj(".", sourcePath)) + .transform(file => { + let content = "" + const transform = new Transform({ + transform(chunk, encoding, callback) { + content += chunk.toString() + callback(null, chunk) + } + }) + transform.on("finish", () => { + const relativePath = path.relative(process.cwd(), file).replace(/^\/*/, "/") + runHint(relativePath, content) + }) + return transform + }) + .bundle((err, res) => { + if (err) { + delete err.stream + throw err // Quit; problem parsing file to bundle + } + resolve(res) + }) + }) + const writer = fs.promises.writeFile(pj(buildDir, targetPath), content) + staticFiles.set(sourcePath, `${targetPath}?static=${hash(content)}`) + runHint(sourcePath, content) + await writer } async function addSass(sourcePath, targetPath) { @@ -167,7 +188,7 @@ async function addSass(sourcePath, targetPath) { if (!(name instanceof sass.types.String)) { throw "$name: expected a string" } - const result = getRelative(targetPath, static_files.get(name.getValue())) + const result = getRelative(targetPath, staticFiles.get(name.getValue())) if (typeof result === "string") { return new sass.types.String(result) } else { @@ -176,8 +197,8 @@ async function addSass(sourcePath, targetPath) { } } }).css; - static_files.set(sourcePath, `${targetPath}?static=${hash(renderedCSS)}`) - await validate(sourcePath, renderedCSS, "css") + staticFiles.set(sourcePath, `${targetPath}?static=${hash(renderedCSS)}`) + validate(sourcePath, renderedCSS, "css") await fs.promises.writeFile(pj(buildDir, targetPath), renderedCSS) } @@ -186,17 +207,17 @@ async function addPug(sourcePath, targetPath) { return getRelative(targetPath, staticTarget) } function getStatic(target) { - return getRelativeHere(static_files.get(target)) + return getRelativeHere(staticFiles.get(target)) } function getStaticName(target) { - return getRelativeHere(static_files.get(target)).replace(/\?.*$/, "") + return getRelativeHere(staticFiles.get(target)).replace(/\?.*$/, "") } function getLink(target) { return getRelativeHere(links.get(target)) } const renderedHTML = pug.compileFile(pj(".", sourcePath), {pretty: true})({getStatic, getStaticName, getLink, ...pugLocals}) let renderedWithoutPHP = renderedHTML.replace(/<\?(?:php|=).*?\?>/gsm, "") - await validate(sourcePath, renderedWithoutPHP, "html") + validate(sourcePath, renderedWithoutPHP, "html") await fs.promises.writeFile(pj(buildDir, targetPath), renderedHTML) } @@ -224,7 +245,7 @@ async function addBabel(sourcePath, targetPath) { const filenameWithQuery = `${targetPath}?static=${hash(compiled.code)}`; - static_files.set(sourcePath, filenameWithQuery) + staticFiles.set(sourcePath, filenameWithQuery) await Promise.all([ fs.promises.writeFile(pj(buildDir, targetPath), originalCode), diff --git a/package-lock.json b/package-lock.json index 228e7ee..5b9d4ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1105,6 +1105,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, "requires": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -1113,12 +1114,14 @@ "acorn": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true }, "acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, "requires": { "acorn": "^7.0.0", "acorn-walk": "^7.0.0", @@ -1128,7 +1131,8 @@ "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true }, "ansi-styles": { "version": "4.2.1", @@ -1153,7 +1157,8 @@ "array-filter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true }, "asap": { "version": "2.0.6", @@ -1165,6 +1170,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -1175,7 +1181,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -1183,6 +1190,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, "requires": { "object-assign": "^4.1.1", "util": "0.10.3" @@ -1191,12 +1199,14 @@ "inherits": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, "requires": { "inherits": "2.0.1" } @@ -1222,6 +1232,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, "requires": { "array-filter": "^1.0.0" } @@ -1247,12 +1258,14 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true }, "basic-auth": { "version": "1.1.0", @@ -1269,12 +1282,14 @@ "bn.js": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1292,12 +1307,14 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "browser-pack": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, "requires": { "JSONStream": "^1.0.3", "combine-source-map": "~0.8.0", @@ -1311,6 +1328,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", + "dev": true, "requires": { "resolve": "^1.17.0" } @@ -1319,6 +1337,7 @@ "version": "17.0.0", "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", + "dev": true, "requires": { "JSONStream": "^1.0.3", "assert": "^1.4.0", @@ -1373,12 +1392,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1393,6 +1414,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1403,6 +1425,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -1410,7 +1433,8 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true } } } @@ -1420,6 +1444,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -1433,6 +1458,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, "requires": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -1443,6 +1469,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, "requires": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -1454,6 +1481,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, "requires": { "bn.js": "^4.1.0", "randombytes": "^2.0.1" @@ -1462,7 +1490,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -1470,6 +1499,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, "requires": { "bn.js": "^5.1.1", "browserify-rsa": "^4.0.1", @@ -1486,6 +1516,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -1495,12 +1526,14 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -1511,6 +1544,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, "requires": { "pako": "~1.0.5" } @@ -1531,6 +1565,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -1539,22 +1574,26 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true }, "cached-path-relative": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", - "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==" + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true }, "caniuse-lite": { "version": "1.0.30001112", @@ -1601,6 +1640,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -1641,6 +1681,7 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, "requires": { "convert-source-map": "~1.1.0", "inline-source-map": "~0.6.0", @@ -1651,19 +1692,22 @@ "convert-source-map": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=" + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true } } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -1674,12 +1718,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1694,6 +1740,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1704,6 +1751,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, "requires": { "date-now": "^0.1.4" } @@ -1721,7 +1769,8 @@ "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true }, "convert-source-map": { "version": "1.7.0", @@ -1753,7 +1802,8 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true }, "corser": { "version": "2.0.1", @@ -1765,6 +1815,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, "requires": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -1773,7 +1824,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -1781,6 +1833,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -1793,6 +1846,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -1806,6 +1860,7 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, "requires": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -1823,12 +1878,14 @@ "dash-ast": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", - "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==" + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true }, "debug": { "version": "3.2.6", @@ -1850,12 +1907,14 @@ "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true }, "deps-sort": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", + "dev": true, "requires": { "JSONStream": "^1.0.3", "shasum-object": "^1.0.0", @@ -1867,6 +1926,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, "requires": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -1876,6 +1936,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, "requires": { "acorn-node": "^1.6.1", "defined": "^1.0.0", @@ -1886,6 +1947,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, "requires": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -1895,7 +1957,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -1932,7 +1995,8 @@ "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true }, "domelementtype": { "version": "1.3.1", @@ -1963,6 +2027,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, "requires": { "readable-stream": "^2.0.2" }, @@ -1970,12 +2035,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1990,6 +2057,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2018,6 +2086,7 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -2031,7 +2100,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -2045,6 +2115,7 @@ "version": "1.17.7", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -2092,8 +2163,30 @@ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", "requires": { "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", "has-symbols": "^1.0.1", "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } } } @@ -2135,12 +2228,14 @@ "events": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -2155,7 +2250,8 @@ "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true }, "fill-range": { "version": "7.0.1", @@ -2175,12 +2271,14 @@ "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "fsevents": { "version": "2.1.3", @@ -2203,12 +2301,14 @@ "get-assigned-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", - "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==" + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2256,6 +2356,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -2266,6 +2367,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2275,12 +2377,14 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -2291,6 +2395,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -2306,6 +2411,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -2315,7 +2421,8 @@ "htmlescape": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=" + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true }, "htmlparser2": { "version": "3.8.3", @@ -2362,17 +2469,20 @@ "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true }, "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2381,12 +2491,14 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "inline-source-map": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, "requires": { "source-map": "~0.5.3" } @@ -2395,6 +2507,7 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", + "dev": true, "requires": { "JSONStream": "^1.0.3", "acorn-node": "^1.5.2", @@ -2420,7 +2533,8 @@ "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true }, "is-binary-path": { "version": "2.1.0", @@ -2434,7 +2548,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-callable": { "version": "1.2.2", @@ -2465,7 +2580,8 @@ "is-generator-function": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", - "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==" + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", + "dev": true }, "is-glob": { "version": "4.0.1", @@ -2514,6 +2630,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "dev": true, "requires": { "available-typed-arrays": "^1.0.0", "es-abstract": "^1.17.4", @@ -2573,7 +2690,8 @@ "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true }, "jstransformer": { "version": "1.0.0", @@ -2589,6 +2707,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", + "dev": true, "requires": { "inherits": "^2.0.1", "stream-splicer": "^2.0.0" @@ -2618,7 +2737,8 @@ "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true }, "loose-envify": { "version": "1.4.0", @@ -2633,6 +2753,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -2643,6 +2764,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -2651,7 +2773,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -2664,17 +2787,20 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2682,7 +2808,8 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true }, "mkdirp": { "version": "0.5.5", @@ -2696,12 +2823,14 @@ "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true }, "module-deps": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", + "dev": true, "requires": { "JSONStream": "^1.0.3", "browser-resolve": "^2.0.0", @@ -2723,12 +2852,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2743,6 +2874,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2776,7 +2908,8 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-inspect": { "version": "1.8.0", @@ -2804,6 +2937,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -2817,17 +2951,20 @@ "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true }, "parents": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, "requires": { "path-platform": "~0.11.15" } @@ -2836,6 +2973,7 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, "requires": { "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", @@ -2847,27 +2985,32 @@ "path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-platform": { "version": "0.11.15", "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=" + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -2896,12 +3039,14 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "promise": { "version": "7.3.1", @@ -2916,6 +3061,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -2928,7 +3074,8 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true } } }, @@ -3059,7 +3206,8 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true }, "qs": { "version": "6.9.4", @@ -3070,17 +3218,20 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true }, "querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -3089,6 +3240,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, "requires": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -3098,6 +3250,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, "requires": { "readable-stream": "^2.0.2" }, @@ -3105,12 +3258,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3125,6 +3280,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3229,6 +3385,7 @@ "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -3237,6 +3394,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -3245,12 +3403,14 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "sass": { "version": "1.26.10", @@ -3277,6 +3437,7 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -3286,6 +3447,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", + "dev": true, "requires": { "fast-safe-stringify": "^2.0.7" } @@ -3293,7 +3455,8 @@ "shell-quote": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true }, "shelljs": { "version": "0.3.0", @@ -3304,17 +3467,20 @@ "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true }, "stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dev": true, "requires": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -3324,6 +3490,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3333,12 +3500,14 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -3349,6 +3518,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -3357,12 +3527,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3377,6 +3549,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3387,6 +3560,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", + "dev": true, "requires": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -3398,6 +3572,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3407,12 +3582,14 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -3423,6 +3600,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", + "dev": true, "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.2" @@ -3431,12 +3609,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3451,6 +3631,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3571,6 +3752,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, "requires": { "minimist": "^1.1.0" } @@ -3588,6 +3770,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, "requires": { "acorn-node": "^1.2.0" } @@ -3595,12 +3778,14 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -3609,12 +3794,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3629,6 +3816,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3639,6 +3827,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, "requires": { "process": "~0.11.0" } @@ -3667,22 +3856,26 @@ "tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true }, "umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==" + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true }, "undeclared-identifiers": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, "requires": { "acorn-node": "^1.3.0", "dash-ast": "^1.0.0", @@ -3732,6 +3925,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -3740,7 +3934,8 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true } } }, @@ -3754,6 +3949,7 @@ "version": "0.12.3", "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "dev": true, "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -3766,12 +3962,14 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true }, "void-elements": { "version": "3.1.0", @@ -3783,6 +3981,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "dev": true, "requires": { "available-typed-arrays": "^1.0.2", "es-abstract": "^1.17.5", @@ -3807,12 +4006,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true } } } diff --git a/package.json b/package.json index 8f2ead4..ab4af6b 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,11 @@ "keywords": [], "author": "", "license": "AGPL-3.0-only", - "dependencies": { - "browserify": "^17.0.0" - }, + "dependencies": {}, "devDependencies": { "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", + "browserify": "^17.0.0", "chalk": "^4.1.0", "http-server": "^0.12.3", "jshint": "^2.12.0", diff --git a/spec.js b/spec.js index d525487..1babec8 100644 --- a/spec.js +++ b/spec.js @@ -19,86 +19,6 @@ module.exports = [ source: "/js/main.js", target: "/static/bundle.js" }, - // { - // type: "js", - // source: "/js/main.js", - // target: "/static/main.js", - // }, - // { - // type: "js", - // source: "/js/basic.js", - // target: "/static/basic.js", - // }, - // { - // type: "js", - // source: "/js/groups.js", - // target: "/static/groups.js", - // }, - // { - // type: "js", - // source: "/js/chat-input.js", - // target: "/static/chat-input.js", - // }, - // { - // type: "js", - // source: "/js/room-picker.js", - // target: "/static/room-picker.js", - // }, - // { - // type: "js", - // source: "/js/store/store.js", - // target: "/static/store/store.js", - // }, - // { - // type: "js", - // source: "/js/store/subscribable.js", - // target: "/static/store/subscribable.js", - // }, - // { - // type: "js", - // source: "/js/store/subscribe_value.js", - // target: "/static/store/subscribe_value.js", - // }, - // { - // type: "js", - // source: "/js/store/subscribe_map_list.js", - // target: "/static/store/subscribe_map_list.js", - // }, - // { - // type: "js", - // source: "/js/store/subscribe_set.js", - // target: "/static/store/subscribe_set.js", - // }, - // { - // type: "js", - // source: "/js/sync/sync.js", - // target: "/static/sync/sync.js", - // }, - // { - // type: "js", - // source: "/js/lsm.js", - // target: "/static/lsm.js", - // }, - // { - // type: "js", - // source: "/js/timeline.js", - // target: "/static/timeline.js", - // }, - // { - // type: "js", - // source: "/js/anchor.js", - // target: "/static/anchor.js", - // }, - // { - // type: "js", - // source: "/js/chat.js", - // target: "/static/chat.js", - // }, - // { - // type: "js", - // source: "/js/functions.js", - // target: "/static/functions.js", - // }, { type: "file", source: "/assets/fonts/whitney-500.woff", From df47c8a88aded98409ce64f5a51467ca90bbe514 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 26 Oct 2020 23:35:33 +1300 Subject: [PATCH 34/92] Style load more; fix message group order --- src/js/timeline.js | 60 ++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/src/js/timeline.js b/src/js/timeline.js index fab0b58..270b5c8 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -177,20 +177,26 @@ class EventGroup extends ElemJS { } -//Displays a spiner and creates an event to notify timeline to load more messages +/** Displays a spinner and creates an event to notify timeline to load more messages */ class LoadMore extends ElemJS { - constructor() { + constructor(id) { super("div") - this.html(`
Loading more... `) + this.class("c-message-notice") + this.id = id + + this.child( + ejs("div").class("c-message-notice__inner").child( + ejs("span").class("loading-icon"), + ejs("span").text("Loading more...") + ) + ) const intersection_observer = new IntersectionObserver(e => this.intersectionHandler(e)) intersection_observer.observe(this.element) - } intersectionHandler(e) { if (e.some(e => e.isIntersecting)) { - const event = new CustomEvent("LoadMore", {bubbles: true}) - this.element.dispatchEvent(event) + store.rooms.get(this.id).value().timeline.loadScrollback() } } } @@ -201,11 +207,12 @@ class ReactiveTimeline extends ElemJS { this.class("c-event-groups") this.id = id this.list = list - this.load_more = new LoadMore() + this.loadMore = new LoadMore(this.id) this.render() } addEvent(event) { + this.loadMore.remove() // if (debug) console.log("running search", this.list, event) // if (debug) debugger; const search = eventSearch(this.list, event) @@ -221,9 +228,8 @@ class ReactiveTimeline extends ElemJS { } else { this.tryAddGroups(event, [search.i]) } - this.load_more.remove() - this.load_more = new LoadMore() - this.childAt(0, this.load_more) + this.loadMore = new LoadMore(this.id) + this.childAt(0, this.loadMore) } tryAddGroups(event, indices) { @@ -256,7 +262,7 @@ class ReactiveTimeline extends ElemJS { render() { this.clearChildren() - this.child(this.load_more) + this.child(this.loadMore) this.list.forEach(group => this.child(group)) this.anchor = new Anchor() this.child(this.anchor) @@ -287,8 +293,6 @@ class Timeline extends Subscribable { this.pending = new Set() this.pendingEdits = [] this.from = null - - this.reactiveTimeline.element.addEventListener("LoadMore", () => this.loadScrollback()) } updateStateEvents(events) { @@ -379,7 +383,7 @@ class Timeline extends Subscribable { url.searchParams.set("access_token", lsm.get("access_token")) url.searchParams.set("from", this.from) url.searchParams.set("dir", "b") - url.searchParams.set("limit", 10) + url.searchParams.set("limit", "20") const filter = { lazy_load_members: true } @@ -390,7 +394,7 @@ class Timeline extends Subscribable { this.broadcast("beforeScrollbackLoad") this.from = root.end - //console.log(this.updateEvents, root.chunk) + // console.log(this.updateEvents, root.chunk) if (root.state) this.updateStateEvents(root.state) this.updateEvents(root.chunk) this.broadcast("afterScrollbackLoad") @@ -420,32 +424,8 @@ class Timeline extends Subscribable { headers: { "Content-Type": "application/json" } - })/*.then(() => { - const subscription = () => { - this.removeEvent(id) - this.unsubscribe("afterChange", subscription) - } - this.subscribe("afterChange", subscription) - })*/ + }) } - /* - getGroupedEvents() { - let currentSender = Symbol("N/A") - let groups = [] - let currentGroup = [] - for (const event of this.list) { - if (event.sender === currentSender) { - currentGroup.push(event) - } else { - if (currentGroup.length) groups.push(currentGroup) - currentGroup = [event] - currentSender = event.sender - } - } - if (currentGroup.length) groups.push(currentGroup) - return groups - } - */ } module.exports = {Timeline} From f4b368ea3ef0ed27245101d271b3ba8ca092e478 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 26 Oct 2020 23:56:40 +1300 Subject: [PATCH 35/92] Better message line breaking --- src/sass/components/messages.sass | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 7f56834..8a33dc2 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -46,6 +46,7 @@ .c-message margin-top: 4px + overflow-wrap: anywhere opacity: 1 transition: opacity 0.2s ease-out From 72b42e7b26b33a988e367c638c7e8e92ff6a596b Mon Sep 17 00:00:00 2001 From: BadAtNames Date: Mon, 26 Oct 2020 09:10:02 +0100 Subject: [PATCH 36/92] Initial work on rich messages --- package-lock.json | 5 +++ package.json | 4 ++- src/js/dateFormatter.js | 3 ++ src/js/events/encrypted.js | 18 ++++++++++ src/js/events/event.js | 59 +++++++++++++++++++++++++++++++ src/js/events/membership.js | 21 +++++++++++ src/js/events/message.js | 67 ++++++++++++++++++++++++++++++++++++ src/js/events/renderEvent.js | 19 ++++++++++ src/js/events/unknown.js | 15 ++++++++ src/js/timeline.js | 67 ++---------------------------------- 10 files changed, 213 insertions(+), 65 deletions(-) create mode 100644 src/js/dateFormatter.js create mode 100644 src/js/events/encrypted.js create mode 100644 src/js/events/event.js create mode 100644 src/js/events/membership.js create mode 100644 src/js/events/message.js create mode 100644 src/js/events/renderEvent.js create mode 100644 src/js/events/unknown.js diff --git a/package-lock.json b/package-lock.json index 5b9d4ec..2174afb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2013,6 +2013,11 @@ "domelementtype": "1" } }, + "dompurify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.0.tgz", + "integrity": "sha512-bqFOQ7XRmmozp0VsKdIEe8UwZYxj0yttz7l80GBtBqdVRY48cOpXH2J/CVO7AEkV51qY0EBVXfilec18mdmQ/w==" + }, "domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", diff --git a/package.json b/package.json index ab4af6b..ebec6de 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "keywords": [], "author": "", "license": "AGPL-3.0-only", - "dependencies": {}, + "dependencies": { + "dompurify": "^2.2.0" + }, "devDependencies": { "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", diff --git a/src/js/dateFormatter.js b/src/js/dateFormatter.js new file mode 100644 index 0000000..c161d76 --- /dev/null +++ b/src/js/dateFormatter.js @@ -0,0 +1,3 @@ +const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", day: "numeric", month: "short", year: "numeric"}) + +module.exports = {dateFormatter} diff --git a/src/js/events/encrypted.js b/src/js/events/encrypted.js new file mode 100644 index 0000000..32dbe19 --- /dev/null +++ b/src/js/events/encrypted.js @@ -0,0 +1,18 @@ +const {Event} = require("./event") + +class EncryptedMessage extends Event { + render() { + super.render() + return this.text("Carbon cannot render encrypted messages yet") + } + + static canRender(event) { + return event.type == "m.room.encrypted" + } + + canGroup() { + return true + } +} + +module.exports = [EncryptedMessage] diff --git a/src/js/events/event.js b/src/js/events/event.js new file mode 100644 index 0000000..ddb412d --- /dev/null +++ b/src/js/events/event.js @@ -0,0 +1,59 @@ +const {ElemJS, ejs} = require("../basic") +const {dateFormatter} = require("../dateFormatter") + +class Event extends ElemJS { + constructor(data) { + super("div") + this.class("c-message") + this.data = null + this.group = null + this.editedAt = null + this.update(data) + } + + // predicates + + canGroup() { + //return this.data.type === "m.room.message" + return false + } + + // operations + + setGroup(group) { + this.group = group + } + + setEdited(time) { + this.editedAt = time + this.render() + } + + update(data) { + this.data = data + this.render() + } + + removeEvent() { + if (this.group) this.group.removeEvent(this) + else this.remove() + } + + render() { + this.element.classList[this.data.pending ? "add" : "remove"]("c-message--pending") + if (this.editedAt) { + this.child(ejs("span").class("c-message__edited").text("(edited)").attribute("title", "at " + dateFormatter.format(this.editedAt))) + } + } + static canRender(_event) { + return false + } +} + + + +function renderEvent(event) { + return new events.find(e => e.canRender(event))(event) +} + +module.exports = {renderEvent, Event} diff --git a/src/js/events/membership.js b/src/js/events/membership.js new file mode 100644 index 0000000..97daea3 --- /dev/null +++ b/src/js/events/membership.js @@ -0,0 +1,21 @@ +const {Event} = require("./event") + +function createMembershipEvent(membership, text) { + return class extends Event { + render() { + super.render() + return this.text(text(this.data)) + } + + static canRender(event) { + return event.type == "m.room.member" && event.content.membership == membership + } + + } +} + +const JoinedEvent = createMembershipEvent("join", () => "joined the room") +const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) +const LeaveEvent = createMembershipEvent("leave", () => "left the room") + +module.exports = [JoinedEvent, InvitedEvent, LeaveEvent] diff --git a/src/js/events/message.js b/src/js/events/message.js new file mode 100644 index 0000000..0218a60 --- /dev/null +++ b/src/js/events/message.js @@ -0,0 +1,67 @@ +const {ejs} = require("../basic") +const DOMPurify = require("dompurify") +const {resolveMxc} = require("../functions") +const {Event} = require("./event") + +const purifier = DOMPurify() +purifier.addHook("afterSanitizeAttributes", (node, hookevent, config) => { + if (node.tagName == "img") { + let src = node.getAttribute("src") + if (src) src = resolveMxc(src) + + node.setAttribute("src", src) + + } + if (node.tagName = "a") { + node.setAttribute("rel", "noopener") + } + return node + +}) + +function sanitize(html) { + return purifier.sanitize(html, DOMPURIFY_CONFIG) +} +const DOMPURIFY_CONFIG = { + ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris + ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], +}; + +class HTMLMessage extends Event { + render() { + super.render() + let html = this.data.content.formatted_body + const content = ejs("div") + html = sanitize(html) + content.html(html) + this.child(content) + } + + static canRender(event) { + const content = event.content + return event.type == "m.room.message" && content.msgtype == "m.text" && content.format == "org.matrix.custom.html" && content.formatted_body + + } + + canGroup() { + return true + } + +} + +class TextMessage extends Event { + render() { + super.render() + return this.text(this.data.content.body) + } + + static canRender(event) { + return event.type == "m.room.message" + } + + canGroup() { + return true + } +} + +module.exports = [HTMLMessage, TextMessage] diff --git a/src/js/events/renderEvent.js b/src/js/events/renderEvent.js new file mode 100644 index 0000000..e4616a2 --- /dev/null +++ b/src/js/events/renderEvent.js @@ -0,0 +1,19 @@ +const messageEvent = require("./message") +const encryptedEvent = require("./encrypted") +const membershipEvent = require("./membership") +const unknownEvent = require("./unknown") + +const events = [ + ...messageEvent, + ...encryptedEvent, + ...membershipEvent, + ...unknownEvent, +] + + +function renderEvent(eventData) { + const constructor = events.find(e => e.canRender(eventData)) + return new constructor(eventData) +} + +module.exports = {renderEvent} diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js new file mode 100644 index 0000000..d644de7 --- /dev/null +++ b/src/js/events/unknown.js @@ -0,0 +1,15 @@ +const {Event} = require("./event") + +class UnknownEvent extends Event { + render() { + super.render() + this.text("Cannot render event") + } + + static canRender(_event) { + return true + } + +} + +module.exports = [UnknownEvent] diff --git a/src/js/timeline.js b/src/js/timeline.js index 270b5c8..de7e63f 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -4,13 +4,13 @@ const {store} = require("./store/store.js") const {Anchor} = require("./anchor.js") const lsm = require("./lsm.js") const {resolveMxc} = require("./functions.js") +const {renderEvent} = require("./events/renderEvent") +const {dateFormatter} = require("./dateFormatter") let debug = false const NO_MAX = Symbol("NO_MAX") -const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", day: "numeric", month: "short", year: "numeric"}) - let sentIndex = 0 function getTxnId() { @@ -38,67 +38,6 @@ function eventSearch(list, event, min = 0, max = NO_MAX) { else return eventSearch(list, event, mid + 1, max) } -class Event extends ElemJS { - constructor(data) { - super("div") - this.class("c-message") - this.data = null - this.group = null - this.editedAt = null - this.update(data) - } - - // predicates - - canGroup() { - return this.data.type === "m.room.message" - } - - // operations - - setGroup(group) { - this.group = group - } - - setEdited(time) { - this.editedAt = time - this.render() - } - - update(data) { - this.data = data - this.render() - } - - removeEvent() { - if (this.group) this.group.removeEvent(this) - else this.remove() - } - - render() { - this.element.classList[this.data.pending ? "add" : "remove"]("c-message--pending") - if (this.data.type === "m.room.message") { - this.text(this.data.content.body) - } else if (this.data.type === "m.room.member") { - if (this.data.content.membership === "join") { - this.child(ejs("i").text("joined the room")) - } else if (this.data.content.membership === "invite") { - this.child(ejs("i").text(`invited ${this.data.content.displayname} to the room`)) - } else if (this.data.content.membership === "leave") { - this.child(ejs("i").text("left the room")) - } else { - this.child(ejs("i").text("unknown membership event")) - } - } else if (this.data.type === "m.room.encrypted") { - this.child(ejs("i").text("Carbon does not yet support encrypted messages.")) - } else { - this.child(ejs("i").text(`Unsupported event type ${this.data.type}`)) - } - if (this.editedAt) { - this.child(ejs("span").class("c-message__edited").text("(edited)").attribute("title", "at " + dateFormatter.format(this.editedAt))) - } - } -} class Sender { constructor(roomID, mxid) { @@ -345,7 +284,7 @@ class Timeline extends Subscribable { continue } // add new event - const event = new Event(eventData) + const event = renderEvent(eventData) this.map.set(id, event) this.reactiveTimeline.addEvent(event) } From 1a8427925c30c62ac29fc867ce4fa64dc3f6f258 Mon Sep 17 00:00:00 2001 From: Bad Date: Mon, 26 Oct 2020 22:55:27 +0100 Subject: [PATCH 37/92] Add unknown memberships --- src/js/events/membership.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/js/events/membership.js b/src/js/events/membership.js index 97daea3..9b554cf 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -1,10 +1,10 @@ const {Event} = require("./event") -function createMembershipEvent(membership, text) { +function createMembershipEvent(membership, message) { return class extends Event { render() { super.render() - return this.text(text(this.data)) + return this.text(message(this.data)) } static canRender(event) { @@ -13,9 +13,20 @@ function createMembershipEvent(membership, text) { } } +class UnknownMembership extends Event { + render() { + super.render() + return this.text("Unsupported membership event") + } + + static canRender(event) { + return event.type == "m.room.member" + } + +} const JoinedEvent = createMembershipEvent("join", () => "joined the room") const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) const LeaveEvent = createMembershipEvent("leave", () => "left the room") -module.exports = [JoinedEvent, InvitedEvent, LeaveEvent] +module.exports = [JoinedEvent, InvitedEvent, LeaveEvent, UnknownMembership] From f46f9abe6efa2e93e38ee9a92ad5399b02cddbd4 Mon Sep 17 00:00:00 2001 From: Bad Date: Mon, 26 Oct 2020 22:55:54 +0100 Subject: [PATCH 38/92] Improve rich text rendering to more closely match the recommendations from the spec --- src/js/events/message.js | 60 +++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/js/events/message.js b/src/js/events/message.js index 0218a60..549b2a0 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -4,28 +4,68 @@ const {resolveMxc} = require("../functions") const {Event} = require("./event") const purifier = DOMPurify() + +purifier.setConfig({ + ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris + ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], + + // In case we mess up none of those attributes should read to XSS + ALLOWED_ATTR: ["data-mx-bg-color", "data-mx-color", "color", + "name", "target", "href", + "width", "height", "alt", "title", "src", "data-mx-emoticon", + "start", "class"], + //Custom config option that allows the array of attributes for a given tag + ALLOWED_ATTR_CUSTOM: { + "FONT": ["data-mx-bg-color", "data-mx-color", "color"], + "SPAN": ["data-mx-bg-color", "data-mx-color", "color"], + "A": ["name", "target", "href"], + "IMG": ["width", "height", "alt", "title", "src", "data-mx-emoticon"], + "OL": ["start"], + "CODE": ["class"], + } +}) + +//Handle our custom tag +purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { + //If purifier already rejected an attribute there is no point in checking it + if (hookevent.keepAttr === false) return; + + const allowed_attributes = config.ALLOWED_ATTR_CUSTOM[node.tagName] || [] + hookevent.keepAttr = allowed_attributes.indexOf(hookevent.attrName) > -1; +}) + +//Remove bad classes from our code element +purifier.addHook("uponSanitizeElement", (node, hookevent, config) => { + if (node.tagName != "CODE") return + node.classList.forEach(c => { + if (!c.startsWith("language-")) { + node.classList.remove(c) + } + }) + return node +}) + purifier.addHook("afterSanitizeAttributes", (node, hookevent, config) => { - if (node.tagName == "img") { + if (node.tagName == "IMG") { let src = node.getAttribute("src") if (src) src = resolveMxc(src) node.setAttribute("src", src) - - } - if (node.tagName = "a") { + } else if (node.tagName == "A") { node.setAttribute("rel", "noopener") + } else if (node.tagName == "FONT" || node.tagName == "SPAN") { + const color = node.getAttribute("data-mx-color") + const bgColor = node.getAttribute("data-mx-bg-color") + if (color) node.style.color = color; + if (bgColor) node.style.backgroundColor = bgColor; } return node - }) + function sanitize(html) { - return purifier.sanitize(html, DOMPURIFY_CONFIG) + return purifier.sanitize(html) } -const DOMPURIFY_CONFIG = { - ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris - ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], -}; class HTMLMessage extends Event { render() { From d983385e166857edcb4ee329c4b8970f8a41b0c6 Mon Sep 17 00:00:00 2001 From: Bad Date: Mon, 26 Oct 2020 22:58:38 +0100 Subject: [PATCH 39/92] Fix compiler warnings --- src/js/events/encrypted.js | 4 ++-- src/js/events/event.js | 8 ++------ src/js/events/membership.js | 6 +++--- src/js/events/message.js | 6 +++--- src/js/events/unknown.js | 4 ++-- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/js/events/encrypted.js b/src/js/events/encrypted.js index 32dbe19..6d9d6d0 100644 --- a/src/js/events/encrypted.js +++ b/src/js/events/encrypted.js @@ -1,6 +1,6 @@ -const {Event} = require("./event") +const {MatrixEvent} = require("./event") -class EncryptedMessage extends Event { +class EncryptedMessage extends MatrixEvent { render() { super.render() return this.text("Carbon cannot render encrypted messages yet") diff --git a/src/js/events/event.js b/src/js/events/event.js index ddb412d..06d8a50 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -1,7 +1,7 @@ const {ElemJS, ejs} = require("../basic") const {dateFormatter} = require("../dateFormatter") -class Event extends ElemJS { +class MatrixEvent extends ElemJS { constructor(data) { super("div") this.class("c-message") @@ -52,8 +52,4 @@ class Event extends ElemJS { -function renderEvent(event) { - return new events.find(e => e.canRender(event))(event) -} - -module.exports = {renderEvent, Event} +module.exports = {MatrixEvent} diff --git a/src/js/events/membership.js b/src/js/events/membership.js index 9b554cf..285fc9e 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -1,7 +1,7 @@ -const {Event} = require("./event") +const {MatrixEvent} = require("./event") function createMembershipEvent(membership, message) { - return class extends Event { + return class extends MatrixEvent { render() { super.render() return this.text(message(this.data)) @@ -13,7 +13,7 @@ function createMembershipEvent(membership, message) { } } -class UnknownMembership extends Event { +class UnknownMembership extends MatrixEvent { render() { super.render() return this.text("Unsupported membership event") diff --git a/src/js/events/message.js b/src/js/events/message.js index 549b2a0..c50ec57 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -1,7 +1,7 @@ const {ejs} = require("../basic") const DOMPurify = require("dompurify") const {resolveMxc} = require("../functions") -const {Event} = require("./event") +const {MatrixEvent} = require("./event") const purifier = DOMPurify() @@ -67,7 +67,7 @@ function sanitize(html) { return purifier.sanitize(html) } -class HTMLMessage extends Event { +class HTMLMessage extends MatrixEvent { render() { super.render() let html = this.data.content.formatted_body @@ -89,7 +89,7 @@ class HTMLMessage extends Event { } -class TextMessage extends Event { +class TextMessage extends MatrixEvent { render() { super.render() return this.text(this.data.content.body) diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js index d644de7..6fb1a02 100644 --- a/src/js/events/unknown.js +++ b/src/js/events/unknown.js @@ -1,6 +1,6 @@ -const {Event} = require("./event") +const {MatrixEvent} = require("./event") -class UnknownEvent extends Event { +class UnknownEvent extends MatrixEvent { render() { super.render() this.text("Cannot render event") From e08b8956943e08f96b1f40a0b663814b895bfdfe Mon Sep 17 00:00:00 2001 From: Bad Date: Mon, 26 Oct 2020 23:16:47 +0100 Subject: [PATCH 40/92] Create a simple event shorthand --- src/js/events/event.js | 13 ++++++++++++- src/js/events/membership.js | 29 +++++------------------------ src/js/events/unknown.js | 17 +++-------------- 3 files changed, 20 insertions(+), 39 deletions(-) diff --git a/src/js/events/event.js b/src/js/events/event.js index 06d8a50..4ec6c54 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -50,6 +50,17 @@ class MatrixEvent extends ElemJS { } } +function simpleEvent(filter, render) { + return class extends MatrixEvent { + render() { + super.render() + return this.text(render(this.data)) + } + static canRender(event) { + return filter(event) + } + } +} -module.exports = {MatrixEvent} +module.exports = {MatrixEvent, simpleEvent} diff --git a/src/js/events/membership.js b/src/js/events/membership.js index 285fc9e..a81a3ca 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -1,31 +1,12 @@ -const {MatrixEvent} = require("./event") +const {simpleEvent} = require("./event") + +const UnknownMembership = simpleEvent((e) => e.type == "m.room.member", (e) => "unknown membership event") function createMembershipEvent(membership, message) { - return class extends MatrixEvent { - render() { - super.render() - return this.text(message(this.data)) - } - - static canRender(event) { - return event.type == "m.room.member" && event.content.membership == membership - } - - } -} -class UnknownMembership extends MatrixEvent { - render() { - super.render() - return this.text("Unsupported membership event") - } - - static canRender(event) { - return event.type == "m.room.member" - } - + return simpleEvent((e) => e.type == "m.room.member" && e.content.membership === membership, message) } -const JoinedEvent = createMembershipEvent("join", () => "joined the room") +const JoinedEvent = createMembershipEvent("join", (e) => {console.log(e); return "joined the room"}) const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) const LeaveEvent = createMembershipEvent("leave", () => "left the room") diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js index 6fb1a02..bbc40f8 100644 --- a/src/js/events/unknown.js +++ b/src/js/events/unknown.js @@ -1,15 +1,4 @@ -const {MatrixEvent} = require("./event") - -class UnknownEvent extends MatrixEvent { - render() { - super.render() - this.text("Cannot render event") - } - - static canRender(_event) { - return true - } - -} - +const {simpleEvent} = require("./event") +const UnknownEvent = simpleEvent(() => true, () => "Cannot render event") +console.log(UnknownEvent) module.exports = [UnknownEvent] From 5bfe98bdf4ab7108b6fc738a0c8e4ebe7c754353 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 29 Oct 2020 17:26:34 +1300 Subject: [PATCH 41/92] Stop scrollback at top of timeline --- src/js/timeline.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/js/timeline.js b/src/js/timeline.js index 270b5c8..59e3120 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -396,7 +396,13 @@ class Timeline extends Subscribable { this.from = root.end // console.log(this.updateEvents, root.chunk) if (root.state) this.updateStateEvents(root.state) - this.updateEvents(root.chunk) + if (root.chunk.length) { + // there are events to display + this.updateEvents(root.chunk) + } else { + // we reached the top of the scrollback + this.reactiveTimeline.loadMore.remove() + } this.broadcast("afterScrollbackLoad") } From a4c7f29ec9dd9484aaa5bd00597eb8b3bfa445ed Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 29 Oct 2020 17:27:38 +1300 Subject: [PATCH 42/92] Emacs files to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 75e7d32..1d83ade 100644 --- a/.gitignore +++ b/.gitignore @@ -288,6 +288,10 @@ modules.xml # End of https://www.toptal.com/developers/gitignore/api/node,vscode,webstorm,webstorm+all +# Emacs +*~ +\#*# + # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) /build/ From ff196a64bb459435b954ad4c0746911a9b3f5c98 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 29 Oct 2020 17:28:12 +1300 Subject: [PATCH 43/92] Improve message sender rendering - Refactor sender class into parts - Sender name colour depends on mxid, like Element - (colours slightly modified for contrast) - Display blank avatar if loading fails - Remove # parts from mxc - Don't replace member state if loaded state is older --- src/js/chat.js | 2 +- src/js/functions.js | 3 +- src/js/sender.js | 120 ++++++++++++++++++++++++++++++ src/js/timeline.js | 43 ++--------- src/sass/components/messages.sass | 2 +- 5 files changed, 130 insertions(+), 40 deletions(-) create mode 100644 src/js/sender.js diff --git a/src/js/chat.js b/src/js/chat.js index 050ff8b..6695c3a 100644 --- a/src/js/chat.js +++ b/src/js/chat.js @@ -42,7 +42,7 @@ class Chat extends ElemJS { } this.addSubscription("beforeChange", timeline, beforeChangeSubscription) - //Make sure after loading scrollback we don't move the scroll position + // Make sure after loading scrollback we don't move the scroll position const beforeScrollbackLoadSubscription = () => { const lastScrollHeight = chatMessages.scrollHeight; diff --git a/src/js/functions.js b/src/js/functions.js index bf94921..da60793 100644 --- a/src/js/functions.js +++ b/src/js/functions.js @@ -1,7 +1,8 @@ const lsm = require("./lsm.js") function resolveMxc(url, size, method) { - const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1) + let [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1) + id = id.replace(/#.*$/, "") if (size && method) { return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}` } else { diff --git a/src/js/sender.js b/src/js/sender.js new file mode 100644 index 0000000..55943dc --- /dev/null +++ b/src/js/sender.js @@ -0,0 +1,120 @@ +const {ElemJS, ejs} = require("./basic.js") +const {store} = require("./store/store.js") +const {resolveMxc} = require("./functions.js") + +function nameToColor(str) { + // code from element's react sdk + const colors = ["#55a7f0", "#da55ff", "#1bc47c", "#ea657e", "#fd8637", "#22cec6", "#8c8de3", "#71bf22"] + let hash = 0 + let i + let chr + if (str.length === 0) { + return hash + } + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i) + hash = ((hash << 5) - hash) + chr + hash |= 0 + } + hash = Math.abs(hash) % 8 + return colors[hash] +} + +class Avatar extends ElemJS { + constructor() { + super("div") + this.class("c-message-group__avatar") + + this.mxc = undefined + this.image = null + + this.update(null) + } + + update(mxc) { + if (mxc === this.mxc) return + this.mxc = mxc + this.hasImage = !!mxc + if (this.hasImage) { + const size = 96 + const url = resolveMxc(mxc, size, "crop") + this.image = ejs("img").class("c-message-group__icon").attribute("src", url).attribute("width", size).attribute("height", size) + this.image.on("error", this.onError.bind(this)) + } + this.render() + } + + onError() { + this.hasImage = false + this.render() + } + + render() { + this.clearChildren() + if (this.hasImage) { + this.child(this.image) + } else { + this.child( + ejs("div").class("c-message-group__icon", "c-message-group__icon--no-icon") + ) + } + } +} + +/** Must update at least once to render. */ +class Name extends ElemJS { + constructor() { + super("div") + this.class("c-message-group__name") + + /** + * Keeps track of whether we have the proper display name or not. + * If we do, then we shoudn't override it with the mxid if the name becomes unavailable. + */ + this.hasName = false + this.name = "" + this.mxid = "" + } + + update(event) { + this.mxid = event.state_key + if (event.content.displayname) { + this.hasName = true + this.name = event.content.displayname + } else if (!this.hasName) { + this.name = this.mxid + } + this.render() + } + + render() { + // set text + this.text(this.name) + // set color + this.style("color", nameToColor(this.mxid)) + } +} + +class Sender { + constructor(roomID, mxid) { + this.sender = store.rooms.get(roomID).value().members.get(mxid) + this.name = new Name() + this.avatar = new Avatar() + this.sender.subscribe("changeSelf", this.update.bind(this)) + this.update() + } + + update() { + if (this.sender.exists()) { + // name + this.name.update(this.sender.value()) + + // avatar + this.avatar.update(this.sender.value().content.avatar_url) + } + } +} + +module.exports = { + Sender +} diff --git a/src/js/timeline.js b/src/js/timeline.js index 59e3120..c03c635 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -2,8 +2,8 @@ const {ElemJS, ejs} = require("./basic.js") const {Subscribable} = require("./store/subscribable.js") const {store} = require("./store/store.js") const {Anchor} = require("./anchor.js") +const {Sender} = require("./sender.js") const lsm = require("./lsm.js") -const {resolveMxc} = require("./functions.js") let debug = false @@ -100,41 +100,6 @@ class Event extends ElemJS { } } -class Sender { - constructor(roomID, mxid) { - this.sender = store.rooms.get(roomID).value().members.get(mxid) - this.sender.subscribe("changeSelf", this.update.bind(this)) - this.name = new ElemJS("div").class("c-message-group__name") - this.avatar = new ElemJS("div").class("c-message-group__avatar") - this.displayingGoodData = false - this.update() - } - - update() { - if (this.sender.exists()) { - // name - if (this.sender.value().content.displayname) { - this.name.text(this.sender.value().content.displayname) - this.displayingGoodData = true - } else if (!this.displayingGoodData) { - this.name.text(this.sender.value().state_key) - } - - // avatar - this.avatar.clearChildren() - if (this.sender.value().content.avatar_url) { - this.avatar.child( - ejs("img").class("c-message-group__icon").attribute("src", resolveMxc(this.sender.value().content.avatar_url, 96, "crop")) - ) - } else { - this.avatar.child( - ejs("div").class("c-message-group__icon", "c-message-group__icon--no-icon") - ) - } - } - } -} - class EventGroup extends ElemJS { constructor(reactive, list) { super("div") @@ -301,7 +266,11 @@ class Timeline extends Subscribable { if (eventData.type === "m.room.member") { // update members if (eventData.membership !== "leave") { - this.room.members.get(eventData.state_key).set(eventData) + const member = this.room.members.get(eventData.state_key) + // only use the latest state + if (!member.exists() || eventData.origin_server_ts > member.data.origin_server_ts) { + member.set(eventData) + } } } } diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 8a33dc2..2a7665a 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -23,7 +23,7 @@ border-radius: 50% &--no-icon - background-color: #48d + background-color: #bbb &__intro display: flex From 66ecf44048c23537edce79effdbe1327af59ee2a Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 29 Oct 2020 10:36:38 +0100 Subject: [PATCH 44/92] Remove console.log from membership --- src/js/events/membership.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/events/membership.js b/src/js/events/membership.js index a81a3ca..f00a765 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -6,7 +6,7 @@ function createMembershipEvent(membership, message) { return simpleEvent((e) => e.type == "m.room.member" && e.content.membership === membership, message) } -const JoinedEvent = createMembershipEvent("join", (e) => {console.log(e); return "joined the room"}) +const JoinedEvent = createMembershipEvent("join", (e) => "joined the room") const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) const LeaveEvent = createMembershipEvent("leave", () => "left the room") From c144d75c99d61d0e19b89d1d321b8f99f452f142 Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 29 Oct 2020 10:38:12 +0100 Subject: [PATCH 45/92] Remove debug console.logs --- src/js/events/unknown.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js index bbc40f8..3e3bbd6 100644 --- a/src/js/events/unknown.js +++ b/src/js/events/unknown.js @@ -1,4 +1,3 @@ const {simpleEvent} = require("./event") const UnknownEvent = simpleEvent(() => true, () => "Cannot render event") -console.log(UnknownEvent) module.exports = [UnknownEvent] From bd9623578f9e31fd640c3edaa09a513382a31811 Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 29 Oct 2020 10:42:17 +0100 Subject: [PATCH 46/92] Add hljs and improve sanitization --- package-lock.json | 5 ++ package.json | 3 +- src/js/events/components.js | 11 +++ src/js/events/event.js | 2 + src/js/events/message.js | 99 +++++++++++++---------- src/sass/components/highlighted-code.sass | 1 + src/sass/main.sass | 1 + 7 files changed, 77 insertions(+), 45 deletions(-) create mode 100644 src/js/events/components.js create mode 100644 src/sass/components/highlighted-code.sass diff --git a/package-lock.json b/package-lock.json index 2174afb..efaf46a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2412,6 +2412,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "highlight.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.1.tgz", + "integrity": "sha512-jeW8rdPdhshYKObedYg5XGbpVgb1/DT4AHvDFXhkU7UnGSIjy9kkJ7zHG7qplhFHMitTSzh5/iClKQk3Kb2RFQ==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index ebec6de..4bdc271 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "author": "", "license": "AGPL-3.0-only", "dependencies": { - "dompurify": "^2.2.0" + "dompurify": "^2.2.0", + "highlight.js": "^10.3.1" }, "devDependencies": { "@babel/core": "^7.11.1", diff --git a/src/js/events/components.js b/src/js/events/components.js new file mode 100644 index 0000000..2280fd6 --- /dev/null +++ b/src/js/events/components.js @@ -0,0 +1,11 @@ +const {ElemJS} = require("../basic") +const hljs = require("highlight.js") + +class HighlightedCode extends ElemJS { + constructor(code) { + super(code) + hljs.highlightBlock(this.element) + } +} + +module.exports = {HighlightedCode} diff --git a/src/js/events/event.js b/src/js/events/event.js index 4ec6c54..765c03a 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -44,7 +44,9 @@ class MatrixEvent extends ElemJS { if (this.editedAt) { this.child(ejs("span").class("c-message__edited").text("(edited)").attribute("title", "at " + dateFormatter.format(this.editedAt))) } + return this } + static canRender(_event) { return false } diff --git a/src/js/events/message.js b/src/js/events/message.js index c50ec57..05ccfdd 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -1,21 +1,17 @@ -const {ejs} = require("../basic") +const {ejs, ElemJS} = require("../basic") +const hljs = require("highlight.js") +const {HighlightedCode} = require("./components") const DOMPurify = require("dompurify") const {resolveMxc} = require("../functions") const {MatrixEvent} = require("./event") const purifier = DOMPurify() -purifier.setConfig({ - ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris - ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], +purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { + //If purifier already rejected an attribute there is no point in checking it + if (hookevent.keepAttr === false) return; - // In case we mess up none of those attributes should read to XSS - ALLOWED_ATTR: ["data-mx-bg-color", "data-mx-color", "color", - "name", "target", "href", - "width", "height", "alt", "title", "src", "data-mx-emoticon", - "start", "class"], - //Custom config option that allows the array of attributes for a given tag - ALLOWED_ATTR_CUSTOM: { + const allowedElementAttributes = { "FONT": ["data-mx-bg-color", "data-mx-color", "color"], "SPAN": ["data-mx-bg-color", "data-mx-color", "color"], "A": ["name", "target", "href"], @@ -23,58 +19,73 @@ purifier.setConfig({ "OL": ["start"], "CODE": ["class"], } -}) -//Handle our custom tag -purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { - //If purifier already rejected an attribute there is no point in checking it - if (hookevent.keepAttr === false) return; - - const allowed_attributes = config.ALLOWED_ATTR_CUSTOM[node.tagName] || [] + const allowed_attributes = allowedElementAttributes[node.tagName] || [] hookevent.keepAttr = allowed_attributes.indexOf(hookevent.attrName) > -1; }) //Remove bad classes from our code element purifier.addHook("uponSanitizeElement", (node, hookevent, config) => { - if (node.tagName != "CODE") return - node.classList.forEach(c => { - if (!c.startsWith("language-")) { - node.classList.remove(c) - } - }) - return node -}) - -purifier.addHook("afterSanitizeAttributes", (node, hookevent, config) => { - if (node.tagName == "IMG") { - let src = node.getAttribute("src") - if (src) src = resolveMxc(src) - - node.setAttribute("src", src) - } else if (node.tagName == "A") { + if (node.tagName == "CODE") { + node.classList.forEach(c => { + if (!c.startsWith("language-")) { + node.classList.remove(c) + } + }) + } + if (node.tagName == "A") { node.setAttribute("rel", "noopener") - } else if (node.tagName == "FONT" || node.tagName == "SPAN") { - const color = node.getAttribute("data-mx-color") - const bgColor = node.getAttribute("data-mx-bg-color") - if (color) node.style.color = color; - if (bgColor) node.style.backgroundColor = bgColor; } return node }) +function cleanHTML(html) { + const config = { + ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris + ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], -function sanitize(html) { + // In case we mess up in the uponSanitizeAttribute hook + ALLOWED_ATTR: ["data-mx-bg-color", "data-mx-color", "color", + "name", "target", "href", + "width", "height", "alt", "title", "src", "data-mx-emoticon", + "start", "class"], + } return purifier.sanitize(html) } +//Here we put all the processing of the messages that isn't as likely to potentially lead to security issues +function postProcessElements(rootNode) { + const element = rootNode.element + element.querySelectorAll("code").forEach((n) => rootNode.child(new HighlightedCode(n))) + + element.querySelectorAll("img").forEach((n) => { + let src = n.getAttribute("src") + if (src) src = resolveMxc(src) + n.setAttribute("src", src) + }) + element.querySelectorAll("font, span").forEach((n) => { + const color = n.getAttribute("data-mx-color") || n.getAttribute("color") + const bgColor = n.getAttribute("data-mx-bg-color") + if (color) n.style.color = color; + if (bgColor) n.style.backgroundColor = bgColor; + }) +} + + class HTMLMessage extends MatrixEvent { render() { - super.render() + this.clearChildren() + let html = this.data.content.formatted_body const content = ejs("div") - html = sanitize(html) + + html = cleanHTML(html) content.html(html) + postProcessElements(content) + this.child(content) + + super.render() } static canRender(event) { @@ -91,8 +102,8 @@ class HTMLMessage extends MatrixEvent { class TextMessage extends MatrixEvent { render() { - super.render() - return this.text(this.data.content.body) + this.text(this.data.content.body) + return super.render() } static canRender(event) { diff --git a/src/sass/components/highlighted-code.sass b/src/sass/components/highlighted-code.sass new file mode 100644 index 0000000..1ec7290 --- /dev/null +++ b/src/sass/components/highlighted-code.sass @@ -0,0 +1 @@ +@use "../../../node_modules/highlight.js/scss/obsidian" diff --git a/src/sass/main.sass b/src/sass/main.sass index 150af73..24cd6ca 100644 --- a/src/sass/main.sass +++ b/src/sass/main.sass @@ -5,4 +5,5 @@ @use "./components/chat" @use "./components/chat-input" @use "./components/anchor" +@use "./components/highlighted-code" @use "./loading" From f80bf36991d2681794e3e9afc6b77ac8af042631 Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 29 Oct 2020 11:09:15 +0100 Subject: [PATCH 47/92] Style fixes --- src/js/events/event.js | 1 - src/js/events/message.js | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/js/events/event.js b/src/js/events/event.js index 765c03a..8f5ff74 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -14,7 +14,6 @@ class MatrixEvent extends ElemJS { // predicates canGroup() { - //return this.data.type === "m.room.message" return false } diff --git a/src/js/events/message.js b/src/js/events/message.js index 05ccfdd..bbd1b15 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -8,7 +8,7 @@ const {MatrixEvent} = require("./event") const purifier = DOMPurify() purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { - //If purifier already rejected an attribute there is no point in checking it + // If purifier already rejected an attribute there is no point in checking it if (hookevent.keepAttr === false) return; const allowedElementAttributes = { @@ -24,8 +24,8 @@ purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { hookevent.keepAttr = allowed_attributes.indexOf(hookevent.attrName) > -1; }) -//Remove bad classes from our code element purifier.addHook("uponSanitizeElement", (node, hookevent, config) => { + // Remove bad classes from our code element if (node.tagName == "CODE") { node.classList.forEach(c => { if (!c.startsWith("language-")) { @@ -53,7 +53,7 @@ function cleanHTML(html) { return purifier.sanitize(html) } -//Here we put all the processing of the messages that isn't as likely to potentially lead to security issues +// Here we put all the processing of the messages that isn't as likely to potentially lead to security issues function postProcessElements(rootNode) { const element = rootNode.element element.querySelectorAll("code").forEach((n) => rootNode.child(new HighlightedCode(n))) From 20bacce0682cc4c31e8a8f655808d99b5d886c98 Mon Sep 17 00:00:00 2001 From: Bad Date: Thu, 29 Oct 2020 11:31:08 +0100 Subject: [PATCH 48/92] Remove the simple event shorthand --- src/js/events/event.js | 15 +---------- src/js/events/membership.js | 50 +++++++++++++++++++++++++++++++------ src/js/events/unknown.js | 14 +++++++++-- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/js/events/event.js b/src/js/events/event.js index 8f5ff74..ac09d5f 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -51,17 +51,4 @@ class MatrixEvent extends ElemJS { } } -function simpleEvent(filter, render) { - return class extends MatrixEvent { - render() { - super.render() - return this.text(render(this.data)) - } - - static canRender(event) { - return filter(event) - } - } -} - -module.exports = {MatrixEvent, simpleEvent} +module.exports = {MatrixEvent} diff --git a/src/js/events/membership.js b/src/js/events/membership.js index f00a765..04c194d 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -1,13 +1,47 @@ -const {simpleEvent} = require("./event") +const {MatrixEvent} = require("./event") -const UnknownMembership = simpleEvent((e) => e.type == "m.room.member", (e) => "unknown membership event") - -function createMembershipEvent(membership, message) { - return simpleEvent((e) => e.type == "m.room.member" && e.content.membership === membership, message) +class MembershipEvent extends MatrixEvent { + static canRender(event) { + return event.type == "m.room.member" + } } -const JoinedEvent = createMembershipEvent("join", (e) => "joined the room") -const InvitedEvent = createMembershipEvent("invite", (e) => `invited ${e.content.displayname} the room`) -const LeaveEvent = createMembershipEvent("leave", () => "left the room") + +class JoinedEvent extends MembershipEvent { + static canRender(event) { + return super.canRender(event) && event.content.membership === "join" + } + render() { + super.render() + return this.text("joined the room") + } +} + +class InvitedEvent extends MembershipEvent { + static canRender(event) { + return super.canRender(event) && event.content.membership === "invite" + } + render() { + super.render() + return this.text(`invited ${this.data.content.displayname}`) + } +} + +class LeaveEvent extends MembershipEvent { + static canRender(event) { + return super.canRender(event) && event.content.membership === "leave" + } + render() { + super.render() + return this.text(`left the room`) + } +} + +class UnknownMembership extends MembershipEvent { + render() { + super.render() + return this.text("unknown membership event") + } +} module.exports = [JoinedEvent, InvitedEvent, LeaveEvent, UnknownMembership] diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js index 3e3bbd6..7fddf5b 100644 --- a/src/js/events/unknown.js +++ b/src/js/events/unknown.js @@ -1,3 +1,13 @@ -const {simpleEvent} = require("./event") -const UnknownEvent = simpleEvent(() => true, () => "Cannot render event") +const {MatrixEvent} = require("./event") + +class UnknownEvent extends MatrixEvent { + static canRender() { + return true + } + render() { + super.render() + return this.text(`cannot render event`) + } +} module.exports = [UnknownEvent] + From 5a41a2c943adbd16edc5dfac537e50f94114dcb3 Mon Sep 17 00:00:00 2001 From: Bad Date: Fri, 30 Oct 2020 23:00:49 +0100 Subject: [PATCH 49/92] Push build artifacts to dev.carbon.chat --- .drone.yml | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/.drone.yml b/.drone.yml index 79b2835..d815318 100644 --- a/.drone.yml +++ b/.drone.yml @@ -11,11 +11,33 @@ steps: - name: package image: fuww/alpine-zip + when: + event: + - push commands: - SHORTREV=`echo $DRONE_COMMIT | cut -b 1-8` - - echo FILENAME=`date +%Y%m%d%H%m`-$SHORTREV.zip >> environment + - echo NAME=`date +%Y%m%d%H%m`-$SHORTREV >> environment - source environment - - zip -r $FILENAME build + - zip -r $NAME.zip build + + - name: dev.carbon.chat + image: drillster/drone-rsync + when: + event: + - push + settings: + hosts: + from_secret: SSH_HOST + port: + from_secret: SSH_PORT + user: + from_secret: SSH_USERNAME + key: + from_secret: SSH_KEY + source: ./build/* + target: ${DRONE_COMMIT_SHA:0:8} + recursive: true + - name: b2 image: tianon/backblaze-b2:2 @@ -28,8 +50,9 @@ steps: from_secret: b2_account_id KEY: from_secret: b2_application_key + COMMIT: ${DRONE_COMMIT_SHA:0:8} commands: - source environment - b2 authorize-account $ACCOUNT $KEY - - b2 upload-file $BUCKET $FILENAME $FILENAME - - echo Build artifacts avaliable at `b2 make-friendly-url $BUCKET $FILENAME` + - b2 upload-file $BUCKET $NAME.zip $NAME.zip + - echo Build artifacts avaliable at `b2 make-friendly-url $BUCKET $NAME.zip` and at https://dev.carbon.chat/$COMMIT From 20e94f05e76df01709bbbfbd384ed971888646d9 Mon Sep 17 00:00:00 2001 From: Bad Date: Sat, 31 Oct 2020 18:17:34 +0100 Subject: [PATCH 50/92] Lazy load highlight.js This significantly reduces the bundle size(over 1MiB!) but it also uses some hacks to dynamically load browserify modules on runtime(see lazy-load-modules.js --- build.js | 9 +++++++-- spec.js | 5 +++++ src/home.pug | 1 + src/js/events/components.js | 4 ++-- src/js/events/message.js | 1 - src/js/hljs.js | 2 ++ src/js/lazy-load-module.js | 19 +++++++++++++++++++ 7 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 src/js/hljs.js create mode 100644 src/js/lazy-load-module.js diff --git a/build.js b/build.js index 0d0d7da..76242d6 100644 --- a/build.js +++ b/build.js @@ -145,9 +145,11 @@ async function addJS(sourcePath, targetPath) { await fs.promises.writeFile(pj(buildDir, targetPath), content) } -async function addBundle(sourcePath, targetPath) { +async function addBundle(sourcePath, targetPath, module = false) { + let opts = {} + if(module) opts.standalone = sourcePath const content = await new Promise(resolve => { - browserify() + browserify([], opts) .add(pj(".", sourcePath)) .transform(file => { let content = "" @@ -287,6 +289,9 @@ async function addBabel(sourcePath, targetPath) { await addPug(item.source, item.target) } else if (item.type === "bundle") { await addBundle(item.source, item.target) + } else if (item.type === "module") { + // Creates a standalone bundle that can be imported on runtime + await addBundle(item.source, item.target, true) } else { throw new Error("Unknown item type: "+item.type) } diff --git a/spec.js b/spec.js index 1babec8..7808403 100644 --- a/spec.js +++ b/spec.js @@ -19,6 +19,11 @@ module.exports = [ source: "/js/main.js", target: "/static/bundle.js" }, + { + type: "module", + source: "/js/hljs.js", + target: "/static/hljs.js" + }, { type: "file", source: "/assets/fonts/whitney-500.woff", diff --git a/src/home.pug b/src/home.pug index 09a00c7..0f3fb7e 100644 --- a/src/home.pug +++ b/src/home.pug @@ -40,6 +40,7 @@ html != JSON.stringify([...static.keys()].map(k => [k, getStatic(k)])) | ) link(rel="stylesheet" type="text/css" href=getStatic("/sass/main.sass")) + link(rel="preload" as="script" href=getStatic("/js/hljs.js")) script(type="module" src=getStatic("/js/main.js")) body main.main diff --git a/src/js/events/components.js b/src/js/events/components.js index 2280fd6..7e1b498 100644 --- a/src/js/events/components.js +++ b/src/js/events/components.js @@ -1,10 +1,10 @@ const {ElemJS} = require("../basic") -const hljs = require("highlight.js") +const {lazyLoad} = require("../lazy-load-module") class HighlightedCode extends ElemJS { constructor(code) { super(code) - hljs.highlightBlock(this.element) + lazyLoad("/static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) } } diff --git a/src/js/events/message.js b/src/js/events/message.js index bbd1b15..b1ea8d9 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -1,5 +1,4 @@ const {ejs, ElemJS} = require("../basic") -const hljs = require("highlight.js") const {HighlightedCode} = require("./components") const DOMPurify = require("dompurify") const {resolveMxc} = require("../functions") diff --git a/src/js/hljs.js b/src/js/hljs.js new file mode 100644 index 0000000..1f224c7 --- /dev/null +++ b/src/js/hljs.js @@ -0,0 +1,2 @@ +const hljs = require("highlight.js") +module.exports = hljs diff --git a/src/js/lazy-load-module.js b/src/js/lazy-load-module.js new file mode 100644 index 0000000..7296234 --- /dev/null +++ b/src/js/lazy-load-module.js @@ -0,0 +1,19 @@ +// I hate this with passion +async function lazyLoad(url) { + const cache = window.lazy_load_cache || new Map() + if (cache.get(url)) return cache.get(url) + + const module = loadModuleWithoutCache(url) + cache.set(url, module) + return module +} + +// Loads the module without caching +async function loadModuleWithoutCache(url) { + const src = await fetch(url).then(r => r.text()) + let module = {} + eval(src) + return module.exports +} + +module.exports = {lazyLoad} From 0738ce4cb1390e17935ba1bf97a5423340ecceca Mon Sep 17 00:00:00 2001 From: Bad Date: Sat, 31 Oct 2020 18:20:56 +0100 Subject: [PATCH 51/92] Rename dateFormatter.js to date-formatter.js --- src/js/{dateFormatter.js => date-formatter.js} | 0 src/js/events/event.js | 2 +- src/js/timeline.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/js/{dateFormatter.js => date-formatter.js} (100%) diff --git a/src/js/dateFormatter.js b/src/js/date-formatter.js similarity index 100% rename from src/js/dateFormatter.js rename to src/js/date-formatter.js diff --git a/src/js/events/event.js b/src/js/events/event.js index ac09d5f..8ad5ff4 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -1,5 +1,5 @@ const {ElemJS, ejs} = require("../basic") -const {dateFormatter} = require("../dateFormatter") +const {dateFormatter} = require("../date-formatter") class MatrixEvent extends ElemJS { constructor(data) { diff --git a/src/js/timeline.js b/src/js/timeline.js index 0c144ef..67bfbcd 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -6,7 +6,7 @@ const {Sender} = require("./sender.js") const lsm = require("./lsm.js") const {resolveMxc} = require("./functions.js") const {renderEvent} = require("./events/renderEvent") -const {dateFormatter} = require("./dateFormatter") +const {dateFormatter} = require("./date-formatter") let debug = false From 1bf17126849f97494e85c2649954b05e436b8367 Mon Sep 17 00:00:00 2001 From: Bad Date: Sat, 31 Oct 2020 18:24:05 +0100 Subject: [PATCH 52/92] Fix dynamic import with relative paths --- src/js/events/components.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/events/components.js b/src/js/events/components.js index 7e1b498..bb0cc63 100644 --- a/src/js/events/components.js +++ b/src/js/events/components.js @@ -4,7 +4,7 @@ const {lazyLoad} = require("../lazy-load-module") class HighlightedCode extends ElemJS { constructor(code) { super(code) - lazyLoad("/static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) + lazyLoad("./static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) } } From ebf6e7ea783ac2279c9e45ec3ec3ed1addf2d3c3 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 16:23:40 +1300 Subject: [PATCH 53/92] Show proper user data in room list --- src/js/room-picker.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/js/room-picker.js b/src/js/room-picker.js index a4d7303..b04bcc6 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -107,7 +107,22 @@ class Room extends ElemJS { // if the room has no alias, use the names of its members ("heroes") const users = this.data.summary["m.heroes"] if (users && users.length) { - const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u) + const usernames = users.map(u => { + // if the member is in the room, use their display name + if (this.members.has(u)) { + const displayname = this.members.get(u).value().content.displayname + if (displayname) { + return displayname + } + } + // we don't have the member, so extract the localpart from the mxid + const match = u.match(/^@([^:]+):/) + if (match) { + return match[1] + } + // localpart extraction failed, use the whole mxid + return u + }) return usernames.join(", ") } // the room is empty @@ -115,6 +130,7 @@ class Room extends ElemJS { } getIcon() { + // if the room has a normal avatar const avatar = this.data.state.events.find(e => e.type === "m.room.avatar") if (avatar) { const url = avatar.content.url || avatar.content.avatar_url @@ -122,6 +138,15 @@ class Room extends ElemJS { return resolveMxc(url, 32, "crop") } } + // if the room has no avatar set, use a member's avatar + const users = this.data.summary["m.heroes"] + if (users && users[0] && this.members.has(users[0])) { + // console.log(users[0], this.members.get(users[0])) + const userAvatar = this.members.get(users[0]).value().content.avatar_url + if (userAvatar) { + return resolveMxc(userAvatar, 32, "crop") + } + } return null } From 714147b9802a0b0c6f0041b9299714f4a462ced5 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 16:32:42 +1300 Subject: [PATCH 54/92] Fix lazy loading cache --- src/js/lazy-load-module.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/lazy-load-module.js b/src/js/lazy-load-module.js index 7296234..efe642d 100644 --- a/src/js/lazy-load-module.js +++ b/src/js/lazy-load-module.js @@ -1,6 +1,7 @@ // I hate this with passion async function lazyLoad(url) { - const cache = window.lazy_load_cache || new Map() + const cache = window.lazyLoadCache || new Map() + window.lazyLoadCache = cache if (cache.get(url)) return cache.get(url) const module = loadModuleWithoutCache(url) From 9cf0952d3a4ed7df449f8da5243356deb231e363 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 16:34:10 +1300 Subject: [PATCH 55/92] Change files to kebab-case --- src/js/events/{renderEvent.js => render-event.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/js/events/{renderEvent.js => render-event.js} (100%) diff --git a/src/js/events/renderEvent.js b/src/js/events/render-event.js similarity index 100% rename from src/js/events/renderEvent.js rename to src/js/events/render-event.js From 8ba9d73b33961c19d60a96f4438ff383283a01a2 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 16:44:22 +1300 Subject: [PATCH 56/92] Small refactors - "event" -> "eventData" - create renderText method - italics --- src/js/events/encrypted.js | 10 +++++++--- src/js/events/event.js | 2 +- src/js/events/membership.js | 40 ++++++++++++++++++++++--------------- src/js/events/unknown.js | 8 +++++++- src/js/timeline.js | 2 +- 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/js/events/encrypted.js b/src/js/events/encrypted.js index 6d9d6d0..f73d4b9 100644 --- a/src/js/events/encrypted.js +++ b/src/js/events/encrypted.js @@ -1,13 +1,17 @@ const {MatrixEvent} = require("./event") +const {ejs} = require("../basic") class EncryptedMessage extends MatrixEvent { render() { + this.clearChildren() + this.child( + ejs("i").text("Carbon cannot render encrypted messages yet") + ) super.render() - return this.text("Carbon cannot render encrypted messages yet") } - static canRender(event) { - return event.type == "m.room.encrypted" + static canRender(eventData) { + return eventData.type === "m.room.encrypted" } canGroup() { diff --git a/src/js/events/event.js b/src/js/events/event.js index 8ad5ff4..a5978a1 100644 --- a/src/js/events/event.js +++ b/src/js/events/event.js @@ -46,7 +46,7 @@ class MatrixEvent extends ElemJS { return this } - static canRender(_event) { + static canRender(eventData) { return false } } diff --git a/src/js/events/membership.js b/src/js/events/membership.js index 04c194d..2e85bae 100644 --- a/src/js/events/membership.js +++ b/src/js/events/membership.js @@ -1,46 +1,54 @@ const {MatrixEvent} = require("./event") +const {ejs} = require("../basic") class MembershipEvent extends MatrixEvent { - static canRender(event) { - return event.type == "m.room.member" + static canRender(eventData) { + return eventData.type === "m.room.member" + } + + renderText(text) { + this.clearChildren() + this.child( + ejs("i").text(text) + ) + super.render() } } class JoinedEvent extends MembershipEvent { - static canRender(event) { - return super.canRender(event) && event.content.membership === "join" + static canRender(eventData) { + return super.canRender(eventData) && eventData.content.membership === "join" } + render() { - super.render() - return this.text("joined the room") + this.renderText("joined the room") } } class InvitedEvent extends MembershipEvent { - static canRender(event) { - return super.canRender(event) && event.content.membership === "invite" + static canRender(eventData) { + return super.canRender(eventData) && eventData.content.membership === "invite" } + render() { - super.render() - return this.text(`invited ${this.data.content.displayname}`) + this.renderText(`invited ${this.data.content.displayname}`) } } class LeaveEvent extends MembershipEvent { - static canRender(event) { - return super.canRender(event) && event.content.membership === "leave" + static canRender(eventData) { + return super.canRender(eventData) && eventData.content.membership === "leave" } + render() { - super.render() - return this.text(`left the room`) + this.renderText("left the room") } } class UnknownMembership extends MembershipEvent { render() { - super.render() - return this.text("unknown membership event") + this.renderText("unknown membership event") } } diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js index 7fddf5b..c3f10dd 100644 --- a/src/js/events/unknown.js +++ b/src/js/events/unknown.js @@ -1,13 +1,19 @@ const {MatrixEvent} = require("./event") +const {ejs} = require("../basic") class UnknownEvent extends MatrixEvent { static canRender() { return true } + render() { + this.clearChildren() + this.child( + ejs("i").text(`Unknown event of type ${this.data.type}`) + ) super.render() - return this.text(`cannot render event`) } } + module.exports = [UnknownEvent] diff --git a/src/js/timeline.js b/src/js/timeline.js index 67bfbcd..3204a5c 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -5,7 +5,7 @@ const {Anchor} = require("./anchor.js") const {Sender} = require("./sender.js") const lsm = require("./lsm.js") const {resolveMxc} = require("./functions.js") -const {renderEvent} = require("./events/renderEvent") +const {renderEvent} = require("./events/render-event") const {dateFormatter} = require("./date-formatter") let debug = false From a7165fe6332ad2fc9d06f2f5fd72ae641363233f Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 17:32:36 +1300 Subject: [PATCH 57/92] Fix purify and highlight - purify: apply target=_blank to links - purify: remove ALLOWED_URI_REGEXP - this breaks external links in anchor elements - purify: return a DOM fragment instead of a string - postprocess: only highlight pre - postprocess: remove nested code inside pre - better style messages with css --- src/js/events/components.js | 12 +++++- src/js/events/message.js | 68 +++++++++++++++++++------------ src/sass/colors.sass | 1 + src/sass/components/messages.sass | 29 ++++++++++++- 4 files changed, 82 insertions(+), 28 deletions(-) diff --git a/src/js/events/components.js b/src/js/events/components.js index bb0cc63..79e3a8a 100644 --- a/src/js/events/components.js +++ b/src/js/events/components.js @@ -2,8 +2,16 @@ const {ElemJS} = require("../basic") const {lazyLoad} = require("../lazy-load-module") class HighlightedCode extends ElemJS { - constructor(code) { - super(code) + constructor(element) { + super(element) + if (this.element.tagName === "PRE" && this.element.children.length === 1 && this.element.children[0].tagName === "CODE") { + // we shouldn't nest code inside a pre. put the text in the pre directly. + const code = this.element.children[0] + this.clearChildren() + for (const child of code.childNodes) { + this.element.appendChild(child) + } + } lazyLoad("./static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) } } diff --git a/src/js/events/message.js b/src/js/events/message.js index b1ea8d9..f0760af 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -12,61 +12,80 @@ purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => { const allowedElementAttributes = { "FONT": ["data-mx-bg-color", "data-mx-color", "color"], - "SPAN": ["data-mx-bg-color", "data-mx-color", "color"], + "SPAN": ["data-mx-bg-color", "data-mx-color"], "A": ["name", "target", "href"], "IMG": ["width", "height", "alt", "title", "src", "data-mx-emoticon"], "OL": ["start"], "CODE": ["class"], } - const allowed_attributes = allowedElementAttributes[node.tagName] || [] - hookevent.keepAttr = allowed_attributes.indexOf(hookevent.attrName) > -1; + const allowedAttributes = allowedElementAttributes[node.tagName] || [] + hookevent.keepAttr = allowedAttributes.indexOf(hookevent.attrName) > -1 }) purifier.addHook("uponSanitizeElement", (node, hookevent, config) => { // Remove bad classes from our code element - if (node.tagName == "CODE") { + if (node.tagName === "CODE") { node.classList.forEach(c => { if (!c.startsWith("language-")) { node.classList.remove(c) } }) } - if (node.tagName == "A") { - node.setAttribute("rel", "noopener") + if (node.tagName === "A") { + node.setAttribute("rel", "noopener") // prevent the opening page from accessing carbon + node.setAttribute("target", "_blank") // open in a new tab instead of replacing carbon } return node }) function cleanHTML(html) { const config = { - ALLOWED_URI_REGEXP: /^mxc:\/\/[a-zA-Z0-9\.]+\/[a-zA-Z0-9]+$/, // As per the spec we only allow mxc uris - ALLOWED_TAGS: ['font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'caption', 'pre', 'span', 'img'], + ALLOWED_TAGS: [ + "font", "del", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "p", + "a", "ul", "ol", "sup", "sub", "li", "b", "i", "u", "strong", "em", + "strike", "code", "hr", "br", "div", "table", "thead", "tbody", "tr", + "th", "td", "caption", "pre", "span", "img", + // matrix tags + "mx-reply" + ], // In case we mess up in the uponSanitizeAttribute hook - ALLOWED_ATTR: ["data-mx-bg-color", "data-mx-color", "color", - "name", "target", "href", - "width", "height", "alt", "title", "src", "data-mx-emoticon", - "start", "class"], + ALLOWED_ATTR: [ + "color", "name", "target", "href", "width", "height", "alt", "title", + "src", "start", "class", "noreferrer", "noopener", + // matrix attrs + "data-mx-emoticon", "data-mx-bg-color", "data-mx-color" + ], + + // Return a DOM fragment instead of a string, avoids potential future mutation XSS + // should also be faster than the browser parsing HTML twice + // https://research.securitum.com/mutation-xss-via-mathml-mutation-dompurify-2-0-17-bypass/ + RETURN_DOM_FRAGMENT: true, + RETURN_DOM_IMPORT: true } - return purifier.sanitize(html) + return purifier.sanitize(html, config) } // Here we put all the processing of the messages that isn't as likely to potentially lead to security issues function postProcessElements(rootNode) { const element = rootNode.element - element.querySelectorAll("code").forEach((n) => rootNode.child(new HighlightedCode(n))) - element.querySelectorAll("img").forEach((n) => { + element.querySelectorAll("pre").forEach(n => { + new HighlightedCode(n) + }) + + element.querySelectorAll("img").forEach(n => { let src = n.getAttribute("src") if (src) src = resolveMxc(src) n.setAttribute("src", src) }) - element.querySelectorAll("font, span").forEach((n) => { + + element.querySelectorAll("font, span").forEach(n => { const color = n.getAttribute("data-mx-color") || n.getAttribute("color") const bgColor = n.getAttribute("data-mx-bg-color") - if (color) n.style.color = color; - if (bgColor) n.style.backgroundColor = bgColor; + if (color) n.style.color = color + if (bgColor) n.style.backgroundColor = bgColor }) } @@ -78,8 +97,8 @@ class HTMLMessage extends MatrixEvent { let html = this.data.content.formatted_body const content = ejs("div") - html = cleanHTML(html) - content.html(html) + const fragment = cleanHTML(html) + content.element.appendChild(fragment) postProcessElements(content) this.child(content) @@ -89,24 +108,23 @@ class HTMLMessage extends MatrixEvent { static canRender(event) { const content = event.content - return event.type == "m.room.message" && content.msgtype == "m.text" && content.format == "org.matrix.custom.html" && content.formatted_body - + return event.type === "m.room.message" && content.msgtype === "m.text" && content.format === "org.matrix.custom.html" && content.formatted_body } canGroup() { return true } - } class TextMessage extends MatrixEvent { render() { + this.clearChildren() this.text(this.data.content.body) - return super.render() + super.render() } static canRender(event) { - return event.type == "m.room.message" + return event.type === "m.room.message" } canGroup() { diff --git a/src/sass/colors.sass b/src/sass/colors.sass index 278fc02..e40189c 100644 --- a/src/sass/colors.sass +++ b/src/sass/colors.sass @@ -5,3 +5,4 @@ $mild: #393c42 $milder: #42454a $divider: #4b4e54 $muted: #999 +$link: #57bffd diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 2a7665a..30bece0 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -1,6 +1,6 @@ @use "../colors" as c -.c-event-groups * +.c-event-groups > * overflow-anchor: none .c-message-group, .c-message-event @@ -67,6 +67,33 @@ &:hover background-color: c.$darker + // message formatting rules + + code, pre + border-radius: 4px + font-size: 0.9em + + pre + background-color: c.$darkest + padding: 8px + border: 1px solid c.$divider + + code + background-color: c.$darker + padding: 2px 4px + + a + color: c.$link + + p, pre + margin: 16px 0px + + &:first-child + margin-top: 0px + + &:last-child + margin-bottom: 0px + .c-message-event padding-top: 10px padding-left: 6px From 017f30be658c9c8a838feecb79fe6e8547d57faa Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 17:39:21 +1300 Subject: [PATCH 58/92] Also format m.notice --- src/js/events/message.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/js/events/message.js b/src/js/events/message.js index f0760af..1b39c8a 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -108,7 +108,12 @@ class HTMLMessage extends MatrixEvent { static canRender(event) { const content = event.content - return event.type === "m.room.message" && content.msgtype === "m.text" && content.format === "org.matrix.custom.html" && content.formatted_body + return ( + event.type === "m.room.message" + && (content.msgtype === "m.text" || content.msgtype === "m.notice") + && content.format === "org.matrix.custom.html" + && content.formatted_body + ) } canGroup() { From 1aebc2c100ae8e53098a873146a2a74a683f64ae Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 17:48:13 +1300 Subject: [PATCH 59/92] Only hint modules once --- build.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.js b/build.js index 76242d6..964af20 100644 --- a/build.js +++ b/build.js @@ -147,7 +147,7 @@ async function addJS(sourcePath, targetPath) { async function addBundle(sourcePath, targetPath, module = false) { let opts = {} - if(module) opts.standalone = sourcePath + if (module) opts.standalone = sourcePath const content = await new Promise(resolve => { browserify([], opts) .add(pj(".", sourcePath)) @@ -175,7 +175,6 @@ async function addBundle(sourcePath, targetPath, module = false) { }) const writer = fs.promises.writeFile(pj(buildDir, targetPath), content) staticFiles.set(sourcePath, `${targetPath}?static=${hash(content)}`) - runHint(sourcePath, content) await writer } From b74f0cc0dddf6b34a7355f11f7af93531865d73f Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 17:57:27 +1300 Subject: [PATCH 60/92] Don't highlight very short code blocks --- src/js/events/components.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/js/events/components.js b/src/js/events/components.js index 79e3a8a..582af8d 100644 --- a/src/js/events/components.js +++ b/src/js/events/components.js @@ -12,7 +12,19 @@ class HighlightedCode extends ElemJS { this.element.appendChild(child) } } - lazyLoad("./static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) + if (this.element.textContent.length > 80) { + /* + no need to highlight very short code blocks: + - content inside might not be code, some users still use code blocks + for plaintext quotes + - language detection will almost certainly be incorrect + - even if it's code and the language is detected, the user will + be able to mentally format small amounts of code themselves + + feel free to change the threshold number + */ + lazyLoad("./static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) + } } } From 1fa7da9ebb059ccf86734878ec1408d561ba9058 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 18:00:36 +1300 Subject: [PATCH 61/92] Use JSDelivr CDN for highlight.js - downside: is somebody else's CDN - upside: changes hljs download size from >1MB to 33k Feel free to debate this. --- spec.js | 5 ----- src/home.pug | 1 - src/js/events/components.js | 2 +- src/js/hljs.js | 2 -- 4 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 src/js/hljs.js diff --git a/spec.js b/spec.js index 7808403..1babec8 100644 --- a/spec.js +++ b/spec.js @@ -19,11 +19,6 @@ module.exports = [ source: "/js/main.js", target: "/static/bundle.js" }, - { - type: "module", - source: "/js/hljs.js", - target: "/static/hljs.js" - }, { type: "file", source: "/assets/fonts/whitney-500.woff", diff --git a/src/home.pug b/src/home.pug index 0f3fb7e..09a00c7 100644 --- a/src/home.pug +++ b/src/home.pug @@ -40,7 +40,6 @@ html != JSON.stringify([...static.keys()].map(k => [k, getStatic(k)])) | ) link(rel="stylesheet" type="text/css" href=getStatic("/sass/main.sass")) - link(rel="preload" as="script" href=getStatic("/js/hljs.js")) script(type="module" src=getStatic("/js/main.js")) body main.main diff --git a/src/js/events/components.js b/src/js/events/components.js index 582af8d..1e9506b 100644 --- a/src/js/events/components.js +++ b/src/js/events/components.js @@ -23,7 +23,7 @@ class HighlightedCode extends ElemJS { feel free to change the threshold number */ - lazyLoad("./static/hljs.js").then(hljs => hljs.highlightBlock(this.element)) + lazyLoad("https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@10/build/highlight.min.js").then(hljs => hljs.highlightBlock(this.element)) } } } diff --git a/src/js/hljs.js b/src/js/hljs.js deleted file mode 100644 index 1f224c7..0000000 --- a/src/js/hljs.js +++ /dev/null @@ -1,2 +0,0 @@ -const hljs = require("highlight.js") -module.exports = hljs From 34af1be7d195b305a11c3adcfa9e7709c7b51dc7 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 5 Nov 2020 18:13:43 +1300 Subject: [PATCH 62/92] Use dependencies instead of devDependencies --- package-lock.json | 716 ++++++++++------------------------------------ package.json | 6 +- 2 files changed, 155 insertions(+), 567 deletions(-) diff --git a/package-lock.json b/package-lock.json index efaf46a..e3fe921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -17,7 +16,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, "requires": { "browserslist": "^4.12.0", "invariant": "^2.2.4", @@ -28,7 +26,6 @@ "version": "7.11.1", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz", "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/generator": "^7.11.0", @@ -51,14 +48,12 @@ "@babel/parser": { "version": "7.11.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.2.tgz", - "integrity": "sha512-Vuj/+7vLo6l1Vi7uuO+1ngCDNeVmNbTngcJFKCR/oEtz8tKz0CJxZEGmPt9KcIloZhOZ3Zit6xbpXT2MDlS9Vw==", - "dev": true + "integrity": "sha512-Vuj/+7vLo6l1Vi7uuO+1ngCDNeVmNbTngcJFKCR/oEtz8tKz0CJxZEGmPt9KcIloZhOZ3Zit6xbpXT2MDlS9Vw==" }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -69,7 +64,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", - "dev": true, "requires": { "@babel/types": "^7.11.0", "jsesc": "^2.5.1", @@ -80,7 +74,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, "requires": { "@babel/types": "^7.10.4" } @@ -89,7 +82,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, "requires": { "@babel/helper-explode-assignable-expression": "^7.10.4", "@babel/types": "^7.10.4" @@ -99,7 +91,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", - "dev": true, "requires": { "@babel/compat-data": "^7.10.4", "browserslist": "^4.12.0", @@ -112,7 +103,6 @@ "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", "@babel/helper-member-expression-to-functions": "^7.10.5", @@ -126,7 +116,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-regex": "^7.10.4", @@ -137,7 +126,6 @@ "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", "@babel/types": "^7.10.5", @@ -148,7 +136,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", - "dev": true, "requires": { "@babel/traverse": "^7.10.4", "@babel/types": "^7.10.4" @@ -158,7 +145,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.10.4", "@babel/template": "^7.10.4", @@ -169,7 +155,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, "requires": { "@babel/types": "^7.10.4" } @@ -178,7 +163,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, "requires": { "@babel/types": "^7.10.4" } @@ -187,7 +171,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, "requires": { "@babel/types": "^7.11.0" } @@ -196,7 +179,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, "requires": { "@babel/types": "^7.10.4" } @@ -205,7 +187,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "dev": true, "requires": { "@babel/helper-module-imports": "^7.10.4", "@babel/helper-replace-supers": "^7.10.4", @@ -220,7 +201,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, "requires": { "@babel/types": "^7.10.4" } @@ -228,14 +208,12 @@ "@babel/helper-plugin-utils": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" }, "@babel/helper-regex": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, "requires": { "lodash": "^4.17.19" } @@ -244,7 +222,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-wrap-function": "^7.10.4", @@ -257,7 +234,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.10.4", "@babel/helper-optimise-call-expression": "^7.10.4", @@ -269,7 +245,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, "requires": { "@babel/template": "^7.10.4", "@babel/types": "^7.10.4" @@ -279,7 +254,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", - "dev": true, "requires": { "@babel/types": "^7.11.0" } @@ -288,7 +262,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, "requires": { "@babel/types": "^7.11.0" } @@ -296,14 +269,12 @@ "@babel/helper-validator-identifier": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/helper-wrap-function": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", "@babel/template": "^7.10.4", @@ -315,7 +286,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", - "dev": true, "requires": { "@babel/template": "^7.10.4", "@babel/traverse": "^7.10.4", @@ -326,7 +296,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -337,7 +306,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -346,7 +314,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -357,7 +324,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -365,20 +331,17 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -388,14 +351,12 @@ "@babel/parser": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz", - "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==", - "dev": true + "integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw==" }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-remap-async-to-generator": "^7.10.4", @@ -406,7 +367,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", - "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -416,7 +376,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-dynamic-import": "^7.8.0" @@ -426,7 +385,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -436,7 +394,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.0" @@ -446,7 +403,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -456,7 +412,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" @@ -466,7 +421,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -476,7 +430,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.0", @@ -487,7 +440,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" @@ -497,7 +449,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", @@ -508,7 +459,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", - "dev": true, "requires": { "@babel/helper-create-class-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -518,7 +468,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -528,7 +477,6 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -537,7 +485,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -546,7 +493,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -555,7 +501,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.3" } @@ -564,7 +509,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -573,7 +517,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -582,7 +525,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -591,7 +533,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -600,7 +541,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -609,7 +549,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -618,7 +557,6 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" } @@ -627,7 +565,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -636,7 +573,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -645,7 +581,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", - "dev": true, "requires": { "@babel/helper-module-imports": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", @@ -656,7 +591,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -665,7 +599,6 @@ "version": "7.11.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -674,7 +607,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-define-map": "^7.10.4", @@ -690,7 +622,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -699,7 +630,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -708,7 +638,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -718,7 +647,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -727,7 +655,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", - "dev": true, "requires": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -737,7 +664,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -746,7 +672,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, "requires": { "@babel/helper-function-name": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -756,7 +681,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -765,7 +689,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -774,7 +697,6 @@ "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", - "dev": true, "requires": { "@babel/helper-module-transforms": "^7.10.5", "@babel/helper-plugin-utils": "^7.10.4", @@ -785,7 +707,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", - "dev": true, "requires": { "@babel/helper-module-transforms": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4", @@ -797,7 +718,6 @@ "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", - "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.10.4", "@babel/helper-module-transforms": "^7.10.5", @@ -809,7 +729,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", - "dev": true, "requires": { "@babel/helper-module-transforms": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -819,7 +738,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.10.4" } @@ -828,7 +746,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -837,7 +754,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-replace-supers": "^7.10.4" @@ -847,7 +763,6 @@ "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -857,7 +772,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -866,7 +780,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", - "dev": true, "requires": { "regenerator-transform": "^0.14.2" } @@ -875,7 +788,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -884,7 +796,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -893,7 +804,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" @@ -903,7 +813,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-regex": "^7.10.4" @@ -913,7 +822,6 @@ "version": "7.10.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", - "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -923,7 +831,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -932,7 +839,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" } @@ -941,7 +847,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" @@ -951,7 +856,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", - "dev": true, "requires": { "@babel/compat-data": "^7.11.0", "@babel/helper-compilation-targets": "^7.10.4", @@ -1027,7 +931,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -1040,7 +943,6 @@ "version": "7.11.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -1049,7 +951,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.10.4", @@ -1060,7 +961,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/generator": "^7.11.0", @@ -1077,7 +977,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -1088,7 +987,6 @@ "version": "7.11.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "lodash": "^4.17.19", @@ -1098,14 +996,12 @@ "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, "requires": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -1114,14 +1010,12 @@ "acorn": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==" }, "acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, "requires": { "acorn": "^7.0.0", "acorn-walk": "^7.0.0", @@ -1131,14 +1025,12 @@ "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -1148,7 +1040,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1157,20 +1048,17 @@ "array-filter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", - "dev": true + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -1181,8 +1069,7 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, @@ -1190,7 +1077,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, "requires": { "object-assign": "^4.1.1", "util": "0.10.3" @@ -1199,14 +1085,12 @@ "inherits": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, "requires": { "inherits": "2.0.1" } @@ -1216,14 +1100,12 @@ "assert-never": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", - "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==", - "dev": true + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, "requires": { "lodash": "^4.17.14" } @@ -1232,7 +1114,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", - "dev": true, "requires": { "array-filter": "^1.0.0" } @@ -1241,7 +1122,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, "requires": { "object.assign": "^4.1.0" } @@ -1250,7 +1130,6 @@ "version": "3.0.0-canary-5", "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", - "dev": true, "requires": { "@babel/types": "^7.9.6" } @@ -1258,38 +1137,32 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "basic-auth": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", - "dev": true + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=" }, "binary-extensions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, "bn.js": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", - "dev": true + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1299,7 +1172,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -1307,14 +1179,12 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browser-pack": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", - "dev": true, "requires": { "JSONStream": "^1.0.3", "combine-source-map": "~0.8.0", @@ -1328,7 +1198,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-2.0.0.tgz", "integrity": "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==", - "dev": true, "requires": { "resolve": "^1.17.0" } @@ -1337,7 +1206,6 @@ "version": "17.0.0", "resolved": "https://registry.npmjs.org/browserify/-/browserify-17.0.0.tgz", "integrity": "sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w==", - "dev": true, "requires": { "JSONStream": "^1.0.3", "assert": "^1.4.0", @@ -1392,14 +1260,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1414,7 +1280,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1425,7 +1290,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -1433,8 +1297,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } } @@ -1444,7 +1307,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -1458,7 +1320,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, "requires": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -1469,7 +1330,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -1481,7 +1341,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, "requires": { "bn.js": "^4.1.0", "randombytes": "^2.0.1" @@ -1490,8 +1349,7 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, @@ -1499,7 +1357,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "dev": true, "requires": { "bn.js": "^5.1.1", "browserify-rsa": "^4.0.1", @@ -1516,7 +1373,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -1526,14 +1382,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -1544,7 +1398,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, "requires": { "pako": "~1.0.5" } @@ -1553,7 +1406,6 @@ "version": "4.14.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", - "dev": true, "requires": { "caniuse-lite": "^1.0.30001111", "electron-to-chromium": "^1.3.523", @@ -1565,7 +1417,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", - "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -1574,38 +1425,32 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, "cached-path-relative": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", - "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", - "dev": true + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==" }, "caniuse-lite": { "version": "1.0.30001112", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001112.tgz", - "integrity": "sha512-J05RTQlqsatidif/38aN3PGULCLrg8OYQOlJUKbeYVzC2mGZkZLIztwRlB3MtrfLmawUmjFlNJvy/uhwniIe1Q==", - "dev": true + "integrity": "sha512-J05RTQlqsatidif/38aN3PGULCLrg8OYQOlJUKbeYVzC2mGZkZLIztwRlB3MtrfLmawUmjFlNJvy/uhwniIe1Q==" }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1615,7 +1460,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "dev": true, "requires": { "is-regex": "^1.0.3" } @@ -1624,7 +1468,6 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", - "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -1640,7 +1483,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -1650,7 +1492,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", - "dev": true, "requires": { "exit": "0.1.2", "glob": "^7.1.1" @@ -1660,7 +1501,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -1668,20 +1508,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, "combine-source-map": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", - "dev": true, "requires": { "convert-source-map": "~1.1.0", "inline-source-map": "~0.6.0", @@ -1692,22 +1529,19 @@ "convert-source-map": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", - "dev": true + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=" } } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -1718,14 +1552,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1740,7 +1572,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1751,7 +1582,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, "requires": { "date-now": "^0.1.4" } @@ -1760,7 +1590,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "dev": true, "requires": { "@babel/parser": "^7.6.0", "@babel/types": "^7.6.1" @@ -1769,14 +1598,12 @@ "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -1785,7 +1612,6 @@ "version": "3.6.5", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", - "dev": true, "requires": { "browserslist": "^4.8.5", "semver": "7.0.0" @@ -1794,28 +1620,24 @@ "semver": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" } } }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "corser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", - "dev": true + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=" }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, "requires": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -1824,8 +1646,7 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, @@ -1833,7 +1654,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -1846,7 +1666,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -1860,7 +1679,6 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, "requires": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -1878,20 +1696,17 @@ "dash-ast": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", - "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", - "dev": true + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==" }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -1907,14 +1722,12 @@ "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, "deps-sort": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.1.tgz", "integrity": "sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw==", - "dev": true, "requires": { "JSONStream": "^1.0.3", "shasum-object": "^1.0.0", @@ -1926,7 +1739,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, "requires": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -1936,7 +1748,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", - "dev": true, "requires": { "acorn-node": "^1.6.1", "defined": "^1.0.0", @@ -1947,7 +1758,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, "requires": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -1957,22 +1767,19 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=", - "dev": true + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, "dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, "requires": { "domelementtype": "^2.0.1", "entities": "^2.0.0" @@ -1981,34 +1788,29 @@ "domelementtype": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", - "dev": true + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" }, "entities": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", - "dev": true + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" } } }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" }, "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" }, "domhandler": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "dev": true, "requires": { "domelementtype": "1" } @@ -2022,7 +1824,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, "requires": { "dom-serializer": "0", "domelementtype": "1" @@ -2032,7 +1833,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, "requires": { "readable-stream": "^2.0.2" }, @@ -2040,14 +1840,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2062,7 +1860,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2073,7 +1870,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", - "dev": true, "requires": { "he": "^1.1.1", "mime": "^1.6.0", @@ -2084,14 +1880,12 @@ "electron-to-chromium": { "version": "1.3.524", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.524.tgz", - "integrity": "sha512-ZUvklIBkfXQyA6IeiEss1nfKRICcdB5afAGZAaPGaExdfrkpUu/WWVO+X7QpNnphaVMllXnAcvKnVPdyM+DCPQ==", - "dev": true + "integrity": "sha512-ZUvklIBkfXQyA6IeiEss1nfKRICcdB5afAGZAaPGaExdfrkpUu/WWVO+X7QpNnphaVMllXnAcvKnVPdyM+DCPQ==" }, "elliptic": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -2105,22 +1899,19 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, "entities": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", - "dev": true + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" }, "es-abstract": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -2209,38 +2000,32 @@ "escalade": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", - "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", - "dev": true + "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "eventemitter3": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", - "dev": true + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" }, "events": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", - "dev": true + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -2249,20 +2034,17 @@ "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" }, "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", - "dev": true + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -2270,26 +2052,22 @@ "follow-redirects": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz", - "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==", - "dev": true + "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==" }, "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, "optional": true }, "function-bind": { @@ -2300,20 +2078,17 @@ "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" }, "get-assigned-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", - "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", - "dev": true + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==" }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2327,7 +2102,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -2335,8 +2109,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "has": { "version": "1.0.3", @@ -2349,8 +2122,7 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.1", @@ -2361,7 +2133,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, "requires": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -2372,7 +2143,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2382,14 +2152,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -2400,7 +2168,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -2409,19 +2176,12 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "highlight.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.1.tgz", - "integrity": "sha512-jeW8rdPdhshYKObedYg5XGbpVgb1/DT4AHvDFXhkU7UnGSIjy9kkJ7zHG7qplhFHMitTSzh5/iClKQk3Kb2RFQ==" + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -2431,14 +2191,12 @@ "htmlescape": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", - "dev": true + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=" }, "htmlparser2": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", - "dev": true, "requires": { "domelementtype": "1", "domhandler": "2.3", @@ -2451,7 +2209,6 @@ "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, "requires": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -2462,7 +2219,6 @@ "version": "0.12.3", "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", - "dev": true, "requires": { "basic-auth": "^1.0.3", "colors": "^1.4.0", @@ -2479,20 +2235,17 @@ "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2501,14 +2254,12 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "inline-source-map": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", - "dev": true, "requires": { "source-map": "~0.5.3" } @@ -2517,7 +2268,6 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.1.tgz", "integrity": "sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg==", - "dev": true, "requires": { "JSONStream": "^1.0.3", "acorn-node": "^1.5.2", @@ -2535,7 +2285,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -2543,14 +2292,12 @@ "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -2558,8 +2305,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.2.2", @@ -2575,7 +2321,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "dev": true, "requires": { "acorn": "^7.1.1", "object-assign": "^4.1.1" @@ -2584,20 +2329,17 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-generator-function": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", - "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==", - "dev": true + "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==" }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -2610,20 +2352,17 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "is-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -2640,7 +2379,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", - "dev": true, "requires": { "available-typed-arrays": "^1.0.0", "es-abstract": "^1.17.4", @@ -2651,32 +2389,27 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=", - "dev": true + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "jshint": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz", "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==", - "dev": true, "requires": { "cli": "~1.0.0", "console-browserify": "1.1.x", @@ -2692,7 +2425,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -2700,14 +2432,12 @@ "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", - "dev": true, "requires": { "is-promise": "^2.0.0", "promise": "^7.0.1" @@ -2717,7 +2447,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz", "integrity": "sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw==", - "dev": true, "requires": { "inherits": "^2.0.1", "stream-splicer": "^2.0.0" @@ -2726,14 +2455,12 @@ "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, "levenary": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, "requires": { "leven": "^3.1.0" } @@ -2741,20 +2468,17 @@ "lodash": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", - "dev": true + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", - "dev": true + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -2763,7 +2487,6 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -2774,7 +2497,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -2783,34 +2505,29 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2818,14 +2535,12 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -2833,14 +2548,12 @@ "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "module-deps": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz", "integrity": "sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA==", - "dev": true, "requires": { "JSONStream": "^1.0.3", "browser-resolve": "^2.0.0", @@ -2862,14 +2575,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2884,7 +2595,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -2894,32 +2604,27 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" }, "node-releases": { "version": "1.1.60", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", - "dev": true + "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==" }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { "version": "1.8.0", @@ -2935,7 +2640,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -2947,7 +2651,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -2955,26 +2658,22 @@ "opener": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", - "dev": true + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==" }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "parents": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", - "dev": true, "requires": { "path-platform": "~0.11.15" } @@ -2983,7 +2682,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, "requires": { "asn1.js": "^5.2.0", "browserify-aes": "^1.0.0", @@ -2995,32 +2693,27 @@ "path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-platform": { "version": "0.11.15", "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", - "dev": true + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=" }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "dev": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -3032,14 +2725,12 @@ "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, "requires": { "async": "^2.6.2", "debug": "^3.1.1", @@ -3049,20 +2740,17 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, "requires": { "asap": "~2.0.3" } @@ -3071,7 +2759,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -3084,8 +2771,7 @@ "bn.js": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" } } }, @@ -3093,7 +2779,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.0.tgz", "integrity": "sha512-inmsJyFBSHZaiGLaguoFgJGViX0If6AcfcElimvwj9perqjDpUpw79UIEDZbWFmoGVidh08aoE+e8tVkjVJPCw==", - "dev": true, "requires": { "pug-code-gen": "^3.0.0", "pug-filters": "^4.0.0", @@ -3109,7 +2794,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "dev": true, "requires": { "constantinople": "^4.0.1", "js-stringify": "^1.0.2", @@ -3120,7 +2804,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.1.tgz", "integrity": "sha512-xJIGvmXTQlkJllq6hqxxjRWcay2F9CU69TuAuiVZgHK0afOhG5txrQOcZyaPHBvSWCU/QQOqEp5XCH94rRZpBQ==", - "dev": true, "requires": { "constantinople": "^4.0.1", "doctypes": "^1.1.0", @@ -3135,14 +2818,12 @@ "pug-error": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", - "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==", - "dev": true + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" }, "pug-filters": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "dev": true, "requires": { "constantinople": "^4.0.1", "jstransformer": "1.0.0", @@ -3155,7 +2836,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.0.tgz", "integrity": "sha512-52xMk8nNpuyQ/M2wjZBN5gXQLIylaGkAoTk5Y1pBhVqaopaoj8Z0iVzpbFZAqitL4RHNVDZRnJDsqEYe99Ti0A==", - "dev": true, "requires": { "character-parser": "^2.2.0", "is-expression": "^4.0.0", @@ -3166,7 +2846,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "dev": true, "requires": { "pug-error": "^2.0.0", "pug-walk": "^2.0.0" @@ -3176,7 +2855,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "dev": true, "requires": { "object-assign": "^4.1.1", "pug-walk": "^2.0.0" @@ -3186,7 +2864,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "dev": true, "requires": { "pug-error": "^2.0.0", "token-stream": "1.0.0" @@ -3195,14 +2872,12 @@ "pug-runtime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.0.tgz", - "integrity": "sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA==", - "dev": true + "integrity": "sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA==" }, "pug-strip-comments": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "dev": true, "requires": { "pug-error": "^2.0.0" } @@ -3210,38 +2885,32 @@ "pug-walk": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", - "dev": true + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", - "dev": true + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -3250,7 +2919,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, "requires": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -3260,7 +2928,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", - "dev": true, "requires": { "readable-stream": "^2.0.2" }, @@ -3268,14 +2935,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3290,7 +2955,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3301,7 +2965,6 @@ "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -3313,7 +2976,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -3321,14 +2983,12 @@ "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", - "dev": true + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==" }, "regenerate-unicode-properties": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, "requires": { "regenerate": "^1.4.0" } @@ -3336,14 +2996,12 @@ "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, "regenerator-transform": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, "requires": { "@babel/runtime": "^7.8.4" } @@ -3352,7 +3010,6 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "dev": true, "requires": { "regenerate": "^1.4.0", "regenerate-unicode-properties": "^8.2.0", @@ -3365,14 +3022,12 @@ "regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" }, "regjsparser": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, "requires": { "jsesc": "~0.5.0" }, @@ -3380,22 +3035,19 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" } } }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -3404,7 +3056,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -3413,20 +3064,17 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { "version": "1.26.10", "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz", "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==", - "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" } @@ -3434,20 +3082,17 @@ "secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", - "dev": true + "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=" }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -3457,7 +3102,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shasum-object/-/shasum-object-1.0.0.tgz", "integrity": "sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg==", - "dev": true, "requires": { "fast-safe-stringify": "^2.0.7" } @@ -3465,32 +3109,27 @@ "shell-quote": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", - "dev": true + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" }, "shelljs": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", - "dev": true + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" }, "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "dev": true, "requires": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -3500,7 +3139,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3510,14 +3148,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -3528,7 +3164,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, "requires": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" @@ -3537,14 +3172,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3559,7 +3192,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3570,7 +3202,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", - "dev": true, "requires": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", @@ -3582,7 +3213,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3592,14 +3222,12 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -3610,7 +3238,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.1.tgz", "integrity": "sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg==", - "dev": true, "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.2" @@ -3619,14 +3246,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3641,7 +3266,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3749,20 +3373,17 @@ "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "strip-json-comments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", - "dev": true + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=" }, "subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, "requires": { "minimist": "^1.1.0" } @@ -3771,7 +3392,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -3780,7 +3400,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dev": true, "requires": { "acorn-node": "^1.2.0" } @@ -3788,14 +3407,12 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" @@ -3804,14 +3421,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -3826,7 +3441,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -3837,7 +3451,6 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", - "dev": true, "requires": { "process": "~0.11.0" } @@ -3845,14 +3458,12 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -3860,32 +3471,27 @@ "token-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=", - "dev": true + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" }, "tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", - "dev": true + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==" }, "undeclared-identifiers": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", - "dev": true, "requires": { "acorn-node": "^1.3.0", "dash-ast": "^1.0.0", @@ -3897,14 +3503,12 @@ "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" }, "unicode-match-property-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^1.0.4", "unicode-property-aliases-ecmascript": "^1.0.4" @@ -3913,20 +3517,17 @@ "unicode-match-property-value-ecmascript": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" }, "unicode-property-aliases-ecmascript": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" }, "union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, "requires": { "qs": "^6.4.0" } @@ -3935,7 +3536,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -3944,22 +3544,19 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" } } }, "url-join": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", - "dev": true + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" }, "util": { "version": "0.12.3", "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", - "dev": true, "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -3972,26 +3569,22 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, "void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=", - "dev": true + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" }, "which-typed-array": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", - "dev": true, "requires": { "available-typed-arrays": "^1.0.2", "es-abstract": "^1.17.5", @@ -4005,7 +3598,6 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "dev": true, "requires": { "@babel/parser": "^7.9.6", "@babel/types": "^7.9.6", @@ -4016,14 +3608,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" } } } diff --git a/package.json b/package.json index 4bdc271..a9902f0 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,6 @@ "license": "AGPL-3.0-only", "dependencies": { "dompurify": "^2.2.0", - "highlight.js": "^10.3.1" - }, - "devDependencies": { "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", "browserify": "^17.0.0", @@ -25,5 +22,6 @@ "node-fetch": "^2.6.0", "pug": "^3.0.0", "sass": "^1.26.10" - } + }, + "devDependencies": {} } From 951a46d8ecef029efa93b56e74d21dedacf72550 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sat, 7 Nov 2020 23:33:49 +1300 Subject: [PATCH 63/92] Add back highlight.js for SCSS imports --- package-lock.json | 5 +++++ package.json | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e3fe921..7a037ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2178,6 +2178,11 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "highlight.js": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.3.2.tgz", + "integrity": "sha512-3jRT7OUYsVsKvukNKZCtnvRcFyCJqSEIuIMsEybAXRiFSwpt65qjPd/Pr+UOdYt7WJlt+lj3+ypUsHiySBp/Jw==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index a9902f0..4dfc834 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,12 @@ "author": "", "license": "AGPL-3.0-only", "dependencies": { - "dompurify": "^2.2.0", "@babel/core": "^7.11.1", "@babel/preset-env": "^7.11.0", "browserify": "^17.0.0", "chalk": "^4.1.0", + "dompurify": "^2.2.0", + "highlight.js": "^10.3.2", "http-server": "^0.12.3", "jshint": "^2.12.0", "node-fetch": "^2.6.0", From d6be694d3b1ec44281f0320b6004f2bb24961105 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 8 Nov 2020 00:11:32 +1300 Subject: [PATCH 64/92] Style blockquotes --- src/sass/components/messages.sass | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 30bece0..341fd82 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -85,7 +85,12 @@ a color: c.$link - p, pre + blockquote + margin-left: 8px + border-left: 4px solid c.$muted + padding: 2px 0px 2px 12px + + p, pre, blockquote margin: 16px 0px &:first-child From 327290e971817102f686fc1dc9aa1c0a34b159f2 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 8 Nov 2020 00:49:12 +1300 Subject: [PATCH 65/92] Autolink URLs in messages --- src/js/basic.js | 10 ++++----- src/js/events/message.js | 35 ++++++++++++++++++++++++------- src/sass/components/messages.sass | 1 + 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/js/basic.js b/src/js/basic.js index 7108662..38d413c 100644 --- a/src/js/basic.js +++ b/src/js/basic.js @@ -19,12 +19,12 @@ const qa = s => document.querySelectorAll(s); */ class ElemJS { constructor(type) { - if (type instanceof HTMLElement) { - // If passed an existing element, bind to it - this.bind(type); - } else { - // Otherwise, create a new detached element to bind to + if (typeof type === "string") { + // Passed a tag name; create an element to bind to this.bind(document.createElement(type)); + } else { + // Passed an existing element; bind to it + this.bind(type); } this.children = []; } diff --git a/src/js/events/message.js b/src/js/events/message.js index 1b39c8a..2bd69ed 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -68,9 +68,7 @@ function cleanHTML(html) { } // Here we put all the processing of the messages that isn't as likely to potentially lead to security issues -function postProcessElements(rootNode) { - const element = rootNode.element - +function postProcessElements(element) { element.querySelectorAll("pre").forEach(n => { new HighlightedCode(n) }) @@ -95,13 +93,11 @@ class HTMLMessage extends MatrixEvent { this.clearChildren() let html = this.data.content.formatted_body - const content = ejs("div") const fragment = cleanHTML(html) - content.element.appendChild(fragment) - postProcessElements(content) + postProcessElements(fragment) - this.child(content) + this.child(ejs(fragment)) super.render() } @@ -121,10 +117,33 @@ class HTMLMessage extends MatrixEvent { } } +function autoLinkText(text) { + const fragment = ejs(new DocumentFragment()) + let lastIndex = 0 + text.replace(/https?:\/\/(?:[A-Za-z-]+\.)+[A-Za-z]{1,10}(?::[0-9]{1,6})?(?:\/[^ ]*)?/g, (url, index) => { + // add text before URL + fragment.addText(text.slice(lastIndex, index)) + // add URL + fragment.child( + ejs("a") + .attribute("target", "_blank") + .attribute("noopener", "") + .attribute("href", url) + .addText(url) + ) + // update state + lastIndex = index + url.length + }) + // add final text + fragment.addText(text.slice(lastIndex)) + return fragment +} + class TextMessage extends MatrixEvent { render() { this.clearChildren() - this.text(this.data.content.body) + const fragment = autoLinkText(this.data.content.body) + this.child(fragment) super.render() } diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 341fd82..ffc1517 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -49,6 +49,7 @@ overflow-wrap: anywhere opacity: 1 transition: opacity 0.2s ease-out + white-space: pre-wrap &--pending opacity: 0.5 From 4acd806e66c9050d22c48aa3d2a0f0aaa66eabdf Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 8 Nov 2020 01:04:42 +1300 Subject: [PATCH 66/92] Render image messages --- src/js/events/image.js | 32 +++++++++++++++++++++++++++++++ src/js/events/message.js | 1 + src/js/events/render-event.js | 2 ++ src/sass/components/messages.sass | 15 ++++++++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/js/events/image.js diff --git a/src/js/events/image.js b/src/js/events/image.js new file mode 100644 index 0000000..ec5a9d4 --- /dev/null +++ b/src/js/events/image.js @@ -0,0 +1,32 @@ +const {ejs, ElemJS} = require("../basic") +const {resolveMxc} = require("../functions") +const {MatrixEvent} = require("./event") + +class Image extends MatrixEvent { + render() { + this.clearChildren() + this.class("c-message--media") + const image = ( + ejs("img") + .class("c-message__image") + .attribute("src", resolveMxc(this.data.content.url)) + ) + const info = this.data.content.info + if (info && info.w && info.h) { + image.attribute("width", info.w) + image.attribute("height", info.h) + } + this.child(image) + super.render() + } + + static canRender(event) { + return event.type === "m.room.message" && event.content.msgtype === "m.image" + } + + canGroup() { + return true + } +} + +module.exports = [Image] diff --git a/src/js/events/message.js b/src/js/events/message.js index 2bd69ed..a6a5ee4 100644 --- a/src/js/events/message.js +++ b/src/js/events/message.js @@ -142,6 +142,7 @@ function autoLinkText(text) { class TextMessage extends MatrixEvent { render() { this.clearChildren() + this.class("c-message--plain") const fragment = autoLinkText(this.data.content.body) this.child(fragment) super.render() diff --git a/src/js/events/render-event.js b/src/js/events/render-event.js index e4616a2..9337a2d 100644 --- a/src/js/events/render-event.js +++ b/src/js/events/render-event.js @@ -1,9 +1,11 @@ +const imageEvent = require("./image") const messageEvent = require("./message") const encryptedEvent = require("./encrypted") const membershipEvent = require("./membership") const unknownEvent = require("./unknown") const events = [ + ...imageEvent, ...messageEvent, ...encryptedEvent, ...membershipEvent, diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index ffc1517..5f0290d 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -49,7 +49,14 @@ overflow-wrap: anywhere opacity: 1 transition: opacity 0.2s ease-out - white-space: pre-wrap + + &--plain + white-space: pre-wrap + + &--media + // fix whitespace + font-size: 0 + margin-top: 8px &--pending opacity: 0.5 @@ -68,6 +75,12 @@ &:hover background-color: c.$darker + &__image + width: auto + height: auto + max-width: 400px + max-height: 300px + // message formatting rules code, pre From f188d6664587cfeafe9942e84fc844f828c9e0a2 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 8 Nov 2020 01:11:31 +1300 Subject: [PATCH 67/92] Fix fullwidth layout --- src/sass/components/messages.sass | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 5f0290d..30ab3f2 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -9,7 +9,8 @@ border-top: 1px solid c.$divider .c-message-group - display: flex + display: grid + grid-template-columns: auto 1fr &__avatar flex-shrink: 0 From eb573fc17c8b3e6341c48a1d97705a587dac7fc5 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sun, 8 Nov 2020 01:20:58 +1300 Subject: [PATCH 68/92] Update readme feature list --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 4536623..357aa4b 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,7 @@ early in development. These important features still need to be implemented: - Unreads -- Chat history - Typing indicators -- Formatting - Emojis - Reactions - Encryption From c87b6dcaa72fcd3a49d8efd60317aa7406d462c7 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 9 Nov 2020 00:19:56 +1300 Subject: [PATCH 69/92] Display typing notifications --- src/home.pug | 1 + src/js/functions.js | 26 +++++++++++- src/js/main.js | 3 +- src/js/room-picker.js | 28 +++++-------- src/js/store/subscribable.js | 2 + src/js/sync/sync.js | 1 + src/js/timeline.js | 12 +++++- src/js/typing.js | 64 +++++++++++++++++++++++++++++ src/sass/components/chat-input.sass | 3 ++ src/sass/components/typing.sass | 21 ++++++++++ src/sass/main.sass | 1 + 11 files changed, 141 insertions(+), 21 deletions(-) create mode 100644 src/js/typing.js create mode 100644 src/sass/components/typing.sass diff --git a/src/home.pug b/src/home.pug index 09a00c7..3277b89 100644 --- a/src/home.pug +++ b/src/home.pug @@ -53,3 +53,4 @@ html .c-chat__inner#c-chat .c-chat-input textarea(placeholder="Send a message..." autocomplete="off").c-chat-input__textarea#c-chat-textarea + .c-typing#c-typing diff --git a/src/js/functions.js b/src/js/functions.js index da60793..db43a16 100644 --- a/src/js/functions.js +++ b/src/js/functions.js @@ -10,4 +10,28 @@ function resolveMxc(url, size, method) { } } -module.exports = {resolveMxc} +function extractLocalpart(mxid) { + // try to extract the localpart from the mxid + let match = mxid.match(/^@([^:]+):/) + if (match) { + return match[1] + } + // localpart extraction failed, use the whole mxid + return mxid +} + +function extractDisplayName(stateEvent) { + const mxid = stateEvent.state_key + // see if a display name is set + if (stateEvent.content.displayname) { + return stateEvent.content.displayname + } + // fall back to the mxid + return extractLocalpart(mxid) +} + +module.exports = { + resolveMxc, + extractLocalpart, + extractDisplayName +} diff --git a/src/js/main.js b/src/js/main.js index a15bc7f..fa5dc06 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -2,7 +2,8 @@ const groups = require("./groups.js") const chat_input = require("./chat-input.js") const room_picker = require("./room-picker.js") const sync = require("./sync/sync.js") -const chat = require("./chat.js") +const chat = require("./chat.js") +require("./typing.js") if (!localStorage.getItem("access_token")) { location.assign("./login/") diff --git a/src/js/room-picker.js b/src/js/room-picker.js index b04bcc6..3046612 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -4,7 +4,7 @@ const {SubscribeMapList} = require("./store/subscribe_map_list.js") const {SubscribeValue} = require("./store/subscribe_value.js") const {Timeline} = require("./timeline.js") const lsm = require("./lsm.js") -const {resolveMxc} = require("./functions.js") +const {resolveMxc, extractLocalpart, extractDisplayName} = require("./functions.js") class ActiveGroupMarker extends ElemJS { constructor() { @@ -93,6 +93,15 @@ class Room extends ElemJS { } } + getMemberName(mxid) { + if (this.members.has(mxid)) { + const state = this.members.get(mxid).value() + return extractDisplayName(state) + } else { + return extractLocalpart(mxid) + } + } + getName() { // if the room has a name let name = this.data.state.events.find(e => e.type === "m.room.name") @@ -107,22 +116,7 @@ class Room extends ElemJS { // if the room has no alias, use the names of its members ("heroes") const users = this.data.summary["m.heroes"] if (users && users.length) { - const usernames = users.map(u => { - // if the member is in the room, use their display name - if (this.members.has(u)) { - const displayname = this.members.get(u).value().content.displayname - if (displayname) { - return displayname - } - } - // we don't have the member, so extract the localpart from the mxid - const match = u.match(/^@([^:]+):/) - if (match) { - return match[1] - } - // localpart extraction failed, use the whole mxid - return u - }) + const usernames = users.map(mxid => this.getMemberName(mxid)) return usernames.join(", ") } // the room is empty diff --git a/src/js/store/subscribable.js b/src/js/store/subscribable.js index e87bab2..56bf971 100644 --- a/src/js/store/subscribable.js +++ b/src/js/store/subscribable.js @@ -20,6 +20,8 @@ class Subscribable { } else { throw new Error(`Cannot subscribe to non-existent event ${event}, available events are: ${Object.keys(this.events).join(", ")}`) } + // return a function we can call to easily unsubscribe + return () => this.unsubscribe(event, callback) } unsubscribe(event, callback) { diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js index c17b0e9..945e5cc 100644 --- a/src/js/sync/sync.js +++ b/src/js/sync/sync.js @@ -57,6 +57,7 @@ function manageSync(root) { if (data.timeline.events.length) newEvents = true timeline.updateStateEvents(data.state.events) timeline.updateEvents(data.timeline.events) + timeline.updateEphemeral(data.ephemeral.events) }) // set up groups diff --git a/src/js/timeline.js b/src/js/timeline.js index 3204a5c..2743404 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -1,5 +1,6 @@ const {ElemJS, ejs} = require("./basic.js") const {Subscribable} = require("./store/subscribable.js") +const {SubscribeValue} = require("./store/subscribe_value.js") const {store} = require("./store/store.js") const {Anchor} = require("./anchor.js") const {Sender} = require("./sender.js") @@ -39,7 +40,6 @@ function eventSearch(list, event, min = 0, max = NO_MAX) { else return eventSearch(list, event, mid + 1, max) } - class EventGroup extends ElemJS { constructor(reactive, list) { super("div") @@ -81,7 +81,6 @@ class EventGroup extends ElemJS { } } - /** Displays a spinner and creates an event to notify timeline to load more messages */ class LoadMore extends ElemJS { constructor(id) { @@ -197,6 +196,7 @@ class Timeline extends Subscribable { this.latest = 0 this.pending = new Set() this.pendingEdits = [] + this.typing = new SubscribeValue().set([]) this.from = null } @@ -275,6 +275,14 @@ class Timeline extends Subscribable { this.broadcast("afterChange") } + updateEphemeral(events) { + for (const eventData of events) { + if (eventData.type === "m.typing") { + this.typing.set(eventData.content.user_ids) + } + } + } + removeEvent(id) { if (!this.map.has(id)) throw new Error(`Tried to delete event ID ${id} which does not exist`) this.map.get(id).removeEvent() diff --git a/src/js/typing.js b/src/js/typing.js new file mode 100644 index 0000000..46990d6 --- /dev/null +++ b/src/js/typing.js @@ -0,0 +1,64 @@ +const {ElemJS, ejs, q} = require("./basic") +const {store} = require("./store/store") + +/** + * Maximum number of typing users to display all names for. + * More will be shown as "X users are typing". + */ +const maxUsers = 4 + +function getMemberName(mxid) { + return store.activeRoom.value().getMemberName(mxid) +} + +class Typing extends ElemJS { + constructor() { + super(q("#c-typing")) + + this.typingUnsubscribe = null + + this.message = ejs("span") + this.child(this.message) + + store.activeRoom.subscribe("changeSelf", this.changeRoom.bind(this)) + } + + changeRoom() { + if (this.typingUnsubscribe) { + this.typingUnsubscribe() + } + if (!store.activeRoom.exists()) return + const room = store.activeRoom.value() + this.typingUnsubscribe = room.timeline.typing.subscribe("changeSelf", this.render.bind(this)) + this.render() + } + + render() { + if (!store.activeRoom.exists()) return + const room = store.activeRoom.value() + const users = room.timeline.typing.value() + if (users.length === 0) { + this.removeClass("c-typing--typing") + } else { + let message = "" + if (users.length === 1) { + message = `${getMemberName(users[0])} is typing...` + } else if (users.length <= maxUsers) { + // feel free to rewrite this loop if you know a better way + for (let i = 0; i < users.length; i++) { + if (i < users.length-1) { + message += `${getMemberName(users[i])}, ` + } else { + message += `and ${getMemberName(users[i])} are typing...` + } + } + } else { + message = `${users.length} people are typing...` + } + this.class("c-typing--typing") + this.message.text(message) + } + } +} + +new Typing() diff --git a/src/sass/components/chat-input.sass b/src/sass/components/chat-input.sass index 892fee6..bb82dde 100644 --- a/src/sass/components/chat-input.sass +++ b/src/sass/components/chat-input.sass @@ -6,11 +6,14 @@ -webkit-appearance: $value .c-chat-input + position: relative width: 100% border-top: 2px solid c.$divider background-color: c.$dark &__textarea + position: relative + z-index: 1 width: calc(100% - 40px) height: 16px + (16px * 1.45) box-sizing: border-box diff --git a/src/sass/components/typing.sass b/src/sass/components/typing.sass new file mode 100644 index 0000000..dad8d90 --- /dev/null +++ b/src/sass/components/typing.sass @@ -0,0 +1,21 @@ +@use "../colors" as c + +.c-typing + height: 39px + background: c.$divider + position: absolute + right: 0 + left: 0 + top: 0 + z-index: 0 + margin: 20px + border-radius: 8px + padding: 0px 12px + font-size: 14px + line-height: 19px + transform: translateY(0px) + transition: transform 0.15s ease + color: #fff + + &--typing + transform: translateY(-21px) diff --git a/src/sass/main.sass b/src/sass/main.sass index 24cd6ca..9d7251f 100644 --- a/src/sass/main.sass +++ b/src/sass/main.sass @@ -4,6 +4,7 @@ @use "./components/messages" @use "./components/chat" @use "./components/chat-input" +@use "./components/typing" @use "./components/anchor" @use "./components/highlighted-code" @use "./loading" From f4b13dbde4584d23524a65d530d9c56cac6b16c8 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 9 Nov 2020 01:11:28 +1300 Subject: [PATCH 70/92] Send own typing status --- src/js/chat-input.js | 61 +++++++++++++++++++++++++++++++ src/js/store/subscribe_value.js | 2 +- src/js/typing.js | 7 +++- src/sass/components/messages.sass | 1 + 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/js/chat-input.js b/src/js/chat-input.js index 9558746..23d06fe 100644 --- a/src/js/chat-input.js +++ b/src/js/chat-input.js @@ -5,24 +5,85 @@ const {chat} = require("./chat.js") const input = q("#c-chat-textarea") +class TypingManager { + constructor() { + this.time = 20000 // how long to appear to type for + this.margin = 5000 // how long before the end of the timeout to send the request again + /** The room that we're typing in. We can semantically only type in one room at a time. */ + this.typingRoom = null + this.timeout = null + } + + request(id, typing) { + const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/rooms/${id}/typing/${lsm.get("mx_user_id")}`) + url.searchParams.set("access_token", lsm.get("access_token")) + const body = {typing} + if (typing) body.timeout = this.time + fetch(url.toString(), { + method: "PUT", + body: JSON.stringify(body) + }) + } + + schedule(id) { + this.request(id, true) + this.timeout = setTimeout(() => { + this.schedule(id) + }, this.time - this.margin) + } + + update(id) { + if (id) { // typing somewhere + if (this.typingRoom === id) return // already typing, don't do anything + // state + this.typingRoom = id + // mark and schedule + this.schedule(id) + // add self to typing list now instead of waiting a round trip + const typing = store.rooms.get(id).value().timeline.typing + typing.edit(list => list.concat(lsm.get("mx_user_id"))) + } else { // stopped typing + if (this.typingRoom) { + clearTimeout(this.timeout) + this.request(this.typingRoom, false) + } + this.typingRoom = null + } + } +} + +const typingManager = new TypingManager() + store.activeRoom.subscribe("changeSelf", () => { + // stop typing. you semantically can't type in a room you're not in. + typingManager.update(null) + // focus input box if (store.activeRoom.exists()) { input.focus() } }) input.addEventListener("keydown", event => { + if (!store.activeRoom.exists()) return + // send message? if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey) { event.preventDefault() const body = input.value send(input.value) input.value = "" fixHeight() + return } }) input.addEventListener("input", () => { fixHeight() + // set typing + if (input.value) { + typingManager.update(store.activeRoom.value().id) + } else { + typingManager.update(null) + } }) function fixHeight() { diff --git a/src/js/store/subscribe_value.js b/src/js/store/subscribe_value.js index eaa2cdd..9c71959 100644 --- a/src/js/store/subscribe_value.js +++ b/src/js/store/subscribe_value.js @@ -30,7 +30,7 @@ class SubscribeValue extends Subscribable { edit(f) { if (this.exists()) { - f(this.data) + this.data = f(this.data) this.set(this.data) } else { throw new Error("Tried to edit a SubscribeValue that had no value") diff --git a/src/js/typing.js b/src/js/typing.js index 46990d6..b6208ef 100644 --- a/src/js/typing.js +++ b/src/js/typing.js @@ -1,5 +1,6 @@ const {ElemJS, ejs, q} = require("./basic") const {store} = require("./store/store") +const lsm = require("./lsm") /** * Maximum number of typing users to display all names for. @@ -26,6 +27,7 @@ class Typing extends ElemJS { changeRoom() { if (this.typingUnsubscribe) { this.typingUnsubscribe() + this.typingUnsubscribe = null } if (!store.activeRoom.exists()) return const room = store.activeRoom.value() @@ -36,8 +38,11 @@ class Typing extends ElemJS { render() { if (!store.activeRoom.exists()) return const room = store.activeRoom.value() - const users = room.timeline.typing.value() + let users = [...room.timeline.typing.value()] + // don't show own typing status + users = users.filter(u => u !== lsm.get("mx_user_id")) if (users.length === 0) { + // nobody is typing this.removeClass("c-typing--typing") } else { let message = "" diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass index 30ab3f2..170ae71 100644 --- a/src/sass/components/messages.sass +++ b/src/sass/components/messages.sass @@ -92,6 +92,7 @@ background-color: c.$darkest padding: 8px border: 1px solid c.$divider + white-space: pre-wrap code background-color: c.$darker From 9f6c955b6304f673e950faba0065aa3b42edc2f0 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 9 Nov 2020 01:12:09 +1300 Subject: [PATCH 71/92] Update progress in readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 357aa4b..a737fa0 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,6 @@ early in development. These important features still need to be implemented: - Unreads -- Typing indicators - Emojis - Reactions - Encryption From c0c727827959e8281390d44e267d53aad9febd25 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 12 Nov 2020 17:29:46 +1300 Subject: [PATCH 72/92] Support Construct homeserver --- src/js/sync/sync.js | 105 +++++++++++++++++++++++++------------------- src/js/timeline.js | 5 ++- 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js index 945e5cc..7434a79 100644 --- a/src/js/sync/sync.js +++ b/src/js/sync/sync.js @@ -39,62 +39,75 @@ function manageSync(root) { let newEvents = false // set up directs - const directs = root.account_data.events.find(e => e.type === "m.direct") - if (directs) { - Object.values(directs.content).forEach(ids => { - ids.forEach(id => store.directs.add(id)) - }) + if (root.account_data) { + const directs = root.account_data.events.find(e => e.type === "m.direct") + if (directs) { + Object.values(directs.content).forEach(ids => { + ids.forEach(id => store.directs.add(id)) + }) + } } // set up rooms - Object.entries(root.rooms.join).forEach(([id, data]) => { - if (!store.rooms.has(id)) { - store.rooms.askAdd(id, data) + if (root.rooms) { + if (root.rooms.join) { + Object.entries(root.rooms.join).forEach(([id, data]) => { + if (!store.rooms.has(id)) { + store.rooms.askAdd(id, data) + } + const room = store.rooms.get(id).value() + const timeline = room.timeline + if (data.state) timeline.updateStateEvents(data.state.events) + if (data.timeline) { + if (!timeline.from) timeline.from = data.timeline.prev_batch + if (data.timeline.events.length) { + newEvents = true + timeline.updateEvents(data.timeline.events) + } + } + if (data.ephemeral) timeline.updateEphemeral(data.ephemeral.events) + }) } - const room = store.rooms.get(id).value() - const timeline = room.timeline - if (!timeline.from) timeline.from = data.timeline.prev_batch - if (data.timeline.events.length) newEvents = true - timeline.updateStateEvents(data.state.events) - timeline.updateEvents(data.timeline.events) - timeline.updateEphemeral(data.ephemeral.events) - }) + } // set up groups - Promise.all( - Object.keys(root.groups.join).map(id => { - if (!store.groups.has(id)) { - return Promise.all(["profile", "rooms"].map(path => { - const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/groups/${id}/${path}`) - url.searchParams.append("access_token", lsm.get("access_token")) - return fetch(url.toString()).then(res => res.json()) - })).then(([profile, rooms]) => { - rooms = rooms.chunk - let order = 999 - let orderEvent = root.account_data.events.find(e => e.type === "im.vector.web.tag_ordering") - if (orderEvent) { - if (orderEvent.content.tags.includes(id)) { - order = orderEvent.content.tags.indexOf(id) + if (root.groups) { + Promise.all( + Object.keys(root.groups.join).map(id => { + if (!store.groups.has(id)) { + return Promise.all(["profile", "rooms"].map(path => { + const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/groups/${id}/${path}`) + url.searchParams.append("access_token", lsm.get("access_token")) + return fetch(url.toString()).then(res => res.json()) + })).then(([profile, rooms]) => { + rooms = rooms.chunk + let order = 999 + let orderEvent = root.account_data.events.find(e => e.type === "im.vector.web.tag_ordering") + if (orderEvent) { + if (orderEvent.content.tags.includes(id)) { + order = orderEvent.content.tags.indexOf(id) + } } - } - const data = { - name: profile.name, - icon: resolveMxc(profile.avatar_url, 96, "crop"), - order - } - store.groups.askAdd(id, data) - rooms.forEach(groupRoom => { - if (store.rooms.has(groupRoom.room_id)) { - store.rooms.get(groupRoom.room_id).value().setGroup(id) + const data = { + name: profile.name, + icon: resolveMxc(profile.avatar_url, 96, "crop"), + order } + store.groups.askAdd(id, data) + rooms.forEach(groupRoom => { + if (store.rooms.has(groupRoom.room_id)) { + store.rooms.get(groupRoom.room_id).value().setGroup(id) + } + }) + store.newEvents.broadcast("changeSelf") // trigger a room list update }) - store.newEvents.broadcast("changeSelf") // trigger a room list update - }) - } + } + }) + ).then(() => { + store.rooms.sort() }) - ).then(() => { - store.rooms.sort() - }) + } + if (newEvents) store.newEvents.broadcast("changeSelf") } catch (e) { console.error(root) diff --git a/src/js/timeline.js b/src/js/timeline.js index 2743404..a4c74fa 100644 --- a/src/js/timeline.js +++ b/src/js/timeline.js @@ -295,7 +295,7 @@ class Timeline extends Subscribable { async loadScrollback() { debug = true - if (!this.from) throw new Error("Can't load scrollback, no from token") + if (!this.from) return // no more scrollback for this timeline const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/rooms/${this.id}/messages`) url.searchParams.set("access_token", lsm.get("access_token")) url.searchParams.set("from", this.from) @@ -316,7 +316,8 @@ class Timeline extends Subscribable { if (root.chunk.length) { // there are events to display this.updateEvents(root.chunk) - } else { + } + if (!root.chunk.length || !root.end) { // we reached the top of the scrollback this.reactiveTimeline.loadMore.remove() } From 0960ca7e9715861fc1360018022c36d034dc7712 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Sat, 14 Nov 2020 17:27:13 +1300 Subject: [PATCH 73/92] Code highlighting fixes: - Fix pre+code element moving - Do not highlight if pre is already formatted --- src/js/events/components.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/js/events/components.js b/src/js/events/components.js index 1e9506b..1de9d91 100644 --- a/src/js/events/components.js +++ b/src/js/events/components.js @@ -5,24 +5,29 @@ class HighlightedCode extends ElemJS { constructor(element) { super(element) if (this.element.tagName === "PRE" && this.element.children.length === 1 && this.element.children[0].tagName === "CODE") { - // we shouldn't nest code inside a pre. put the text in the pre directly. + // we shouldn't nest inside
. put the text in 
 directly.
 			const code = this.element.children[0]
 			this.clearChildren()
-			for (const child of code.childNodes) {
-				this.element.appendChild(child)
+			while (code.firstChild) {
+				this.element.appendChild(code.firstChild)
 			}
 		}
-		if (this.element.textContent.length > 80) {
+		let shouldHighlight = (
+			// if there are child _elements_, it's already formatted, we shouldn't mess that up
+			this.element.children.length === 0
 			/*
 			  no need to highlight very short code blocks:
 			  - content inside might not be code, some users still use code blocks
-			    for plaintext quotes
+			  for plaintext quotes
 			  - language detection will almost certainly be incorrect
 			  - even if it's code and the language is detected, the user will
-			    be able to mentally format small amounts of code themselves
+			  be able to mentally format small amounts of code themselves
 
 			  feel free to change the threshold number
 			*/
+				&& this.element.textContent.length > 80
+		)
+		if (shouldHighlight) {
 			lazyLoad("https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@10/build/highlight.min.js").then(hljs => hljs.highlightBlock(this.element))
 		}
 	}

From 03c7501bf1a778614b02e04dc7b32ab378f459a6 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Tue, 24 Nov 2020 18:58:27 +1300
Subject: [PATCH 74/92] Formatting for sent messages

---
 package-lock.json    | 35 +++++++++++++++++++++++++++++++++++
 package.json         |  1 +
 src/js/chat-input.js | 16 +++++++++++++---
 src/js/timeline.js   | 10 +++-------
 4 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 7a037ce..c3a0468 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -998,6 +998,20 @@
       "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
       "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
     },
+    "@types/prop-types": {
+      "version": "15.7.3",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
+      "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
+    },
+    "@types/react": {
+      "version": "17.0.0",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz",
+      "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==",
+      "requires": {
+        "@types/prop-types": "*",
+        "csstype": "^3.0.2"
+      }
+    },
     "JSONStream": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
@@ -1693,6 +1707,11 @@
         "randomfill": "^1.0.3"
       }
     },
+    "csstype": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz",
+      "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ=="
+    },
     "dash-ast": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz",
@@ -1771,6 +1790,14 @@
         }
       }
     },
+    "discord-markdown": {
+      "version": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#e5d0a953764cd8e8e0f7ea84c93a882fe16f6e3d",
+      "from": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#e5d0a953764cd8e8e0f7ea84c93a882fe16f6e3d",
+      "requires": {
+        "highlight.js": "^10.3.2",
+        "simple-markdown": "^0.7.2"
+      }
+    },
     "doctypes": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
@@ -3126,6 +3153,14 @@
       "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
       "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
     },
+    "simple-markdown": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/simple-markdown/-/simple-markdown-0.7.2.tgz",
+      "integrity": "sha512-XfCvqqzMyzRj4L7eIxJgGaQ2Gaxr20GhTFMB+1yuY8q3xffjzmOg4Q5tC0kcaJPV42NNUHCQDaRK6jzi3/RhrA==",
+      "requires": {
+        "@types/react": ">=16.0.0"
+      }
+    },
     "source-map": {
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
diff --git a/package.json b/package.json
index 4dfc834..8c1d24a 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "@babel/preset-env": "^7.11.0",
     "browserify": "^17.0.0",
     "chalk": "^4.1.0",
+    "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#e5d0a953764cd8e8e0f7ea84c93a882fe16f6e3d",
     "dompurify": "^2.2.0",
     "highlight.js": "^10.3.2",
     "http-server": "^0.12.3",
diff --git a/src/js/chat-input.js b/src/js/chat-input.js
index 23d06fe..63921fd 100644
--- a/src/js/chat-input.js
+++ b/src/js/chat-input.js
@@ -2,13 +2,16 @@ const {q} = require("./basic.js")
 const {store} = require("./store/store.js")
 const lsm = require("./lsm.js")
 const {chat} = require("./chat.js")
+const {toHTML} = require("discord-markdown")
 
 const input = q("#c-chat-textarea")
 
 class TypingManager {
 	constructor() {
-		this.time = 20000 // how long to appear to type for
-		this.margin = 5000 // how long before the end of the timeout to send the request again
+		/** How long to appear to type for. */
+		this.time = 20000
+		/** How long before the end of the timeout to send the request again. */
+		this.margin = 5000
 		/** The room that we're typing in. We can semantically only type in one room at a time. */
 		this.typingRoom = null
 		this.timeout = null
@@ -95,5 +98,12 @@ function fixHeight() {
 function send(body) {
 	if (!store.activeRoom.exists()) return
 	if (!body.trim().length) return
-	return store.activeRoom.value().timeline.send(body)
+	const content = {
+		msgtype: "m.text",
+		format: "org.matrix.custom.html",
+		body,
+		formatted_body: toHTML(body),
+		"chat.carbon.input_body": body
+	}
+	return store.activeRoom.value().timeline.send("m.room.message", content)
 }
diff --git a/src/js/timeline.js b/src/js/timeline.js
index a4c74fa..63f7a04 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -324,17 +324,13 @@ class Timeline extends Subscribable {
 		this.broadcast("afterScrollbackLoad")
 	}
 
-	send(body) {
+	send(type, content) {
 		const tx = getTxnId()
 		const id = `pending$${tx}`
 		this.pending.add(id)
-		const content = {
-			msgtype: "m.text",
-			body,
-			"chat.carbon.message.pending_id": id
-		}
+		content["chat.carbon.message.pending_id"] = id
 		const fakeEvent = {
-			type: "m.room.message",
+			type,
 			origin_server_ts: Date.now(),
 			event_id: id,
 			sender: lsm.get("mx_user_id"),

From e90a2c7da88a667e3656601f90b8374b2446f310 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Tue, 24 Nov 2020 20:00:45 +1300
Subject: [PATCH 75/92] Rename property to message namespace

---
 src/js/chat-input.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/js/chat-input.js b/src/js/chat-input.js
index 63921fd..e3fd796 100644
--- a/src/js/chat-input.js
+++ b/src/js/chat-input.js
@@ -103,7 +103,7 @@ function send(body) {
 		format: "org.matrix.custom.html",
 		body,
 		formatted_body: toHTML(body),
-		"chat.carbon.input_body": body
+		"chat.carbon.message.input_body": body
 	}
 	return store.activeRoom.value().timeline.send("m.room.message", content)
 }

From 229e6903fd8c94bc592dc91a5322b661386e68a0 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Wed, 25 Nov 2020 01:27:41 +1300
Subject: [PATCH 76/92] Display unread/notification counters on rooms

---
 src/js/room-picker.js              | 82 +++++++++++++++++++++++++-----
 src/js/store/store.js              |  3 +-
 src/js/store/subscribe_map.js      | 48 ++++++++++-------
 src/js/store/subscribe_map_list.js | 23 +++++++++
 src/js/sync/sync.js                | 10 ++++
 src/js/timeline.js                 | 18 +++++++
 src/sass/components/rooms.sass     | 19 +++++++
 7 files changed, 171 insertions(+), 32 deletions(-)

diff --git a/src/js/room-picker.js b/src/js/room-picker.js
index 3046612..810f5ee 100644
--- a/src/js/room-picker.js
+++ b/src/js/room-picker.js
@@ -56,12 +56,65 @@ class Group extends ElemJS {
 	}
 }
 
+class RoomNotifier extends ElemJS {
+	constructor() {
+		super("div")
+
+		this.classes = [
+			"notifications",
+			"unreads",
+			"none"
+		]
+
+		this.class("c-room__number")
+		this.state = {
+			notifications: 0,
+			unreads: 0
+		}
+		this.render()
+	}
+
+	/**
+	 * @param {object} state
+	 * @param {number} [state.notifications]
+	 * @param {number} [state.unreads]
+	 */
+	update(state) {
+		Object.assign(this.state, state)
+		this.render()
+	}
+
+	render() {
+		const display = {
+			number: this.state.notifications || this.state.unreads,
+			kind: this.state.notifications ? "notifications" : "unreads"
+		}
+		// set number
+		if (display.number) {
+			this.text(display.number)
+		} else {
+			this.text("")
+			display.kind = "none"
+		}
+		// set class
+		this.classes.forEach(c => {
+			const name = "c-room__number--" + c
+			if (c === display.kind) {
+				this.class(name)
+			} else {
+				this.removeClass(name)
+			}
+		})
+	}
+}
+
 class Room extends ElemJS {
 	constructor(id, data) {
 		super("div")
 
 		this.id = id
 		this.data = data
+		this.number = new RoomNotifier()
 		this.timeline = new Timeline(this)
 		this.group = null
 		this.members = new SubscribeMapList(SubscribeValue)
@@ -75,22 +128,21 @@ class Room extends ElemJS {
 	}
 
 	get order() {
-		if (this.group) {
-			let chars = 36
-			let total = 0
-			const name = this.getName()
-			for (let i = 0; i < name.length; i++) {
-				const c = name[i]
-				let d = 0
-				if (c >= "A" && c <= "Z") d = c.charCodeAt(0) - 65 + 10
-				else if (c >= "a" && c <= "z") d = c.charCodeAt(0) - 97 + 10
-				else if (c >= "0" && c <= "9") d = +c
-				total += d * chars ** (-i)
-			}
-			return total
+		let string = ""
+		if (this.number.state.notifications) {
+			string += "N"
+		} else if (this.number.state.unreads) {
+			string += "U"
 		} else {
-			return -this.timeline.latest
+			string += "_"
 		}
+		if (this.group) {
+			string += this.name
+		} else {
+			string += (4000000000000 - this.timeline.latest) // good until 2065 :)
+		}
+		console.log(string)
+		return string
 	}
 
 	getMemberName(mxid) {
@@ -174,6 +226,7 @@ class Room extends ElemJS {
 			this.child(ejs("div").class("c-room__icon", "c-room__icon--no-icon"))
 		}
 		this.child(ejs("div").class("c-room__name").text(this.getName()))
+		this.child(this.number)
 		// active
 		const active = store.activeRoom.value() === this
 		this.element.classList[active ? "add" : "remove"]("c-room--active")
@@ -193,6 +246,7 @@ class Rooms extends ElemJS {
 		store.activeGroup.subscribe("changeSelf", this.render.bind(this))
 		store.directs.subscribe("changeItem", this.render.bind(this))
 		store.newEvents.subscribe("changeSelf", this.sort.bind(this))
+		store.notificationsChange.subscribe("changeSelf", this.sort.bind(this))
 
 		this.render()
 	}
diff --git a/src/js/store/store.js b/src/js/store/store.js
index e11905f..8ef4511 100644
--- a/src/js/store/store.js
+++ b/src/js/store/store.js
@@ -9,7 +9,8 @@ const store = {
 	directs: new SubscribeSet(),
 	activeGroup: new SubscribeValue(),
 	activeRoom: new SubscribeValue(),
-	newEvents: new Subscribable()
+	newEvents: new Subscribable(),
+	notificationsChange: new Subscribable()
 }
 
 window.store = store
diff --git a/src/js/store/subscribe_map.js b/src/js/store/subscribe_map.js
index 9ee3eac..a15d4e2 100644
--- a/src/js/store/subscribe_map.js
+++ b/src/js/store/subscribe_map.js
@@ -1,40 +1,54 @@
 const {Subscribable} = require("./subscribable.js")
-const {SubscribeValue} = require("./subscribe_value.js")
 
 class SubscribeMap extends Subscribable {
 	constructor() {
 		super()
 		Object.assign(this.events, {
 			addItem: [],
+			editItem: [],
+			deleteItem: [],
 			changeItem: [],
-			removeItem: []
+			askSet: []
 		})
-		this.map = new Map()
+		Object.assign(this.eventDeps, {
+			addItem: ["changeItem"],
+			editItem: ["changeItem"],
+			deleteItem: ["changeItem"],
+			changeItem: [],
+			askSet: []
+		})
+		this.backing = new Map()
 	}
 
 	has(key) {
-		return this.map.has(key) && this.map.get(key).exists()
+		return this.backing.has(key)
 	}
 
-	get(key) {
-		if (this.map.has(key)) {
-			return this.map.get(key)
-		} else {
-			this.map.set(key, new SubscribeValue())
+	forEach(f) {
+		for (const key of this.backing.keys()) {
+			f(key, this.backing.get(key))
 		}
 	}
 
+	askSet(key, value) {
+		this.broadcast("askSet", key, value)
+	}
+
 	set(key, value) {
-		let s
-		if (this.map.has(key)) {
-			s = this.map.get(key).set(value)
-			this.broadcast("changeItem", key)
-		} else {
-			s = new SubscribeValue().set(value)
-			this.map.set(key, s)
+		const existed = this.backing.has(key)
+		this.backing.set(key, value)
+		if (existed) {
 			this.broadcast("addItem", key)
+		} else {
+			this.broadcast("editItem", key)
+		}
+	}
+
+	delete(key) {
+		if (this.backing.has(key)) {
+			this.backing.delete(key)
+			this.broadcast("deleteItem", key)
 		}
-		return s
 	}
 }
 
diff --git a/src/js/store/subscribe_map_list.js b/src/js/store/subscribe_map_list.js
index 28a5ce2..dfbdc6c 100644
--- a/src/js/store/subscribe_map_list.js
+++ b/src/js/store/subscribe_map_list.js
@@ -54,6 +54,15 @@ class SubscribeMapList extends Subscribable {
 	}
 
 	sort() {
+		const key = this.list[0]
+		if (typeof this.map.get(key).value().order === "number") {
+			this.sortByNumber()
+		} else {
+			this.sortByString()
+		}
+	}
+
+	sortByNumber() {
 		this.list.sort((a, b) => {
 			const orderA = this.map.get(a).value().order
 			const orderB = this.map.get(b).value().order
@@ -62,6 +71,20 @@ class SubscribeMapList extends Subscribable {
 		this.broadcast("changeItem")
 	}
 
+	sortByString() {
+		this.list.sort((a, b) => {
+			let r
+			const orderA = this.map.get(a).value().order
+			const orderB = this.map.get(b).value().order
+			if (orderA < orderB) r = -1
+			else if (orderA > orderB) r = 1
+			else r = 0
+			console.log("comparing", orderA, orderB, r)
+			return r
+		})
+		this.broadcast("changeItem")
+	}
+
 	_add(key, value, start) {
 		let s
 		if (this.map.has(key)) {
diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js
index 7434a79..90c92b9 100644
--- a/src/js/sync/sync.js
+++ b/src/js/sync/sync.js
@@ -37,6 +37,7 @@ function sync() {
 function manageSync(root) {
 	try {
 		let newEvents = false
+		let notificationsChange = false
 
 		// set up directs
 		if (root.account_data) {
@@ -66,6 +67,14 @@ function manageSync(root) {
 						}
 					}
 					if (data.ephemeral) timeline.updateEphemeral(data.ephemeral.events)
+					if (data.unread_notifications) {
+						timeline.updateNotificationCount(data.unread_notifications.notification_count)
+						notificationsChange = true
+					}
+					if (data["org.matrix.msc2654.unread_count"] != undefined) {
+						timeline.updateUnreadCount(data["org.matrix.msc2654.unread_count"])
+						notificationsChange = true
+					}
 				})
 			}
 		}
@@ -109,6 +118,7 @@ function manageSync(root) {
 		}
 
 		if (newEvents) store.newEvents.broadcast("changeSelf")
+		if (notificationsChange) store.notificationsChange.broadcast("changeSelf")
 	} catch (e) {
 		console.error(root)
 		throw e
diff --git a/src/js/timeline.js b/src/js/timeline.js
index 63f7a04..fa797da 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -1,6 +1,7 @@
 const {ElemJS, ejs} = require("./basic.js")
 const {Subscribable} = require("./store/subscribable.js")
 const {SubscribeValue} = require("./store/subscribe_value.js")
+const {SubscribeMap} = require("./store/subscribe_map.js")
 const {store} = require("./store/store.js")
 const {Anchor} = require("./anchor.js")
 const {Sender} = require("./sender.js")
@@ -197,6 +198,7 @@ class Timeline extends Subscribable {
 		this.pending = new Set()
 		this.pendingEdits = []
 		this.typing = new SubscribeValue().set([])
+		this.userReads = new SubscribeMap()
 		this.from = null
 	}
 
@@ -280,9 +282,25 @@ class Timeline extends Subscribable {
 			if (eventData.type === "m.typing") {
 				this.typing.set(eventData.content.user_ids)
 			}
+			if (eventData.type === "m.receipt") {
+				for (const eventID of Object.keys(eventData.content)) {
+					for (const user of Object.keys(eventData.content[eventID]["m.read"])) {
+						this.userReads.set(user, eventID)
+					}
+				}
+				// console.log("Updated read receipts:", this.userReads)
+			}
 		}
 	}
 
+	updateUnreadCount(count) {
+		this.room.number.update({unreads: count})
+	}
+
+	updateNotificationCount(count) {
+		this.room.number.update({notifications: count})
+	}
+
 	removeEvent(id) {
 		if (!this.map.has(id)) throw new Error(`Tried to delete event ID ${id} which does not exist`)
 		this.map.get(id).removeEvent()
diff --git a/src/sass/components/rooms.sass b/src/sass/components/rooms.sass
index 462e13c..a8bca58 100644
--- a/src/sass/components/rooms.sass
+++ b/src/sass/components/rooms.sass
@@ -43,3 +43,22 @@ $icon-padding: 8px
     white-space: nowrap
     overflow: hidden
     text-overflow: ellipsis
+    flex: 1
+
+  &__number
+    flex-shrink: 0
+    line-height: 1
+    padding: 4px 5px
+    border-radius: 5px
+    font-size: 14px
+
+    &--none
+      display: none
+
+    &--unreads
+      background-color: #ddd
+      color: #111
+
+    &--notifications
+      background-color: #ffac4b
+      color: #000

From babd098d18a58fca405fc594b0a29422d1d5bb32 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Wed, 25 Nov 2020 01:56:49 +1300
Subject: [PATCH 77/92] Remove console.log

---
 src/js/room-picker.js              | 1 -
 src/js/store/subscribe_map_list.js | 9 +++------
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/js/room-picker.js b/src/js/room-picker.js
index 810f5ee..63d1da7 100644
--- a/src/js/room-picker.js
+++ b/src/js/room-picker.js
@@ -141,7 +141,6 @@ class Room extends ElemJS {
 		} else {
 			string += (4000000000000 - this.timeline.latest) // good until 2065 :)
 		}
-		console.log(string)
 		return string
 	}
 
diff --git a/src/js/store/subscribe_map_list.js b/src/js/store/subscribe_map_list.js
index dfbdc6c..794a8da 100644
--- a/src/js/store/subscribe_map_list.js
+++ b/src/js/store/subscribe_map_list.js
@@ -73,14 +73,11 @@ class SubscribeMapList extends Subscribable {
 
 	sortByString() {
 		this.list.sort((a, b) => {
-			let r
 			const orderA = this.map.get(a).value().order
 			const orderB = this.map.get(b).value().order
-			if (orderA < orderB) r = -1
-			else if (orderA > orderB) r = 1
-			else r = 0
-			console.log("comparing", orderA, orderB, r)
-			return r
+			if (orderA < orderB) return -1
+			else if (orderA > orderB) return 1
+			else return 0
 		})
 		this.broadcast("changeItem")
 	}

From b7905bc3be2b822618ef80762365b48902d21683 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Wed, 25 Nov 2020 19:54:09 +1300
Subject: [PATCH 78/92] Read marker lines in chat, badges on groups

Also fixed stopping typing after sending a message.
---
 src/js/chat-input.js                 |   1 +
 src/js/events/event.js               |   2 +
 src/js/room-picker.js                |  52 ++++++++++++-
 src/js/store/subscribe_map.js        |  41 +++++++---
 src/js/timeline.js                   | 109 +++++++++++++++++++++++++--
 src/sass/components/groups.sass      |  25 ++++++
 src/sass/components/read-marker.sass |  37 +++++++++
 src/sass/components/rooms.sass       |   1 +
 src/sass/main.sass                   |   3 +-
 9 files changed, 248 insertions(+), 23 deletions(-)
 create mode 100644 src/sass/components/read-marker.sass

diff --git a/src/js/chat-input.js b/src/js/chat-input.js
index e3fd796..a2fa323 100644
--- a/src/js/chat-input.js
+++ b/src/js/chat-input.js
@@ -73,6 +73,7 @@ input.addEventListener("keydown", event => {
 		event.preventDefault()
 		const body = input.value
 		send(input.value)
+		typingManager.update(null) // stop typing
 		input.value = ""
 		fixHeight()
 		return
diff --git a/src/js/events/event.js b/src/js/events/event.js
index a5978a1..189d6e2 100644
--- a/src/js/events/event.js
+++ b/src/js/events/event.js
@@ -1,5 +1,6 @@
 const {ElemJS, ejs} = require("../basic")
 const {dateFormatter} = require("../date-formatter")
+const {SubscribeSet} = require("../store/subscribe_set.js")
 
 class MatrixEvent extends ElemJS {
 	constructor(data) {
@@ -8,6 +9,7 @@ class MatrixEvent extends ElemJS {
 		this.data = null
 		this.group = null
 		this.editedAt = null
+		this.readBy = new SubscribeSet()
 		this.update(data)
 	}
 
diff --git a/src/js/room-picker.js b/src/js/room-picker.js
index 63d1da7..d0bc0b9 100644
--- a/src/js/room-picker.js
+++ b/src/js/room-picker.js
@@ -25,12 +25,43 @@ class ActiveGroupMarker extends ElemJS {
 
 const activeGroupMarker = new ActiveGroupMarker()
 
+class GroupNotifier extends ElemJS {
+	constructor() {
+		super("div")
+
+		this.class("c-group__number")
+		this.state = {}
+		this.render()
+	}
+
+	update(state) {
+		Object.assign(this.state, state)
+		this.render()
+	}
+
+	clear() {
+		this.state = {}
+		this.render()
+	}
+
+	render() {
+		let total = Object.values(this.state).reduce((a, c) => a + c, 0)
+		if (total > 0) {
+			this.text(total)
+			this.class("c-group__number--active")
+		} else {
+			this.removeClass("c-group__number--active")
+		}
+	}
+}
+
 class Group extends ElemJS {
 	constructor(key, data) {
 		super("div")
 
 		this.data = data
 		this.order = this.data.order
+		this.number = new GroupNotifier()
 
 		this.class("c-group")
 		this.child(
@@ -38,6 +69,7 @@ class Group extends ElemJS {
 			 ? ejs("img").class("c-group__icon").attribute("src", this.data.icon)
 			 : ejs("div").class("c-group__icon")
 			),
+			this.number,
 			ejs("div").class("c-group__name").text(this.data.name)
 		)
 
@@ -57,16 +89,17 @@ class Group extends ElemJS {
 }
 
 class RoomNotifier extends ElemJS {
-	constructor() {
+	constructor(room) {
 		super("div")
 
+		this.class("c-room__number")
+
+		this.room = room
 		this.classes = [
 			"notifications",
 			"unreads",
 			"none"
 		]
-
-		this.class("c-room__number")
 		this.state = {
 			notifications: 0,
 			unreads: 0
@@ -81,9 +114,16 @@ class RoomNotifier extends ElemJS {
 	 */
 	update(state) {
 		Object.assign(this.state, state)
+		this.informGroup()
 		this.render()
 	}
 
+	informGroup() {
+		this.room.getGroup().number.update({[this.room.id]: (
+			this.state.notifications || (this.state.unreads ? 1 : 0)
+		)})
+	}
+
 	render() {
 		const display = {
 			number: this.state.notifications || this.state.unreads,
@@ -114,7 +154,7 @@ class Room extends ElemJS {
 
 		this.id = id
 		this.data = data
-		this.number = new RoomNotifier()
+		this.number = new RoomNotifier(this)
 		this.timeline = new Timeline(this)
 		this.group = null
 		this.members = new SubscribeMapList(SubscribeValue)
@@ -306,8 +346,12 @@ class Groups extends ElemJS {
 	render() {
 		this.clearChildren()
 		store.groups.forEach((key, item) => {
+			item.value().number.clear()
 			this.child(item.value())
 		})
+		store.rooms.forEach((id, room) => {
+			room.value().number.informGroup() // update group notification number
+		})
 	}
 }
 const groups = new Groups()
diff --git a/src/js/store/subscribe_map.js b/src/js/store/subscribe_map.js
index a15d4e2..6159597 100644
--- a/src/js/store/subscribe_map.js
+++ b/src/js/store/subscribe_map.js
@@ -1,8 +1,9 @@
 const {Subscribable} = require("./subscribable.js")
 
 class SubscribeMap extends Subscribable {
-	constructor() {
+	constructor(inner) {
 		super()
+		this.inner = inner
 		Object.assign(this.events, {
 			addItem: [],
 			editItem: [],
@@ -17,31 +18,49 @@ class SubscribeMap extends Subscribable {
 			changeItem: [],
 			askSet: []
 		})
-		this.backing = new Map()
+		this.map = new Map()
 	}
 
 	has(key) {
-		return this.backing.has(key)
+		return this.map.has(key) && this.map.get(key).exists()
+	}
+
+	get(key) {
+		if (this.map.has(key)) {
+			return this.map.get(key)
+		} else {
+			const item = new this.inner()
+			this.map.set(key, item)
+			return item
+		}
 	}
 
 	forEach(f) {
-		for (const key of this.backing.keys()) {
-			f(key, this.backing.get(key))
+		for (const entry of this.map.entries()) {
+			f(entry[0], entry[1])
 		}
 	}
 
 	askSet(key, value) {
-		this.broadcast("askSet", key, value)
+		this.broadcast("askSet", {key, value})
 	}
 
 	set(key, value) {
-		const existed = this.backing.has(key)
-		this.backing.set(key, value)
-		if (existed) {
-			this.broadcast("addItem", key)
+		let s
+		if (this.map.has(key)) {
+			const exists = this.map.get(key).exists()
+			s = this.map.get(key).set(value)
+			if (exists) {
+				this.broadcast("editItem", key)
+			} else {
+				this.broadcast("addItem", key)
+			}
 		} else {
-			this.broadcast("editItem", key)
+			s = new this.inner().set(value)
+			this.map.set(key, s)
+			this.broadcast("addItem", key)
 		}
+		return s
 	}
 
 	delete(key) {
diff --git a/src/js/timeline.js b/src/js/timeline.js
index fa797da..de17b9c 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -1,4 +1,4 @@
-const {ElemJS, ejs} = require("./basic.js")
+const {ElemJS, ejs, q} = require("./basic.js")
 const {Subscribable} = require("./store/subscribable.js")
 const {SubscribeValue} = require("./store/subscribe_value.js")
 const {SubscribeMap} = require("./store/subscribe_map.js")
@@ -20,6 +20,16 @@ function getTxnId() {
 	return Date.now() + (sentIndex++)
 }
 
+function markFullyRead(roomID, eventID) {
+	return fetch(`${lsm.get("domain")}/_matrix/client/r0/rooms/${roomID}/read_markers?access_token=${lsm.get("access_token")}`, {
+		method: "POST",
+		body: JSON.stringify({
+			"m.fully_read": eventID,
+			"m.read": eventID
+		})
+	})
+}
+
 function eventSearch(list, event, min = 0, max = NO_MAX) {
 	if (list.length === 0) return {success: false, i: 0}
 
@@ -174,6 +184,62 @@ class ReactiveTimeline extends ElemJS {
 	}
 }
 
+class ReadMarker extends ElemJS {
+	constructor(timeline) {
+		super("div")
+
+		this.class("c-read-marker")
+		this.loadingIcon = ejs("div")
+			.class("c-read-marker__loading", "loading-icon")
+			.style("display", "none")
+		this.child(
+			ejs("div").class("c-read-marker__inner").child(
+				ejs("div").class("c-read-marker__text").child(this.loadingIcon).addText("New")
+			)
+		)
+
+		let processing = false
+		const observer = new IntersectionObserver(entries => {
+			const entry = entries[0]
+			if (!entry.isIntersecting) return
+			if (processing) return
+			processing = true
+			this.loadingIcon.style("display", "")
+			markFullyRead(this.timeline.id, this.timeline.latestEventID).then(() => {
+				this.loadingIcon.style("display", "none")
+				processing = false
+			})
+		}, {
+			root: document.getElementById("c-chat-messages"),
+			rootMargin: "-80px 0px 0px 0px", // marker must be this distance inside the top of the screen to be counted as read
+			threshold: 0.01
+		})
+		observer.observe(this.element)
+
+		this.timeline = timeline
+		this.timeline.userReads.get(lsm.get("mx_user_id")).subscribe("changeSelf", (_, eventID) => {
+			// read marker updated, attach to it
+			const event = this.timeline.map.get(eventID)
+			this.attach(event)
+		})
+		this.timeline.subscribe("afterChange", () => {
+			// timeline has new events, attach to last read one
+			const eventID = this.timeline.userReads.get(lsm.get("mx_user_id")).value()
+			const event = this.timeline.map.get(eventID)
+			this.attach(event)
+		})
+	}
+
+	attach(event) {
+		if (event && event.data.origin_server_ts !== this.timeline.latest) {
+			this.class("c-read-marker--attached")
+			event.element.insertAdjacentElement("beforeend", this.element)
+		} else {
+			this.removeClass("c-read-marker--attached")
+		}
+	}
+}
+
 class Timeline extends Subscribable {
 	constructor(room) {
 		super()
@@ -195,10 +261,12 @@ class Timeline extends Subscribable {
 		this.map = new Map()
 		this.reactiveTimeline = new ReactiveTimeline(this.id, [])
 		this.latest = 0
+		this.latestEventID = null
 		this.pending = new Set()
 		this.pendingEdits = []
 		this.typing = new SubscribeValue().set([])
-		this.userReads = new SubscribeMap()
+		this.userReads = new SubscribeMap(SubscribeValue)
+		this.readMarker = new ReadMarker(this)
 		this.from = null
 	}
 
@@ -224,13 +292,21 @@ class Timeline extends Subscribable {
 		this.updateStateEvents(events)
 		for (const eventData of events) {
 			// set variables
-			this.latest = Math.max(this.latest, eventData.origin_server_ts)
 			let id = eventData.event_id
+			if (eventData.origin_server_ts > this.latest) {
+				this.latest = eventData.origin_server_ts
+				this.latestEventID = id
+			}
 			// handle local echoes
 			if (eventData.sender === lsm.get("mx_user_id") && eventData.content && this.pending.has(eventData.content["chat.carbon.message.pending_id"])) {
-				const target = this.map.get(eventData.content["chat.carbon.message.pending_id"])
-				this.map.set(id, target)
-				this.map.delete(eventData.content["chat.carbon.message.pending_id"])
+				const pendingID = eventData.content["chat.carbon.message.pending_id"]
+				if (id !== pendingID) {
+					const target = this.map.get(pendingID)
+					this.map.set(id, target)
+					this.map.delete(pendingID)
+					// update fully read marker - assume we have fully read up to messages we send
+					markFullyRead(this.id, id)
+				}
 			}
 			// handle timeline events
 			if (this.map.has(id)) {
@@ -259,6 +335,8 @@ class Timeline extends Subscribable {
 				const event = renderEvent(eventData)
 				this.map.set(id, event)
 				this.reactiveTimeline.addEvent(event)
+				// update read receipt for sender on their own event
+				this.moveReadReceipt(eventData.sender, id)
 			}
 		}
 		// apply edits
@@ -285,7 +363,7 @@ class Timeline extends Subscribable {
 			if (eventData.type === "m.receipt") {
 				for (const eventID of Object.keys(eventData.content)) {
 					for (const user of Object.keys(eventData.content[eventID]["m.read"])) {
-						this.userReads.set(user, eventID)
+						this.moveReadReceipt(user, eventID)
 					}
 				}
 				// console.log("Updated read receipts:", this.userReads)
@@ -293,6 +371,23 @@ class Timeline extends Subscribable {
 		}
 	}
 
+	moveReadReceipt(user, eventID) {
+		if (!this.map.has(eventID)) return // ignore receipts we don't have events for
+		// check for a previous event to move from
+		const prev = this.userReads.get(user)
+		if (prev.exists()) {
+			const prevID = prev.value()
+			if (this.map.has(prevID)) {
+				// ensure new message came later
+				if (this.map.get(eventID).data.origin_server_ts < this.map.get(prevID).data.origin_server_ts) return
+				this.map.get(prevID).readBy.delete(user)
+			}
+		}
+		// set on new message
+		this.userReads.set(user, eventID)
+		if (this.map.has(eventID)) this.map.get(eventID).readBy.add(user)
+	}
+
 	updateUnreadCount(count) {
 		this.room.number.update({unreads: count})
 	}
diff --git a/src/sass/components/groups.sass b/src/sass/components/groups.sass
index e91f4cc..6eca6ec 100644
--- a/src/sass/components/groups.sass
+++ b/src/sass/components/groups.sass
@@ -36,11 +36,13 @@ $out-width: $base-width + rooms.$list-width
     box-sizing: border-box
 
 .c-group
+  position: relative
   display: flex
   align-items: center
   padding: $icon-padding / 2 $icon-padding
   cursor: pointer
   border-radius: 8px
+  background-color: c.$darkest
 
   &:hover
     background-color: c.$darker
@@ -62,6 +64,29 @@ $out-width: $base-width + rooms.$list-width
     overflow: hidden
     text-overflow: ellipsis
 
+  &__number
+    position: absolute
+    right: 240px
+    bottom: 0px
+    background: #ddd
+    color: #000
+    font-size: 14px
+    line-height: 1
+    padding: 3px 4px
+    border-radius: 7px
+    border: 3px solid c.$darkest
+    opacity: 0
+    transform: translate(6px, 6px)
+    transition: transform 0.15s ease-out, opacity 0.15s ease-out
+    pointer-events: none
+
+    @at-root .c-group:hover &
+      border-color: c.$darker
+
+    &--active
+      opacity: 1
+      transform: translate(0px, 0px)
+
 .c-group-marker
   position: absolute
   top: 5px
diff --git a/src/sass/components/read-marker.sass b/src/sass/components/read-marker.sass
new file mode 100644
index 0000000..84435fb
--- /dev/null
+++ b/src/sass/components/read-marker.sass
@@ -0,0 +1,37 @@
+.c-read-marker
+  display: none
+  position: relative
+
+  &--attached
+    display: block
+
+  &__inner
+    position: absolute
+    left: -64px
+    right: 0px
+    height: 2px
+    top: 0px
+    background-color: #ffac4b // TODO
+
+    @at-root .c-message:last-child &
+      top: 11px
+
+  &__text
+    position: absolute
+    right: -14px
+    top: -9px
+    display: flex
+    align-items: center
+    background-color: #ffac4b // TODO
+    color: #000
+    font-size: 12px
+    font-weight: 600
+    line-height: 1
+    padding: 4px
+    border-radius: 5px
+    text-transform: uppercase
+
+  &__loading
+    background-color: #000
+    width: 10px
+    height: 10px
diff --git a/src/sass/components/rooms.sass b/src/sass/components/rooms.sass
index a8bca58..670d9ba 100644
--- a/src/sass/components/rooms.sass
+++ b/src/sass/components/rooms.sass
@@ -51,6 +51,7 @@ $icon-padding: 8px
     padding: 4px 5px
     border-radius: 5px
     font-size: 14px
+    pointer-events: none
 
     &--none
       display: none
diff --git a/src/sass/main.sass b/src/sass/main.sass
index 9d7251f..11c85ec 100644
--- a/src/sass/main.sass
+++ b/src/sass/main.sass
@@ -1,4 +1,5 @@
 @use "./base"
+@use "./loading"
 @use "./components/groups"
 @use "./components/rooms"
 @use "./components/messages"
@@ -7,4 +8,4 @@
 @use "./components/typing"
 @use "./components/anchor"
 @use "./components/highlighted-code"
-@use "./loading"
+@use "./components/read-marker"

From 69a9e2ed2fc32adb3d5348c217526462a587f56a Mon Sep 17 00:00:00 2001
From: Bad 
Date: Wed, 25 Nov 2020 08:18:46 +0100
Subject: [PATCH 79/92] =?UTF-8?q?=F0=9F=92=9A=20Fix=20drone=20build=20due?=
 =?UTF-8?q?=20to=20missing=20git?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .drone.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.drone.yml b/.drone.yml
index d815318..981c632 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -6,6 +6,8 @@ steps:
   - name: build
     image: node:current-alpine3.12
     commands:
+      - apk update
+      - apk add git
       - npm install -D
       - npm run rebuild
 

From 6297350418fb2c915a940b3e23baeb69a7f99999 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Thu, 26 Nov 2020 01:10:54 +1300
Subject: [PATCH 80/92] Use minified discord-markdown

---
 package-lock.json    | 5 ++---
 package.json         | 2 +-
 src/js/chat-input.js | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index c3a0468..97b56a5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1791,10 +1791,9 @@
       }
     },
     "discord-markdown": {
-      "version": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#e5d0a953764cd8e8e0f7ea84c93a882fe16f6e3d",
-      "from": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#e5d0a953764cd8e8e0f7ea84c93a882fe16f6e3d",
+      "version": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#5ad8046d8d62a7fb8047e1a697c3848744d4e64d",
+      "from": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#5ad8046d8d62a7fb8047e1a697c3848744d4e64d",
       "requires": {
-        "highlight.js": "^10.3.2",
         "simple-markdown": "^0.7.2"
       }
     },
diff --git a/package.json b/package.json
index 8c1d24a..f63bf03 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
     "@babel/preset-env": "^7.11.0",
     "browserify": "^17.0.0",
     "chalk": "^4.1.0",
-    "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#e5d0a953764cd8e8e0f7ea84c93a882fe16f6e3d",
+    "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#5ad8046d8d62a7fb8047e1a697c3848744d4e64d",
     "dompurify": "^2.2.0",
     "highlight.js": "^10.3.2",
     "http-server": "^0.12.3",
diff --git a/src/js/chat-input.js b/src/js/chat-input.js
index a2fa323..fd481a8 100644
--- a/src/js/chat-input.js
+++ b/src/js/chat-input.js
@@ -2,7 +2,7 @@ const {q} = require("./basic.js")
 const {store} = require("./store/store.js")
 const lsm = require("./lsm.js")
 const {chat} = require("./chat.js")
-const {toHTML} = require("discord-markdown")
+const {toHTML} = require("discord-markdown/dist/discord-markdown.min.js")
 
 const input = q("#c-chat-textarea")
 

From bc861125d85f54018489f2ed7fba66c8599a1c54 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Thu, 26 Nov 2020 14:01:05 +1300
Subject: [PATCH 81/92] Revert. Don't use minified discord-markdown.

It doesn't seem like it exposes any exports for use with `require`.
---
 src/js/chat-input.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/js/chat-input.js b/src/js/chat-input.js
index fd481a8..a2fa323 100644
--- a/src/js/chat-input.js
+++ b/src/js/chat-input.js
@@ -2,7 +2,7 @@ const {q} = require("./basic.js")
 const {store} = require("./store/store.js")
 const lsm = require("./lsm.js")
 const {chat} = require("./chat.js")
-const {toHTML} = require("discord-markdown/dist/discord-markdown.min.js")
+const {toHTML} = require("discord-markdown")
 
 const input = q("#c-chat-textarea")
 

From 6e209bafd67a4a86ce2ea0d097ba5d61e8809db0 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Thu, 26 Nov 2020 16:38:12 +1300
Subject: [PATCH 82/92] Add extremely janky unread messages banner

---
 src/home.pug                         |   3 +-
 src/js/events/message.js             |   4 +-
 src/js/focus.js                      |  11 ++
 src/js/main.js                       |   1 +
 src/js/read-marker.js                | 149 +++++++++++++++++++++++++++
 src/js/timeline.js                   |  67 +-----------
 src/sass/base.sass                   |  36 +++++++
 src/sass/colors.sass                 |   1 +
 src/sass/components/chat-banner.sass |  47 +++++++++
 src/sass/components/chat.sass        |   3 +-
 src/sass/components/read-marker.sass |   6 +-
 src/sass/main.sass                   |   1 +
 12 files changed, 257 insertions(+), 72 deletions(-)
 create mode 100644 src/js/focus.js
 create mode 100644 src/js/read-marker.js
 create mode 100644 src/sass/components/chat-banner.sass

diff --git a/src/home.pug b/src/home.pug
index 3277b89..a9450f3 100644
--- a/src/home.pug
+++ b/src/home.pug
@@ -41,7 +41,7 @@ html
       | )
     link(rel="stylesheet" type="text/css" href=getStatic("/sass/main.sass"))
     script(type="module" src=getStatic("/js/main.js"))
-  body
+  body.show-focus
     main.main
       .c-groups
         .c-groups__display#c-groups-display
@@ -49,6 +49,7 @@ html
           .c-groups__container#c-groups-list
       .c-rooms#c-rooms
       .c-chat
+        .c-chat-banner#c-chat-banner
         .c-chat__messages#c-chat-messages
           .c-chat__inner#c-chat
         .c-chat-input
diff --git a/src/js/events/message.js b/src/js/events/message.js
index a6a5ee4..9528abe 100644
--- a/src/js/events/message.js
+++ b/src/js/events/message.js
@@ -12,7 +12,7 @@ purifier.addHook("uponSanitizeAttribute", (node, hookevent, config) => {
 
 	const allowedElementAttributes = {
 		"FONT": ["data-mx-bg-color", "data-mx-color", "color"],
-		"SPAN": ["data-mx-bg-color", "data-mx-color"],
+		"SPAN": ["data-mx-bg-color", "data-mx-color", "data-mx-spoiler"],
 		"A": ["name", "target", "href"],
 		"IMG": ["width", "height", "alt", "title", "src", "data-mx-emoticon"],
 		"OL": ["start"],
@@ -55,7 +55,7 @@ function cleanHTML(html) {
 			"color", "name", "target", "href", "width", "height", "alt", "title",
 			"src", "start", "class", "noreferrer", "noopener",
 			// matrix attrs
-			"data-mx-emoticon", "data-mx-bg-color", "data-mx-color"
+			"data-mx-emoticon", "data-mx-bg-color", "data-mx-color", "data-mx-spoiler"
 		],
 
 		// Return a DOM fragment instead of a string, avoids potential future mutation XSS
diff --git a/src/js/focus.js b/src/js/focus.js
new file mode 100644
index 0000000..2413484
--- /dev/null
+++ b/src/js/focus.js
@@ -0,0 +1,11 @@
+document.body.classList.remove("show-focus")
+
+document.addEventListener("mousedown", () => {
+	document.body.classList.remove("show-focus")
+})
+
+document.addEventListener("keydown", event => {
+	if (event.key === "Tab") {
+		document.body.classList.add("show-focus")
+	}
+})
diff --git a/src/js/main.js b/src/js/main.js
index fa5dc06..1bc0be0 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -1,3 +1,4 @@
+require("./focus.js")
 const groups = require("./groups.js")
 const chat_input = require("./chat-input.js")
 const room_picker = require("./room-picker.js")
diff --git a/src/js/read-marker.js b/src/js/read-marker.js
new file mode 100644
index 0000000..53b4658
--- /dev/null
+++ b/src/js/read-marker.js
@@ -0,0 +1,149 @@
+const {ElemJS, ejs, q} = require("./basic.js")
+const {store} = require("./store/store.js")
+const lsm = require("./lsm.js")
+
+function markFullyRead(roomID, eventID) {
+	return fetch(`${lsm.get("domain")}/_matrix/client/r0/rooms/${roomID}/read_markers?access_token=${lsm.get("access_token")}`, {
+		method: "POST",
+		body: JSON.stringify({
+			"m.fully_read": eventID,
+			"m.read": eventID
+		})
+	})
+}
+
+class ReadBanner extends ElemJS {
+	constructor() {
+		super(q("#c-chat-banner"))
+
+		this.newMessages = ejs("span")
+		this.child(
+			ejs("div").class("c-chat-banner__inner").child(
+				ejs("button").class("c-chat-banner__part").on("click", this.jumpTo.bind(this)).child(
+					ejs("div").class("c-chat-banner__part-inner")
+						.child(this.newMessages)
+						.addText(" new messages")
+				),
+				ejs("button").class("c-chat-banner__part", "c-chat-banner__last").on("click", this.markRead.bind(this)).child(
+					ejs("div").class("c-chat-banner__part-inner").text("Mark as read")
+				)
+			)
+		)
+
+		store.activeRoom.subscribe("changeSelf", this.render.bind(this))
+		store.notificationsChange.subscribe("changeSelf", this.render.bind(this))
+		this.render()
+	}
+
+	async jumpTo() {
+		if (!store.activeRoom.exists()) return
+		const timeline = store.activeRoom.value().timeline
+		const readMarker = timeline.readMarker
+		while (true) {
+			if (readMarker.attached) {
+				readMarker.element.scrollIntoView({behavior: "smooth", block: "center"})
+				return
+			} else {
+				q("#c-chat-messages").scrollTo({
+					top: 0,
+					left: 0,
+					behavior: "smooth"
+				})
+				await new Promise(resolve => {
+					const unsubscribe = timeline.subscribe("afterScrollbackLoad", () => {
+						unsubscribe()
+						resolve()
+					})
+				})
+			}
+		}
+	}
+
+	markRead() {
+		if (!store.activeRoom.exists()) return
+		const timeline = store.activeRoom.value().timeline
+		markFullyRead(timeline.id, timeline.latestEventID)
+	}
+
+	render() {
+		let count = 0
+		if (store.activeRoom.exists()) {
+			count = store.activeRoom.value().number.state.unreads
+		}
+		if (count !== 0) {
+			this.newMessages.text(count)
+			this.class("c-chat-banner--active")
+		} else {
+			this.removeClass("c-chat-banner--active")
+		}
+	}
+}
+const readBanner = new ReadBanner()
+
+class ReadMarker extends ElemJS {
+	constructor(timeline) {
+		super("div")
+
+		this.class("c-read-marker")
+		this.loadingIcon = ejs("div")
+			.class("c-read-marker__loading", "loading-icon")
+			.style("display", "none")
+		this.child(
+			ejs("div").class("c-read-marker__inner").child(
+				ejs("div").class("c-read-marker__text").child(this.loadingIcon).addText("New")
+			)
+		)
+
+		let processing = false
+		const observer = new IntersectionObserver(entries => {
+			const entry = entries[0]
+			if (!entry.isIntersecting) return
+			if (processing) return
+			processing = true
+			this.loadingIcon.style("display", "")
+			markFullyRead(this.timeline.id, this.timeline.latestEventID).then(() => {
+				this.loadingIcon.style("display", "none")
+				processing = false
+			})
+		}, {
+			root: document.getElementById("c-chat-messages"),
+			rootMargin: "-80px 0px 0px 0px", // marker must be this distance inside the top of the screen to be counted as read
+			threshold: 0.01
+		})
+		observer.observe(this.element)
+
+		this.attached = false
+		this.timeline = timeline
+		this.timeline.userReads.get(lsm.get("mx_user_id")).subscribe("changeSelf", (_, eventID) => {
+			// read marker updated, attach to it
+			const event = this.timeline.map.get(eventID)
+			this.attach(event)
+		})
+		this.timeline.subscribe("afterChange", () => {
+			// timeline has new events, attach to last read one
+			const eventID = this.timeline.userReads.get(lsm.get("mx_user_id")).value()
+			const event = this.timeline.map.get(eventID)
+			this.attach(event)
+		})
+	}
+
+	attach(event) {
+		if (event && event.data.origin_server_ts !== this.timeline.latest) {
+			this.class("c-read-marker--attached")
+			event.element.insertAdjacentElement("beforeend", this.element)
+			this.attached = true
+		} else {
+			this.removeClass("c-read-marker--attached")
+			this.attached = false
+		}
+		if (store.activeRoom.value() === this.timeline.room) {
+			readBanner.render()
+		}
+	}
+}
+
+module.exports = {
+	ReadMarker,
+	readBanner,
+	markFullyRead
+}
diff --git a/src/js/timeline.js b/src/js/timeline.js
index de17b9c..cd3ef05 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -5,6 +5,7 @@ const {SubscribeMap} = require("./store/subscribe_map.js")
 const {store} = require("./store/store.js")
 const {Anchor} = require("./anchor.js")
 const {Sender} = require("./sender.js")
+const {ReadMarker, markFullyRead} = require("./read-marker.js")
 const lsm = require("./lsm.js")
 const {resolveMxc} = require("./functions.js")
 const {renderEvent} = require("./events/render-event")
@@ -20,16 +21,6 @@ function getTxnId() {
 	return Date.now() + (sentIndex++)
 }
 
-function markFullyRead(roomID, eventID) {
-	return fetch(`${lsm.get("domain")}/_matrix/client/r0/rooms/${roomID}/read_markers?access_token=${lsm.get("access_token")}`, {
-		method: "POST",
-		body: JSON.stringify({
-			"m.fully_read": eventID,
-			"m.read": eventID
-		})
-	})
-}
-
 function eventSearch(list, event, min = 0, max = NO_MAX) {
 	if (list.length === 0) return {success: false, i: 0}
 
@@ -184,62 +175,6 @@ class ReactiveTimeline extends ElemJS {
 	}
 }
 
-class ReadMarker extends ElemJS {
-	constructor(timeline) {
-		super("div")
-
-		this.class("c-read-marker")
-		this.loadingIcon = ejs("div")
-			.class("c-read-marker__loading", "loading-icon")
-			.style("display", "none")
-		this.child(
-			ejs("div").class("c-read-marker__inner").child(
-				ejs("div").class("c-read-marker__text").child(this.loadingIcon).addText("New")
-			)
-		)
-
-		let processing = false
-		const observer = new IntersectionObserver(entries => {
-			const entry = entries[0]
-			if (!entry.isIntersecting) return
-			if (processing) return
-			processing = true
-			this.loadingIcon.style("display", "")
-			markFullyRead(this.timeline.id, this.timeline.latestEventID).then(() => {
-				this.loadingIcon.style("display", "none")
-				processing = false
-			})
-		}, {
-			root: document.getElementById("c-chat-messages"),
-			rootMargin: "-80px 0px 0px 0px", // marker must be this distance inside the top of the screen to be counted as read
-			threshold: 0.01
-		})
-		observer.observe(this.element)
-
-		this.timeline = timeline
-		this.timeline.userReads.get(lsm.get("mx_user_id")).subscribe("changeSelf", (_, eventID) => {
-			// read marker updated, attach to it
-			const event = this.timeline.map.get(eventID)
-			this.attach(event)
-		})
-		this.timeline.subscribe("afterChange", () => {
-			// timeline has new events, attach to last read one
-			const eventID = this.timeline.userReads.get(lsm.get("mx_user_id")).value()
-			const event = this.timeline.map.get(eventID)
-			this.attach(event)
-		})
-	}
-
-	attach(event) {
-		if (event && event.data.origin_server_ts !== this.timeline.latest) {
-			this.class("c-read-marker--attached")
-			event.element.insertAdjacentElement("beforeend", this.element)
-		} else {
-			this.removeClass("c-read-marker--attached")
-		}
-	}
-}
-
 class Timeline extends Subscribable {
 	constructor(room) {
 		super()
diff --git a/src/sass/base.sass b/src/sass/base.sass
index 0232c2e..a649cce 100644
--- a/src/sass/base.sass
+++ b/src/sass/base.sass
@@ -21,3 +21,39 @@ body
 .main
   height: 100vh
   display: flex
+
+button
+  appearance: none
+  border: none
+  background: none
+  color: inherit
+  font-family: inherit
+  font-size: inherit
+  font-style: inherit
+  font-weight: inherit
+  padding: 0
+  margin: 0
+  line-height: inherit
+  cursor: inherit
+
+// focus resets
+
+:focus
+  outline: none
+
+:-moz-focusring
+  outline: none
+
+::-moz-focus-inner
+  border: 0
+
+select:-moz-focusring
+  color: transparent
+  text-shadow: 0 0 0 #ddd
+
+body.show-focus
+  a, select, button, input, video
+    outline-color: #fff
+
+    &:focus
+      outline: 2px dotted
diff --git a/src/sass/colors.sass b/src/sass/colors.sass
index e40189c..a603edd 100644
--- a/src/sass/colors.sass
+++ b/src/sass/colors.sass
@@ -6,3 +6,4 @@ $milder: #42454a
 $divider: #4b4e54
 $muted: #999
 $link: #57bffd
+$notify-highlight: #ffac4b
diff --git a/src/sass/components/chat-banner.sass b/src/sass/components/chat-banner.sass
new file mode 100644
index 0000000..ba7a94d
--- /dev/null
+++ b/src/sass/components/chat-banner.sass
@@ -0,0 +1,47 @@
+@use "../colors" as c
+
+.c-chat-banner
+  position: sticky
+  z-index: 1
+  top: 0
+  left: 0
+  right: 0
+  margin-right: 12px
+  outline-color: #000
+  opacity: 0
+
+  &--active
+    opacity: 1
+
+  &__inner
+    display: grid
+    grid-template-columns: 1fr auto
+    background: c.$notify-highlight
+    color: #000
+    margin: 0px 12px
+    padding: 0px 12px
+    border-radius: 0px 0px 10px 10px
+    line-height: 1
+    box-shadow: 0px 5px 5px -2px rgba(0, 0, 0, 0.1)
+    cursor: pointer
+
+    &:hover
+      box-shadow: 0px 5px 5px -2px rgba(0, 0, 0, 0.6)
+
+  &__part
+    padding: 6px 0px 8px
+
+    &:hover
+      text-decoration: underline
+
+  &__part-inner
+    display: block
+    width: 100% // yes, really.
+    text-align: left
+
+  &__last
+    margin-left: 8px
+
+  &__last &__part-inner
+    border-left: 1px solid #222
+    padding-left: 8px
diff --git a/src/sass/components/chat.sass b/src/sass/components/chat.sass
index 5ca48e0..c8bb391 100644
--- a/src/sass/components/chat.sass
+++ b/src/sass/components/chat.sass
@@ -2,11 +2,12 @@
 
 .c-chat
   display: grid
-  grid-template-rows: 1fr 82px // fixed so that input box height adjustment doesn't mess up scroll
+  grid-template-rows: 0 1fr 82px // fixed so that input box height adjustment doesn't mess up scroll
   align-items: end
   flex: 1
 
   &__messages
+    position: relative
     height: 100%
     overflow-y: scroll
     scrollbar-color: c.$darkest c.$darker
diff --git a/src/sass/components/read-marker.sass b/src/sass/components/read-marker.sass
index 84435fb..7e45910 100644
--- a/src/sass/components/read-marker.sass
+++ b/src/sass/components/read-marker.sass
@@ -1,3 +1,5 @@
+@use "../colors" as c
+
 .c-read-marker
   display: none
   position: relative
@@ -11,7 +13,7 @@
     right: 0px
     height: 2px
     top: 0px
-    background-color: #ffac4b // TODO
+    background-color: c.$notify-highlight
 
     @at-root .c-message:last-child &
       top: 11px
@@ -22,7 +24,7 @@
     top: -9px
     display: flex
     align-items: center
-    background-color: #ffac4b // TODO
+    background-color: c.$notify-highlight
     color: #000
     font-size: 12px
     font-weight: 600
diff --git a/src/sass/main.sass b/src/sass/main.sass
index 11c85ec..7134edf 100644
--- a/src/sass/main.sass
+++ b/src/sass/main.sass
@@ -9,3 +9,4 @@
 @use "./components/anchor"
 @use "./components/highlighted-code"
 @use "./components/read-marker"
+@use "./components/chat-banner"

From 70cae25aa7c61e07873b1a1dc8a720462bdaf4de Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Thu, 26 Nov 2020 17:57:05 +1300
Subject: [PATCH 83/92] Update readme

---
 README.md | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index a737fa0..e226e30 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,6 @@ Carbon is currently _technically_ usable as a chat app, but is very
 early in development. These important features still need to be
 implemented:
 
-- Unreads
 - Emojis
 - Reactions
 - Encryption
@@ -63,6 +62,9 @@ implemented:
 - Pinned channels
 - Mumble integration
 
+For more information, see [issue
+#10.](https://gitdab.com/cadence/Carbon/issues/10)
+
 ## The code
 
 ### Downloading a CI build
@@ -71,12 +73,20 @@ Visit [drone CI](https://drone.badat.dev/cadence/Carbon/branches),
 select the branch you want to use, select `b2` on the left, scroll
 down, and open the URL on the last line to download the build.
 
-### Building yourself
+### Building from source yourself
+
+Dependencies:
+
+- git
+- node
+- npm (bundled with node)
+
+Build:
 
     npm install -D
     npm run rebuild
 
-### Hosting
+### Hosting a build
 
 Send the files from the `build` folder to a static file server. Apply
 a long cache-control header to everything served under `/static`, and
@@ -91,4 +101,4 @@ Files will be rebuilt as you save them.
 Use `python3 -m http.server -d build` to serve the build on
 [http://localhost:8000](http://localhost:8000).
 
-(Avoid `npx http-server`, since this applies too much caching.)
+(Avoid `npx http-server`, it caches too much stuff.)

From e6fc1de276b473158b050e1b75c4b712edd0f218 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Fri, 27 Nov 2020 02:48:30 +1300
Subject: [PATCH 84/92] Greatly improved membership event display

---
 spec.js                            | 15 ++++++
 src/assets/icons/invite-event.svg  | 81 +++++++++++++++++++++++++++++
 src/assets/icons/join-event.svg    |  6 +--
 src/assets/icons/leave-event.svg   | 80 ++++++++++++++++++++++++++++
 src/assets/icons/profile-event.svg | 83 ++++++++++++++++++++++++++++++
 src/js/events/encrypted.js         |  8 +--
 src/js/events/event.js             | 20 ++++++-
 src/js/events/image.js             |  4 +-
 src/js/events/membership.js        | 70 ++++++++++++++++++++++---
 src/js/events/message.js           | 14 ++---
 src/js/events/unknown.js           |  4 +-
 src/js/timeline.js                 | 20 +++++--
 src/sass/components/messages.sass  | 23 +++++++--
 13 files changed, 384 insertions(+), 44 deletions(-)
 create mode 100644 src/assets/icons/invite-event.svg
 create mode 100644 src/assets/icons/leave-event.svg
 create mode 100644 src/assets/icons/profile-event.svg

diff --git a/spec.js b/spec.js
index 1babec8..d1429c2 100644
--- a/spec.js
+++ b/spec.js
@@ -39,6 +39,21 @@ module.exports = [
 		source: "/assets/icons/join-event.svg",
 		target: "/static/join-event.svg",
 	},
+	{
+		type: "file",
+		source: "/assets/icons/leave-event.svg",
+		target: "/static/leave-event.svg",
+	},
+	{
+		type: "file",
+		source: "/assets/icons/invite-event.svg",
+		target: "/static/invite-event.svg",
+	},
+	{
+		type: "file",
+		source: "/assets/icons/profile-event.svg",
+		target: "/static/profile-event.svg",
+	},
 	{
 		type: "sass",
 		source: "/sass/main.sass",
diff --git a/src/assets/icons/invite-event.svg b/src/assets/icons/invite-event.svg
new file mode 100644
index 0000000..fa44732
--- /dev/null
+++ b/src/assets/icons/invite-event.svg
@@ -0,0 +1,81 @@
+
+
+
+
+  
+  
+    
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+    
+  
+
diff --git a/src/assets/icons/join-event.svg b/src/assets/icons/join-event.svg
index 042e3bd..2b6b901 100644
--- a/src/assets/icons/join-event.svg
+++ b/src/assets/icons/join-event.svg
@@ -25,9 +25,9 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="15.649008"
-     inkscape:cy="8.3751893"
+     inkscape:zoom="11.313708"
+     inkscape:cx="-4.2728481"
+     inkscape:cy="-2.1951295"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="true"
diff --git a/src/assets/icons/leave-event.svg b/src/assets/icons/leave-event.svg
new file mode 100644
index 0000000..f836616
--- /dev/null
+++ b/src/assets/icons/leave-event.svg
@@ -0,0 +1,80 @@
+
+
+
+
+  
+  
+    
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+    
+    
+    
+  
+
diff --git a/src/assets/icons/profile-event.svg b/src/assets/icons/profile-event.svg
new file mode 100644
index 0000000..6fcdadf
--- /dev/null
+++ b/src/assets/icons/profile-event.svg
@@ -0,0 +1,83 @@
+
+
+
+
+  
+  
+    
+  
+  
+    
+      
+        image/svg+xml
+        
+        
+      
+    
+  
+  
+    
+    
+    
+  
+
diff --git a/src/js/events/encrypted.js b/src/js/events/encrypted.js
index f73d4b9..efc3ccf 100644
--- a/src/js/events/encrypted.js
+++ b/src/js/events/encrypted.js
@@ -1,7 +1,7 @@
-const {MatrixEvent} = require("./event")
+const {GroupableEvent} = require("./event")
 const {ejs} = require("../basic")
 
-class EncryptedMessage extends MatrixEvent {
+class EncryptedMessage extends GroupableEvent {
 	render() {
 		this.clearChildren()
 		this.child(
@@ -13,10 +13,6 @@ class EncryptedMessage extends MatrixEvent {
 	static canRender(eventData) {
 		return eventData.type === "m.room.encrypted"
 	}
-
-	canGroup() {
-		return true
-	}
 }
 
 module.exports = [EncryptedMessage]
diff --git a/src/js/events/event.js b/src/js/events/event.js
index 189d6e2..040d94e 100644
--- a/src/js/events/event.js
+++ b/src/js/events/event.js
@@ -5,7 +5,6 @@ const {SubscribeSet} = require("../store/subscribe_set.js")
 class MatrixEvent extends ElemJS {
 	constructor(data) {
 		super("div")
-		this.class("c-message")
 		this.data = null
 		this.group = null
 		this.editedAt = null
@@ -53,4 +52,21 @@ class MatrixEvent extends ElemJS {
 	}
 }
 
-module.exports = {MatrixEvent}
+class GroupableEvent extends MatrixEvent {
+	constructor(data) {
+		super(data)
+		this.class("c-message")
+	}
+
+	canGroup() {
+		return true
+	}
+}
+
+class UngroupableEvent extends MatrixEvent {
+}
+
+module.exports = {
+	GroupableEvent,
+	UngroupableEvent
+}
diff --git a/src/js/events/image.js b/src/js/events/image.js
index ec5a9d4..6d8d771 100644
--- a/src/js/events/image.js
+++ b/src/js/events/image.js
@@ -1,8 +1,8 @@
 const {ejs, ElemJS} = require("../basic")
 const {resolveMxc} = require("../functions")
-const {MatrixEvent} = require("./event")
+const {GroupableEvent} = require("./event")
 
-class Image extends MatrixEvent {
+class Image extends GroupableEvent {
 	render() {
 		this.clearChildren()
 		this.class("c-message--media")
diff --git a/src/js/events/membership.js b/src/js/events/membership.js
index 2e85bae..0f10b04 100644
--- a/src/js/events/membership.js
+++ b/src/js/events/membership.js
@@ -1,15 +1,35 @@
-const {MatrixEvent} = require("./event")
+const {UngroupableEvent} = require("./event")
 const {ejs} = require("../basic")
+const {extractDisplayName, resolveMxc, extractLocalpart} = require("../functions")
+
+class MembershipEvent extends UngroupableEvent {
+	constructor(data) {
+		super(data)
+		this.class("c-message-event")
+		this.senderName = extractDisplayName(data)
+		if (data.content.avatar_url) {
+			this.smallAvatar = ejs("img")
+				.attribute("width", "32")
+				.attribute("height", "32")
+				.attribute("src", resolveMxc(data.content.avatar_url, 32, "crop"))
+				.class("c-message-event__avatar")
+		} else {
+			this.smallAvatar = ""
+		}
+		this.render()
+	}
 
-class MembershipEvent extends MatrixEvent {
 	static canRender(eventData) {
 		return eventData.type === "m.room.member"
 	}
 
-	renderText(text) {
+	renderInner(iconURL, elements) {
 		this.clearChildren()
 		this.child(
-			ejs("i").text(text)
+			ejs("div").class("c-message-event__inner").child(
+				iconURL ? ejs("img").class("c-message-event__icon").attribute("width", "20").attribute("height", "20").attribute("src", iconURL) : "",
+				...elements
+			)
 		)
 		super.render()
 	}
@@ -22,7 +42,30 @@ class JoinedEvent extends MembershipEvent {
 	}
 
 	render() {
-		this.renderText("joined the room")
+		const changes = []
+		const prev = this.data.unsigned.prev_content
+		if (prev && prev.membership === "join") {
+			if (prev.avatar_url !== this.data.content.avatar_url) {
+				changes.push("changed their avatar")
+			}
+			if (prev.displayname !== this.data.content.displayname) {
+				changes.push(`changed their display name (was ${this.data.unsigned.prev_content.displayname})`)
+			}
+		}
+		let message
+		let iconURL
+		if (changes.length) {
+			message = " " + changes.join(", ")
+			iconURL = "static/profile-event.svg"
+		} else {
+			message = " joined the room"
+			iconURL = "static/join-event.svg"
+		}
+		this.renderInner(iconURL, [
+			this.smallAvatar,
+			this.senderName,
+			message
+		])
 	}
 }
 
@@ -32,7 +75,10 @@ class InvitedEvent extends MembershipEvent {
 	}
 
 	render() {
-		this.renderText(`invited ${this.data.content.displayname}`)
+		this.renderInner("static/invite-event.svg", [
+			this.smallAvatar,
+			`${extractLocalpart(this.data.sender)} invited ${this.data.state_key}` // full mxid for clarity
+		])
 	}
 }
 
@@ -42,13 +88,21 @@ class LeaveEvent extends MembershipEvent {
 	}
 
 	render() {
-		this.renderText("left the room")
+		this.renderInner("static/leave-event.svg", [
+			this.smallAvatar,
+			this.senderName,
+			" left the room"
+		])
 	}
 }
 
 class UnknownMembership extends MembershipEvent {
 	render() {
-		this.renderText("unknown membership event")
+		this.renderInner("", [
+			this.smallAvatar,
+			this.senderName,
+			ejs("i").text(" unknown membership event")
+		])
 	}
 }
 
diff --git a/src/js/events/message.js b/src/js/events/message.js
index 9528abe..50cd3b5 100644
--- a/src/js/events/message.js
+++ b/src/js/events/message.js
@@ -2,7 +2,7 @@ const {ejs, ElemJS} = require("../basic")
 const {HighlightedCode} = require("./components")
 const DOMPurify = require("dompurify")
 const {resolveMxc} = require("../functions")
-const {MatrixEvent} = require("./event")
+const {GroupableEvent} = require("./event")
 
 const purifier = DOMPurify()
 
@@ -88,7 +88,7 @@ function postProcessElements(element) {
 }
 
 
-class HTMLMessage extends MatrixEvent {
+class HTMLMessage extends GroupableEvent {
 	render() {
 		this.clearChildren()
 
@@ -111,10 +111,6 @@ class HTMLMessage extends MatrixEvent {
 				&& content.formatted_body
 		)
 	}
-
-	canGroup() {
-		return true
-	}
 }
 
 function autoLinkText(text) {
@@ -139,7 +135,7 @@ function autoLinkText(text) {
 	return fragment
 }
 
-class TextMessage extends MatrixEvent {
+class TextMessage extends GroupableEvent {
 	render() {
 		this.clearChildren()
 		this.class("c-message--plain")
@@ -151,10 +147,6 @@ class TextMessage extends MatrixEvent {
 	static canRender(event) {
 		return event.type === "m.room.message"
 	}
-
-	canGroup() {
-		return true
-	}
 }
 
 module.exports = [HTMLMessage, TextMessage]
diff --git a/src/js/events/unknown.js b/src/js/events/unknown.js
index c3f10dd..5133aa8 100644
--- a/src/js/events/unknown.js
+++ b/src/js/events/unknown.js
@@ -1,7 +1,7 @@
-const {MatrixEvent} = require("./event")
+const {GroupableEvent} = require("./event")
 const {ejs} = require("../basic")
 
-class UnknownEvent extends MatrixEvent {
+class UnknownEvent extends GroupableEvent {
 	static canRender() {
 		return true
 	}
diff --git a/src/js/timeline.js b/src/js/timeline.js
index cd3ef05..3ef646b 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -65,6 +65,11 @@ class EventGroup extends ElemJS {
 		)
 	}
 
+	canGroup() {
+		if (this.list.length) return this.list[0].canGroup()
+		else return true
+	}
+
 	addEvent(event) {
 		const index = eventSearch(this.list, event).i
 		event.setGroup(this)
@@ -142,16 +147,21 @@ class ReactiveTimeline extends ElemJS {
 		const success = indices.some(i => {
 			if (!this.list[i]) {
 				// if (printed++ < 100) console.log("tryadd success, created group")
-				const group = new EventGroup(this, [event])
 				if (i === -1) {
 					// here, -1 means at the start, before the first group
 					i = 0 // jank but it does the trick
 				}
-				this.list.splice(i, 0, group)
-				this.childAt(i, group)
-				event.setGroup(group)
+				if (event.canGroup()) {
+					const group = new EventGroup(this, [event])
+					this.list.splice(i, 0, group)
+					this.childAt(i, group)
+					event.setGroup(group)
+				} else {
+					this.list.splice(i, 0, event)
+					this.childAt(i, event)
+				}
 				return true
-			} else if (this.list[i] && this.list[i].data.sender === event.data.sender) {
+			} else if (event.canGroup() && this.list[i] && this.list[i].canGroup() && this.list[i].data.sender === event.data.sender) {
 				// if (printed++ < 100) console.log("tryadd success, using existing group")
 				this.list[i].addEvent(event)
 				return true
diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass
index 170ae71..4b61003 100644
--- a/src/sass/components/messages.sass
+++ b/src/sass/components/messages.sass
@@ -116,17 +116,30 @@
       margin-bottom: 0px
 
 .c-message-event
-  padding-top: 10px
+  // closer spacing than normal messages
+  padding-top: 2px
   padding-left: 6px
+  margin-bottom: -4px
+  line-height: 1.2
 
   &__inner
-    display: flex
-    align-items: center
+    text-indent: -36px
+    margin-left: 36px
+
+  img
+    // let me know if there's a smarter way to line this shit up
+    position: relative
+    top: -5px
+    transform: translateY(50%)
 
   &__icon
     margin-right: 8px
-    position: relative
-    top: 1px
+
+  &__avatar
+    width: 16px
+    height: 16px
+    border-radius: 50%
+    margin: 0px 6px
 
 .c-message-notice
   padding: 12px

From b4dfefbac93b15ed76c249b370618ba6ec7b7f7d Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Sat, 28 Nov 2020 17:07:54 +1300
Subject: [PATCH 85/92] Render ban events

---
 src/js/events/membership.js | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/js/events/membership.js b/src/js/events/membership.js
index 0f10b04..5617d3e 100644
--- a/src/js/events/membership.js
+++ b/src/js/events/membership.js
@@ -96,6 +96,24 @@ class LeaveEvent extends MembershipEvent {
 	}
 }
 
+class BanEvent extends MembershipEvent {
+	static canRender(eventData) {
+		return super.canRender(eventData) && eventData.content.membership === "ban"
+	}
+
+	render() {
+		let message =
+			 ` left (banned by ${this.data.sender}`
+			 + (this.data.content.reason ? `, reason: ${this.data.content.reason}` : "")
+			 + ")"
+		this.renderInner("static/leave-event.svg", [
+			this.smallAvatar,
+			this.senderName,
+			message
+		])
+	}
+}
+
 class UnknownMembership extends MembershipEvent {
 	render() {
 		this.renderInner("", [
@@ -106,4 +124,4 @@ class UnknownMembership extends MembershipEvent {
 	}
 }
 
-module.exports = [JoinedEvent, InvitedEvent, LeaveEvent, UnknownMembership]
+module.exports = [JoinedEvent, InvitedEvent, LeaveEvent, BanEvent, UnknownMembership]

From a004e84adc519519dd7426b5e98ba9b1ede1d34b Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Sat, 28 Nov 2020 17:14:47 +1300
Subject: [PATCH 86/92] Add spoilers

---
 src/js/events/message.js             | 12 ++++++++++++
 src/js/sync/sync.js                  |  2 +-
 src/sass/base.sass                   |  2 +-
 src/sass/components/chat-banner.sass |  3 +++
 src/sass/components/read-marker.sass |  3 +++
 src/sass/components/spoilers.sass    |  8 ++++++++
 src/sass/main.sass                   |  1 +
 7 files changed, 29 insertions(+), 2 deletions(-)
 create mode 100644 src/sass/components/spoilers.sass

diff --git a/src/js/events/message.js b/src/js/events/message.js
index 50cd3b5..ad885ff 100644
--- a/src/js/events/message.js
+++ b/src/js/events/message.js
@@ -85,6 +85,18 @@ function postProcessElements(element) {
 		if (color) n.style.color = color
 		if (bgColor) n.style.backgroundColor = bgColor
 	})
+
+	element.querySelectorAll("[data-mx-spoiler]").forEach(spoiler => {
+		spoiler.classList.add("mx-spoiler")
+		spoiler.setAttribute("tabindex", 0)
+		function toggle() {
+			spoiler.classList.toggle("mx-spoiler--shown")
+		}
+		spoiler.addEventListener("click", toggle)
+		spoiler.addEventListener("keydown", event => {
+			if (event.key === "Enter") toggle()
+		})
+	})
 }
 
 
diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js
index 90c92b9..b445253 100644
--- a/src/js/sync/sync.js
+++ b/src/js/sync/sync.js
@@ -11,7 +11,7 @@ function sync() {
 		room: {
 			// pulling more from the timeline massively increases download size
 			timeline: {
-				limit: 5
+				limit: 1
 			},
 			// members are not currently needed
 			state: {
diff --git a/src/sass/base.sass b/src/sass/base.sass
index a649cce..dfb9f7a 100644
--- a/src/sass/base.sass
+++ b/src/sass/base.sass
@@ -52,7 +52,7 @@ select:-moz-focusring
   text-shadow: 0 0 0 #ddd
 
 body.show-focus
-  a, select, button, input, video
+  a, select, button, input, video, div, span
     outline-color: #fff
 
     &:focus
diff --git a/src/sass/components/chat-banner.sass b/src/sass/components/chat-banner.sass
index ba7a94d..f17acce 100644
--- a/src/sass/components/chat-banner.sass
+++ b/src/sass/components/chat-banner.sass
@@ -9,9 +9,12 @@
   margin-right: 12px
   outline-color: #000
   opacity: 0
+  transform: translateY(-40px)
+  transition: transform 0.2s ease, opacity 0.2s ease-out
 
   &--active
     opacity: 1
+    transform: translateY(0px)
 
   &__inner
     display: grid
diff --git a/src/sass/components/read-marker.sass b/src/sass/components/read-marker.sass
index 7e45910..7e1c572 100644
--- a/src/sass/components/read-marker.sass
+++ b/src/sass/components/read-marker.sass
@@ -18,6 +18,9 @@
     @at-root .c-message:last-child &
       top: 11px
 
+    @at-root .c-message-event &
+      top: 7px
+
   &__text
     position: absolute
     right: -14px
diff --git a/src/sass/components/spoilers.sass b/src/sass/components/spoilers.sass
new file mode 100644
index 0000000..e8a1784
--- /dev/null
+++ b/src/sass/components/spoilers.sass
@@ -0,0 +1,8 @@
+.mx-spoiler
+  color: #331911
+  background-color:  #331911
+  outline-color: #fff !important
+  cursor: pointer
+
+  &--shown
+    color: inherit
diff --git a/src/sass/main.sass b/src/sass/main.sass
index 7134edf..c050148 100644
--- a/src/sass/main.sass
+++ b/src/sass/main.sass
@@ -10,3 +10,4 @@
 @use "./components/highlighted-code"
 @use "./components/read-marker"
 @use "./components/chat-banner"
+@use "./components/spoilers"

From 2e91ff8ff27888612451981e3aa663aec30d8efc Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Sat, 28 Nov 2020 18:45:08 +1300
Subject: [PATCH 87/92] Recognise image spoilers (based on "body")

---
 src/js/events/image.js            | 18 +++++++++++++++-
 src/sass/components/messages.sass | 35 +++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/src/js/events/image.js b/src/js/events/image.js
index 6d8d771..2c8a7d3 100644
--- a/src/js/events/image.js
+++ b/src/js/events/image.js
@@ -16,7 +16,23 @@ class Image extends GroupableEvent {
 			image.attribute("width", info.w)
 			image.attribute("height", info.h)
 		}
-		this.child(image)
+		const wrapper = ejs("div").class("c-media__wrapper").child(
+			image
+		)
+		if (this.data.content.body && this.data.content.body.startsWith("SPOILER")) {
+			wrapper.attribute("tabindex", 0)
+			wrapper.class("c-media--spoiler")
+			const wall = ejs("div").class("c-media__spoiler").text("Spoiler")
+			wrapper.child(wall)
+			function toggle() {
+				wrapper.element.classList.toggle("c-media--shown")
+			}
+			wrapper.on("click", toggle)
+			wrapper.on("keydown", event => {
+				if (event.key === "Enter") toggle()
+			})
+		}
+		this.child(wrapper)
 		super.render()
 	}
 
diff --git a/src/sass/components/messages.sass b/src/sass/components/messages.sass
index 4b61003..060dec4 100644
--- a/src/sass/components/messages.sass
+++ b/src/sass/components/messages.sass
@@ -58,6 +58,7 @@
     // fix whitespace
     font-size: 0
     margin-top: 8px
+    display: flex
 
   &--pending
     opacity: 0.5
@@ -149,3 +150,37 @@
     padding: 12px
     background-color: c.$milder
     border-radius: 8px
+
+.c-media
+  &__wrapper
+    overflow: hidden
+    position: relative
+
+  &--spoiler
+    cursor: pointer
+
+    img
+      filter: blur(40px)
+
+  &--shown img
+    filter: none
+
+  &__spoiler
+    position: absolute
+    top: 0
+    bottom: 0
+    left: 0
+    right: 0
+    display: flex
+    align-items: center
+    justify-content: center
+    font-size: 18px
+    font-weight: 500
+    color: #fff
+    text-transform: uppercase
+    background: rgba(0, 0, 0, 0.3)
+    cursor: pointer
+    pointer-events: none
+
+  &--shown &__spoiler
+    display: none

From ea6ccc08ee6d882f43f3fb9919efd58796b1f764 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Sun, 29 Nov 2020 19:47:19 +1300
Subject: [PATCH 88/92] Render call events in timeline

---
 spec.js                            | 20 ++++++
 src/assets/icons/call-accepted.svg | 94 ++++++++++++++++++++++++++++
 src/assets/icons/call-in.svg       | 99 ++++++++++++++++++++++++++++++
 src/assets/icons/call-out.svg      | 98 +++++++++++++++++++++++++++++
 src/assets/icons/call-rejected.svg | 98 +++++++++++++++++++++++++++++
 src/js/events/call.js              | 67 ++++++++++++++++++++
 src/js/events/image.js             |  2 +-
 src/js/events/render-event.js      |  2 +
 src/js/functions.js                |  4 +-
 src/js/timeline.js                 |  2 +-
 10 files changed, 483 insertions(+), 3 deletions(-)
 create mode 100644 src/assets/icons/call-accepted.svg
 create mode 100644 src/assets/icons/call-in.svg
 create mode 100644 src/assets/icons/call-out.svg
 create mode 100644 src/assets/icons/call-rejected.svg
 create mode 100644 src/js/events/call.js

diff --git a/spec.js b/spec.js
index d1429c2..4244cdf 100644
--- a/spec.js
+++ b/spec.js
@@ -54,6 +54,26 @@ module.exports = [
 		source: "/assets/icons/profile-event.svg",
 		target: "/static/profile-event.svg",
 	},
+	{
+		type: "file",
+		source: "/assets/icons/call-out.svg",
+		target: "/static/call-out.svg",
+	},
+	{
+		type: "file",
+		source: "/assets/icons/call-in.svg",
+		target: "/static/call-in.svg",
+	},
+	{
+		type: "file",
+		source: "/assets/icons/call-accepted.svg",
+		target: "/static/call-accepted.svg",
+	},
+	{
+		type: "file",
+		source: "/assets/icons/call-rejected.svg",
+		target: "/static/call-rejected.svg",
+	},
 	{
 		type: "sass",
 		source: "/sass/main.sass",
diff --git a/src/assets/icons/call-accepted.svg b/src/assets/icons/call-accepted.svg
new file mode 100644
index 0000000..fea7031
--- /dev/null
+++ b/src/assets/icons/call-accepted.svg
@@ -0,0 +1,94 @@
+
+
+  
+    
+      
+        image/svg+xml
+        
+        free-icons-solid
+      
+    
+  
+  
+    
+  
+  
+    
+  
+  free-icons-solid
+  
+  
+  
+
diff --git a/src/assets/icons/call-in.svg b/src/assets/icons/call-in.svg
new file mode 100644
index 0000000..484fe5c
--- /dev/null
+++ b/src/assets/icons/call-in.svg
@@ -0,0 +1,99 @@
+
+
+  
+    
+      
+        image/svg+xml
+        
+        free-icons-solid
+      
+    
+  
+  
+    
+  
+  
+    
+  
+  free-icons-solid
+  
+  
+    
+    
+  
+
diff --git a/src/assets/icons/call-out.svg b/src/assets/icons/call-out.svg
new file mode 100644
index 0000000..877bad4
--- /dev/null
+++ b/src/assets/icons/call-out.svg
@@ -0,0 +1,98 @@
+
+
+  
+    
+      
+        image/svg+xml
+        
+        free-icons-solid
+      
+    
+  
+  
+    
+  
+  
+    
+  
+  free-icons-solid
+  
+  
+    
+    
+  
+
diff --git a/src/assets/icons/call-rejected.svg b/src/assets/icons/call-rejected.svg
new file mode 100644
index 0000000..55b3994
--- /dev/null
+++ b/src/assets/icons/call-rejected.svg
@@ -0,0 +1,98 @@
+
+
+  
+    
+      
+        image/svg+xml
+        
+        free-icons-solid
+      
+    
+  
+  
+    
+  
+  
+    
+  
+  free-icons-solid
+  
+  
+    
+    
+  
+
diff --git a/src/js/events/call.js b/src/js/events/call.js
new file mode 100644
index 0000000..a918a48
--- /dev/null
+++ b/src/js/events/call.js
@@ -0,0 +1,67 @@
+const {UngroupableEvent} = require("./event")
+const {ejs} = require("../basic")
+const lsm = require("../lsm")
+const {extractDisplayName, resolveMxc, extractLocalpart} = require("../functions")
+
+class CallEvent extends UngroupableEvent {
+	constructor(data) {
+		super(data)
+		this.class("c-message-event")
+		this.senderName = extractLocalpart(this.data.sender)
+		this.render()
+	}
+
+	renderInner(iconURL, elements) {
+		this.clearChildren()
+		this.child(
+			ejs("div").class("c-message-event__inner").child(
+				iconURL ? ejs("img").class("c-message-event__icon").attribute("width", "20").attribute("height", "20").attribute("src", iconURL) : "",
+				...elements
+			)
+		)
+		super.render()
+	}
+}
+
+class CallInviteEvent extends CallEvent {
+	static canRender(eventData) {
+		return eventData.type === "m.call.invite"
+	}
+
+	render() {
+		const icon = this.data.sender === lsm.get("mx_user_id") ? "static/call-out.svg" : "static/call-in.svg"
+		this.renderInner(icon, [
+			this.senderName,
+			" started a VOIP call, but Carbon doesn't support VOIP calls"
+		])
+	}
+}
+
+class CallAnswerEvent extends CallEvent {
+	static canRender(eventData) {
+		return eventData.type === "m.call.answer"
+	}
+
+	render() {
+		this.renderInner("static/call-accepted.svg", [
+			this.senderName,
+			" answered the call"
+		])
+	}
+}
+
+class CallHangupEvent extends CallEvent {
+	static canRender(eventData) {
+		return eventData.type === "m.call.hangup"
+	}
+
+	render() {
+		const reason = this.data.content.reason === "invite_timeout" ? "missed the call" : "hung up the call"
+		this.renderInner("static/call-rejected.svg", [
+			this.senderName,
+			" " + reason
+		])
+	}
+}
+
+module.exports = [CallInviteEvent, CallAnswerEvent, CallHangupEvent]
diff --git a/src/js/events/image.js b/src/js/events/image.js
index 2c8a7d3..1a32bcf 100644
--- a/src/js/events/image.js
+++ b/src/js/events/image.js
@@ -24,7 +24,7 @@ class Image extends GroupableEvent {
 			wrapper.class("c-media--spoiler")
 			const wall = ejs("div").class("c-media__spoiler").text("Spoiler")
 			wrapper.child(wall)
-			function toggle() {
+			const toggle = () => {
 				wrapper.element.classList.toggle("c-media--shown")
 			}
 			wrapper.on("click", toggle)
diff --git a/src/js/events/render-event.js b/src/js/events/render-event.js
index 9337a2d..627c60f 100644
--- a/src/js/events/render-event.js
+++ b/src/js/events/render-event.js
@@ -3,12 +3,14 @@ const messageEvent = require("./message")
 const encryptedEvent = require("./encrypted")
 const membershipEvent = require("./membership")
 const unknownEvent = require("./unknown")
+const callEvent = require("./call")
 
 const events = [
 	...imageEvent,
 	...messageEvent,
 	...encryptedEvent,
 	...membershipEvent,
+	...callEvent,
 	...unknownEvent,
 ]
 
diff --git a/src/js/functions.js b/src/js/functions.js
index db43a16..3c346c4 100644
--- a/src/js/functions.js
+++ b/src/js/functions.js
@@ -1,7 +1,9 @@
 const lsm = require("./lsm.js")
 
 function resolveMxc(url, size, method) {
-	let [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
+	const match = url.match(/^mxc:\/\/([^/]+)\/(.*)/)
+	if (!match) return url
+	let [server, id] = match.slice(1)
 	id = id.replace(/#.*$/, "")
 	if (size && method) {
 		return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
diff --git a/src/js/timeline.js b/src/js/timeline.js
index 3ef646b..4a55406 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -259,7 +259,7 @@ class Timeline extends Subscribable {
 				this.map.get(id).update(eventData)
 			} else {
 				// skip displaying events that we don't know how to
-				if (eventData.type === "m.reaction") {
+				if (["m.reaction", "m.call.candidates"].includes(eventData.type)) {
 					continue
 				}
 				// skip redacted events

From 9dce348a4cb01f42726590d4aa3cadbf0313caea Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Mon, 30 Nov 2020 22:40:44 +1300
Subject: [PATCH 89/92] Fix loading desynced messages

For example, in the construct room.
---
 src/js/timeline.js | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/js/timeline.js b/src/js/timeline.js
index 4a55406..8a79d00 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -144,22 +144,25 @@ class ReactiveTimeline extends ElemJS {
 	}
 
 	tryAddGroups(event, indices) {
+		const createGroupAt = i => {
+			// if (printed++ < 100) console.log("tryadd success, created group")
+			if (i === -1) {
+				// here, -1 means at the start, before the first group
+				i = 0 // jank but it does the trick
+			}
+			if (event.canGroup()) {
+				const group = new EventGroup(this, [event])
+				this.list.splice(i, 0, group)
+				this.childAt(i, group)
+				event.setGroup(group)
+			} else {
+				this.list.splice(i, 0, event)
+				this.childAt(i, event)
+			}
+		}
 		const success = indices.some(i => {
 			if (!this.list[i]) {
-				// if (printed++ < 100) console.log("tryadd success, created group")
-				if (i === -1) {
-					// here, -1 means at the start, before the first group
-					i = 0 // jank but it does the trick
-				}
-				if (event.canGroup()) {
-					const group = new EventGroup(this, [event])
-					this.list.splice(i, 0, group)
-					this.childAt(i, group)
-					event.setGroup(group)
-				} else {
-					this.list.splice(i, 0, event)
-					this.childAt(i, event)
-				}
+				createGroupAt(i)
 				return true
 			} else if (event.canGroup() && this.list[i] && this.list[i].canGroup() && this.list[i].data.sender === event.data.sender) {
 				// if (printed++ < 100) console.log("tryadd success, using existing group")
@@ -167,7 +170,8 @@ class ReactiveTimeline extends ElemJS {
 				return true
 			}
 		})
-		if (!success) console.log("tryadd failure", indices, this.list.map(l => l.data.sender), event.data)
+		// if (!success) console.log("tryadd failure", indices, this.list.map(l => l.data.sender), event.data) // I believe all the bugs are now fixed. Lol.
+		if (!success) createGroupAt(indices[0])
 	}
 
 	removeGroup(group) {

From 879c09f70beee61d18fcdea2a65bca336813a7e8 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Sun, 6 Dec 2020 23:45:33 +1300
Subject: [PATCH 90/92] Support read markers on invisible events

---
 src/js/events/hidden.js       | 18 ++++++++++++++++++
 src/js/events/render-event.js |  3 ++-
 src/js/timeline.js            |  7 +------
 3 files changed, 21 insertions(+), 7 deletions(-)
 create mode 100644 src/js/events/hidden.js

diff --git a/src/js/events/hidden.js b/src/js/events/hidden.js
new file mode 100644
index 0000000..372bb72
--- /dev/null
+++ b/src/js/events/hidden.js
@@ -0,0 +1,18 @@
+const {UngroupableEvent} = require("./event")
+
+class HiddenEvent extends UngroupableEvent {
+	constructor(data) {
+		super(data)
+		this.class("c-hidden-event")
+		this.clearChildren()
+	}
+
+	static canRender(eventData) {
+		return ["m.reaction", "m.call.candidates"].includes(eventData.type)
+	}
+
+	render() {
+	}
+}
+
+module.exports = [HiddenEvent]
diff --git a/src/js/events/render-event.js b/src/js/events/render-event.js
index 627c60f..620da3e 100644
--- a/src/js/events/render-event.js
+++ b/src/js/events/render-event.js
@@ -4,6 +4,7 @@ const encryptedEvent = require("./encrypted")
 const membershipEvent = require("./membership")
 const unknownEvent = require("./unknown")
 const callEvent = require("./call")
+const hiddenEvent = require("./hidden")
 
 const events = [
 	...imageEvent,
@@ -11,10 +12,10 @@ const events = [
 	...encryptedEvent,
 	...membershipEvent,
 	...callEvent,
+	...hiddenEvent,
 	...unknownEvent,
 ]
 
-
 function renderEvent(eventData) {
 	const constructor = events.find(e => e.canRender(eventData))
 	return new constructor(eventData)
diff --git a/src/js/timeline.js b/src/js/timeline.js
index 8a79d00..0a36d14 100644
--- a/src/js/timeline.js
+++ b/src/js/timeline.js
@@ -262,10 +262,6 @@ class Timeline extends Subscribable {
 				// update existing event
 				this.map.get(id).update(eventData)
 			} else {
-				// skip displaying events that we don't know how to
-				if (["m.reaction", "m.call.candidates"].includes(eventData.type)) {
-					continue
-				}
 				// skip redacted events
 				if (eventData.unsigned && eventData.unsigned.redacted_by) {
 					continue
@@ -321,12 +317,11 @@ class Timeline extends Subscribable {
 	}
 
 	moveReadReceipt(user, eventID) {
-		if (!this.map.has(eventID)) return // ignore receipts we don't have events for
 		// check for a previous event to move from
 		const prev = this.userReads.get(user)
 		if (prev.exists()) {
 			const prevID = prev.value()
-			if (this.map.has(prevID)) {
+			if (this.map.has(prevID) && this.map.has(eventID)) {
 				// ensure new message came later
 				if (this.map.get(eventID).data.origin_server_ts < this.map.get(prevID).data.origin_server_ts) return
 				this.map.get(prevID).readBy.delete(user)

From f8020318489f45d58f593de105c3ac1b16b12542 Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Tue, 15 Dec 2020 01:05:00 +1300
Subject: [PATCH 91/92] Sync with Dendrite

---
 src/js/room-picker.js | 13 +++++++++++--
 src/js/sync/sync.js   |  6 +++---
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/js/room-picker.js b/src/js/room-picker.js
index d0bc0b9..9192ae9 100644
--- a/src/js/room-picker.js
+++ b/src/js/room-picker.js
@@ -193,6 +193,15 @@ class Room extends ElemJS {
 		}
 	}
 
+	getHeroes() {
+		if (this.data.summary) {
+			return this.data.summary["m.heroes"]
+		} else {
+			const me = lsm.get("mx_user_id")
+			return this.data.state.events.filter(e => e.type === "m.room.member" && e.content.membership === "join" && e.state_key !== me).map(e => e.state_key)
+		}
+	}
+
 	getName() {
 		// if the room has a name
 		let name = this.data.state.events.find(e => e.type === "m.room.name")
@@ -205,7 +214,7 @@ class Room extends ElemJS {
 			return canonicalAlias.content.alias
 		}
 		// if the room has no alias, use the names of its members ("heroes")
-		const users = this.data.summary["m.heroes"]
+		const users = this.getHeroes()
 		if (users && users.length) {
 			const usernames = users.map(mxid => this.getMemberName(mxid))
 			return usernames.join(", ")
@@ -224,7 +233,7 @@ class Room extends ElemJS {
 			}
 		}
 		// if the room has no avatar set, use a member's avatar
-		const users = this.data.summary["m.heroes"]
+		const users = this.getHeroes()
 		if (users && users[0] && this.members.has(users[0])) {
 			// console.log(users[0], this.members.get(users[0]))
 			const userAvatar = this.members.get(users[0]).value().content.avatar_url
diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js
index b445253..f8bfc52 100644
--- a/src/js/sync/sync.js
+++ b/src/js/sync/sync.js
@@ -58,15 +58,15 @@ function manageSync(root) {
 					}
 					const room = store.rooms.get(id).value()
 					const timeline = room.timeline
-					if (data.state) timeline.updateStateEvents(data.state.events)
-					if (data.timeline) {
+					if (data.state && data.state.events) timeline.updateStateEvents(data.state.events)
+					if (data.timeline && data.timeline.events) {
 						if (!timeline.from) timeline.from = data.timeline.prev_batch
 						if (data.timeline.events.length) {
 							newEvents = true
 							timeline.updateEvents(data.timeline.events)
 						}
 					}
-					if (data.ephemeral) timeline.updateEphemeral(data.ephemeral.events)
+					if (data.ephemeral && data.ephemeral.events) timeline.updateEphemeral(data.ephemeral.events)
 					if (data.unread_notifications) {
 						timeline.updateNotificationCount(data.unread_notifications.notification_count)
 						notificationsChange = true

From 734506a300611ed95b29750e50d14e203892c36f Mon Sep 17 00:00:00 2001
From: Cadence Ember 
Date: Thu, 18 Mar 2021 00:52:56 +1300
Subject: [PATCH 92/92] Abandoned

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index e226e30..77dabec 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,10 @@ Carbon is the Matrix client for Discord and Guilded refugees.
 Visit the hosted instance on
 [https://carbon.chat](https://carbon.chat).
 
+## Status
+
+Carbon is **abandoned** by its author, but it is still solid code to build on for anyone with the time and inclination to pick it up.
+
 ## Report bugs and suggest features
 
 Please briefly check this README and the issues page first to make