├── .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 | 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 [/