diff --git a/README.md b/README.md index 3b78bd05..15530a67 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,10 @@ turn your phone or raspi into a portable file server with resumable uploads/down * [ ] download as zip * [x] volumes * [x] accounts +* [x] markdown viewer +* [ ] markdown editor? w -summary: close to beta +summary: it works! you can use it! (but technically not even close to beta) # dependencies @@ -82,6 +84,7 @@ in the `scripts` folder: roughly sorted by priority +* sortable browser columns * up2k handle filename too long * up2k fails on empty files? alert then stuck * unexpected filepath on dupe up2k diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 8b62937e..cf2d52f1 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -706,6 +706,48 @@ class HttpCli(object): self.log(logmsg) return True + def tx_md(self, fs_path): + logmsg = "{:4} {} ".format("", self.req) + html_path = os.path.join(E.mod, "web/md.html") + + st = os.stat(fsenc(fs_path)) + sz_md = st.st_size + ts_md = st.st_mtime + + st = os.stat(fsenc(html_path)) + ts_html = st.st_mtime + + file_ts = max(ts_md, ts_html) + file_lastmod, do_send = self._chk_lastmod(file_ts) + self.out_headers["Last-Modified"] = file_lastmod + status = 200 if do_send else 304 + + targs = { + "title": html_escape(self.vpath, quote=False), + "md": "", + } + sz_html = len(self.conn.tpl_md.render(**targs).encode("utf-8")) + self.send_headers(sz_html + sz_md, status) + + logmsg += str(status) + if self.mode == "HEAD" or not do_send: + self.log(logmsg) + return True + + with open(fsenc(fs_path), "rb") as f: + md = f.read() + + targs["md"] = md.decode("utf-8", "replace") + html = self.conn.tpl_md.render(**targs).encode("utf-8") + try: + self.s.sendall(html) + except: + self.log(logmsg + " \033[31md/c\033[0m") + return False + + self.log(logmsg + " " + str(len(html))) + return True + def tx_mounts(self): rvol = [x + "/" if x else x for x in self.rvol] wvol = [x + "/" if x else x for x in self.wvol] @@ -735,6 +777,9 @@ class HttpCli(object): raise Pebkac(404) if not os.path.isdir(fsenc(abspath)): + if abspath.endswith(".md") and "raw" not in self.uparam: + return self.tx_md(abspath) + return self.tx_file(abspath) fsroot, vfs_ls, vfs_virt = vn.ls(rem, self.uname) diff --git a/copyparty/httpconn.py b/copyparty/httpconn.py index 704f1b4c..e1bda616 100644 --- a/copyparty/httpconn.py +++ b/copyparty/httpconn.py @@ -35,6 +35,7 @@ class HttpConn(object): self.tpl_mounts = env.get_template("splash.html") self.tpl_browser = env.get_template("browser.html") self.tpl_msg = env.get_template("msg.html") + self.tpl_md = env.get_template("md.html") def respath(self, res_name): return os.path.join(E.mod, "web", res_name) diff --git a/copyparty/web/md.css b/copyparty/web/md.css new file mode 100644 index 00000000..58f5543c --- /dev/null +++ b/copyparty/web/md.css @@ -0,0 +1,375 @@ +html, body { + color: #333; + background: #eee; + font-family: sans-serif; + line-height: 1.5em; +} +#mw { + width: 48.5em; + margin: 0 auto; + margin-bottom: 6em; +} +pre, code, a { + color: #480; + background: #f7f7f7; + border: .07em solid #ddd; + border-radius: .2em; + padding: .1em .3em; + margin: 0 .1em; +} +code { + font-size: .96em; +} +pre, code { + font-family: monospace, monospace; + white-space: pre-wrap; + word-break: break-all; +} +pre { + counter-reset: precode; +} +pre code { + counter-increment: precode; + display: inline-block; + margin: 0 -.3em; + padding: .4em .5em; + border: none; + border-bottom: 1px solid #cdc; + min-width: calc(100% - .6em); + line-height: 1.1em; +} +pre code:last-child { + border-bottom: none; +} +pre code:before { + content: counter(precode); + -webkit-user-select: none; + display: inline-block; + text-align: right; + font-size: .75em; + color: #48a; + width: 4em; + padding-right: 1.5em; + margin-left: -5.5em; +} +pre code:hover { + background: #fec; + color: #360; +} +h1, h2 { + line-height: 1.5em; +} +h1 { + font-size: 1.7em; + text-align: center; + border: 1em solid #777; + border-width: .05em 0; + margin: 3em 0; +} +h2 { + font-size: 1.5em; + font-weight: normal; + background: #f7f7f7; + border-top: .07em solid #fff; + border-bottom: .07em solid #bbb; + border-radius: .5em .5em 0 0; + padding-left: .4em; + margin-top: 3em; +} +h1 a, h3 a, h5 a, +h2 a, h4 a, h6 a { + color: inherit; + background: none; + border: none; + padding: 0; + margin: 0; +} +#m ul, +#m ol { + border-left: .3em solid #ddd; +} +#m>ul, +#m>ol { + border-color: #bbb; +} +#m ul>li { + list-style-type: disc; +} +#m ul>li, +#m ol>li { + margin: .7em 0; +} +p>em, +li>em { + color: #c50; + padding: .1em; + border-bottom: .1em solid #bbb; +} +blockquote { + font-family: serif; + background: #f7f7f7; + border: .07em dashed #ccc; + padding: 0 2em; + margin: 1em 0; +} +small { + opacity: .8; +} +#toc { + width: 48.5em; + margin: 0 auto; +} +#toc ul { + padding-left: 1em; +} +#toc>ul { + text-align: left; + padding-left: .5em; +} +#toc li { + list-style-type: none; + line-height: 1.2em; + margin: .5em 0; +} +#toc a { + color: #057; + border: none; + background: none; + display: block; + margin-left: -.3em; + padding: .2em .3em; +} +#toc a.act { + color: #fff; + background: #07a; +} +.todo_pend, +.todo_done { + z-index: 99; + position: relative; + display: inline-block; + font-family: monospace, monospace; + font-weight: bold; + font-size: 1.3em; + line-height: .1em; + margin: -.5em 0 -.5em -.85em; + top: .1em; + color: #b29; +} +.todo_done { + color: #6b3; + text-shadow: .02em 0 0 #6b3; +} +table { + border-collapse: collapse; +} +td { + padding: .2em .5em; + border: .12em solid #aaa; +} +th { + border: .12em solid #aaa; +} +blink { + animation: blinker .7s cubic-bezier(.9, 0, .1, 1) infinite; +} +@keyframes blinker { + 10% { + opacity: 0; + } + 60% { + opacity: 1; + } +} +@media screen { + a { + color: #fff; + background: #39b; + text-decoration: none; + padding: 0 .3em; + border: none; + border-bottom: .07em solid #079; + } + h2 { + color: #fff; + background: #555; + margin-top: 2em; + border-bottom: .22em solid #999; + border-top: none; + } + h1 { + color: #fff; + background: #444; + font-weight: normal; + border-top: .4em solid #fb0; + border-bottom: .4em solid #777; + border-radius: 0 1em 0 1em; + margin: 3em 0 1em 0; + padding: .5em 0; + } + #mn { + text-shadow: 1px 1px 0 #000; + xfont-variant: small-caps; + font-weight: normal; + margin: 1.3em 0 0 0; + font-size: 1.4em; + } + #mn a { + background: #2c2c2c; + margin: 0 0 0 -.2em; + padding: 0 0 0 .4em; + /* ie: */ + border-bottom: .1em solid #777\9; + margin-right: 1em\9; + } + #mn a:first-child { + padding-left: .5em; + } + #mn a:last-child { + padding-right: .5em; + } + #mn a:not(:last-child):after { + content: ''; + width: 1.05em; + height: 1.05em; + margin: -.2em .3em -.2em -.4em; + display: inline-block; + border: 1px solid rgba(255,224,192,0.3); + border-width: .05em .05em 0 0; + transform: rotate(45deg); + background: linear-gradient(45deg, rgba(0,0,0,0) 40%, rgba(0,0,0,0.25) 75%, rgba(0,0,0,0.35)); + } + #mn a:hover { + color: #fff; + background: linear-gradient(90deg, rgba(0,0,0,0), rgba(0,0,0,0.2), rgba(0,0,0,0)); + } + #mh { + margin: 1.5em 0; + } + + + + html.dark, + html.dark body { + background: #222; + color: #ccc; + } + html.dark #toc a { + color: #ccc; + border-left: .4em solid #444; + border-bottom: .1em solid #333; + } + html.dark #toc a.act { + color: #fff; + border-left: .4em solid #3ad; + } + html.dark #toc li { + border-width: 0; + } + html.dark #m a, + html.dark #mh a { + background: #057; + } + html.dark #m h1 a, html.dark #m h4 a, + html.dark #m h2 a, html.dark #m h5 a, + html.dark #m h3 a, html.dark #m h6 a { + color: inherit; + background: none; + } + html.dark pre, + html.dark code { + color: #8c0; + background: #1a1a1a; + border: .07em solid #333; + } + html.dark #m ul, + html.dark #m ol { + border-color: #444; + } + html.dark #m>ul, + html.dark #m>ol { + border-color: #555; + } + html.dark p>em, + html.dark li>em { + color: #f94; + border-color: #666; + } + html.dark h1 { + background: #383838; + border-top: .4em solid #b80; + border-bottom: .4em solid #4c4c4c; + } + html.dark h2 { + background: #444; + border-bottom: .22em solid #555; + } + html.dark td, + html.dark th { + border-color: #444; + } + html.dark blockquote { + background: #282828; + border: .07em dashed #444; + } +} +@media screen and (min-width: 64em) { + #mw { + margin-left: 14em; + margin-left: calc(100% - 50em); + } + #toc { + width: 13em; + width: calc(100% - 52.3em); + background: #eee; + position: fixed; + top: 0; + left: 0; + height: 100%; + overflow-y: auto; + padding: 0; + margin: 0; + box-shadow: 0 0 1em #ccc; + scrollbar-color: #eb0 #f7f7f7; + xscrollbar-width: thin; + } + #toc li { + border-left: .3em solid #ccc; + } + #toc::-webkit-scrollbar-track { + background: #f7f7f7; + } + #toc::-webkit-scrollbar { + background: #f7f7f7; + width: .8em; + } + #toc::-webkit-scrollbar-thumb { + background: #eb0; + } + + + + html.dark #toc { + background: #282828; + box-shadow: 0 0 1em #181818; + scrollbar-color: #b80 #282828; + } +} +@media screen and (min-width: 84em) { + #toc { width: 30em } + #mw { margin-left: 32em } +} +@media print { + a { + color: #079; + text-decoration: none; + border-bottom: .07em solid #4ac; + padding: 0 .3em; + } + #toc>ul { + border-left: .1em solid #84c4dd; + } + #mn, #mh { + display: none; + } +} \ No newline at end of file diff --git a/copyparty/web/md.html b/copyparty/web/md.html new file mode 100644 index 00000000..9cbb39fa --- /dev/null +++ b/copyparty/web/md.html @@ -0,0 +1,47 @@ + + + 📝🎉 {{ title }} + + + + + +
+
+
+
+ dark + there will soon be more buttons on this row so it looks less dumb +
+
+
+
Loading
+ if you're still reading this, check that javascript is allowed +
+
+
+ +
+
+ + + + diff --git a/copyparty/web/md.js b/copyparty/web/md.js new file mode 100644 index 00000000..c3577c94 --- /dev/null +++ b/copyparty/web/md.js @@ -0,0 +1,232 @@ +/*var conv = new showdown.Converter(); +conv.setFlavor('github'); +conv.setOption('tasklists', 0); +var mhtml = conv.makeHtml(dom_md.value); +*/ + +var dom_toc = document.getElementById('toc'); +var dom_wrap = document.getElementById('mw'); +var dom_head = document.getElementById('mh'); +var dom_nav = document.getElementById('mn'); +var dom_doc = document.getElementById('m'); +var dom_md = document.getElementById('mt'); + +// add toolbar buttons +(function () { + var n = document.location + ''; + n = n.substr(n.indexOf('//') + 2).split('/'); + n[0] = 'top'; + var nav = []; + var url = '/'; + for (var a = 0; a < n.length; a++) { + if (a > 0) + url += n[a] + '/'; + + nav.push('' + n[a] + ''); + } + dom_nav.innerHTML = nav.join(''); +})(); + +function convert_markdown(md_text) { + marked.setOptions({ + //headerPrefix: 'h-', + breaks: true, + gfm: true + }); + var html = marked(md_text); + dom_doc.innerHTML = html; + + var loader = document.getElementById('ml'); + loader.parentNode.removeChild(loader); + + // todo-lists (should probably be a marked extension) + var nodes = dom_doc.getElementsByTagName('input'); + for (var a = nodes.length - 1; a >= 0; a--) { + var dom_box = nodes[a]; + if (dom_box.getAttribute('type') !== 'checkbox') + continue; + + var dom_li = dom_box.parentNode; + var done = dom_box.getAttribute('checked'); + done = done !== null; + var clas = done ? 'done' : 'pend'; + var char = done ? 'Y' : 'N'; + + dom_li.setAttribute('class', 'task-list-item'); + dom_li.style.listStyleType = 'none'; + var html = dom_li.innerHTML; + dom_li.innerHTML = + '' + char + '' + + html.substr(html.indexOf('>') + 1); + } +} + +function init_toc() { + var anchors = []; // list of toc entries, complex objects + var anchor = null; // current toc node + var id_seen = {}; // taken IDs + var html = []; // generated toc html + var lv = 0; // current indentation level in the toc html + var re = new RegExp('^[Hh]([1-3])'); + + var manip_nodes_dyn = dom_doc.getElementsByTagName('*'); + var manip_nodes = []; + for (var a = 0, aa = manip_nodes_dyn.length; a < aa; a++) + manip_nodes.push(manip_nodes_dyn[a]); + + for (var a = 0, aa = manip_nodes.length; a < aa; a++) { + var elm = manip_nodes[a]; + var m = re.exec(elm.tagName); + + var is_header = + m !== null; + + var is_precode = + !is_header && + elm.tagName == 'PRE' && + elm.childNodes.length === 1 && + elm.childNodes[0].tagName == 'CODE'; + + if (is_header) { + var nlv = m[1]; + while (lv < nlv) { + html.push(''); + lv--; + } + + var orig_id = elm.getAttribute('id'); + var id = orig_id; + if (id_seen[id]) { + for (var n = 1; n < 4096; n++) { + id = orig_id + '-' + n; + if (!id_seen[id]) + break; + } + elm.setAttribute('id', id); + } + id_seen[id] = 1; + + var ahref = '' + + elm.innerHTML + ''; + + html.push('
  • ' + ahref + '
  • '); + elm.innerHTML = ahref; + + if (anchor != null) + anchors.push(anchor); + + anchor = { + elm: elm, + kids: [], + y: null + }; + } + else if (is_precode) { + elm.innerHTML = elm.innerHTML.replace( + /\r?\n<\/code>$/i, '').split(/\r?\n/g).join('\n'); + } + + if (!is_header && anchor) + anchor.kids.push(elm); + } + dom_toc.innerHTML = html.join('\n'); + if (anchor != null) + anchors.push(anchor); + + // copy toc links into the toc list + var atoc = dom_toc.getElementsByTagName('a'); + for (var a = 0, aa = anchors.length; a < aa; a++) + anchors[a].lnk = atoc[a]; + + // collect vertical position of all toc items (headers in document) + function freshen_offsets() { + var top = window.pageYOffset || document.documentElement.scrollTop; + for (var a = anchors.length - 1; a >= 0; a--) { + var y = top + anchors[a].elm.getBoundingClientRect().top; + y = Math.round(y * 10.0) / 10; + if (anchors[a].y === y) + break; + + anchors[a].y = y; + } + } + + // hilight the correct toc items + scroll into view + function freshen_toclist() { + if (anchors.length == 0) + return; + + var ptop = window.pageYOffset || document.documentElement.scrollTop; + var hit = -1; + for (var a = 0; a < anchors.length; a++) { + if (anchors[a].y >= ptop - 8) { //??? + hit = a; + break; + } + } + + var links = dom_toc.getElementsByTagName('a'); + if (!anchors[hit].active) { + for (var a = 0; a < anchors.length; a++) { + if (anchors[a].active) { + anchors[a].active = false; + links[a].setAttribute('class', ''); + } + } + anchors[hit].active = true; + links[hit].setAttribute('class', 'act'); + } + + var pane_height = parseInt(getComputedStyle(dom_toc).height); + var link_bounds = links[hit].getBoundingClientRect(); + var top = link_bounds.top - (pane_height / 6); + var btm = link_bounds.bottom + (pane_height / 6); + if (top < 0) + dom_toc.scrollTop -= -top; + else if (btm > pane_height) + dom_toc.scrollTop += btm - pane_height; + } + + function refresh() { + freshen_offsets(); + freshen_toclist(); + } + + return { "refresh": refresh } +} + + +// "main" :p +convert_markdown(dom_md.value); +var toc = init_toc(); + + +// scroll handler +(function () { + var timer_active = false; + var final = null; + + function onscroll() { + clearTimeout(final); + timer_active = false; + toc.refresh(); + } + onscroll(); + + window.onscroll = function () { + // long timeout: scroll ended + clearTimeout(final); + final = setTimeout(onscroll, 100); + + // short timeout: continuous updates + if (timer_active) + return; + + timer_active = true; + setTimeout(onscroll, 10); + }; +})(); diff --git a/scripts/deps-docker/Dockerfile b/scripts/deps-docker/Dockerfile index bce1e573..b7c64ca6 100644 --- a/scripts/deps-docker/Dockerfile +++ b/scripts/deps-docker/Dockerfile @@ -1,25 +1,50 @@ FROM alpine:3.11 WORKDIR /z ENV ver_asmcrypto=2821dd1dedd1196c378f5854037dda5c869313f3 \ + ver_markdownit=10.0.0 \ + ver_showdown=1.9.1 \ + ver_marked=1.0.0 \ ver_ogvjs=1.6.1 # download RUN apk add make g++ git bash npm patch wget tar pigz brotli gzip unzip \ - && wget https://github.com/brion/ogv.js/releases/download/$ver_ogvjs/ogvjs-$ver_ogvjs.zip \ - && wget https://github.com/asmcrypto/asmcrypto.js/archive/$ver_asmcrypto.tar.gz \ - && unzip ogvjs-$ver_ogvjs.zip \ - && tar -xf $ver_asmcrypto.tar.gz \ - && cd asmcrypto.js-$ver_asmcrypto \ - && npm install \ + && wget https://github.com/brion/ogv.js/releases/download/$ver_ogvjs/ogvjs-$ver_ogvjs.zip -O ogvjs.zip \ + && wget https://github.com/asmcrypto/asmcrypto.js/archive/$ver_asmcrypto.tar.gz -O asmcrypto.tgz \ + && wget https://github.com/markedjs/marked/archive/v$ver_marked.tar.gz -O marked.tgz \ + && unzip ogvjs.zip \ + && (tar -xf asmcrypto.tgz \ + && cd asmcrypto.js-$ver_asmcrypto \ + && npm install ) \ + && (tar -xf marked.tgz \ + && cd marked-$ver_marked \ + && npm install \ + && npm i grunt uglify-js -g ) \ && mkdir /z/dist + +# uncomment if you wanna test the abandoned markdown converters +#ENV build_abandoned=1 + + +RUN [ $build_abandoned ] || exit 0; \ + git clone --depth 1 --branch $ver_showdown https://github.com/showdownjs/showdown/ \ + && wget https://github.com/markdown-it/markdown-it/archive/$ver_markdownit.tar.gz -O markdownit.tgz \ + && (cd showdown \ + && npm install \ + && npm i grunt -g ) \ + && (tar -xf markdownit.tgz \ + && cd markdown-it-$ver_markdownit \ + && npm install ) + + # build asmcrypto RUN cd asmcrypto.js-$ver_asmcrypto \ && echo "export { Sha512 } from './hash/sha512/sha512';" > src/entry-export_all.ts \ && node -r esm build.js \ && mv asmcrypto.all.es5.js /z/dist/sha512.js + # build ogvjs RUN cd ogvjs-$ver_ogvjs \ && cp -pv \ @@ -40,8 +65,90 @@ RUN cd ogvjs-$ver_ogvjs \ dynamicaudio.swf \ /z/dist + +# build marked +COPY marked.patch /z/ +RUN cd marked-$ver_marked \ + && patch -p1 < /z/marked.patch \ + && npm run build \ + && cp -pv marked.min.js /z/dist/marked.js \ + && cp -pv lib/marked.js /z/dist/marked.full.js +# && npm run test \ + + +# build showdown (abandoned; disabled by default) +COPY showdown.patch /z/ +RUN [ $build_abandoned ] || exit 0; \ + cd showdown \ + && rm -rf bin dist \ +# # remove ellipsis plugin \ + && rm \ + src/subParsers/ellipsis.js \ + test/cases/ellipsis* \ +# # remove html-to-md converter \ + && rm \ + test/node/testsuite.makemd.js \ + test/node/showdown.Converter.makeMarkdown.js \ +# # remove emojis \ + && rm src/subParsers/emoji.js \ + && awk '/^showdown.helper.emojis/ {o=1} !o; /^\}/ {o=0}' \ + >f marked.patch; make && printf '%d ' $(wc -c <$f) $(gzip -d <$f | wc -c); echo diff --git a/scripts/deps-docker/markdown-it.patch b/scripts/deps-docker/markdown-it.patch new file mode 100644 index 00000000..bf096e7c --- /dev/null +++ b/scripts/deps-docker/markdown-it.patch @@ -0,0 +1,10 @@ +diff -NarU1 markdown-it-10.0.0-orig/lib/common/entities.js markdown-it-10.0.0-edit/lib/common/entities.js +--- markdown-it-10.0.0-orig/lib/common/entities.js 2019-09-10 21:39:58.000000000 +0000 ++++ markdown-it-10.0.0-edit/lib/common/entities.js 2020-04-26 10:24:33.043023331 +0000 +@@ -5,2 +5,5 @@ + /*eslint quotes:0*/ +-module.exports = require('entities/lib/maps/entities.json'); ++//module.exports = require('entities/lib/maps/entities.json'); ++module.exports = { ++ "amp": "&", "quot": "\"", "gt": ">", "lt": "<" ++} diff --git a/scripts/deps-docker/marked.patch b/scripts/deps-docker/marked.patch new file mode 100644 index 00000000..e308cf2e --- /dev/null +++ b/scripts/deps-docker/marked.patch @@ -0,0 +1,269 @@ +diff -NarU1 marked-1.0.0-orig/src/defaults.js marked-1.0.0-edit/src/defaults.js +--- marked-1.0.0-orig/src/defaults.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/src/defaults.js 2020-04-25 19:16:56.124621393 +0000 +@@ -9,10 +9,6 @@ + langPrefix: 'language-', +- mangle: true, + pedantic: false, + renderer: null, +- sanitize: false, +- sanitizer: null, + silent: false, + smartLists: false, +- smartypants: false, + tokenizer: null, +diff -NarU1 marked-1.0.0-orig/src/helpers.js marked-1.0.0-edit/src/helpers.js +--- marked-1.0.0-orig/src/helpers.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/src/helpers.js 2020-04-25 18:58:43.001320210 +0000 +@@ -65,16 +65,3 @@ + const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; +-function cleanUrl(sanitize, base, href) { +- if (sanitize) { +- let prot; +- try { +- prot = decodeURIComponent(unescape(href)) +- .replace(nonWordAndColonTest, '') +- .toLowerCase(); +- } catch (e) { +- return null; +- } +- if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { +- return null; +- } +- } ++function cleanUrl(base, href) { + if (base && !originIndependentUrl.test(href)) { +@@ -224,8 +211,2 @@ + +-function checkSanitizeDeprecation(opt) { +- if (opt && opt.sanitize && !opt.silent) { +- console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options'); +- } +-} +- + module.exports = { +@@ -240,4 +221,3 @@ + rtrim, +- findClosingBracket, +- checkSanitizeDeprecation ++ findClosingBracket + }; +diff -NarU1 marked-1.0.0-orig/src/Lexer.js marked-1.0.0-edit/src/Lexer.js +--- marked-1.0.0-orig/src/Lexer.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/src/Lexer.js 2020-04-25 22:46:54.107584066 +0000 +@@ -6,3 +6,3 @@ + * smartypants text replacement +- */ ++ * + function smartypants(text) { +@@ -27,3 +27,3 @@ + * mangle email addresses +- */ ++ * + function mangle(text) { +@@ -388,3 +388,3 @@ + // autolink +- if (token = this.tokenizer.autolink(src, mangle)) { ++ if (token = this.tokenizer.autolink(src)) { + src = src.substring(token.raw.length); +@@ -395,3 +395,3 @@ + // url (gfm) +- if (!inLink && (token = this.tokenizer.url(src, mangle))) { ++ if (!inLink && (token = this.tokenizer.url(src))) { + src = src.substring(token.raw.length); +@@ -402,3 +402,3 @@ + // text +- if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) { ++ if (token = this.tokenizer.inlineText(src, inRawBlock)) { + src = src.substring(token.raw.length); +diff -NarU1 marked-1.0.0-orig/src/marked.js marked-1.0.0-edit/src/marked.js +--- marked-1.0.0-orig/src/marked.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/src/marked.js 2020-04-25 22:42:55.140924439 +0000 +@@ -8,3 +8,2 @@ + merge, +- checkSanitizeDeprecation, + escape +@@ -37,3 +36,2 @@ + opt = merge({}, marked.defaults, opt || {}); +- checkSanitizeDeprecation(opt); + const highlight = opt.highlight; +@@ -101,6 +99,5 @@ + opt = merge({}, marked.defaults, opt || {}); +- checkSanitizeDeprecation(opt); + return Parser.parse(Lexer.lex(src, opt), opt); + } catch (e) { +- e.message += '\nPlease report this to https://github.com/markedjs/marked.'; ++ e.message += '\nmake issue @ https://github.com/9001/copyparty'; + if ((opt || marked.defaults).silent) { +diff -NarU1 marked-1.0.0-orig/src/Renderer.js marked-1.0.0-edit/src/Renderer.js +--- marked-1.0.0-orig/src/Renderer.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/src/Renderer.js 2020-04-25 18:59:15.091319265 +0000 +@@ -134,3 +134,3 @@ + link(href, title, text) { +- href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); ++ href = cleanUrl(this.options.baseUrl, href); + if (href === null) { +@@ -147,3 +147,3 @@ + image(href, title, text) { +- href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); ++ href = cleanUrl(this.options.baseUrl, href); + if (href === null) { +diff -NarU1 marked-1.0.0-orig/src/Tokenizer.js marked-1.0.0-edit/src/Tokenizer.js +--- marked-1.0.0-orig/src/Tokenizer.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/src/Tokenizer.js 2020-04-25 22:47:07.610917004 +0000 +@@ -256,9 +256,6 @@ + return { +- type: this.options.sanitize +- ? 'paragraph' +- : 'html', +- raw: cap[0], +- pre: !this.options.sanitizer +- && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), +- text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0] ++ type: 'html', ++ raw: cap[0], ++ pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style', ++ text: cap[0] + }; +@@ -382,5 +379,3 @@ + return { +- type: this.options.sanitize +- ? 'text' +- : 'html', ++ type: 'html', + raw: cap[0], +@@ -388,7 +383,3 @@ + inRawBlock, +- text: this.options.sanitize +- ? (this.options.sanitizer +- ? this.options.sanitizer(cap[0]) +- : escape(cap[0])) +- : cap[0] ++ text: cap[0] + }; +@@ -504,3 +495,3 @@ + +- autolink(src, mangle) { ++ autolink(src) { + const cap = this.rules.inline.autolink.exec(src); +@@ -509,3 +500,3 @@ + if (cap[2] === '@') { +- text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]); ++ text = escape(cap[1]); + href = 'mailto:' + text; +@@ -532,3 +523,3 @@ + +- url(src, mangle) { ++ url(src) { + let cap; +@@ -537,3 +528,3 @@ + if (cap[2] === '@') { +- text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]); ++ text = escape(cap[0]); + href = 'mailto:' + text; +@@ -569,3 +560,3 @@ + +- inlineText(src, inRawBlock, smartypants) { ++ inlineText(src, inRawBlock) { + const cap = this.rules.inline.text.exec(src); +@@ -574,5 +565,5 @@ + if (inRawBlock) { +- text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]; ++ text = cap[0]; + } else { +- text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]); ++ text = escape(cap[0]); + } +diff -NarU1 marked-1.0.0-orig/test/bench.js marked-1.0.0-edit/test/bench.js +--- marked-1.0.0-orig/test/bench.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/test/bench.js 2020-04-25 19:02:27.227980287 +0000 +@@ -34,3 +34,2 @@ + pedantic: false, +- sanitize: false, + smartLists: false +@@ -46,3 +45,2 @@ + pedantic: false, +- sanitize: false, + smartLists: false +@@ -59,3 +57,2 @@ + pedantic: false, +- sanitize: false, + smartLists: false +@@ -71,3 +68,2 @@ + pedantic: false, +- sanitize: false, + smartLists: false +@@ -84,3 +80,2 @@ + pedantic: true, +- sanitize: false, + smartLists: false +@@ -96,3 +91,2 @@ + pedantic: true, +- sanitize: false, + smartLists: false +diff -NarU1 marked-1.0.0-orig/test/specs/run-spec.js marked-1.0.0-edit/test/specs/run-spec.js +--- marked-1.0.0-orig/test/specs/run-spec.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/test/specs/run-spec.js 2020-04-25 19:05:24.321308408 +0000 +@@ -21,6 +21,2 @@ + } +- if (spec.options.sanitizer) { +- // eslint-disable-next-line no-eval +- spec.options.sanitizer = eval(spec.options.sanitizer); +- } + (spec.only ? fit : (spec.skip ? xit : it))('should ' + passFail + example, async() => { +@@ -49,2 +45 @@ + runSpecs('ReDOS', './redos'); +-runSpecs('Security', './security', false, { silent: true }); // silent - do not show deprecation warning +diff -NarU1 marked-1.0.0-orig/test/unit/Lexer-spec.js marked-1.0.0-edit/test/unit/Lexer-spec.js +--- marked-1.0.0-orig/test/unit/Lexer-spec.js 2020-04-21 01:03:48.000000000 +0000 ++++ marked-1.0.0-edit/test/unit/Lexer-spec.js 2020-04-25 22:47:27.170916427 +0000 +@@ -464,3 +464,3 @@ + +- it('sanitize', () => { ++ /*it('sanitize', () => { + expectTokens({ +@@ -482,3 +482,3 @@ + }); +- }); ++ });*/ + }); +@@ -586,3 +586,3 @@ + +- it('html sanitize', () => { ++ /*it('html sanitize', () => { + expectInlineTokens({ +@@ -596,3 +596,3 @@ + }); +- }); ++ });*/ + +@@ -825,3 +825,3 @@ + +- it('autolink mangle email', () => { ++ /*it('autolink mangle email', () => { + expectInlineTokens({ +@@ -845,3 +845,3 @@ + }); +- }); ++ });*/ + +@@ -882,3 +882,3 @@ + +- it('url mangle email', () => { ++ /*it('url mangle email', () => { + expectInlineTokens({ +@@ -902,3 +902,3 @@ + }); +- }); ++ });*/ + }); +@@ -918,3 +918,3 @@ + +- describe('smartypants', () => { ++ /*describe('smartypants', () => { + it('single quotes', () => { +@@ -988,3 +988,3 @@ + }); +- }); ++ });*/ + }); diff --git a/scripts/deps-docker/showdown.patch b/scripts/deps-docker/showdown.patch new file mode 100644 index 00000000..c66c2e71 --- /dev/null +++ b/scripts/deps-docker/showdown.patch @@ -0,0 +1,214 @@ +diff -NarU1 showdown-orig/Gruntfile.js showdown-mod/Gruntfile.js +--- showdown-orig/Gruntfile.js 2020-04-23 06:22:01.486676149 +0000 ++++ showdown-mod/Gruntfile.js 2020-04-23 08:03:56.700219788 +0000 +@@ -27,3 +27,2 @@ + 'src/subParsers/*.js', +- 'src/subParsers/makeMarkdown/*.js', + 'src/loader.js' +diff -NarU1 showdown-orig/src/converter.js showdown-mod/src/converter.js +--- showdown-orig/src/converter.js 2020-04-23 06:22:01.496676150 +0000 ++++ showdown-mod/src/converter.js 2020-04-23 08:20:11.056920123 +0000 +@@ -84,5 +84,5 @@ + +- if (options.extensions) { ++ /*if (options.extensions) { + showdown.helper.forEach(options.extensions, _parseExtension); +- } ++ }*/ + } +@@ -95,3 +95,3 @@ + */ +- function _parseExtension (ext, name) { ++ /*function _parseExtension (ext, name) { + +@@ -159,3 +159,3 @@ + */ +- function legacyExtensionLoading (ext, name) { ++ /*function legacyExtensionLoading (ext, name) { + if (typeof ext === 'function') { +@@ -351,3 +351,3 @@ + */ +- this.makeMarkdown = this.makeMd = function (src, HTMLParser) { ++ /*this.makeMarkdown = this.makeMd = function (src, HTMLParser) { + +@@ -482,3 +482,3 @@ + */ +- this.addExtension = function (extension, name) { ++ /*this.addExtension = function (extension, name) { + name = name || null; +@@ -491,3 +491,3 @@ + */ +- this.useExtension = function (extensionName) { ++ /*this.useExtension = function (extensionName) { + _parseExtension(extensionName); +@@ -526,3 +526,3 @@ + */ +- this.removeExtension = function (extension) { ++ /*this.removeExtension = function (extension) { + if (!showdown.helper.isArray(extension)) { +@@ -549,3 +549,3 @@ + */ +- this.getAllExtensions = function () { ++ /*this.getAllExtensions = function () { + return { +diff -NarU1 showdown-orig/src/options.js showdown-mod/src/options.js +--- showdown-orig/src/options.js 2020-04-23 06:22:01.496676150 +0000 ++++ showdown-mod/src/options.js 2020-04-23 08:24:29.176929018 +0000 +@@ -118,3 +118,3 @@ + }, +- ghMentions: { ++ /*ghMentions: { + defaultValue: false, +@@ -127,3 +127,3 @@ + type: 'string' +- }, ++ },*/ + encodeEmails: { +diff -NarU1 showdown-orig/src/showdown.js showdown-mod/src/showdown.js +--- showdown-orig/src/showdown.js 2020-04-23 06:22:01.496676150 +0000 ++++ showdown-mod/src/showdown.js 2020-04-23 08:25:01.976930148 +0000 +@@ -7,3 +7,2 @@ + parsers = {}, +- extensions = {}, + globalOptions = getDefaultOpts(true), +@@ -25,5 +24,4 @@ + ghCompatibleHeaderId: true, +- ghMentions: true, ++ //ghMentions: true, + backslashEscapesHTMLTags: true, +- emoji: true, + splitAdjacentBlockquotes: true +@@ -48,3 +46,3 @@ + requireSpaceBeforeHeadingText: true, +- ghMentions: false, ++ //ghMentions: false, + encodeEmails: true +@@ -65,3 +63,2 @@ + */ +-showdown.extensions = {}; + +@@ -193,3 +190,3 @@ + */ +-showdown.extension = function (name, ext) { ++/*showdown.extension = function (name, ext) { + 'use strict'; +@@ -235,3 +232,3 @@ + */ +-showdown.getAllExtensions = function () { ++/*showdown.getAllExtensions = function () { + 'use strict'; +@@ -244,3 +241,3 @@ + */ +-showdown.removeExtension = function (name) { ++/*showdown.removeExtension = function (name) { + 'use strict'; +@@ -252,3 +249,3 @@ + */ +-showdown.resetExtensions = function () { ++/*showdown.resetExtensions = function () { + 'use strict'; +@@ -263,3 +260,3 @@ + */ +-function validate (extension, name) { ++/*function validate (extension, name) { + 'use strict'; +@@ -370,3 +367,3 @@ + */ +-showdown.validateExtension = function (ext) { ++/*showdown.validateExtension = function (ext) { + 'use strict'; +@@ -380 +377,2 @@ + }; ++*/ +diff -NarU1 showdown-orig/src/subParsers/anchors.js showdown-mod/src/subParsers/anchors.js +--- showdown-orig/src/subParsers/anchors.js 2020-04-23 06:22:01.496676150 +0000 ++++ showdown-mod/src/subParsers/anchors.js 2020-04-23 08:25:26.880264347 +0000 +@@ -76,3 +76,3 @@ + // Lastly handle GithubMentions if option is enabled +- if (options.ghMentions) { ++ /*if (options.ghMentions) { + text = text.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gmi, function (wm, st, escape, mentions, username) { +@@ -93,3 +93,3 @@ + }); +- } ++ }*/ + +diff -NarU1 showdown-orig/src/subParsers/spanGamut.js showdown-mod/src/subParsers/spanGamut.js +--- showdown-orig/src/subParsers/spanGamut.js 2020-04-23 06:22:01.496676150 +0000 ++++ showdown-mod/src/subParsers/spanGamut.js 2020-04-23 08:07:50.460227880 +0000 +@@ -22,3 +22,2 @@ + text = showdown.subParser('simplifiedAutoLinks')(text, options, globals); +- text = showdown.subParser('emoji')(text, options, globals); + text = showdown.subParser('underline')(text, options, globals); +@@ -26,3 +25,2 @@ + text = showdown.subParser('strikethrough')(text, options, globals); +- text = showdown.subParser('ellipsis')(text, options, globals); + +diff -NarU1 showdown-orig/test/node/showdown.Converter.js showdown-mod/test/node/showdown.Converter.js +--- showdown-orig/test/node/showdown.Converter.js 2020-04-23 06:22:01.520009484 +0000 ++++ showdown-mod/test/node/showdown.Converter.js 2020-04-23 08:14:58.086909318 +0000 +@@ -29,3 +29,3 @@ + +- describe('Converter.options extensions', function () { ++ /*describe('Converter.options extensions', function () { + var runCount; +@@ -48,3 +48,3 @@ + }); +- }); ++ });*/ + +@@ -115,3 +115,3 @@ + +- describe('extension methods', function () { ++ /*describe('extension methods', function () { + var extObjMock = { +@@ -145,3 +145,3 @@ + }); +- }); ++ });*/ + +diff -NarU1 showdown-orig/test/node/showdown.js showdown-mod/test/node/showdown.js +--- showdown-orig/test/node/showdown.js 2020-04-23 06:22:01.523342816 +0000 ++++ showdown-mod/test/node/showdown.js 2020-04-23 08:14:31.733575073 +0000 +@@ -25,3 +25,3 @@ + +-describe('showdown.extension()', function () { ++/*describe('showdown.extension()', function () { + 'use strict'; +@@ -110,3 +110,3 @@ + }); +-}); ++});*/ + +diff -NarU1 showdown-orig/test/node/testsuite.features.js showdown-mod/test/node/testsuite.features.js +--- showdown-orig/test/node/testsuite.features.js 2020-04-23 06:22:01.523342816 +0000 ++++ showdown-mod/test/node/testsuite.features.js 2020-04-23 08:25:48.880265106 +0000 +@@ -13,3 +13,2 @@ + rawPrefixHeaderIdSuite = bootstrap.getTestSuite('test/features/rawPrefixHeaderId/'), +- emojisSuite = bootstrap.getTestSuite('test/features/emojis/'), + underlineSuite = bootstrap.getTestSuite('test/features/underline/'), +@@ -69,4 +68,4 @@ + converter = new showdown.Converter({ghCompatibleHeaderId: true}); +- } else if (testsuite[i].name === 'ghMentions') { +- converter = new showdown.Converter({ghMentions: true}); ++ //} else if (testsuite[i].name === 'ghMentions') { ++ // converter = new showdown.Converter({ghMentions: true}); + } else if (testsuite[i].name === 'disable-email-encoding') { +@@ -185,17 +184,2 @@ + it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter)); +- } +- }); +- +- /** test emojis support **/ +- describe('emojis support', function () { +- var converter, +- suite = emojisSuite; +- for (var i = 0; i < suite.length; ++i) { +- if (suite[i].name === 'simplifiedautolinks') { +- converter = new showdown.Converter({emoji: true, simplifiedAutoLink: true}); +- } else { +- converter = new showdown.Converter({emoji: true}); +- } +- +- it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter)); + } diff --git a/srv/test.md b/srv/test.md new file mode 100644 index 00000000..3611220a --- /dev/null +++ b/srv/test.md @@ -0,0 +1,155 @@ +*fails marked/showdown/tui/simplemde (just italics), **OK: markdown-it/simplemde:*** +testing just google.com and underscored _google.com_ also with _google.com,_ trailing comma and _google.com_, comma after + +*fails tui (just italics), **OK: marked/showdown/markdown-it/simplemde:*** +testing just https://google.com and underscored _https://google.com_ links like that + +*fails marked (no markup) and showdown/tui/simplemde (no links at all), **OK: markdown-it:*** +let's try bracketed and __ underscored bracketed + +*fails marked (literal underscore), **OK: showdown/markdown-it/simplemde:*** +let's try bracketed and __ underscored bracketed + +*fails none:* +and then [google](google.com) verbose and _[google](google.com)_ underscored + +*fails none:* +and then [google](https://google.com/) verbose and _[google](https://google.com/)_ underscored + +*all behave similarly (only verbose ones):* +and then or maybe <./local> fsgfds fsgfds +and then [local] or maybe [./local] fsgfds [/absolute] fsgfds +and then (local) or maybe (./local) fsgfds (/absolute) fsgfds +and then [](local) or maybe [](./local) fsgfds [](/absolute) fsgfds +and then [.](local) or maybe [.](./local) fsgfds [.](/absolute) fsgfds +and then [asdf](local) or maybe [asdf](./local) fsgfds [asdf](/absolute) fsgfds + +*`ng/OK/OK/OK markdown-it` +`ng/OK/ng/OK marked` +`ng/OK/OK/OK showdown` +`OK/OK/OK/OK simplemde`* +[with spaces](/with spaces) plain, [with spaces](/with%20spaces) %20, [with spaces]() brackets, [with spaces](/with%20spaces) %20 + +*this fails marked, **OK: markdown-it, simplemde:*** + +* testing a list with: + `some code after a newline` + +* testing a list with: + just a newline + +and here is really just +a newline toplevel + +*this fails showdown/hypermd, **OK: marked/markdown-it/simplemde:*** + +* testing a list with + + code here + and a newline + this should have two leading spaces + + * second list level + + more code here + and a newline + this should have two leading spaces + +. + +* testing a list with + + code here + and a newline + this should have two leading spaces + + * second list level + + more code here + and a newline + this should have two leading spaces + +*this fails stackedit, **OK: showdown/marked/markdown-it/simplemde:*** + +||| +|--|--| +| a table | with no header | +| second row | foo bar | + +*this fails showdown/stackedit, **OK: marked/markdown-it/simplemde:*** + +||| +|--|--:| +| a table | on the right | +| second row | foo bar | + +* list entry +* [x] yes +* [ ] no +* another entry + +# s1 +## ep1 +## ep2 +# s2 +## ep1 +## ep2 +# s3 +## ep1 +## ep2 + + +####################################################################### + + + +marked: + works in last ff/chrome for xp + bug: config{breaks:true} does nothing in 1.0 + use whitespace, no tabs + +showdown: + ie6 and ie8 broken, works in last ff/chrome for xp + +markdown-it: + works in last ff/chrome for xp + use whitespace, no tabs + no header anchors + +tui wysiwyg: + requires links to be or [title](location) + + + +links: + http://demo.showdownjs.com/ + https://marked.js.org/demo/ + https://markdown-it.github.io/ + https://simplemde.com/ + + + +all-pass: + +https://github.com/joemccann/dillinger + https://dillinger.io/ + uses markdown-it + +https://github.com/markdown-it/markdown-it + https://markdown-it.github.io/ + + + +almost-all-pass: + +https://simplemde.com/ + +https://github.com/nhn/tui.editor + https://nhn.github.io/tui.editor/latest/tutorial-example01-editor-basic + ie10 and up + + + +unrelated neat stuff: + https://github.com/gnab/remark +