Replace CodeFlask with CodeMirror
Boris Kubiak kubiakboris@gmail.com
Mon, 17 Feb 2020 16:05:42 +0100
4 files changed,
118 insertions(+),
483 deletions(-)
M
index.html
→
index.html
@@ -10,23 +10,28 @@ <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Paste</title> <script src="https://cdn.jsdelivr.net/combine/ npm/lzma@2.3.2/src/lzma.min.js, -npm/codeflask@1.4.1/build/codeflask.min.js, -npm/prismjs@1.14.0/plugins/autoloader/prism-autoloader.min.js, npm/slim-select@1.25.0/dist/slimselect.min.js, -npm/clipboard@2/dist/clipboard.min.js +npm/clipboard@2/dist/clipboard.min.js, +npm/codemirror@5.51.0/lib/codemirror.min.js, +npm/codemirror@5.51.0/addon/mode/loadmode.min.js, +npm/codemirror@5.51.0/mode/meta.min.js "></script> - <script src="languages.js"></script> <link rel="stylesheet" type="text/css" - href="https://cdn.jsdelivr.net/npm/slim-select@1.25.0/dist/slimselect.min.css" + href="https://cdn.jsdelivr.net/combine/ +npm/slim-select@1.25.0/dist/slimselect.min.css, +npm/codemirror@5.51.0/lib/codemirror.min.css, +npm/codemirror@5.51.0/theme/dracula.min.css +" /> <link rel="stylesheet" href="style.css" /> </head> <body> <div id="editor"></div> <div id="footer"> - <select id="language"></select> + <label for="language"></label + ><select id="language"></select> <span class="grow"></span> <button onclick="generateLink()" type="button">Generate link</button> </div>@@ -38,8 +43,8 @@ id="copy-link"
class="grow" onClick="this.setSelectionRange(0, this.value.length)" /> - <button class="clipboard" data-clipboard-target="#copy-link" type="button">Copy</button> - <button onclick="copyElement.style.display = 'none'" type="button">Cancel</button> + <button class="clipboard" id="copy-btn" data-clipboard-target="#copy-link" type="button">Copy</button> + <button onclick="hideCopyBar(false)" type="button">Cancel</button> </div> </body>
M
index.js
→
index.js
@@ -1,80 +1,126 @@
-let copyElement = document.getElementById('copy'); -const flask = new CodeFlask('#editor', { language: 'javascript', lineNumbers: true, defaultTheme: false }); const lzma = new LZMA('lzma.min.js'); -Prism.plugins.autoloader.languages_path = 'https://cdn.jsdelivr.net/npm/prismjs@1.14.0/components/'; -let select; +let editor = null; +let select = null; +let clipboard = null; -function init() { +const init = () => { + initCodeEditor(); initLangSelector(); initCode(); - const clipboard = new ClipboardJS('.clipboard'); - clipboard.on('success', function() { - copyElement.style.display = 'none'; + initClipboard(); +}; + +const initCodeEditor = () => { + CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.51.0/mode/%N/%N.js'; + editor = new CodeMirror(document.getElementById('editor'), { + lineNumbers: true, + theme: 'dracula' }); -} +}; -function initLangSelector() { +const initLangSelector = () => { select = new SlimSelect({ select: '#language', - data: Object.entries(languages).map(([value, text]) => ({ text, value })), + data: CodeMirror.modeInfo.map(e => ({ text: e.name })), showContent: 'up', onChange: e => { - flask.updateLanguage(e.value); + let mode = CodeMirror.findModeByName(e.text); + mode = mode ? mode.mode : null; + editor.setOption('mode', mode); + CodeMirror.autoLoadMode(editor, mode); } }); const urlParams = new URLSearchParams(window.location.search); - select.set(Object.keys(languages).indexOf(urlParams.get('lang')) === -1 ? 'javascript' : urlParams.get('lang')); -} + select.set(decodeURIComponent(urlParams.get('lang') || 'Plain Text')); +}; -function initCode() { +const initCode = () => { const base64 = location.hash.substr(1); if (base64.length === 0) { return; } + decompress(base64, (code, err) => { + if (err) { + alert('Failed to decompress data: ' + err); + return; + } + editor.setValue(code); + }); +}; - if (!fetch) { - alert('Your browser does not support this page. Sorry! :('); +const initClipboard = () => { + clipboard = new ClipboardJS('.clipboard'); + clipboard.on('success', () => { + hideCopyBar(true); + }); +}; + +const generateLink = () => { + compress(editor.getValue(), (base64, err) => { + if (err) { + alert('Failed to compress data: ' + err); + return; + } + const url = buildUrl(base64); + showCopyBar(url); + }); +}; + +// Open the "Copy" bar and select the content +const showCopyBar = dataToCopy => { + const linkInput = document.getElementById('copy-link'); + linkInput.value = dataToCopy; + linkInput.setSelectionRange(0, dataToCopy.length); + document.getElementById('copy').style.display = 'flex'; +}; + +// Close the "Copy" bar +const hideCopyBar = success => { + const copyButton = document.getElementById('copy-btn'); + const copyBar = document.getElementById('copy'); + if (!success) { + copyBar.style.display = 'none'; return; } + copyButton.innerText = 'Copied !'; + setTimeout(() => { + copyBar.style.display = 'none'; + copyButton.innerText = 'Copy'; + }, 800); +}; - fetch('data:application/octet-stream;base64,' + base64) - .then(r => r.blob()) - .then(function(blob) { - const reader = new FileReader(); - reader.onload = function() { - lzma.decompress(Array.from(new Uint8Array(reader.result)), function(plaintext, error) { - if (error) { - alert('Failed to decompress data: ' + error); - return; - } - flask.updateCode(plaintext); - }); - }; - reader.readAsArrayBuffer(blob); - }); -} +// Build a shareable URL +const buildUrl = rawData => { + return `${location.protocol}//${location.host}${location.pathname}?lang=${encodeURIComponent( + select.selected() + )}#${rawData}`; +}; + +// Transform a compressed base64 string into a plain text string +const decompress = (base64, cb) => { + const req = new XMLHttpRequest(); + req.open('GET', 'data:application/octet;base64,' + base64); + req.responseType = 'arraybuffer'; + req.onload = e => { + lzma.decompress(new Uint8Array(e.target.response), cb); + }; + req.send(); +}; -function generateLink() { - const code = flask.getCode(); - lzma.compress(code, 1, function(compressed, error) { - if (error) { - alert('Failed to compress data: ' + error); +// Transform a plain text string into a compressed base64 string +const compress = (str, cb) => { + lzma.compress(str, 1, (compressed, err) => { + if (err) { + cb(compressed, err); return; } const reader = new FileReader(); - reader.onload = function() { - const base64 = reader.result.substr(reader.result.indexOf(',') + 1); - const url = `${location.protocol}//${location.host}${ - location.pathname - }?lang=${select.selected()}#${base64}`; - const linkInput = document.getElementById('copy-link'); - linkInput.value = url; - linkInput.setSelectionRange(0, url.length); - copyElement.style.display = 'flex'; + reader.onload = () => { + cb(reader.result.substr(reader.result.indexOf(',') + 1)); }; reader.readAsDataURL(new Blob([new Uint8Array(compressed)])); }); -} +}; init();
D
languages.js
@@ -1,204 +0,0 @@
-const languages = { - markup: 'Markup (HTML, XML, SVG)', - css: 'CSS', - clike: 'C-like', - javascript: 'JavaScript', - abap: 'ABAP', - abnf: 'Augmented Backus–Naur form', - actionscript: 'ActionScript', - ada: 'Ada', - antlr4: 'ANTLR4', - apacheconf: 'Apache Configuration', - apl: 'APL', - applescript: 'AppleScript', - aql: 'AQL', - arduino: 'Arduino', - arff: 'ARFF', - asciidoc: 'AsciiDoc', - asm6502: '6502 Assembly', - aspnet: 'ASP.NET (C#)', - autohotkey: 'AutoHotkey', - autoit: 'AutoIt', - bash: 'Bash', - basic: 'BASIC', - batch: 'Batch', - bbcode: 'BBcode', - bison: 'Bison', - bnf: 'Backus–Naur form', - brainfuck: 'Brainfuck', - brightscript: 'BrightScript', - bro: 'Bro', - c: 'C', - csharp: 'C#', - cpp: 'C++', - cil: 'CIL', - coffeescript: 'CoffeeScript', - cmake: 'CMake', - clojure: 'Clojure', - crystal: 'Crystal', - csp: 'Content-Security-Policy', - 'css-extras': 'CSS Extras', - d: 'D', - dart: 'Dart', - diff: 'Diff', - django: 'Django/Jinja2', - 'dns-zone-file': 'DNS zone file', - docker: 'Docker', - ebnf: 'Extended Backus–Naur form', - eiffel: 'Eiffel', - ejs: 'EJS', - elixir: 'Elixir', - elm: 'Elm', - etlua: 'Embedded Lua templating', - erb: 'ERB', - erlang: 'Erlang', - fsharp: 'F#', - 'firestore-security-rules': 'Firestore security rules', - flow: 'Flow', - fortran: 'Fortran', - ftl: 'FreeMarker Template Language', - gcode: 'G-code', - gdscript: 'GDScript', - gedcom: 'GEDCOM', - gherkin: 'Gherkin', - git: 'Git', - glsl: 'GLSL', - gml: 'GameMaker Language', - go: 'Go', - graphql: 'GraphQL', - groovy: 'Groovy', - haml: 'Haml', - handlebars: 'Handlebars', - haskell: 'Haskell', - haxe: 'Haxe', - hcl: 'HCL', - http: 'HTTP', - hpkp: 'HTTP Public-Key-Pins', - hsts: 'HTTP Strict-Transport-Security', - ichigojam: 'IchigoJam', - icon: 'Icon', - inform7: 'Inform 7', - ini: 'Ini', - io: 'Io', - j: 'J', - java: 'Java', - javadoc: 'JavaDoc', - javadoclike: 'JavaDoc-like', - javastacktrace: 'Java stack trace', - jolie: 'Jolie', - jq: 'JQ', - jsdoc: 'JSDoc', - 'js-extras': 'JS Extras', - 'js-templates': 'JS Templates', - json: 'JSON', - jsonp: 'JSONP', - json5: 'JSON5', - julia: 'Julia', - keyman: 'Keyman', - kotlin: 'Kotlin', - latex: 'LaTeX', - latte: 'Latte', - less: 'Less', - lilypond: 'LilyPond', - liquid: 'Liquid', - lisp: 'Lisp', - livescript: 'LiveScript', - lolcode: 'LOLCODE', - lua: 'Lua', - makefile: 'Makefile', - markdown: 'Markdown', - 'markup-templating': 'Markup templating', - matlab: 'MATLAB', - mel: 'MEL', - mizar: 'Mizar', - monkey: 'Monkey', - moonscript: 'MoonScript', - n1ql: 'N1QL', - n4js: 'N4JS', - 'nand2tetris-hdl': 'Nand To Tetris HDL', - nasm: 'NASM', - neon: 'NEON', - nginx: 'nginx', - nim: 'Nim', - nix: 'Nix', - nsis: 'NSIS', - objectivec: 'Objective-C', - ocaml: 'OCaml', - opencl: 'OpenCL', - oz: 'Oz', - parigp: 'PARI/GP', - parser: 'Parser', - pascal: 'Pascal', - pascaligo: 'Pascaligo', - pcaxis: 'PC-Axis', - perl: 'Perl', - php: 'PHP', - phpdoc: 'PHPDoc', - 'php-extras': 'PHP Extras', - plsql: 'PL/SQL', - powershell: 'PowerShell', - processing: 'Processing', - prolog: 'Prolog', - properties: '.properties', - protobuf: 'Protocol Buffers', - pug: 'Pug', - puppet: 'Puppet', - pure: 'Pure', - python: 'Python', - q: 'Q (kdb+ database)', - qml: 'QML', - qore: 'Qore', - r: 'R', - jsx: 'React JSX', - tsx: 'React TSX', - renpy: "Ren'py", - reason: 'Reason', - regex: 'Regex', - rest: 'reST (reStructuredText)', - rip: 'Rip', - roboconf: 'Roboconf', - robotframework: 'Robot Framework', - ruby: 'Ruby', - rust: 'Rust', - sas: 'SAS', - sass: 'Sass (Sass)', - scss: 'Sass (Scss)', - scala: 'Scala', - scheme: 'Scheme', - 'shell-session': 'Shell session', - smalltalk: 'Smalltalk', - smarty: 'Smarty', - solidity: 'Solidity (Ethereum)', - soy: 'Soy (Closure Template)', - sparql: 'SPARQL', - 'splunk-spl': 'Splunk SPL', - sqf: 'SQF: Status Quo Function (Arma 3)', - sql: 'SQL', - stylus: 'Stylus', - swift: 'Swift', - tap: 'TAP', - tcl: 'Tcl', - textile: 'Textile', - toml: 'TOML', - tt2: 'Template Toolkit 2', - turtle: 'Turtle', - twig: 'Twig', - typescript: 'TypeScript', - 't4-cs': 'T4 Text Templates (C#)', - 't4-vb': 'T4 Text Templates (VB)', - 't4-templating': 'T4 templating', - vala: 'Vala', - vbnet: 'VB.Net', - velocity: 'Velocity', - verilog: 'Verilog', - vhdl: 'VHDL', - vim: 'vim', - 'visual-basic': 'Visual Basic', - wasm: 'WebAssembly', - wiki: 'Wiki markup', - xeora: 'Xeora', - xojo: 'Xojo (REALbasic)', - xquery: 'XQuery', - yaml: 'YAML', - zig: 'Zig' -};
M
style.css
→
style.css
@@ -14,10 +14,14 @@ top: 0;
bottom: 46px; } +.CodeMirror { + height: 100%; +} + #footer, #copy { height: 38px; - padding: 8px 10px 0 42px; + padding: 8px 8px 0; background-color: #3b3b47; display: flex; flex-wrap: wrap;@@ -30,10 +34,6 @@ .grow {
flex-grow: 1; } -.codeflask textarea { - box-shadow: 5px -5px 10px rgba(0, 0, 0, 0.3) inset; -} - /* Form elements */ .ss-main {@@ -53,15 +53,6 @@ border-radius: 2px !important;
border: 1px solid #ccc !important; font-size: 14px !important; } -.ss-content { - background-color: #282936; - color: #dedede; - font-size: 14px; -} -.ss-content .ss-disabled { - background-color: #3b3b47 !important; -} - input[type='text'], input[type='search'] { height: 26px !important;@@ -73,7 +64,6 @@ }
input::selection { background-color: rgba(90, 95, 128, 0.99); } - button { cursor: pointer; padding: 4px 8px;@@ -81,213 +71,11 @@ }
button:hover { background-color: rgba(255, 255, 255, 0.1) !important; } - -/* Code editor theme */ - -.codeflask { - color: #ccc; - background: #282936; -} -.codeflask textarea { - color: #282936; - caret-color: rgba(241, 250, 140, 1); -} - -.codeflask .codeflask__flatten { - font-size: 15px; -} - -.codeflask textarea::-moz-selection, -.codeflask textarea ::-moz-selection { - background-color: rgba(90, 95, 128, 0.99); - color: #5a5f80; -} - -.codeflask textarea::selection, -.codeflask textarea ::selection { - background-color: rgba(90, 95, 128, 0.99); - color: #5a5f80; -} - -.codeflask.codeflask--has-line-numbers::before { - background: #3b3b47 !important; -} - -/* Code editor syntax highlight */ -/* Inspiration from https://github.com/dracula/prism */ -.token.comment { - color: rgba(98, 114, 164, 1); -} - -.token.prolog { - color: rgba(207, 207, 194, 1); -} - -.token.tag { - color: rgba(220, 104, 170, 1); -} - -.token.entity { - color: rgba(139, 233, 253, 1); -} - -.token.atrule { - color: rgba(98, 239, 117, 1); -} - -.token.url { - color: rgba(102, 217, 239, 1); -} - -.token.selector { - color: rgba(207, 207, 194, 1); -} - -.token.string { - color: rgba(241, 250, 140, 1); -} - -.token.property { - color: rgba(255, 184, 108, 1); -} - -.token.important { - color: rgba(255, 121, 198, 1); - font-weight: bold; -} - -.token.punctuation { - color: white; -} - -.token.number { - color: rgba(189, 147, 249, 1); -} - -.token.function { - color: rgba(80, 250, 123, 1); -} - -.token.class-name { - color: rgba(255, 184, 108, 1); -} - -.token.keyword { - color: rgba(255, 121, 198, 1); -} - -.token.boolean { - color: rgba(255, 184, 108, 1); -} - -.token.operator { - color: rgba(139, 233, 253, 1); -} - -.token.char { - color: rgba(255, 135, 157, 1); -} - -.token.regex { - color: rgba(80, 250, 123, 1); -} - -.token.variable { - color: rgba(80, 250, 123, 1); +.ss-content { + background-color: #282936; + color: #dedede; + font-size: 14px; } - -.token.constant { - color: rgba(255, 184, 108, 1); -} - -.token.symbol { - color: rgba(255, 184, 108, 1); -} - -.token.builtin { - color: rgba(255, 121, 198, 1); -} - -.token.attr-value { - color: #7ec699; -} - -.token.deleted { - color: #e2777a; -} - -.token.namespace { - color: #e2777a; -} - -.token.bold { - font-weight: bold; -} - -.token.italic { - font-style: italic; -} - -.token { - color: #ff79c6; -} - -.language-cpp .token.string { - color: #8be9fd; -} - -.language-c .token.string { - color: #8be9fd; -} - -.language-css .token.selector { - color: rgba(80, 250, 123, 1); -} - -.language-css .token.property { - color: rgba(255, 184, 108, 1); -} - -.language-java span.token.class-name { - color: #8be9fd; -} - -.language-java .token.class-name { - color: #8be9fd; -} - -.language-markup .token.attr-value { - color: rgba(102, 217, 239, 1); -} - -.language-markup .token.tag { - color: rgba(80, 250, 123, 1); -} - -.language-objectivec .token.property { - color: #66d9ef; -} - -.language-objectivec .token.string { - color: #50fa7b; -} - -.language-php .token.boolean { - color: #8be9fd; -} - -.language-php .token.function { - color: #ff79c6; -} - -.language-php .token.keyword { - color: #66d9ef; -} - -.language-ruby .token.symbol { - color: #8be9fd; -} - -.language-ruby .token.class-name { - color: #cfcfc2; +.ss-content .ss-disabled { + background-color: #3b3b47 !important; }