Replace CodeFlask with CodeMirror

master
Boris Kubiak 2020-02-17 16:05:42 +01:00
parent 227c50429a
commit 9f23498cd0
4 changed files with 119 additions and 484 deletions

View File

@ -10,23 +10,28 @@
<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 @@ npm/clipboard@2/dist/clipboard.min.js
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>

144
index.js
View File

@ -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();
};
function initLangSelector() {
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'
});
};
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;
}
if (!fetch) {
alert('Your browser does not support this page. Sorry! :(');
decompress(base64, (code, err) => {
if (err) {
alert('Failed to decompress data: ' + err);
return;
}
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);
editor.setValue(code);
});
};
reader.readAsArrayBuffer(blob);
});
}
function generateLink() {
const code = flask.getCode();
lzma.compress(code, 1, function(compressed, error) {
if (error) {
alert('Failed to compress data: ' + error);
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);
};
// 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();
};
// 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();

View File

@ -1,204 +0,0 @@
const languages = {
markup: 'Markup (HTML, XML, SVG)',
css: 'CSS',
clike: 'C-like',
javascript: 'JavaScript',
abap: 'ABAP',
abnf: 'Augmented BackusNaur 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: 'BackusNaur 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 BackusNaur 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'
};

234
style.css
View File

@ -14,10 +14,14 @@
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 @@
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 @@ input[type='search'] {
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::-moz-selection {
input::selection {
background-color: rgba(90, 95, 128, 0.99);
}
button {
cursor: pointer;
padding: 4px 8px;
@ -81,213 +71,11 @@ button {
button:hover {
background-color: rgba(255, 255, 255, 0.1) !important;
}
/* Code editor theme */
.codeflask {
color: #ccc;
background: #282936;
.ss-content {
background-color: #282936;
color: #dedede;
font-size: 14px;
}
.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);
}
.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;
}