├── .dockerignore
├── .editorconfig
├── .github
└── workflows
│ └── deploy.yml
├── .gitignore
├── .vscode
├── extensions.json
└── settings.json
├── Dockerfile
├── Dockerfile.local
├── LICENSE
├── README.md
├── client
├── .vscode
│ └── settings.json
├── download.html
├── download.ts
├── editor.css
├── editor.html
├── editor.ts
├── firepad
│ ├── LICENSE
│ ├── README.md
│ ├── firepad.css
│ ├── firepad.eot
│ ├── firepad.js
│ └── firepad.min.js
├── index.css
├── lib
│ ├── DiffEditor.ts
│ ├── FirepadEditor.ts
│ ├── api.ts
│ └── utils.ts
├── modal.css
├── notfound.html
├── notfound.ts
├── package.json
├── tsconfig.json
└── yarn.lock
├── danoweb.code-workspace
├── heroku.yml
└── server
├── .vscode
└── settings.json
├── auth.ts
├── deps.ts
├── handlers
├── api.ts
├── editor.ts
├── systemFile.ts
└── userFile.ts
├── io.ts
├── lib
├── abap.78954cb7.js
├── abap.78954cb7.js.map
├── apex.f337d4c7.js
├── apex.f337d4c7.js.map
├── azcli.342de0e9.js
├── azcli.342de0e9.js.map
├── bat.1c3588d6.js
├── bat.1c3588d6.js.map
├── cameligo.90405ab1.js
├── cameligo.90405ab1.js.map
├── client.d24e0815.css
├── client.d24e0815.css.map
├── clojure.7ecb76e7.js
├── clojure.7ecb76e7.js.map
├── codicon.06463639.ttf
├── coffee.ac527e58.js
├── coffee.ac527e58.js.map
├── cpp.ec086cb5.js
├── cpp.ec086cb5.js.map
├── csharp.4810c44e.js
├── csharp.4810c44e.js.map
├── csp.3fe6fa3e.js
├── csp.3fe6fa3e.js.map
├── css.8843dced.js
├── css.8843dced.js.map
├── css.worker.js
├── cssMode.9f625c46.js
├── cssMode.9f625c46.js.map
├── dockerfile.70a5d4ef.js
├── dockerfile.70a5d4ef.js.map
├── download.ea9627df.js
├── download.ea9627df.js.map
├── download.html
├── editor.1b654aa4.css
├── editor.1b654aa4.css.map
├── editor.85c28932.js
├── editor.85c28932.js.map
├── editor.8cdc07cf.css
├── editor.8cdc07cf.css.map
├── editor.html
├── editor.worker.js
├── firepad.df5a2b0e.eot
├── fsharp.16208b5f.js
├── fsharp.16208b5f.js.map
├── go.0898d2da.js
├── go.0898d2da.js.map
├── graphql.ca227cf9.js
├── graphql.ca227cf9.js.map
├── handlebars.f3fb514d.js
├── handlebars.f3fb514d.js.map
├── html.c4b22ba0.js
├── html.c4b22ba0.js.map
├── html.worker.js
├── htmlMode.8dfe4565.js
├── htmlMode.8dfe4565.js.map
├── ini.5b69da85.js
├── ini.5b69da85.js.map
├── java.7a30ec4b.js
├── java.7a30ec4b.js.map
├── javascript.0409fa73.js
├── javascript.0409fa73.js.map
├── json.worker.js
├── jsonMode.eed4af07.js
├── jsonMode.eed4af07.js.map
├── kotlin.cc603b95.js
├── kotlin.cc603b95.js.map
├── less.2b81da74.js
├── less.2b81da74.js.map
├── lua.f5a53dc9.js
├── lua.f5a53dc9.js.map
├── markdown.20c3cbab.js
├── markdown.20c3cbab.js.map
├── mips.36c9d6e7.js
├── mips.36c9d6e7.js.map
├── modal.f20b33f0.css
├── modal.f20b33f0.css.map
├── msdax.ffb1b23c.js
├── msdax.ffb1b23c.js.map
├── mysql.8a0bd7b6.js
├── mysql.8a0bd7b6.js.map
├── notfound.cd3d98b4.js
├── notfound.cd3d98b4.js.map
├── notfound.html
├── objective-c.bcd32e8a.js
├── objective-c.bcd32e8a.js.map
├── pascal.ca5eabc6.js
├── pascal.ca5eabc6.js.map
├── pascaligo.7d28588e.js
├── pascaligo.7d28588e.js.map
├── perl.16ed5773.js
├── perl.16ed5773.js.map
├── pgsql.5c455d32.js
├── pgsql.5c455d32.js.map
├── php.4188850d.js
├── php.4188850d.js.map
├── postiats.e8c2de5d.js
├── postiats.e8c2de5d.js.map
├── powerquery.2b7d57ea.js
├── powerquery.2b7d57ea.js.map
├── powershell.ca7c736d.js
├── powershell.ca7c736d.js.map
├── pug.02ee696c.js
├── pug.02ee696c.js.map
├── python.33a06b76.js
├── python.33a06b76.js.map
├── r.7b65ede8.js
├── r.7b65ede8.js.map
├── razor.41ce5d97.js
├── razor.41ce5d97.js.map
├── redis.3b966a9e.js
├── redis.3b966a9e.js.map
├── redshift.5dfb52a9.js
├── redshift.5dfb52a9.js.map
├── restructuredtext.1f8956df.js
├── restructuredtext.1f8956df.js.map
├── ruby.423554f5.js
├── ruby.423554f5.js.map
├── rust.c757cf18.js
├── rust.c757cf18.js.map
├── sb.262a3c8c.js
├── sb.262a3c8c.js.map
├── scheme.c46b571a.js
├── scheme.c46b571a.js.map
├── scss.c8f173a2.js
├── scss.c8f173a2.js.map
├── shell.d89a9747.js
├── shell.d89a9747.js.map
├── solidity.d4b1a257.js
├── solidity.d4b1a257.js.map
├── sophia.c291ee27.js
├── sophia.c291ee27.js.map
├── sql.39e542ec.js
├── sql.39e542ec.js.map
├── st.325871b4.js
├── st.325871b4.js.map
├── swift.6233b044.js
├── swift.6233b044.js.map
├── tcl.11456ec9.js
├── tcl.11456ec9.js.map
├── ts.worker.js
├── tsMode.d9db41ab.js
├── tsMode.d9db41ab.js.map
├── twig.9c99d9a5.js
├── twig.9c99d9a5.js.map
├── vb.55d344a5.js
├── vb.55d344a5.js.map
├── xml.bdf01ab7.js
├── xml.bdf01ab7.js.map
├── yaml.360ea1f7.js
└── yaml.360ea1f7.js.map
├── package.json
├── public
├── index.html
├── index.ts
└── style.css
├── server.ts
├── utils.ts
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | client
3 | server/.vscode
4 | server/node_modules
5 | server/package.json
6 | server/yarn.lock
7 | server/tsconfig.json
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_size = 2
7 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and deploy Docker app to Azure
2 | on:
3 | push:
4 | branches:
5 | - main
6 | env:
7 | AZURE_WEBAPP_NAME: danoweb
8 | jobs:
9 | build:
10 | name: Build
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Set env vars
14 | run: |
15 | IMAGE_PATH=${{ github.repository }}
16 | IMAGE_PATH=$(echo ${IMAGE_PATH} | tr '[A-Z]' '[a-z]')
17 | echo "IMAGE_PATH=${IMAGE_PATH}" >> ${GITHUB_ENV}
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | - name: Build image
21 | run: docker build -t ${IMAGE_PATH} .
22 | - name: Docker login
23 | run: echo "${{ secrets.DH_TOKEN }}" | docker login -u ${{ secrets.DH_USERNAME }} --password-stdin
24 | - name: Docker push
25 | run: docker push ${IMAGE_PATH}
26 | deploy:
27 | name: Deploy
28 | runs-on: ubuntu-latest
29 | needs: build
30 | steps:
31 | - name: Set env vars
32 | run: |
33 | IMAGE_PATH=${{ github.repository }}
34 | IMAGE_PATH=$(echo ${IMAGE_PATH} | tr '[A-Z]' '[a-z]')
35 | echo "IMAGE_PATH=${IMAGE_PATH}" >> ${GITHUB_ENV}
36 | - name: Azure authentication
37 | uses: azure/login@v1
38 | with:
39 | creds: ${{ secrets.AZURE_CREDENTIALS }}
40 | - name: Set Web App Docker Hub authentication
41 | uses: Azure/appservice-settings@v1
42 | with:
43 | app-name: ${{ env.AZURE_WEBAPP_NAME }}
44 | app-settings-json: |
45 | [
46 | {
47 | "name": "DOCKER_REGISTRY_SERVER_PASSWORD",
48 | "value": "${{ secrets.DH_TOKEN }}",
49 | "slotSetting": false
50 | },
51 | {
52 | "name": "DOCKER_REGISTRY_SERVER_URL",
53 | "value": "https://index.docker.io",
54 | "slotSetting": false
55 | },
56 | {
57 | "name": "DOCKER_REGISTRY_SERVER_USERNAME",
58 | "value": "${{ secrets.DH_USERNAME }}",
59 | "slotSetting": false
60 | },
61 | {
62 | "name": "WEBSITES_PORT",
63 | "value": "8000",
64 | "slotSetting": false
65 | },
66 | {
67 | "name": "API_KEY",
68 | "value": "${{ secrets.DW_API_KEY }}",
69 | "slotSetting": false
70 | },
71 | {
72 | "name": "AUTH_DOMAIN",
73 | "value": "${{ secrets.DW_AUTH_DOMAIN }}",
74 | "slotSetting": false
75 | },
76 | {
77 | "name": "DATABASE_URL",
78 | "value": "${{ secrets.DW_DATABASE_URL }}",
79 | "slotSetting": false
80 | },
81 | {
82 | "name": "PROJECT_ID",
83 | "value": "${{ secrets.DW_PROJECT_ID }}",
84 | "slotSetting": false
85 | },
86 | {
87 | "name": "STORAGE_BUCKET",
88 | "value": "${{ secrets.DW_STORAGE_BUCKET }}",
89 | "slotSetting": false
90 | },
91 | {
92 | "name": "MESSAGING_SENDER_ID",
93 | "value": "${{ secrets.DW_MESSAGING_SENDER_ID }}",
94 | "slotSetting": false
95 | },
96 | {
97 | "name": "APP_ID",
98 | "value": "${{ secrets.DW_APP_ID }}",
99 | "slotSetting": false
100 | },
101 | {
102 | "name": "DATABASE_PREFIX",
103 | "value": "${{ secrets.DW_DATABASE_PREFIX }}",
104 | "slotSetting": false
105 | },
106 | {
107 | "name": "USER_PASSWORD",
108 | "value": "${{ secrets.DW_USER_PASSWORD }}",
109 | "slotSetting": false
110 | },
111 | {
112 | "name": "HOST",
113 | "value": "${{ secrets.DW_HOST }}",
114 | "slotSetting": false
115 | }
116 | ]
117 | - name: Deploy to Azure Web App for Container
118 | uses: azure/webapps-deploy@v2
119 | with:
120 | app-name: ${{ env.AZURE_WEBAPP_NAME }}
121 | images: ${{ env.IMAGE_PATH }}:latest
122 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | # Parcel bundler cache
64 | .cache
65 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["denoland.vscode-deno"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "files.exclude": {
4 | "server": true,
5 | "client": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:jessie-slim
2 | RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl unzip
3 |
4 | # install deno
5 | ARG DENO_VERSION=v1.7.1
6 | RUN curl -fsSL https://deno.land/x/install/install.sh | sh -s ${DENO_VERSION}
7 | ENV PATH="/root/.deno/bin:$PATH"
8 | RUN deno --version
9 |
10 | # copy files
11 | WORKDIR /work
12 | ADD ./server /work/server
13 |
14 | # start the server
15 | ENV HOST=0.0.0.0
16 | CMD cd /work/server && deno run --allow-env --allow-net --allow-read --allow-write --unstable ./server.ts
17 |
--------------------------------------------------------------------------------
/Dockerfile.local:
--------------------------------------------------------------------------------
1 | FROM arcatdmz/danoweb:latest
2 |
3 | EXPOSE 8000
4 | CMD cd /work/server && deno --allow-env --allow-net --allow-read --allow-write --unstable ./server.ts
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2021 Jun Kato
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/client/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true
3 | }
4 |
--------------------------------------------------------------------------------
/client/download.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | file download | danoweb
11 |
12 |
13 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/client/download.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { download } from "./lib/api";
4 |
5 | window.onload = function() {
6 | // update click handler
7 | (document.querySelector(
8 | "#modal .download"
9 | ) as HTMLAnchorElement).onclick = ev => {
10 | ev.preventDefault();
11 | download();
12 | return false;
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/client/editor.css:
--------------------------------------------------------------------------------
1 | @import "./firepad/firepad.css";
2 |
3 | body {
4 | overflow: hidden;
5 | }
6 |
7 | #firepad,
8 | #diff {
9 | position: absolute;
10 | width: 100%;
11 | height: 100%;
12 | }
13 | #firepad {
14 | z-index: 1;
15 | }
16 | #diff {
17 | display: none;
18 | z-index: 3;
19 | }
20 |
21 | #firepad-controls,
22 | #diff-controls {
23 | position: absolute;
24 | left: 0;
25 | bottom: 0.5rem;
26 | padding: 0 1rem;
27 | }
28 | #firepad-controls {
29 | z-index: 2;
30 | }
31 | #diff-controls {
32 | display: none;
33 | z-index: 4;
34 | }
35 |
36 | #authenticate {
37 | z-index: 5;
38 | }
39 |
40 | #overlay {
41 | z-index: 10;
42 | }
43 |
--------------------------------------------------------------------------------
/client/editor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | danoweb
10 |
11 |
12 |
13 |
14 | initializing...
16 |
17 |
18 |
31 |
32 |
33 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/client/editor.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { getEnv, getTextFile, isAuthenticated, authenticate } from "./lib/api";
4 | import { Modal, Loader } from "./lib/utils";
5 | import { Editor } from "./lib/FirepadEditor";
6 | import { DiffEditor } from "./lib/DiffEditor";
7 |
8 | window.onload = async function() {
9 | // set window title
10 | const filePath = location.pathname;
11 | document.title = `${filePath} | danoweb`;
12 |
13 | // show the editor
14 | const env = await getEnv();
15 | const editor = new Editor({ filePath, env });
16 | try {
17 | await editor.initialize();
18 | console.log("editor initialized successfully");
19 | } catch (e) {
20 | console.error("editor failed to initialize", e);
21 | }
22 |
23 | // prepare the diff editor
24 | const diffEditor = new DiffEditor();
25 |
26 | // prepare the loader
27 | const loader = new Loader(document.querySelector("#overlay"));
28 | loader.stop();
29 |
30 | // prepare the authentication modal
31 | const authRoot = document.querySelector("#authenticate") as HTMLElement;
32 | const modal = new Modal(authRoot);
33 | const authForm = authRoot.querySelector("form");
34 | authForm.onsubmit = ev => {
35 | ev.preventDefault();
36 | const token = (authForm.querySelector(
37 | "input#authenticate-token"
38 | ) as HTMLInputElement).value;
39 | authenticate(token).then(isAuthenticated => {
40 | if (isAuthenticated) {
41 | modal.stop();
42 | }
43 | console.log("authenticated", isAuthenticated);
44 | });
45 | };
46 |
47 | // set event handlers
48 | (document.querySelector(
49 | "#firepad-controls .save"
50 | ) as HTMLButtonElement).onclick = async _ev => {
51 | await loader.start("loading");
52 | let original = await getTextFile(filePath);
53 | if (original) original = original.replace(/(?:\r\n|\r|\n)/g, "\n");
54 | diffEditor.update({
55 | a: original,
56 | b: editor.getText()
57 | });
58 | diffEditor.show();
59 | loader.stop();
60 | };
61 |
62 | (document.querySelector(
63 | "#diff-controls .save"
64 | ) as HTMLButtonElement).onclick = async _ev => {
65 | await loader.start("saving");
66 | if (await isAuthenticated()) {
67 | await editor.save();
68 | diffEditor.hide();
69 | loader.stop();
70 | return;
71 | }
72 |
73 | // show the authentication form
74 | loader.stop();
75 | return modal.start();
76 | };
77 |
78 | (document.querySelector(
79 | "#diff-controls .cancel"
80 | ) as HTMLButtonElement).onclick = async _ev => {
81 | diffEditor.hide();
82 | };
83 | };
84 |
--------------------------------------------------------------------------------
/client/firepad/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Firebase, https://firebase.google.com/
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
21 | --
22 |
23 | ot.js
24 |
25 | Copyright © 2012-2015 Tim Baumann, http://timbaumann.info
26 |
27 | Permission is hereby granted, free of charge, to any person obtaining a copy
28 | of this software and associated documentation files (the “Software”), to deal
29 | in the Software without restriction, including without limitation the rights
30 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31 | copies of the Software, and to permit persons to whom the Software is
32 | furnished to do so, subject to the following conditions:
33 |
34 | The above copyright notice and this permission notice shall be included in
35 | all copies or substantial portions of the Software.
36 |
37 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43 | THE SOFTWARE.
44 |
--------------------------------------------------------------------------------
/client/firepad/firepad.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arcatdmz/danoweb/198f348a54937d4bb7b844493409f2bd7ee28af3/client/firepad/firepad.eot
--------------------------------------------------------------------------------
/client/index.css:
--------------------------------------------------------------------------------
1 | @import "./node_modules/milligram/dist/milligram.min.css";
2 |
3 | html,
4 | body {
5 | margin: 0;
6 | padding: 0;
7 | font-family: "Hiragino Kaku Gothic Pro", Meiryo, Helvetica, Arial, sans-serif !important;
8 | }
9 | html {
10 | height: 100%;
11 | }
12 | body {
13 | height: 100%;
14 | position: relative;
15 | }
16 |
17 | .modal {
18 | position: absolute;
19 | display: none;
20 | width: 100%;
21 | height: 100%;
22 | background: rgba(255, 255, 255, 0.85);
23 | justify-content: center;
24 | align-items: center;
25 | opacity: 0;
26 | }
27 | .modal > .modal-content {
28 | max-width: 640px;
29 | }
30 | .modal.enabled {
31 | display: flex;
32 | transition: 0.1s opacity ease-out;
33 | opacity: 0;
34 | }
35 | .modal.enabled.active {
36 | transition: 0.3s opacity ease-out;
37 | opacity: 1;
38 | }
39 |
40 | .buttons a.button,
41 | .buttons button {
42 | margin-right: 0.7rem;
43 | }
44 | .buttons a.button:last-child,
45 | .buttons button:last-child {
46 | margin-right: 2px;
47 | }
48 | a.button:hover,
49 | button:hover {
50 | box-shadow: 1px 1px 3px #999;
51 | }
52 |
--------------------------------------------------------------------------------
/client/lib/DiffEditor.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import * as monaco__ from "monaco-editor";
5 | import * as monaco_ from "monaco-editor/esm/vs/editor/editor.main.js";
6 | import { setupMonaco } from "./utils";
7 | const monaco = monaco_ as typeof monaco__;
8 |
9 | export interface DiffEditorOptions {
10 | a: string;
11 | b: string;
12 | }
13 |
14 | export class DiffEditor {
15 | private options: DiffEditorOptions;
16 | private editor: monaco.editor.IStandaloneDiffEditor;
17 |
18 | constructor(options?: DiffEditorOptions) {
19 | this.options = options || { a: "", b: "" };
20 | }
21 |
22 | /**
23 | * show this editor
24 | */
25 | show() {
26 | // dispose the existing diff editor
27 | if (this.editor) {
28 | this.hide();
29 | }
30 |
31 | // show the diff editor
32 | const wrapper = document.getElementById("diff");
33 | wrapper.style.display = "block";
34 | const controls = document.getElementById("diff-controls");
35 | controls.style.display = "block";
36 |
37 | // initialize the diff editor
38 | setupMonaco();
39 | this.editor = monaco.editor.createDiffEditor(wrapper, {
40 | automaticLayout: true
41 | }) as any;
42 |
43 | // fill the content
44 | if (this.options) {
45 | this.update(this.options);
46 | }
47 | }
48 |
49 | /**
50 | * update the editor view
51 | * @param options editor text (original and modified)
52 | */
53 | update(options: DiffEditorOptions) {
54 | if (!this.editor) {
55 | this.options = options;
56 | return;
57 | }
58 | const { a, b } = options;
59 | const original = monaco.editor.createModel(a, "text/plain");
60 | const modified = monaco.editor.createModel(b, "text/plain");
61 | this.editor.setModel({ original, modified });
62 | }
63 |
64 | /**
65 | * dispose this editor
66 | */
67 | hide() {
68 | const wrapper = document.getElementById("diff");
69 | wrapper.style.display = "none";
70 | const controls = document.getElementById("diff-controls");
71 | controls.style.display = "none";
72 | this.editor.dispose();
73 | this.editor = null;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/client/lib/FirepadEditor.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | /**
5 | * Monaco editor on Parcel.js bundler:
6 | * https://github.com/Microsoft/monaco-editor-samples/tree/master/browser-esm-parcel
7 | */
8 |
9 | /**
10 | * Firepad with Monaco editor:
11 | * https://github.com/FirebaseExtended/firepad/blob/master/examples/monaco.html
12 | */
13 |
14 | /**
15 | * Rewritten Monaco Adapter
16 | * https://github.com/FirebaseExtended/firepad/pull/325
17 | * with a slight modification
18 | * https://github.com/arcatdmz/firepad/commit/7152e606dd466a65c359a8b0fcc56c0037cc696b
19 | */
20 |
21 | import * as monaco__ from "monaco-editor";
22 | import * as monaco_ from "monaco-editor/esm/vs/editor/editor.main.js";
23 | const monaco = monaco_ as typeof monaco__;
24 |
25 | import * as firebase from "firebase/app";
26 | import "firebase/database";
27 |
28 | import Firepad from "../firepad/firepad.js";
29 |
30 | import { getTextFile, putTextFile } from "./api";
31 | import { setupFirebase, setupMonaco, getMonacoLanguage } from "./utils";
32 |
33 | export interface EditorOptions {
34 | filePath?: string;
35 | env?: { [key: string]: string };
36 | }
37 |
38 | export class Editor {
39 | private options: EditorOptions;
40 | private editor: monaco.editor.IStandaloneCodeEditor;
41 | private firepad: any;
42 |
43 | constructor(options: EditorOptions) {
44 | this.options = options || {};
45 | }
46 | async initialize() {
47 | setupFirebase(firebase, this.options.env);
48 | setupMonaco();
49 | this.editor = monaco.editor.create(document.getElementById("firepad"), {
50 | language: getMonacoLanguage(this.options.filePath),
51 | automaticLayout: true
52 | }) as any;
53 | this.editor.getModel().setEOL(monaco.editor.EndOfLineSequence.LF);
54 |
55 | const firepadRef = firebase
56 | .database()
57 | .ref(`files/${this.getFirebasePath(this.options.filePath)}`);
58 | let defaultText = await getTextFile(this.options.filePath);
59 | if (defaultText) defaultText = defaultText.replace(/(?:\r\n|\r|\n)/g, "\n");
60 | this.firepad = Firepad.fromMonaco(firepadRef, this.editor, {
61 | defaultText
62 | });
63 | }
64 |
65 | async save() {
66 | return putTextFile(this.options.filePath, {
67 | content: this.firepad.getText()
68 | });
69 | }
70 |
71 | getText() {
72 | // return this.editor.getValue();
73 | return this.firepad.getText() as string;
74 | }
75 |
76 | getFirebasePath(filePath: string) {
77 | return (
78 | ((this.options.env && this.options.env.DATABASE_PREFIX) || "") +
79 | encodeURIComponent(filePath).replace(/\./g, "%2E")
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/client/lib/api.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * api.ts
3 | *
4 | * Functions to call server APIs
5 | */
6 | import axios from "axios";
7 | import Cookies from "js-cookie";
8 |
9 | export interface APIOptions {
10 | endpoint?: string;
11 | }
12 |
13 | export async function getEnv(
14 | options?: APIOptions
15 | ): Promise<{ [key: string]: string }> {
16 | try {
17 | const res = await axios.get(getEndpoint(options) + "/lib/api/info");
18 | if (res.data && res.status === 200) {
19 | return res.data.env;
20 | }
21 | } catch (e) {
22 | // do nothing.
23 | }
24 | return {};
25 | }
26 |
27 | export async function authenticate(token: string, options?: APIOptions) {
28 | try {
29 | const res = await axios.post(getEndpoint(options) + "/lib/api/auth", null, {
30 | auth: {
31 | username: "danoweb",
32 | password: token
33 | }
34 | });
35 | if (!res.data || !res.data.success) {
36 | return false;
37 | }
38 | Cookies.set("token", token, {
39 | expires: 60 /* days */
40 | });
41 | return true;
42 | } catch (e) {
43 | return false;
44 | }
45 | }
46 |
47 | export async function isAuthenticated() {
48 | const token = Cookies.get("token");
49 | return token && authenticate(token);
50 | }
51 |
52 | export async function download(options?: APIOptions) {
53 | return axios
54 | .get(getEndpoint(options) + "/lib/api/download", {
55 | auth: {
56 | username: "danoweb",
57 | password: Cookies.get("token")
58 | },
59 | responseType: "blob"
60 | })
61 | .then(res => {
62 | let fileName = "file.dat";
63 | if (res.headers["content-disposition"]) {
64 | const match = /^attachment;\s*filename="(.+)"$/.exec(
65 | res.headers["content-disposition"]
66 | );
67 | if (match) fileName = match[1];
68 | }
69 | const url = URL.createObjectURL(new Blob([res.data]));
70 | const link = document.createElement("a");
71 | link.href = url;
72 | link.setAttribute("download", fileName);
73 | document.body.appendChild(link);
74 | link.click();
75 | document.body.removeChild(link);
76 | });
77 | }
78 |
79 | export async function getTextFile(filePath: string, options?: APIOptions) {
80 | try {
81 | const res = await axios.get(getEndpoint(options) + filePath);
82 | return res.data as string;
83 | } catch (_e) {
84 | return null;
85 | }
86 | }
87 |
88 | export async function putTextFile(
89 | filePath: string,
90 | options: APIOptions & { content: string }
91 | ) {
92 | const fileName = filePath.substr(filePath.lastIndexOf("/") + 1);
93 | const formData = new FormData();
94 | formData.append("path", filePath);
95 | formData.append(
96 | "content",
97 | new Blob([options.content], { type: "text/plain" }),
98 | fileName
99 | );
100 | const res = await axios.put(getEndpoint(options) + filePath, formData, {
101 | auth: {
102 | username: "danoweb",
103 | password: Cookies.get("token")
104 | }
105 | });
106 | return res.data as string;
107 | }
108 |
109 | function getEndpoint(options?: APIOptions) {
110 | return (options && options.endpoint) || "";
111 | }
112 |
--------------------------------------------------------------------------------
/client/lib/utils.ts:
--------------------------------------------------------------------------------
1 | export function setupFirebase(firebase: any, env: { [key: string]: string }) {
2 | firebase.initializeApp({
3 | apiKey: env.API_KEY,
4 | authDomain: env.AUTH_DOMAIN,
5 | databaseURL: env.DATABASE_URL,
6 | projectId: env.PROJECT_ID,
7 | storageBucket: env.STORAGE_BUCKET,
8 | messagingSenderId: env.MESSAGING_SENDER_ID,
9 | appId: env.APP_ID
10 | });
11 | }
12 |
13 | export function setupMonaco() {
14 | window["MonacoEnvironment"] = {
15 | getWorkerUrl: function(_moduleId, label) {
16 | if (label === "json") {
17 | return "/lib/json.worker.js";
18 | }
19 | if (label === "css") {
20 | return "/lib/css.worker.js";
21 | }
22 | if (label === "html") {
23 | return "/lib/html.worker.js";
24 | }
25 | if (label === "typescript" || label === "javascript") {
26 | return "/lib/ts.worker.js";
27 | }
28 | return "/lib/editor.worker.js";
29 | }
30 | };
31 | }
32 |
33 | export function getMonacoLanguage(filePath: string) {
34 | if (!filePath || filePath.length <= 0) return null;
35 | switch (filePath.substr(filePath.lastIndexOf(".") + 1).toLowerCase()) {
36 | case "json":
37 | return "json";
38 | case "css":
39 | return "css";
40 | case "html":
41 | return "html";
42 | case "ts":
43 | case "tsx":
44 | case "js":
45 | case "jsx":
46 | return "typescript";
47 | }
48 | return null;
49 | }
50 |
51 | export class Modal {
52 | protected root: HTMLElement;
53 | protected preStart: (options?: any) => void;
54 | protected onStart: () => void;
55 | protected onStop: () => void;
56 | constructor(root: HTMLElement) {
57 | this.root = root;
58 | }
59 |
60 | async start(options?: any) {
61 | this.preStart && this.preStart(options);
62 |
63 | // set CSS animation
64 | this.root.classList.add("enabled");
65 | return new Promise(r => {
66 | setTimeout(() => {
67 | this.root.classList.add("active");
68 |
69 | // trigger event hook
70 | this.onStart && this.onStart();
71 |
72 | r();
73 | }, 1);
74 | });
75 | }
76 |
77 | stop() {
78 | // set CSS animation
79 | this.root.classList.remove("active");
80 | setTimeout(() => {
81 | this.root.classList.remove("enabled");
82 | }, 100);
83 |
84 | // trigger event hook
85 | this.onStop && this.onStop();
86 | }
87 | }
88 |
89 | export class Loader extends Modal {
90 | private handler;
91 | private step = 0;
92 |
93 | constructor(root: HTMLElement) {
94 | super(root);
95 | this.preStart = (message: string) => {
96 | if (this.handler) {
97 | this.stop();
98 | }
99 |
100 | // set content
101 | const span = this.root.querySelector(".message") as HTMLSpanElement;
102 | while (span.childNodes.length > 0) span.removeChild(span.firstChild);
103 | span.appendChild(document.createTextNode(message));
104 | };
105 | this.onStart = () => {
106 | this.handler = setInterval(this.animate.bind(this), 300);
107 | };
108 | this.onStop = () => {
109 | if (!this.handler) return;
110 | clearInterval(this.handler);
111 | this.handler = null;
112 | };
113 | }
114 |
115 | animate() {
116 | const numSteps = 3;
117 | for (let i = 0; i < numSteps; i++) {
118 | (this.root.querySelector(
119 | `.dot-${i + 1}`
120 | ) as HTMLSpanElement).style.opacity = this.step <= i ? "0.3" : "1";
121 | }
122 | this.step = (this.step + 1) % (numSteps + 1);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/client/modal.css:
--------------------------------------------------------------------------------
1 | body {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | }
6 |
7 | #modal {
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/client/notfound.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | not found | danoweb
11 |
12 |
13 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/client/notfound.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | window.onload = function() {
4 | // update link href
5 | (document.querySelector("#modal .create") as HTMLAnchorElement).setAttribute(
6 | "href",
7 | `${location.pathname}?mode=edit`
8 | );
9 | };
10 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "danoweb-client",
3 | "version": "1.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "start": "parcel serve *.html --out-dir=../server/lib/ --public-url /lib/",
7 | "build": "run-p build:*",
8 | "build:pages": "parcel build *.html --out-dir=../server/lib/ --public-url /lib/",
9 | "build:json": "parcel build node_modules/monaco-editor/esm/vs/language/json/json.worker.js --no-source-maps --out-dir=../server/lib/ --public-url /lib/",
10 | "build:css": "parcel build node_modules/monaco-editor/esm/vs/language/css/css.worker.js --no-source-maps --out-dir=../server/lib/ --public-url /lib/",
11 | "build:html": "parcel build node_modules/monaco-editor/esm/vs/language/html/html.worker.js --no-source-maps --out-dir=../server/lib/ --public-url /lib/",
12 | "build:ts": "parcel build node_modules/monaco-editor/esm/vs/language/typescript/ts.worker.js --no-source-maps --out-dir=../server/lib/ --public-url /lib/",
13 | "build:worker": "parcel build node_modules/monaco-editor/esm/vs/editor/editor.worker.js --no-source-maps --out-dir=../server/lib/ --public-url /lib/"
14 | },
15 | "dependencies": {
16 | "@types/dotenv": "^8.2.0",
17 | "axios": "^0.21.1",
18 | "firebase": "^7.13.1",
19 | "js-cookie": "^2.2.1",
20 | "milligram": "^1.3.0",
21 | "monaco-editor": "^0.20.0"
22 | },
23 | "devDependencies": {
24 | "@types/js-cookie": "^2.2.5",
25 | "npm-run-all": "^4.1.5",
26 | "parcel-bundler": "^1.12.4",
27 | "typescript": "^4.1.5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "moduleResolution": "node"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/danoweb.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": ".",
5 | "name": "root"
6 | },
7 | {
8 | "path": "server",
9 | "name": "server"
10 | },
11 | {
12 | "path": "client",
13 | "name": "client"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | web: Dockerfile
4 |
--------------------------------------------------------------------------------
/server/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "deno.enable": true,
4 | "deno.import_intellisense_origins": {
5 | "https://deno.land": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/server/auth.ts:
--------------------------------------------------------------------------------
1 | import { ServerRequest } from "./deps.ts";
2 |
3 | export interface AuthHandler {
4 | check(req: ServerRequest): boolean;
5 | }
6 |
7 | export class AuthError extends Error {
8 | constructor(message: string) {
9 | super(message);
10 | }
11 | }
12 |
13 | export interface BasicAuthDb {
14 | [username: string]: string;
15 | }
16 |
17 | export class BasicAuthHandler implements AuthHandler {
18 | private db: BasicAuthDb;
19 | constructor(db: BasicAuthDb) {
20 | this.db = db;
21 | }
22 | check(req: ServerRequest) {
23 | const auth = req.headers.get("authorization");
24 | const res = auth && /^Basic \s*([a-zA-Z0-9]+=*)\s*$/.exec(auth);
25 | if (!auth || !res) {
26 | throw new AuthError("authentication required");
27 | }
28 | let authenticated = false;
29 | try {
30 | const [username, password] = atob(res[1]).split(":", 2);
31 | authenticated = this.db[username] === password;
32 | } catch (e) {
33 | // do nothing
34 | }
35 | return authenticated;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/server/deps.ts:
--------------------------------------------------------------------------------
1 | import { config } from "https://deno.land/x/dotenv/dotenv.ts";
2 |
3 | import { Tar } from "https://deno.land/std/archive/tar.ts";
4 |
5 | import {
6 | ServerRequest,
7 | Response,
8 | serve,
9 | } from "https://deno.land/std/http/server.ts";
10 | import { Status } from "https://deno.land/std/http/http_status.ts";
11 |
12 | import { MultiReader } from "https://deno.land/std/io/readers.ts";
13 |
14 | import {
15 | FormFile,
16 | isFormFile,
17 | MultipartWriter,
18 | MultipartReader,
19 | } from "https://deno.land/std/mime/multipart.ts";
20 |
21 | import { ensureDir, move } from "https://deno.land/std/fs/mod.ts";
22 | import {
23 | normalize,
24 | resolve,
25 | extname,
26 | sep,
27 | posix,
28 | win32,
29 | } from "https://deno.land/std/path/mod.ts";
30 |
31 | import { lookup } from "https://deno.land/x/media_types/mod.ts";
32 |
33 | export type { Response, FormFile };
34 |
35 | export {
36 | config,
37 | Tar,
38 | ServerRequest,
39 | serve,
40 | Status,
41 | MultiReader,
42 | isFormFile,
43 | MultipartWriter,
44 | MultipartReader,
45 | ensureDir,
46 | move,
47 | normalize,
48 | resolve,
49 | extname,
50 | sep,
51 | posix,
52 | win32,
53 | lookup,
54 | };
55 |
--------------------------------------------------------------------------------
/server/handlers/api.ts:
--------------------------------------------------------------------------------
1 | import { Status, Response, Tar, sep } from "../deps.ts";
2 |
3 | import { RequestHandlerOptions, RequestHandler } from "../utils.ts";
4 | import { serveJSON } from "../io.ts";
5 | import { AuthHandler } from "../auth.ts";
6 |
7 | export interface APIRequestHandlerOptions {
8 | encoder: TextEncoder;
9 | address: string;
10 | env: { [index: string]: string };
11 | debug: boolean;
12 | auth: AuthHandler;
13 | userDir: string;
14 | systemPath: string;
15 | }
16 |
17 | /**
18 | * Handle requests to /api endpoint
19 | */
20 | export class APIRequestHandler implements RequestHandler {
21 | private options: APIRequestHandlerOptions;
22 |
23 | constructor(options: APIRequestHandlerOptions) {
24 | this.options = options;
25 | }
26 |
27 | async handle(path: string, options: RequestHandlerOptions) {
28 | const pathPrefix = this.options.systemPath + "/api";
29 | if (path.indexOf(`${pathPrefix}/`) !== 0) return null;
30 | path = path.substr(pathPrefix.length);
31 |
32 | return (
33 | this.info(path, options) ||
34 | this.auth(path, options) ||
35 | this.download(path, options)
36 | );
37 | }
38 |
39 | info(path: string, _options: RequestHandlerOptions) {
40 | if (path !== "/info") return null;
41 | const { encoder, address, debug, env } = this.options;
42 | return serveJSON({ address, debug, env }, encoder);
43 | }
44 |
45 | auth(path: string, options: RequestHandlerOptions) {
46 | if (path !== "/auth") return null;
47 | const { encoder, auth } = this.options;
48 | const { req } = options;
49 | try {
50 | if (!auth.check(req)) {
51 | throw new Error("authentication failed");
52 | }
53 | } catch (e) {
54 | const res = serveJSON(
55 | {
56 | success: false,
57 | error: e.message,
58 | },
59 | encoder
60 | );
61 | res.status = Status.Unauthorized;
62 | return res;
63 | }
64 | return serveJSON(
65 | {
66 | success: true,
67 | },
68 | encoder
69 | );
70 | }
71 |
72 | async download(path: string, options: RequestHandlerOptions) {
73 | if (path !== "/download") return null;
74 | const { encoder, auth, userDir } = this.options;
75 | const { req } = options;
76 | try {
77 | if (!auth.check(req)) {
78 | throw new Error("authentication failed");
79 | }
80 | } catch (e) {
81 | const res = serveJSON(
82 | {
83 | success: false,
84 | error: e.message,
85 | },
86 | encoder
87 | );
88 | res.status = Status.Unauthorized;
89 | return res;
90 | }
91 |
92 | const headers = new Headers();
93 | headers.set("content-type", "application/tar");
94 | headers.set("content-disposition", 'attachment;filename="danoweb.tar"');
95 |
96 | const tar = new Tar(),
97 | dirs = [userDir],
98 | promises: Promise[] = [];
99 | while (dirs.length > 0) {
100 | const dir = dirs.pop() as string;
101 | for await (const item of Deno.readDir(dir)) {
102 | const filePath = dir + sep + item.name;
103 | if (item.isDirectory) {
104 | dirs.push(filePath);
105 | } else {
106 | const relativePath = filePath
107 | .substr(userDir.length + 1)
108 | .split(sep)
109 | .join("/");
110 | promises.push(
111 | tar.append(relativePath, {
112 | filePath,
113 | })
114 | );
115 | }
116 | }
117 | }
118 | await Promise.all(promises);
119 | const body = tar.getReader();
120 | return {
121 | body,
122 | headers,
123 | } as Response;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/server/handlers/editor.ts:
--------------------------------------------------------------------------------
1 | import { Response } from "../deps.ts";
2 |
3 | import { RequestHandlerOptions, RequestHandler } from "../utils.ts";
4 | import { serveFile, redirect } from "../io.ts";
5 |
6 | const { stat } = Deno;
7 |
8 | export interface EditorRequestHandlerOptions {
9 | encoder: TextEncoder;
10 | userDir: string;
11 | editorFile: string;
12 | }
13 |
14 | /**
15 | * Serve the editor file
16 | */
17 | export class EditorRequestHandler implements RequestHandler {
18 | private options: EditorRequestHandlerOptions;
19 |
20 | constructor(options: EditorRequestHandlerOptions) {
21 | this.options = options;
22 | }
23 |
24 | async handle(
25 | path: string,
26 | options: RequestHandlerOptions
27 | ): Promise {
28 | if (options.query.mode !== "edit") return null;
29 | const { encoder, userDir, editorFile: editorPath } = this.options;
30 | const filePath = userDir + path;
31 | try {
32 | const fileInfo = await stat(filePath);
33 | if (fileInfo.isDirectory) {
34 | path = path.charAt(path.length - 1) === "/" ? path : `${path}/`;
35 | return redirect(`${path}index.html?mode=edit`, encoder);
36 | }
37 | return serveFile(editorPath);
38 | } catch (e) {
39 | // file not found
40 | return serveFile(editorPath);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/server/handlers/systemFile.ts:
--------------------------------------------------------------------------------
1 | import { Response, Status } from "../deps.ts";
2 |
3 | import { RequestHandlerOptions, RequestHandler } from "../utils.ts";
4 | import { serveHead, serveFile, serveJSON } from "../io.ts";
5 |
6 | const { stat } = Deno;
7 |
8 | export interface SystemFileRequestHandlerOptions {
9 | encoder: TextEncoder;
10 | systemDir: string;
11 | systemPath: string;
12 | }
13 |
14 | /**
15 | * Serve system files
16 | */
17 | export class SystemFileRequestHandler implements RequestHandler {
18 | private options: SystemFileRequestHandlerOptions;
19 |
20 | constructor(options: SystemFileRequestHandlerOptions) {
21 | this.options = options;
22 | }
23 |
24 | async handle(
25 | path: string,
26 | options: RequestHandlerOptions
27 | ): Promise {
28 | const { systemDir, systemPath } = this.options;
29 | if (path === systemPath || path.indexOf(`${systemPath}/`) !== 0)
30 | return null;
31 | const filePath = systemDir + path.substr(systemPath.length);
32 | try {
33 | const fileInfo = await stat(filePath);
34 | if (fileInfo.isDirectory) {
35 | return this.handle(
36 | (path.substr(-1) === "/" ? "" : "/") + "index.html",
37 | options
38 | );
39 | }
40 | if (options.method === "head") {
41 | return serveHead(filePath, fileInfo);
42 | }
43 | return serveFile(filePath, fileInfo);
44 | } catch (e) {
45 | return this.handleError(options);
46 | }
47 | }
48 |
49 | private handleError(options: RequestHandlerOptions) {
50 | if (options.method === "head") {
51 | return serveHead();
52 | }
53 | const { encoder } = this.options;
54 | const res = serveJSON(
55 | {
56 | error: "system file not found",
57 | },
58 | encoder
59 | );
60 | res.status = Status.Forbidden;
61 | return res;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/server/io.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * io.ts
3 | *
4 | * I/O-related utility methods and classes
5 | */
6 | import {
7 | Response,
8 | Status,
9 | extname,
10 | sep,
11 | posix,
12 | win32,
13 | ensureDir,
14 | move,
15 | lookup,
16 | FormFile,
17 | } from "./deps.ts";
18 |
19 | const { open, stat, writeFile } = Deno;
20 |
21 | export function serveHead(
22 | filePath?: string,
23 | fileInfo?: Deno.FileInfo
24 | ): Response {
25 | filePath = filePath && filePath.replace(/\//g, sep);
26 | const headers = new Headers();
27 | if (filePath && fileInfo) {
28 | const mediaType = contentType(filePath);
29 | headers.set("content-length", fileInfo.size.toString());
30 | if (mediaType) {
31 | headers.set("content-type", mediaType);
32 | }
33 | }
34 | return {
35 | status: fileInfo ? Status.OK : Status.NotFound,
36 | headers,
37 | };
38 | }
39 |
40 | export async function serveFile(
41 | filePath: string,
42 | fileInfo?: Deno.FileInfo
43 | ): Promise {
44 | filePath = filePath.replace(/\//g, sep);
45 | let mediaType = contentType(extname(filePath)) || "text/plain";
46 | if (mediaType.substr(0, 5) === "text/") mediaType += "; charset=utf-8"
47 | const file = await open(filePath);
48 | if (!fileInfo) fileInfo = await stat(filePath);
49 | const headers = new Headers();
50 | headers.set("content-length", fileInfo.size.toString());
51 | headers.set("content-type", mediaType);
52 | return {
53 | body: file,
54 | status: Status.OK,
55 | headers,
56 | };
57 | }
58 |
59 | export function serveJSON(json: any, encoder: TextEncoder): Response {
60 | const body = encoder.encode(JSON.stringify(json));
61 | const headers = new Headers();
62 | headers.set("content-length", body.byteLength.toString());
63 | headers.set("content-type", "application/json");
64 | if (
65 | json &&
66 | !json.success &&
67 | typeof json.error === "string" &&
68 | json.error === "authentication required"
69 | ) {
70 | headers.set("www-authenticate", 'Basic realm="Danoweb", charset="UTF-8"');
71 | return {
72 | body,
73 | status: Status.Unauthorized,
74 | headers,
75 | };
76 | }
77 | return {
78 | body,
79 | headers,
80 | };
81 | }
82 |
83 | export function serveHeadOfResponse(res: Response): Response {
84 | const { status, headers } = res;
85 | return {
86 | status,
87 | headers,
88 | };
89 | }
90 |
91 | export function redirect(redirect: string, encoder: TextEncoder): Response {
92 | const res = serveJSON(
93 | {
94 | success: true,
95 | redirect,
96 | },
97 | encoder
98 | );
99 | const headers = new Headers();
100 | headers.set("location", redirect);
101 | res.headers = headers;
102 | res.status = Status.PermanentRedirect;
103 | return res;
104 | }
105 |
106 | export async function saveFormFile(
107 | formFile: FormFile,
108 | path: string
109 | ): Promise {
110 | // ensure that the parent directory exists
111 | const { dirname } = sep === "\\" ? win32 : posix;
112 | await ensureDir(dirname(path));
113 |
114 | // save the file
115 | if (formFile.tempfile) {
116 | return move(formFile.tempfile, path, { overwrite: true });
117 | } else if (formFile.content) {
118 | return writeFile(path, formFile.content, { create: true });
119 | }
120 | }
121 |
122 | export class Uint8ArrayReader implements Deno.Reader {
123 | private offset: number;
124 | private arr: Uint8Array;
125 |
126 | constructor(arr: Uint8Array) {
127 | this.arr = arr;
128 | this.offset = 0;
129 | }
130 |
131 | async read(p: Uint8Array): Promise {
132 | const n = Math.min(p.byteLength, this.arr.byteLength - this.offset);
133 | if (n === 0) {
134 | return null;
135 | }
136 | p.set(this.arr.slice(this.offset, this.offset + n));
137 | this.offset += n;
138 | return n;
139 | }
140 | }
141 |
142 | export class StreamReader {
143 | private stream: AsyncIterableIterator;
144 | private chunk: IteratorResult | null;
145 | private chunkOffset: number;
146 |
147 | constructor(stream: AsyncIterableIterator) {
148 | this.stream = stream;
149 | this.chunk = null;
150 | this.chunkOffset = 0;
151 | }
152 |
153 | async read(p: Uint8Array): Promise {
154 | let { stream, chunk, chunkOffset } = this;
155 | if (!chunk) {
156 | this.chunk = chunk = await stream.next();
157 | this.chunkOffset = chunkOffset = 0;
158 | }
159 | let nread: number;
160 | if (chunk.value) {
161 | const chunkLeft = chunk.value.byteLength - chunkOffset;
162 | nread = Math.min(p.byteLength, chunkLeft);
163 | if (nread > 0) p.set(chunk.value);
164 | const nothingLeft = chunkOffset + chunkLeft >= chunk.value.byteLength;
165 | if (nothingLeft) {
166 | this.chunk = null;
167 | this.chunkOffset = 0;
168 | }
169 | } else {
170 | nread = 0;
171 | }
172 | this.chunkOffset += nread;
173 | return chunk.done ? null : nread;
174 | }
175 | }
176 |
177 | export function contentType(ext: string) {
178 | if (ext.toLowerCase().endsWith(".ts")) {
179 | return "text/plain";
180 | }
181 | return lookup(ext);
182 | }
183 |
--------------------------------------------------------------------------------
/server/lib/apex.f337d4c7.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:))")}}};exports.conf=e;var t=["abstract","activate","and","any","array","as","asc","assert","autonomous","begin","bigdecimal","blob","boolean","break","bulk","by","case","cast","catch","char","class","collect","commit","const","continue","convertcurrency","decimal","default","delete","desc","do","double","else","end","enum","exception","exit","export","extends","false","final","finally","float","for","from","future","get","global","goto","group","having","hint","if","implements","import","in","inner","insert","instanceof","int","interface","into","join","last_90_days","last_month","last_n_days","last_week","like","limit","list","long","loop","map","merge","native","new","next_90_days","next_month","next_n_days","next_week","not","null","nulls","number","object","of","on","or","outer","override","package","parallel","pragma","private","protected","public","retrieve","return","returning","rollback","savepoint","search","select","set","short","sort","stat","static","strictfp","super","switch","synchronized","system","testmethod","then","this","this_month","this_week","throw","throws","today","tolabel","tomorrow","transaction","transient","trigger","true","try","type","undelete","update","upsert","using","virtual","void","volatile","webservice","when","where","while","yesterday"],o=function(e){return e.charAt(0).toUpperCase()+e.substr(1)},s=[];t.forEach(function(e){s.push(e),s.push(e.toUpperCase()),s.push(o(e))});var n={defaultToken:"",tokenPostfix:".apex",keywords:s,operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string",'@string."'],[/'/,"string","@string.'"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@apexdoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],apexdoc:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}};exports.language=n;
3 | },{}]},{},["dhZi"], null)
4 | //# sourceMappingURL=/lib/apex.f337d4c7.js.map
--------------------------------------------------------------------------------
/server/lib/azcli.342de0e9.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"}]};exports.conf=e;var t={defaultToken:"",tokenPostfix:".cameligo",ignoreCase:!0,brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],keywords:["abs","begin","Bytes","Crypto","Current","else","end","failwith","false","fun","if","in","let","let%entry","let%init","List","list","Map","map","match","match%nat","mod","not","operation","Operation","of","Set","set","sender","source","String","then","true","type","with"],typeKeywords:["int","unit","string","tz"],operators:["=",">","<","<=",">=","<>",":",":=","and","mod","or","+","-","*","/","@","&","^","%","->","<-"],symbols:/[=><:@\^&|+\-*\/\^%]+/,tokenizer:{root:[[/[a-zA-Z_][\w]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/\$[0-9a-fA-F]{1,16}/,"number.hex"],[/\d+/,"number"],[/[;,.]/,"delimiter"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'/,"string","@string"],[/'[^\\']'/,"string"],[/'/,"string.invalid"],[/\#\d+/,"string"]],comment:[[/[^\(\*]+/,"comment"],[/\*\)/,"comment","@pop"],[/\(\*/,"comment"]],string:[[/[^\\']+/,"string"],[/\\./,"string.escape.invalid"],[/'/,{token:"string.quote",bracket:"@close",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,"white"],[/\(\*/,"comment","@comment"],[/\/\/.*$/,"comment"]]}};exports.language=t;
3 | },{}]},{},["a0k9"], null)
4 | //# sourceMappingURL=/lib/cameligo.90405ab1.js.map
--------------------------------------------------------------------------------
/server/lib/codicon.06463639.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arcatdmz/danoweb/198f348a54937d4bb7b844493409f2bd7ee28af3/server/lib/codicon.06463639.ttf
--------------------------------------------------------------------------------
/server/lib/coffee.ac527e58.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c\/\?\s]+)/g,comments:{blockComment:["###","###"],lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{markers:{start:new RegExp("^\\s*#region\\b"),end:new RegExp("^\\s*#endregion\\b")}}};exports.conf=e;var r={defaultToken:"",ignoreCase:!0,tokenPostfix:".coffee",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"}],regEx:/\/(?!\/\/)(?:[^\/\\]|\\.)*\/[igm]*/,keywords:["and","or","is","isnt","not","on","yes","@","no","off","true","false","null","this","new","delete","typeof","in","instanceof","return","throw","break","continue","debugger","if","else","switch","for","while","do","try","catch","finally","class","extends","super","undefined","then","unless","until","loop","of","by","when"],symbols:/[=>file download | danoweb
--------------------------------------------------------------------------------
/server/lib/editor.html:
--------------------------------------------------------------------------------
1 | danoweb
--------------------------------------------------------------------------------
/server/lib/firepad.df5a2b0e.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arcatdmz/danoweb/198f348a54937d4bb7b844493409f2bd7ee28af3/server/lib/firepad.df5a2b0e.eot
--------------------------------------------------------------------------------
/server/lib/fsharp.16208b5f.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c\]/,"annotation"],[/^#(if|else|endif)/,"keyword"],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,"delimiter"],[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0x[0-9a-fA-F]+LF/,"number.float"],[/0x[0-9a-fA-F]+(@integersuffix)/,"number.hex"],[/0b[0-1]+(@integersuffix)/,"number.bin"],[/\d+(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string",'@string."""'],[/"/,"string",'@string."'],[/\@"/,{token:"string.quote",next:"@litstring"}],[/'[^\\']'B?/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\(\*(?!\))/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^*(]+/,"comment"],[/\*\)/,"comment","@pop"],[/\*/,"comment"],[/\(\*\)/,"comment"],[/\(/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/("""|"B?)/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]]}};exports.language=n;
3 | },{}]},{},["uxRn"], null)
4 | //# sourceMappingURL=/lib/fsharp.16208b5f.js.map
--------------------------------------------------------------------------------
/server/lib/go.0898d2da.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c>","&^","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=","&^=","&&","||","<-","++","--","==","<",">","=","!","!=","<=",">=",":=","...","(",")","","]","{","}",",",";",".",":"],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/,"number.hex"],[/0[0-7']*[0-7]/,"number.octal"],[/0[bB][0-1']*[0-1]/,"number.binary"],[/\d[\d']*/,"number"],[/\d/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/`/,"string","@rawstring"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@doccomment"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],doccomment:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],rawstring:[[/[^\`]/,"string"],[/`/,"string","@pop"]]}};exports.language=n;
3 | },{}]},{},["mF1P"], null)
4 | //# sourceMappingURL=/lib/go.0898d2da.js.map
--------------------------------------------------------------------------------
/server/lib/graphql.ca227cf9.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:))")}}};exports.conf=e;var t={defaultToken:"",tokenPostfix:".java",keywords:["abstract","continue","for","new","switch","assert","default","goto","package","synchronized","boolean","do","if","private","this","break","double","implements","protected","throw","byte","else","import","public","throws","case","enum","instanceof","return","transient","catch","extends","int","short","try","char","final","interface","static","void","class","finally","long","strictfp","volatile","const","float","native","super","while","true","false"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/0[xX](@hexdigits)[Ll]?/,"number.hex"],[/0(@octaldigits)[Ll]?/,"number.octal"],[/0[bB](@binarydigits)[Ll]?/,"number.binary"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@javadoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],javadoc:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]]}};exports.language=t;
3 | },{}]},{},["RDty"], null)
4 | //# sourceMappingURL=/lib/java.7a30ec4b.js.map
--------------------------------------------------------------------------------
/server/lib/javascript.0409fa73.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:))")}}};exports.conf=e;var t={defaultToken:"",tokenPostfix:".kt",keywords:["as","as?","break","class","continue","do","else","false","for","fun","if","in","!in","interface","is","!is","null","object","package","return","super","this","throw","true","try","typealias","val","var","when","while","by","catch","constructor","delegate","dynamic","field","file","finally","get","import","init","param","property","receiver","set","setparam","where","actual","abstract","annotation","companion","const","crossinline","data","enum","expect","external","final","infix","inline","inner","internal","lateinit","noinline","open","operator","out","override","private","protected","public","reified","sealed","suspend","tailrec","vararg","field","it"],operators:["+","-","*","/","%","=","+=","-=","*=","/=","%=","++","--","&&","||","!","==","!=","===","!==",">","<","<=",">=","[","]","!!","?.","?:","::","..",":","?","->","@",";","$","_"],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/0[xX](@hexdigits)[Ll]?/,"number.hex"],[/0(@octaldigits)[Ll]?/,"number.octal"],[/0[bB](@binarydigits)[Ll]?/,"number.binary"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string","@multistring"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@javadoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\/\*/,"comment","@comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],javadoc:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc","@push"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],multistring:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"""/,"string","@pop"],[/./,"string"]]}};exports.language=t;
3 | },{}]},{},["INAg"], null)
4 | //# sourceMappingURL=/lib/kotlin.cc603b95.js.map
--------------------------------------------------------------------------------
/server/lib/less.2b81da74.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c",token:"delimiter.angle"}],tokenizer:{root:[{include:"@nestedJSBegin"},["[ \\t\\r\\n]+",""],{include:"@comments"},{include:"@keyword"},{include:"@strings"},{include:"@numbers"},["[*_]?[a-zA-Z\\-\\s]+(?=:.*(;|(\\\\$)))","attribute.name","@attribute"],["url(\\-prefix)?\\(",{token:"tag",next:"@urldeclaration"}],["[{}()\\[\\]]","@brackets"],["[,:;]","delimiter"],["#@identifierPlus","tag.id"],["&","tag"],["\\.@identifierPlus(?=\\()","tag.class","@attribute"],["\\.@identifierPlus","tag.class"],["@identifierPlus","tag"],{include:"@operators"},["@(@identifier(?=[:,\\)]))","variable","@attribute"],["@(@identifier)","variable"],["@","key","@atRules"]],nestedJSBegin:[["``","delimiter.backtick"],["`",{token:"delimiter.backtick",next:"@nestedJSEnd",nextEmbedded:"text/javascript"}]],nestedJSEnd:[["`",{token:"delimiter.backtick",next:"@pop",nextEmbedded:"@pop"}]],operators:[["[<>=\\+\\-\\*\\/\\^\\|\\~]","operator"]],keyword:[["(@[\\s]*import|![\\s]*important|true|false|when|iscolor|isnumber|isstring|iskeyword|isurl|ispixel|ispercentage|isem|hue|saturation|lightness|alpha|lighten|darken|saturate|desaturate|fadein|fadeout|fade|spin|mix|round|ceil|floor|percentage)\\b","keyword"]],urldeclaration:[{include:"@strings"},["[^)\r\n]+","string"],["\\)",{token:"tag",next:"@pop"}]],attribute:[{include:"@nestedJSBegin"},{include:"@comments"},{include:"@strings"},{include:"@numbers"},{include:"@keyword"},["[a-zA-Z\\-]+(?=\\()","attribute.value","@attribute"],[">","operator","@pop"],["@identifier","attribute.value"],{include:"@operators"},["@(@identifier)","variable"],["[)\\}]","@brackets","@pop"],["[{}()\\[\\]>]","@brackets"],["[;]","delimiter","@pop"],["[,=:]","delimiter"],["\\s",""],[".","attribute.value"]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],numbers:[["(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],strings:[['~?"',{token:"string.delimiter",next:"@stringsEndDoubleQuote"}],["~?'",{token:"string.delimiter",next:"@stringsEndQuote"}]],stringsEndDoubleQuote:[['\\\\"',"string"],['"',{token:"string.delimiter",next:"@popall"}],[".","string"]],stringsEndQuote:[["\\\\'","string"],["'",{token:"string.delimiter",next:"@popall"}],[".","string"]],atRules:[{include:"@comments"},{include:"@strings"},["[()]","delimiter"],["[\\{;]","delimiter","@pop"],[".","key"]]}};exports.language=t;
3 | },{}]},{},["wDWx"], null)
4 | //# sourceMappingURL=/lib/less.2b81da74.js.map
--------------------------------------------------------------------------------
/server/lib/lua.f5a53dc9.js:
--------------------------------------------------------------------------------
1 | parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c=","<",">","=",";",":",",",".","..","..."],symbols:/[=>",notIn:["string"]}],surroundingPairs:[{open:"(",close:")"},{open:"[",close:"]"},{open:"`",close:"`"}],folding:{markers:{start:new RegExp("^\\s*\x3c!--\\s*#?region\\b.*--\x3e"),end:new RegExp("^\\s*\x3c!--\\s*#?endregion\\b.*--\x3e")}}};exports.conf=e;var t={defaultToken:"",tokenPostfix:".md",control:/[\\`*_\[\]{}()#+\-\.!]/,noncontrol:/[^\\`*_\[\]{}()#+\-\.!]/,escapes:/\\(?:@control)/,jsescapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,empty:["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param"],tokenizer:{root:[[/^\s*\|/,"@rematch","@table_header"],[/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/,["white","keyword","keyword","keyword"]],[/^\s*(=+|\-+)\s*$/,"keyword"],[/^\s*((\*[ ]?)+)\s*$/,"meta.separator"],[/^\s*>+/,"comment"],[/^\s*([\*\-+:]|\d+\.)\s/,"keyword"],[/^(\t|[ ]{4})[^ ].*$/,"string"],[/^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/,{token:"string",next:"@codeblock"}],[/^\s*```\s*((?:\w|[\/\-#])+).*$/,{token:"string",next:"@codeblockgh",nextEmbedded:"$1"}],[/^\s*```\s*$/,{token:"string",next:"@codeblock"}],{include:"@linecontent"}],table_header:[{include:"@table_common"},[/[^\|]+/,"keyword.table.header"]],table_body:[{include:"@table_common"},{include:"@linecontent"}],table_common:[[/\s*[\-:]+\s*/,{token:"keyword",switchTo:"table_body"}],[/^\s*\|/,"keyword.table.left"],[/^\s*[^\|]/,"@rematch","@pop"],[/^\s*$/,"@rematch","@pop"],[/\|/,{cases:{"@eos":"keyword.table.right","@default":"keyword.table.middle"}}]],codeblock:[[/^\s*~~~\s*$/,{token:"string",next:"@pop"}],[/^\s*```\s*$/,{token:"string",next:"@pop"}],[/.*$/,"variable.source"]],codeblockgh:[[/```\s*$/,{token:"variable.source",next:"@pop",nextEmbedded:"@pop"}],[/[^`]+/,"variable.source"]],linecontent:[[/&\w+;/,"string.escape"],[/@escapes/,"escape"],[/\b__([^\\_]|@escapes|_(?!_))+__\b/,"strong"],[/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/,"strong"],[/\b_[^_]+_\b/,"emphasis"],[/\*([^\\*]|@escapes)+\*/,"emphasis"],[/`([^\\`]|@escapes)+`/,"variable"],[/\{+[^}]+\}+/,"string.target"],[/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/,["string.link","","string.link"]],[/(!?\[)((?:[^\]\\]|@escapes)*)(\])/,"string.link"],{include:"html"}],html:[[/<(\w+)\/>/,"tag"],[/<(\w+)/,{cases:{"@empty":{token:"tag",next:"@tag.$1"},"@default":{token:"tag",next:"@tag.$1"}}}],[/<\/(\w+)\s*>/,{token:"tag"}],[//,"comment","@pop"],[//,{token:"comment",next:"@pop"}],[/'],\r\n },\r\n brackets: [\r\n ['<', '>']\r\n ],\r\n autoClosingPairs: [\r\n { open: '<', close: '>' },\r\n { open: '\\'', close: '\\'' },\r\n { open: '\"', close: '\"' },\r\n ],\r\n surroundingPairs: [\r\n { open: '<', close: '>' },\r\n { open: '\\'', close: '\\'' },\r\n { open: '\"', close: '\"' },\r\n ]\r\n};\r\nexport var language = {\r\n defaultToken: '',\r\n tokenPostfix: '.xml',\r\n ignoreCase: true,\r\n // Useful regular expressions\r\n qualifiedName: /(?:[\\w\\.\\-]+:)?[\\w\\.\\-]+/,\r\n tokenizer: {\r\n root: [\r\n [/[^<&]+/, ''],\r\n { include: '@whitespace' },\r\n // Standard opening tag\r\n [/(<)(@qualifiedName)/, [\r\n { token: 'delimiter' },\r\n { token: 'tag', next: '@tag' }\r\n ]],\r\n // Standard closing tag\r\n [/(<\\/)(@qualifiedName)(\\s*)(>)/, [\r\n { token: 'delimiter' },\r\n { token: 'tag' },\r\n '',\r\n { token: 'delimiter' }\r\n ]],\r\n // Meta tags - instruction\r\n [/(<\\?)(@qualifiedName)/, [\r\n { token: 'delimiter' },\r\n { token: 'metatag', next: '@tag' }\r\n ]],\r\n // Meta tags - declaration\r\n [/(<\\!)(@qualifiedName)/, [\r\n { token: 'delimiter' },\r\n { token: 'metatag', next: '@tag' }\r\n ]],\r\n // CDATA\r\n [/<\\!\\[CDATA\\[/, { token: 'delimiter.cdata', next: '@cdata' }],\r\n [/&\\w+;/, 'string.escape'],\r\n ],\r\n cdata: [\r\n [/[^\\]]+/, ''],\r\n [/\\]\\]>/, { token: 'delimiter.cdata', next: '@pop' }],\r\n [/\\]/, '']\r\n ],\r\n tag: [\r\n [/[ \\t\\r\\n]+/, ''],\r\n [/(@qualifiedName)(\\s*=\\s*)(\"[^\"]*\"|'[^']*')/, ['attribute.name', '', 'attribute.value']],\r\n [/(@qualifiedName)(\\s*=\\s*)(\"[^\">?\\/]*|'[^'>?\\/]*)(?=[\\?\\/]\\>)/, ['attribute.name', '', 'attribute.value']],\r\n [/(@qualifiedName)(\\s*=\\s*)(\"[^\">]*|'[^'>]*)/, ['attribute.name', '', 'attribute.value']],\r\n [/@qualifiedName/, 'attribute.name'],\r\n [/\\?>/, { token: 'delimiter', next: '@pop' }],\r\n [/(\\/)(>)/, [\r\n { token: 'tag' },\r\n { token: 'delimiter', next: '@pop' }\r\n ]],\r\n [/>/, { token: 'delimiter', next: '@pop' }],\r\n ],\r\n whitespace: [\r\n [/[ \\t\\r\\n]+/, ''],\r\n [//, { token: 'comment', next: '@pop' }],\r\n [/