From f492b312db4dfad4b5a1cafbe865b9e606c60f2f Mon Sep 17 00:00:00 2001 From: Joe Biellik Date: Thu, 2 Jul 2020 20:15:59 +0100 Subject: [PATCH] Refactor paste processing, UI and configuration --- config/default.json | 462 +++++++++++++++++++++--------------------- controllers/pastes.js | 105 ++++++++-- public/main.js | 48 ++++- router.js | 1 + views/highlight.pug | 23 +-- views/index.pug | 117 +++++++---- views/layout.pug | 5 +- 7 files changed, 446 insertions(+), 315 deletions(-) diff --git a/config/default.json b/config/default.json index fa62a2e..413ce5b 100644 --- a/config/default.json +++ b/config/default.json @@ -7,245 +7,235 @@ "random key here" ], "prettyHtml": true, - "cacheAge": 86400, + "cacheAge": 2592000, "sizeLimit": "2mb", - "expiresDefault": 604800, "expires": { - "600": "10 minutes", - "3600": "1 hour", - "86400": "1 day", - "604800": "1 week", - "2592000": "1 month", - "31536000 ": "1 year" + "default": { + "value": 2, + "multiplier": 604800 + }, + "multipliers": { + "1": "second", + "60": "minute", + "3600": "hour", + "86400": "day", + "604800": "week", + "2592000": "month" + } }, "highlights": { - "abap": "ABAP", - "abnf": "Augmented Backus–Naur form", - "actionscript": "ActionScript", - "ada": "Ada", - "al": "AL", - "antlr4": "ANTLR4", - "apacheconf": "Apache Configuration", - "apl": "APL", - "applescript": "AppleScript", - "aql": "AQL", - "arduino": "Arduino", - "arff": "ARFF", - "asciidoc": "AsciiDoc", - "asm6502": "6502 Assembly", - "aspnet": "ASP.NET (C#)", - "atom": "Atom", - "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", - "cil": "CIL", - "clike": "C-like", - "clojure": "Clojure", - "cmake": "CMake", - "coffeescript": "CoffeeScript", - "concurnas": "Concurnas", - "cpp": "C++", - "crystal": "Crystal", - "csharp": "C#", - "csp": "Content-Security-Policy", - "css-extras": "CSS Extras", - "css": "CSS", - "d": "D", - "dart": "Dart", - "dax": "DAX", - "diff": "Diff", - "django": "Django", - "dns-zone-file": "DNS zone file", - "docker": "Docker", - "ebnf": "Extended Backus–Naur form", - "eiffel": "Eiffel", - "ejs": "EJS", - "elixir": "Elixir", - "elm": "Elm", - "erb": "ERB", - "erlang": "Erlang", - "etlua": "Embedded Lua templating", - "excel-formula": "Excel Formula", - "factor": "Factor", - "firestore-security-rules": "Firestore security rules", - "flow": "Flow", - "fortran": "Fortran", - "fsharp": "F#", - "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", - "hlsl": "HLSL", - "hpkp": "HTTP Public-Key-Pins", - "hsts": "HTTP Strict-Transport-Security", - "html": "HTML", - "http": "HTTP", - "ichigojam": "IchigoJam", - "icon": "Icon", - "iecst": "Structured Text (IEC 61131-3)", - "inform7": "Inform 7", - "ini": "Ini", - "io": "Io", - "j": "J", - "java": "Java", - "javadoc": "JavaDoc", - "javadoclike": "JavaDoc-like", - "javascript": "JavaScript", - "javastacktrace": "Java stack trace", - "jinja2": "Jinja2", - "jolie": "Jolie", - "jq": "JQ", - "js-extras": "JS Extras", - "js-templates": "JS Templates", - "jsdoc": "JSDoc", - "json": "JSON", - "json5": "JSON5", - "jsonp": "JSONP", - "jsstacktrace": "JS stack trace", - "jsx": "React JSX", - "julia": "Julia", - "keyman": "Keyman", - "kotlin": "Kotlin", - "latex": "LaTeX", - "latte": "Latte", - "less": "Less", - "lilypond": "LilyPond", - "liquid": "Liquid", - "lisp": "Lisp", - "livescript": "LiveScript", - "llvm": "LLVM IR", - "lolcode": "LOLCODE", - "lua": "Lua", - "makefile": "Makefile", - "markdown": "Markdown", - "markup-templating": "Markup templating", - "mathml": "MathML", - "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", - "peoplecode": "PeopleCode", - "perl": "Perl", - "php-extras": "PHP Extras", - "php": "PHP", - "phpdoc": "PHPDoc", - "plain": "Plain text", - "plsql": "PL/SQL", - "powerquery": "PowerQuery", - "powershell": "PowerShell", - "processing": "Processing", - "prolog": "Prolog", - "properties": ".properties", - "protobuf": "Protocol Buffers", - "pug": "Pug", - "puppet": "Puppet", - "pure": "Pure", - "purebasic": "PureBasic", - "python": "Python", - "q": "Q (kdb+ database)", - "qml": "QML", - "qore": "Qore", - "r": "R", - "racket": "Racket", - "reason": "Reason", - "regex": "Regex", - "renpy": "Ren'py", - "rest": "reST (reStructuredText)", - "rip": "Rip", - "roboconf": "Roboconf", - "robotframework": "Robot Framework", - "rss": "RSS", - "ruby": "Ruby", - "rust": "Rust", - "sas": "SAS", - "sass": "Sass (Sass)", - "scala": "Scala", - "scheme": "Scheme", - "scss": "Sass (Scss)", - "shell-session": "Shell session", - "shell": "Shell", - "smalltalk": "Smalltalk", - "smarty": "Smarty", - "solidity": "Solidity (Ethereum)", - "solution-file": "Solution file", - "soy": "Soy (Closure Template)", - "sparql": "SPARQL", - "splunk-spl": "Splunk SPL", - "sqf": "SQF", - "sql": "SQL", - "ssml": "SSML", - "stylus": "Stylus", - "svg": "SVG", - "swift": "Swift", - "t4-cs": "T4 Text Templates (C#)", - "t4-templating": "T4 templating", - "t4-vb": "T4 Text Templates (VB)", - "tap": "TAP", - "tcl": "Tcl", - "textile": "Textile", - "toml": "TOML", - "tsx": "React TSX", - "tt2": "Template Toolkit 2", - "turtle": "Turtle", - "twig": "Twig", - "typescript": "TypeScript", - "unrealscript": "UnrealScript", - "vala": "Vala", - "vbnet": "VB.Net", - "velocity": "Velocity", - "verilog": "Verilog", - "vhdl": "VHDL", - "vim": "vim", - "visual-basic": "Visual Basic", - "warpscript": "WarpScript", - "wasm": "WebAssembly", - "wiki": "Wiki markup", - "xeora": "Xeora", - "xml-doc": "XML doc (.net)", - "xml": "XML", - "xojo": "Xojo (REALbasic)", - "xquery": "XQuery", - "yaml": "YAML", - "zig": "Zig" + "": {"name": "Plain text"}, + "abap": {"name": "ABAP"}, + "abnf": {"name": "Augmented Backus–Naur form"}, + "actionscript": {"name": "ActionScript"}, + "ada": {"name": "Ada"}, + "antlr4": {"name": "ANTLR4", "alias": ["g4"]}, + "apacheconf": {"name": "Apache Configuration"}, + "apl": {"name": "APL"}, + "applescript": {"name": "AppleScript"}, + "aql": {"name": "AQL"}, + "arduino": {"name": "Arduino"}, + "arff": {"name": "ARFF"}, + "asciidoc": {"name": "AsciiDoc", "alias": ["adoc"]}, + "asm6502": {"name": "6502 Assembly", "alias": ["asm"]}, + "aspnet": {"name": "ASP.NET (C#)", "alias": ["dotnet"]}, + "autohotkey": {"name": "AutoHotkey"}, + "autoit": {"name": "AutoIt"}, + "bash": {"name": "Bash", "alias": ["shell"]}, + "basic": {"name": "BASIC"}, + "batch": {"name": "Batch"}, + "bbcode": {"name": "BBcode", "alias": ["shortcode"]}, + "bison": {"name": "Bison"}, + "bnf": {"name": "Backus–Naur form", "alias": ["rbnf"]}, + "brainfuck": {"name": "Brainfuck"}, + "brightscript": {"name": "BrightScript"}, + "bro": {"name": "Bro"}, + "c": {"name": "C"}, + "cil": {"name": "CIL"}, + "clike": {"name": "C-like"}, + "clojure": {"name": "Clojure"}, + "cmake": {"name": "CMake"}, + "coffeescript": {"name": "CoffeeScript", "alias": ["coffee"]}, + "concurnas": {"name": "Concurnas", "alias": ["conc"]}, + "cpp": {"name": "C++", "alias": ["cpp"]}, + "crystal": {"name": "Crystal"}, + "csharp": {"name": "C#", "alias": ["cs", "dotnet", "csharp", ".net"]}, + "csp": {"name": "Content-Security-Policy", "alias": ["csp"]}, + "css": {"name": "CSS"}, + "css-extras": {"name": "CSS Extras"}, + "d": {"name": "D"}, + "dart": {"name": "Dart"}, + "dax": {"name": "DAX"}, + "diff": {"name": "Diff"}, + "django": {"name": "Django/Jinja2", "alias": ["jinja2"]}, + "dns-zone-file": {"name": "DNS zone file", "alias": ["dns-zone", "bind", "named"]}, + "docker": {"name": "Docker", "alias": ["dockerfile"]}, + "ebnf": {"name": "Extended Backus–Naur form"}, + "eiffel": {"name": "Eiffel"}, + "ejs": {"name": "EJS"}, + "elixir": {"name": "Elixir"}, + "elm": {"name": "Elm"}, + "erb": {"name": "ERB"}, + "erlang": {"name": "Erlang"}, + "etlua": {"name": "Embedded Lua templating"}, + "excel-formula": {"name": "Excel Formula", "alias": ["xlsx", "xls"]}, + "factor": {"name": "Factor"}, + "firestore-security-rules": {"name": "Firestore security rules"}, + "flow": {"name": "Flow"}, + "fortran": {"name": "Fortran"}, + "fsharp": {"name": "F#"}, + "ftl": {"name": "FreeMarker Template Language"}, + "gcode": {"name": "G-code"}, + "gdscript": {"name": "GDScript"}, + "gedcom": {"name": "GEDCOM"}, + "gherkin": {"name": "Gherkin"}, + "git": {"name": "Git"}, + "glsl": {"name": "GLSL"}, + "gml": {"name": "GameMaker Language", "alias": ["gamemakerlanguage"]}, + "go": {"name": "Go"}, + "graphql": {"name": "GraphQL"}, + "groovy": {"name": "Groovy"}, + "haml": {"name": "Haml"}, + "handlebars": {"name": "Handlebars"}, + "haskell": {"name": "Haskell", "alias": ["hs"]}, + "haxe": {"name": "Haxe"}, + "hcl": {"name": "HCL"}, + "hpkp": {"name": "HTTP Public-Key-Pins", "alias": ["hpkp"]}, + "hsts": {"name": "HTTP Strict-Transport-Security"}, + "html": {"name": "HTML"}, + "http": {"name": "HTTP"}, + "ichigojam": {"name": "IchigoJam"}, + "icon": {"name": "Icon"}, + "inform7": {"name": "Inform 7"}, + "ini": {"name": "Ini"}, + "io": {"name": "Io"}, + "j": {"name": "J"}, + "java": {"name": "Java"}, + "javadoc": {"name": "JavaDoc"}, + "javadoclike": {"name": "JavaDoc-like"}, + "javascript": {"name": "JavaScript", "alias": ["js"]}, + "javastacktrace": {"name": "Java stack trace"}, + "jolie": {"name": "Jolie"}, + "jq": {"name": "JQ"}, + "js-extras": {"name": "JavaScript Extras"}, + "js-templates": {"name": "JavaScript Templates"}, + "jsdoc": {"name": "JSDoc"}, + "json": {"name": "JSON", "alias": ["javascript object notation"]}, + "json5": {"name": "JSON5", "alias": ["javascript object notation"]}, + "jsonp": {"name": "JSONP", "alias": ["javascript object notation"]}, + "jsx": {"name": "React JSX"}, + "julia": {"name": "Julia"}, + "keyman": {"name": "Keyman"}, + "kotlin": {"name": "Kotlin"}, + "latex": {"name": "LaTeX", "alias": ["tex", "context"]}, + "latte": {"name": "Latte"}, + "less": {"name": "Less"}, + "lilypond": {"name": "LilyPond", "alias": ["ly"]}, + "liquid": {"name": "Liquid"}, + "lisp": {"name": "Lisp", "alias": ["emacs", "elisp", "emacs-lisp"]}, + "livescript": {"name": "LiveScript"}, + "llvm": {"name": "LLVM IR"}, + "lolcode": {"name": "LOLCODE"}, + "lua": {"name": "Lua"}, + "makefile": {"name": "Makefile"}, + "markdown": {"name": "Markdown", "alias": ["md"]}, + "markup-templating": {"name": "Markup templating"}, + "mathml": {"name": "MathML"}, + "matlab": {"name": "MATLAB"}, + "mel": {"name": "MEL"}, + "mizar": {"name": "Mizar"}, + "monkey": {"name": "Monkey"}, + "moonscript": {"name": "MoonScript", "alias": ["moon"]}, + "n1ql": {"name": "N1QL"}, + "n4js": {"name": "N4JS", "alias": ["n4jsd"]}, + "nand2tetris-hdl": {"name": "Nand To Tetris HDL"}, + "nasm": {"name": "NASM", "alias": ["assembly"]}, + "neon": {"name": "NEON"}, + "nginx": {"name": "nginx"}, + "nim": {"name": "Nim"}, + "nix": {"name": "Nix"}, + "nsis": {"name": "NSIS"}, + "objectivec": {"name": "Objective-C"}, + "ocaml": {"name": "OCaml"}, + "opencl": {"name": "OpenCL"}, + "oz": {"name": "Oz"}, + "parigp": {"name": "PARI/GP"}, + "parser": {"name": "Parser"}, + "pascal": {"name": "Pascal", "alias": ["objectpascal"]}, + "pascaligo": {"name": "Pascaligo"}, + "pcaxis": {"name": "PC-Axis", "alias": ["px"]}, + "perl": {"name": "Perl"}, + "php": {"name": "PHP"}, + "php-extras": {"name": "PHP Extras"}, + "phpdoc": {"name": "PHPDoc"}, + "plsql": {"name": "PL/SQL"}, + "powerquery": {"name": "PowerQuery", "alias": ["pq", "mscript"]}, + "powershell": {"name": "PowerShell"}, + "processing": {"name": "Processing"}, + "prolog": {"name": "Prolog"}, + "properties": {"name": ".properties"}, + "protobuf": {"name": "Protocol Buffers", "alias": ["protobuf"]}, + "pug": {"name": "Pug", "alias": ["jade"]}, + "puppet": {"name": "Puppet"}, + "pure": {"name": "Pure"}, + "python": {"name": "Python", "alias": ["py"]}, + "q": {"name": "Q (kdb+ database)"}, + "qml": {"name": "QML"}, + "qore": {"name": "Qore"}, + "r": {"name": "R"}, + "reason": {"name": "Reason"}, + "regex": {"name": "Regex", "alias": ["regular expression"]}, + "renpy": {"name": "Ren'py"}, + "rest": {"name": "reST (reStructuredText)"}, + "rip": {"name": "Rip"}, + "roboconf": {"name": "Roboconf"}, + "robotframework": {"name": "Robot Framework", "alias": ["robot"]}, + "ruby": {"name": "Ruby", "alias": ["rb"]}, + "rust": {"name": "Rust"}, + "sas": {"name": "SAS"}, + "sass": {"name": "Sass (Sass)"}, + "scala": {"name": "Scala"}, + "scheme": {"name": "Scheme"}, + "scss": {"name": "Sass (Scss)"}, + "shell-session": {"name": "Shell session", "alias": ["terminal", "console"]}, + "smalltalk": {"name": "Smalltalk"}, + "smarty": {"name": "Smarty"}, + "solidity": {"name": "Solidity (Ethereum)"}, + "solution-file": {"name": "Visual Studio Solution", "alias": ["sln", ".net", "dotnet"]}, + "soy": {"name": "Soy (Closure Template)"}, + "sparql": {"name": "SPARQL", "alias": ["rq"]}, + "splunk-spl": {"name": "Splunk SPL"}, + "sqf": {"name": "SQF"}, + "sql": {"name": "SQL"}, + "stylus": {"name": "Stylus"}, + "svg": {"name": "SVG"}, + "swift": {"name": "Swift"}, + "t4-cs": {"name": "T4 Text Templates (C#)", "alias": ["t4"]}, + "t4-templating": {"name": "T4 templating"}, + "t4-vb": {"name": "T4 Text Templates (VB)"}, + "tap": {"name": "TAP"}, + "tcl": {"name": "Tcl"}, + "textile": {"name": "Textile"}, + "toml": {"name": "TOML"}, + "tsx": {"name": "React TSX"}, + "tt2": {"name": "Template Toolkit 2"}, + "turtle": {"name": "Turtle", "alias": ["trig"]}, + "twig": {"name": "Twig"}, + "typescript": {"name": "TypeScript", "alias": ["ts"]}, + "vala": {"name": "Vala"}, + "vbnet": {"name": "Visual Basic .NET", "alias": ["vb", "dotnet"]}, + "velocity": {"name": "Velocity"}, + "verilog": {"name": "Verilog"}, + "vhdl": {"name": "VHDL"}, + "vim": {"name": "vim"}, + "visual-basic": {"name": "Visual Basic", "alias": ["vb"]}, + "wasm": {"name": "WebAssembly", "alias": ["wasm", "asm"]}, + "wiki": {"name": "Wiki markup"}, + "xeora": {"name": "Xeora", "alias": ["xeoracube"]}, + "xml": {"name": "XML"}, + "xojo": {"name": "Xojo (REALbasic)"}, + "xquery": {"name": "XQuery"}, + "yaml": {"name": "YAML", "alias": ["yml"]}, + "zig": {"name": "Zig"} } } diff --git a/controllers/pastes.js b/controllers/pastes.js index dab7326..223e673 100644 --- a/controllers/pastes.js +++ b/controllers/pastes.js @@ -1,11 +1,12 @@ const config = require('config'); +const fs = require('fs').promises; const Paste = require('../models/paste'); module.exports = { async view(ctx) { try { - let paste = await Paste.findById(ctx.params.id).exec(); - let lang = (Object.keys(ctx.query)[0] || '').toLowerCase(); + const paste = await Paste.findById(ctx.params.id).exec(); + const lang = (Object.keys(ctx.query)[0] || '').toLowerCase(); ctx.set('Cache-Control', 'public'); ctx.set('Expires', paste.expiresAt.toUTCString()); @@ -18,7 +19,6 @@ module.exports = { lang: Object.keys(config.highlights).includes(lang) ? lang : 'unknown' }); } else { - ctx.type = 'text/plain'; ctx.body = paste.paste; } } catch (ex) { @@ -33,39 +33,106 @@ module.exports = { async create(ctx) { ctx.set('Cache-Control', 'no-cache'); - if (ctx.request.body.fields) { - if (ctx.request.body.fields.paste) { - ctx.request.body.paste = ctx.request.body.fields.paste; - } - if (ctx.request.body.fields.highlight) { - ctx.request.body.highlight = ctx.request.body.fields.highlight.toLowerCase(); + // Request body + if (!ctx.request.body.paste && ctx.request.files) { + try { + // Request body, xxx + let path = Object.values(ctx.request.files)[0].path; + + // Request body, paste=xxx + if (ctx.request.files.paste) { + path = ctx.request.files.paste.path; + } + + ctx.request.body.paste = await fs.readFile(path); + + try { + await fs.unlink(path); + } catch (ex) { + // Ignore + } + } catch (ex) { + ctx.throw(400, 'Error Processing Request Body', { + headers: { + 'Cache-Control': 'no-cache' + } + }); } - if (ctx.request.body.fields.expire) { - ctx.request.body.expire = ctx.request.body.fields.expire; + } + + // Expiry multiplier + try { + if (ctx.request.body.expire && ctx.request.body.multiplier) { + ctx.request.body.expire = ctx.request.body.expire * ctx.request.body.multiplier; } + } catch (ex) { + ctx.throw(400, 'Error Processing Paste Expiry', { + headers: { + 'Cache-Control': 'no-cache' + } + }); + } + + // Raw request body + if (typeof ctx.request.body === 'string') { + ctx.request.body = { + paste: ctx.request.body + }; + } + + if (!ctx.request.body.paste) { + ctx.throw(400, 'Error No Paste Provided', { + headers: { + 'Cache-Control': 'no-cache' + } + }); } + // /?expire=xxx + if (!ctx.request.body.expire && ctx.query.expire) { + ctx.request.body.expire = ctx.query.expire; + } + + // No expire provided if (!ctx.request.body.expire) { - ctx.request.body.expire = config.expiresDefault; + ctx.request.body.expire = config.expires.default.value * config.expires.default.multiplier; } - let paste = new Paste({ + const paste = new Paste({ paste: ctx.request.body.paste, expiresAt: new Date(Date.now() + ctx.request.body.expire * 1000) }); - await paste.save(); + try { + await paste.save(); + } catch (ex) { + ctx.throw(500, 'Error Storing Paste', { + headers: { + 'Cache-Control': 'no-cache' + } + }); + } + + // /?highlight=xxx + if (!ctx.request.body.highlight && ctx.query.highlight) { + ctx.request.body.highlight = ctx.query.highlight; + } + + // /?xxx + if (!ctx.request.body.highlight && !ctx.query.highlight && ctx.query) { + ctx.request.body.highlight = Object.keys(ctx.query).filter(k => k != 'redirect' && k != 'expire')[0]; + } - let link = paste.id; + let highlight = ''; - if (ctx.request.body.highlight && ctx.request.body.highlight != 'plain' && Object.keys(config.highlights).includes(ctx.request.body.highlight)) { - link += '?' + ctx.request.body.highlight; + if (ctx.request.body.highlight && Object.keys(config.highlights).includes(ctx.request.body.highlight)) { + highlight = '?' + ctx.request.body.highlight; } if (Object.keys(ctx.query).includes('redirect')) { - ctx.redirect(link); + ctx.redirect(ctx.request.origin + '/' + paste.id + highlight); } else { - ctx.body = ctx.request.origin + '/' + link + '\n'; + ctx.body = ctx.request.origin + '/' + paste.id + highlight + '\n'; } } }; diff --git a/public/main.js b/public/main.js index d46c62b..2a3f9de 100644 --- a/public/main.js +++ b/public/main.js @@ -2,30 +2,63 @@ /* global $ autosize */ $(function() { + if (localStorage.getItem('expire-value') != null) $('input#expire').val(localStorage.getItem('expire-value')); + if (localStorage.getItem('expire-multiplier') != null) $('#multiplier').val(localStorage.getItem('expire-multiplier')); + $.fn.selectpicker.Constructor.BootstrapVersion = '4'; autosize($('textarea')); + $('select#highlight').removeClass('custom-select'); + $('input#expire').removeClass('mr-3'); + $('select#highlight').selectpicker(); + $('input#expire').TouchSpin({ + min: 1, + max: 999, + buttondown_class: 'btn text-dark border-gray', + buttonup_class: 'btn text-dark border-gray' + }); + $('textarea').on('keydown', function(e) { if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey && $(this).val()) { $(this).closest('form').submit(); } }); - $('pre').on('click', function() { - var range = document.createRange(); + $('input#expire').on('change', function(e) { + var value = $(e.target).val(); + + $('#multiplier option').text(function(_, t) { + var plural = t.substring(t.length - 1) == 's'; - range.setStart(this.firstChild, 19); - range.setEnd(this.firstChild, this.firstChild.textContent.length); + if (value > 1) { + if (!plural) { + return t + 's'; + } + } else { + if (plural) { + return t.slice(0, -1); + } + } - window.getSelection().removeAllRanges(); - window.getSelection().addRange(range); + return t; + }); + + localStorage.setItem('expire-value', value); + }); + + $('#multiplier').on('change', function(e) { + localStorage.setItem('expire-multiplier', $(e.target).val()); }); $('select#highlight').on('change', function() { - location.hash = $(this).val(); + if ($(this).val() == '') { + history.pushState({}, '', '/'); + } else { + location.hash = $(this).val(); + } }); $(window).on('hashchange', function() { @@ -39,4 +72,5 @@ $(function() { }); $(window).trigger('hashchange'); + $('input#expire').trigger('change'); }); diff --git a/router.js b/router.js index 8fed064..0952bed 100644 --- a/router.js +++ b/router.js @@ -13,6 +13,7 @@ router title: config.name, url: ctx.request.origin, expires: config.expires, + expiresDefault: config.expiresDefault, highlights: config.highlights }); }) diff --git a/views/highlight.pug b/views/highlight.pug index 3925f33..0c4fcbc 100644 --- a/views/highlight.pug +++ b/views/highlight.pug @@ -1,10 +1,9 @@ extends layout block head - link(rel='stylesheet', href='https://fonts.googleapis.com/css?family=Open+Sans:400,300') - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/themes/prism-coy.min.css', integrity='sha256-VcuSs+n31yebPlEcehu6PvnidJ808ScFBsK8+tJKX+Q=', crossorigin='anonymous') - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.css', integrity='sha256-P45OhhEWm49G8sadt2n5rDaWLa3xZbDOQhJliuaojH0=', crossorigin='anonymous') - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.css', integrity='sha256-Afz2ZJtXw+OuaPX10lZHY7fN1+FuTE/KdCs+j7WZTGc=', crossorigin='anonymous') + link(rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/themes/prism-coy.min.css' integrity='sha256-VcuSs+n31yebPlEcehu6PvnidJ808ScFBsK8+tJKX+Q=' crossorigin='anonymous') + link(rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.css' integrity='sha256-P45OhhEWm49G8sadt2n5rDaWLa3xZbDOQhJliuaojH0=' crossorigin='anonymous') + link(rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.css' integrity='sha256-Afz2ZJtXw+OuaPX10lZHY7fN1+FuTE/KdCs+j7WZTGc=' crossorigin='anonymous') style. html, body, .code-toolbar, pre, code { @@ -19,10 +18,6 @@ block head pre[class*="language-"] > code { border-left: none; } - .line-numbers-rows { - -webkit-user-select: none; - user-select: none; - } div.code-toolbar > .toolbar { top: 1rem; right: 2.5rem; @@ -44,7 +39,7 @@ block head display: inline-block; font-weight: 400; transition: color 0.15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; - font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; border: 1px solid transparent; } div.code-toolbar > .toolbar a:focus, div.code-toolbar > .toolbar a:hover, div.code-toolbar > .toolbar button:focus, div.code-toolbar > .toolbar button:hover, div.code-toolbar > .toolbar span:focus, div.code-toolbar > .toolbar span:hover { @@ -58,9 +53,9 @@ block content code(class='language-' + lang) | #{paste} - script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/prism.min.js', integrity='sha256-3teItwIfMuVB74Alnxw/y5HAZ2irOsCULFff3EgbtEs=', crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/prism.min.js' integrity='sha256-3teItwIfMuVB74Alnxw/y5HAZ2irOsCULFff3EgbtEs=' crossorigin='anonymous') script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/components/prism-' + lang + '.min.js') - script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.js', integrity='sha256-7I/IdbPM17QdjqRNwpVYj4iDGAQw7ZFHy9RSSU1yvLE=', crossorigin='anonymous') - script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.js', integrity='sha256-hep5s8952MqR7Y79JYfCXZD6vQjVHs7sOu/ZGrs1OEQ=', crossorigin='anonymous') - script(src='https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js', integrity='sha256-inc5kl9MA1hkeYUt+EC3BhlIgyp/2jDIyBLS6k3UxPI=', crossorigin='anonymous') - script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js', integrity='sha256-5F8rynXScCOEtnwlm5P293TlCvTT1beoEJcmWHCg4BU=', crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.js' integrity='sha256-7I/IdbPM17QdjqRNwpVYj4iDGAQw7ZFHy9RSSU1yvLE=' crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.js' integrity='sha256-hep5s8952MqR7Y79JYfCXZD6vQjVHs7sOu/ZGrs1OEQ=' crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js' integrity='sha256-inc5kl9MA1hkeYUt+EC3BhlIgyp/2jDIyBLS6k3UxPI=' crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js' integrity='sha256-5F8rynXScCOEtnwlm5P293TlCvTT1beoEJcmWHCg4BU=' crossorigin='anonymous') diff --git a/views/index.pug b/views/index.pug index ca23b40..c4c963d 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,18 +1,15 @@ extends layout block head - link(rel='stylesheet', href='https://fonts.googleapis.com/css?family=Open+Sans:400,300') - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.17/css/bootstrap-select.min.css', integrity='sha256-VMPhaMmJn7coDSbzwqB0jflvb+CDnoAlfStC5RogOQo=', crossorigin='anonymous') + link(rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/css/bootstrap-select.min.css' integrity='sha256-wiMI7buOV5UBzdWN4s4kXqE/LZ794hx5oJOOIQlDBaM=' crossorigin='anonymous') + link(rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-touchspin/4.3.0/jquery.bootstrap-touchspin.min.css' integrity='sha256-lo84g8NnZnmj6M802u7YMGf8mMuoQYV4xKEIb2DrRnk=' crossorigin='anonymous') style. html { position: relative; min-height: 100%; } body { - margin-bottom: 4rem; - } - body, button, input, optgroup, select, textarea { - font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } @media (min-width: 1800px) { .container { @@ -24,58 +21,104 @@ block head min-width: 1800px; } } + @media (min-width: 2800px) { + .container { + min-width: 80%; + } + } .display-3 { font-size: 5.5rem; } - textarea, textarea:required, textarea:invalid { - font-family: monospace, serif; - line-height: 1.5em; + @media (max-width: 575.98px) { + .display-3 { + font-size: 4rem; + } + } + textarea { + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + line-height: 1.5rem; box-shadow: none; } + label { + width: 7.5rem; + font-size: 1.2rem; + } .border-gray { border: 1px solid #ced4da; } + .bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { + width: 18rem; + } + @media (max-width: 318px) { + .bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { + width: 100%; + } + } + #highlight { + max-width: 18rem; + } + #expire { + max-width: 7.5rem; + } + .bootstrap-touchspin { + max-width: 7.5rem; + float: left; + margin-right: 1rem; + margin-bottom: 1rem; + } + #multiplier { + max-width: 9.5rem; + } + button.btn-lg { + font-size: 1.5rem; + } + @media (min-width: 768px) { + button.btn-lg { + margin-top: -6rem; + } + } footer { - width: 100%; height: 2.5rem; - position: absolute; bottom: 0; line-height: 2rem; } block content - a(href='https://github.com/JoeBiellik/paste', style='position: absolute; top: 0; right: 0; border: 0;') - img(src='https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png', alt='Fork me on GitHub') + a.position-absolute.dropdown-menu-right.d-none.d-sm-block(href='https://github.com/JoeBiellik/paste') + img(src='https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png' alt='Fork me on GitHub') - main.container.pt-5.mx-6 - h1.display-3.mb-5 #{title} + main.container.py-3.pt-sm-5.pb-md-5 + h1.display-3 #{title} - form(action='/?redirect', method='POST', accept-charset='UTF-8') - fieldset.form-group - textarea.form-control(name='paste', rows='10', required, autofocus) + form(action='/?redirect' method='POST' accept-charset='UTF-8') + label.sr-only(for="paste") Paste content + textarea#paste.my-3.my-sm-5.form-control(name='paste' rows='10' required autofocus) - div.row.mt-4 - div.col-md-auto.col-sm-12 - select#highlight.mt-3(name='highlight', data-live-search='true', data-width='auto', data-style='text-dark border-gray', data-styleBase='custom-select') - option(value='', selected) Choose syntax highlighting + .row + .col-12.col-md-9.mb-3.mb-sm-4.clearfix + label.d-block.float-sm-left(for="highlight") Highlight + select#highlight.custom-select.float-sm-left(name='highlight' data-live-search='true' data-style='text-dark border-gray' data-styleBase='custom-select') each val, key in highlights - option(value=key) #{val} + option(value=key data-tokens=(val.alias || []).join(' ') || false) #{val.name} - div.col-md-auto.col-sm-12 - select#expire.custom-select.mt-3(name='expire') - option(value='', selected) Choose expiry - each val, key in expires - option(value=key) #{val} + .col-md-9.col-sm-12.mb-3.clearfix + label.d-block.float-sm-left(for="expire") Expiry + input#expire.mr-3.form-control.text-center.float-sm-left(name='expire' type='text' value=expires.default.value required) + label.sr-only(for="multiplier") Expiry multiplier + select#multiplier.custom-select.float-sm-left(name='multiplier' required) + each val, key in expires.multipliers + option(value=key selected=key == expires.default.multiplier) #{val} - div.col.text-md-right - button.btn.btn-primary.btn-lg.mt-3(type='submit') Upload + .col-12.col-md-3.mb-3.text-md-right + button.btn.btn-primary.btn-lg(type='submit') Upload - footer.hidden-sm-down - pre.text-center.text-muted.mb-0. - echo 'Hello World' | curl -F 'paste=<-' #{url} + footer.w-100.position-absolute.d-none.d-md-block + pre.text-center.text-muted.mb-0 echo 'Hello World' + span.user-select-all | curl -F 'paste=<-' #{url} - script(src='https://cdnjs.cloudflare.com/ajax/libs/autosize.js/4.0.2/autosize.min.js' integrity='sha256-dW8u4dvEKDThJpWRwLgGugbARnA3O2wqBcVerlg9LMc=', crossorigin='anonymous') - script(src='https://code.jquery.com/jquery-3.5.1.min.js', integrity='sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=', crossorigin='anonymous') - script(src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.bundle.min.js', integrity='sha256-Xt8pc4G0CdcRvI0nZ2lRpZ4VHng0EoUDMlGcBSQ9HiQ=', crossorigin='anonymous') - script(src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.17/js/bootstrap-select.min.js', integrity='sha256-QOE02Glo1C1gHzP96JOaxyIMt4XSFv/exZaYLY4dwO0=', crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/autosize.js/4.0.2/autosize.min.js' integrity='sha256-dW8u4dvEKDThJpWRwLgGugbARnA3O2wqBcVerlg9LMc=' crossorigin='anonymous') + script(src='https://code.jquery.com/jquery-3.5.1.min.js' integrity='sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=' crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.bundle.min.js' integrity='sha256-Xt8pc4G0CdcRvI0nZ2lRpZ4VHng0EoUDMlGcBSQ9HiQ=' crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.18/js/bootstrap-select.min.js' integrity='sha256-qo0Cam4XJ0QQ06XnCiCFYBh3GDXU45j3lpUp+em2yBU=' crossorigin='anonymous') + script(src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-touchspin/4.3.0/jquery.bootstrap-touchspin.min.js' integrity='sha256-i215zpldm5iRs4r/PqXbdfyahPFuW/gtPECq5Dn3gSc=' crossorigin='anonymous') script(src='/main.js') diff --git a/views/layout.pug b/views/layout.pug index 0291a0e..828ee4c 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -2,9 +2,10 @@ doctype html html(lang='en') head meta(charset='utf-8') - meta(name='viewport', content='width=device-width, initial-scale=1, shrink-to-fit=no') + meta(name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no') title #{title} - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css', integrity='sha256-aAr2Zpq8MZ+YA/D6JtRD3xtrwpEz2IqOS+pWD/7XKIw=', crossorigin='anonymous') + link(rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css' integrity='sha256-aAr2Zpq8MZ+YA/D6JtRD3xtrwpEz2IqOS+pWD/7XKIw=' crossorigin='anonymous') + link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Open+Sans:400,300') block head