From af47cfedf2aadabfe2ffe749f0e20d4d1999fc09 Mon Sep 17 00:00:00 2001 From: Keith Robertson Date: Fri, 9 Jun 2017 00:40:10 -0400 Subject: [PATCH] Initial working version --- .gitignore | 2 + .gitmodules | 6 +++ README.md | 12 ++++- build.bat | 24 ++++++++++ build.sh | 20 ++++++++ ext/208x128.png | Bin 0 -> 1073 bytes ext/32x20.png | Bin 0 -> 223 bytes ext/48x30.png | Bin 0 -> 318 bytes ext/66x40.png | Bin 0 -> 415 bytes ext/LICENSE | 9 ++++ ext/background.js | 20 ++++++++ ext/content.js | 106 ++++++++++++++++++++++++++++++++++++++++++ lib/highlightjs | 1 + lib/markdown-it | 1 + lib/sss/LICENSE.md | 21 +++++++++ lib/sss/sss.css | 89 +++++++++++++++++++++++++++++++++++ lib/sss/sss.print.css | 18 +++++++ manifest.json | 28 +++++++++++ test/LICENSE | 7 +++ test/hello-world.md | 1 + test/sub/hello-sub.md | 1 + test/test-file.md | 70 ++++++++++++++++++++++++++++ 22 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 build.bat create mode 100644 build.sh create mode 100644 ext/208x128.png create mode 100644 ext/32x20.png create mode 100644 ext/48x30.png create mode 100644 ext/66x40.png create mode 100644 ext/LICENSE create mode 100644 ext/background.js create mode 100644 ext/content.js create mode 160000 lib/highlightjs create mode 160000 lib/markdown-it create mode 100644 lib/sss/LICENSE.md create mode 100644 lib/sss/sss.css create mode 100644 lib/sss/sss.print.css create mode 100644 manifest.json create mode 100644 test/LICENSE create mode 100644 test/hello-world.md create mode 100644 test/sub/hello-sub.md create mode 100644 test/test-file.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c0f16d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/staging/ +/web-ext-artifacts/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bd6ba1f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/highlightjs"] + path = lib/highlightjs + url = https://github.com/components/highlightjs +[submodule "lib/markdown-it"] + path = lib/markdown-it + url = https://github.com/markdown-it/markdown-it diff --git a/README.md b/README.md index dd39094..68984e1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # markdown-viewer -Markdown (.md) file viewer WebExtension for your browser. +Markdown (.md) file viewer [WebExtension](https://developer.mozilla.org/en-US/Add-ons/WebExtensions) for your browser. + +## Build +* Run `build.bat` (Windows) or `build.sh` (Linux) +* Uses [web-ext](https://github.com/mozilla/web-ext/) which requires [nodejs](https://nodejs.org/) with npm. + +Firefox won't install the generated .zip unless it's signed by Mozilla; you have to test from the staging folder. + +## Testing +* Navigate to the staging folder and run `web-ext run`. +* Or install `staging/manifest.json` [temporarily in Firefox](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox). diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..953712f --- /dev/null +++ b/build.bat @@ -0,0 +1,24 @@ + +@rmdir /s /q staging 2>nul +@rmdir /s /q web-ext-artifacts 2>nul +@for %%f in ( + LICENSE + manifest.json + ext\*.* + lib\highlightjs\LICENSE + lib\highlightjs\highlight.pack.min.js + lib\highlightjs\styles\default.css + lib\markdown-it\LICENSE + lib\markdown-it\dist\markdown-it.min.js + lib\sss\LICENSE.md + lib\sss\sss.css + lib\sss\sss.print.css +) do @call :copyfile %%f staging\%%f + +@web-ext build -s staging +@goto :EOF + +:copyfile +@mkdir %~dp2 2>nul +@copy %1 %2 >nul +@exit /b diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..d9083ed --- /dev/null +++ b/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh +rm -r staging 2>/dev/null +rm -r web-ext-artifacts 2>/dev/null +for f in LICENSE \ + manifest.json \ + ext/* \ + lib/highlightjs/LICENSE \ + lib/highlightjs/highlight.pack.min.js \ + lib/highlightjs/styles/default.css \ + lib/markdown-it/LICENSE \ + lib/markdown-it/dist/markdown-it.min.js \ + lib/sss/LICENSE.md \ + lib/sss/sss.css \ + lib/sss/sss.print.css +do + mkdir -p `dirname staging/$f` + cp $f staging/$f +done + +web-ext build -s staging diff --git a/ext/208x128.png b/ext/208x128.png new file mode 100644 index 0000000000000000000000000000000000000000..9470f0878f45a9a2cff72ff6fcbfd550daac338f GIT binary patch literal 1073 zcmeAS@N?(olHy`uVBq!ia0vp^7l62dgBeKrei7dYq`CrpLR^9LAOXTm9Qr_~=#~We z1v9Yyd~!_M(vP`aV%n=e&u8XrO#7C6X^sIu&jEYRlioIG4zDN;YvGZ1xiA})M+sipGn)>Uzi2f0< zawwCo6Iht@KcDlaZhVK)8&i({E)z$|DM(0V1CP=1WgNGz$TXKfi%1FB+1GIOw2{K< zEe~UNCs`@1p7tQ}Wr~-A^nuNdnTHg>lot1CyA3PIru9OTBQX$!t1ftLm<(NsJ4xeXssydZNK; z-x*E$1uTbduuC~}v-nFqOVp1#&b9t?qQKz?d`=Q8EVW)qn%V>HzT?QjoM@{3Y^Lr< zkQ|Sb2FF$}LI0&~{y^7+3mkHc@VWQhN34S(NkYbvgMHz}>g$)Y6_`2_3)mE8S|09R zps3UU^39(Ghd#bj;5#APRwVmS;n?rL3C9@|-|#tEbbRFFFY^(2_D-RtS>pYNgySrR zJUmVo8aDTM%2GhyZfQRB_-w*)W{_~AK-D~uj>m>}3`u)z5BBjNYL_d2!M{z6 zL$-dyR)?PL);Ee2j@_2cu@!jymT#LJ$9|tX2dA+b-s9P3)&VqTtK*9u&1<=n_DJTW z3Ou%r1_pVB@r_Fg$7UZ4V*{!xllmWGA>$rB}@w|>6NxNG+0+CLx_l+`Z(`1%xw{maT4ewZCqz9($evVZC;Q_hz$DF^?SO@Dvz u{g1@k-QQ9Trt4aK*d^zBrvLiaKb#L%vFaWBxB4$A<9oXLxvX=?nA962=$@%A9k3&%6dA!dq)yo2U6rfmkX)m zf{5x987)B`Spu*mAM|vc+_oOP=l3}hQ7%sjwD~~{5-qHVF zK;!nF=EID;GGv#Z1gzfUgJ9f19vK+_P2kI#1;^sp0yP00dpaZgFVqF6QpYJh7gFRx Z?+(d*G;XwnIZ*%r002ovPDHLkV1m;%S$zNi literal 0 HcmV?d00001 diff --git a/ext/48x30.png b/ext/48x30.png new file mode 100644 index 0000000000000000000000000000000000000000..c207a654ead59dc30fe4e02ef10d46fe896e9678 GIT binary patch literal 318 zcmV-E0m1%>P)U>R0;7PSQv=!y`&f}6_#P#Hj4qd1^^mft{Vsg zB|ynQtRDymfCG^R2$yh4qyusnyi5QLH3@J$wA14?14AQcpgOdpx=f61raliqLtcC) zh5(v!VYs;_^8s8HLb%EpfR49AyML?0`~b(oF~$R29|CT%7*KLwbqYYYFu%*9fSJoq z>3yBsb1DTuNnhwIkp`gVf!go;Mf(qALR*Tog)H^2<2GmQc`6e0sXb>u03z>QjQSQ? Qg#Z8m07*qoM6N<$f_rp+DgXcg literal 0 HcmV?d00001 diff --git a/ext/66x40.png b/ext/66x40.png new file mode 100644 index 0000000000000000000000000000000000000000..9d73e3edcf7ebccdaa49c6f968d6d17deb80a597 GIT binary patch literal 415 zcmV;Q0bu@#P)3b}YlAg74es<+aQXnz9^~# z#48I`+d{9phX|L9R=&|LiEbi7LHP=z+la7D@wSN+hz~LVnGb0Y;h||@vWFrSBAhb# zOQ%FiMA-MGNP(z*?7{9KhB+&$bk?x$A(-Z&>X&KtBX)}h?QR`GCd$gFi5RR;)Tz(l zb`b0oMeU>xf^n`2)w^mO?I8vz6upTLXnwW!iMIzh0+ z+b=DlDFi!3Sv% { + + const markdownFileExtension = /\.m(arkdown|kdn?|d(o?wn)?)(\?.*)?(#.*)?$/i; + + if (changeInfo.status == 'complete' && markdownFileExtension.test(tab.url)) { + + // Support loading additional scripts on demand by content script. + browser.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.scriptToInject) { + browser.tabs.executeScript(sender.tab.id, { file: message.scriptToInject }, () => { + sendResponse({ success: true }); + }); + return true; + } + return false; + }); + + browser.tabs.executeScript(id, { file: "ext/content.js" }); + } +}); diff --git a/ext/content.js b/ext/content.js new file mode 100644 index 0000000..5911df9 --- /dev/null +++ b/ext/content.js @@ -0,0 +1,106 @@ +function addStylesheet(path, media) { + var style = document.createElement('link'); + style.rel = 'stylesheet'; + if (media) { style.setAttribute('media', media); } + style.type = 'text/css'; + style.href = browser.extension.getURL(path); + document.head.appendChild(style); +} + +function processMarkdown(textContent) { + // Parse the content Markdown => HTML + var md = markdownit({ + html: true, + linkify: true, + // Shameless copypasta https://github.com/markdown-it/markdown-it#syntax-highlighting + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(lang, str).value; + } catch (__) {} + } + + try { + return hljs.highlightAuto(str).value; + } catch (__) {} + + return ''; // use external default escaping + } + }); + + var html = md.render(textContent); + + // Style the page and code highlights. + addStylesheet('lib/sss/sss.css'); + addStylesheet('lib/sss/sss.print.css', 'print'); + addStylesheet('lib/highlightjs/styles/default.css'); + + // This is considered a good practice for mobiles. + var viewport = document.createElement('meta'); + viewport.name = 'viewport'; + viewport.content = 'width=device-width, initial-scale=1'; + document.head.appendChild(viewport); + + // Insert html for the markdown into an element for processing. + var markdownRoot = document.createElement('div'); + markdownRoot.className = "markdownRoot"; + markdownRoot.innerHTML = html; + + // Trample out script elements. + markdownRoot.querySelectorAll('script').forEach(each => { + each.innerText = ''; + each.src = ''; + }); + // Remove hrefs that don't look like URLs. + const likeUrl = /^[-a-z]*:\/\//i; + markdownRoot.querySelectorAll('[href]').forEach(each => { + if (!likeUrl.test(each.href)) { + each.href = ''; + } + }); + // Remove event handlers. (Others?) + var events = ['onclick', 'onload', 'onmouseover', 'onmouseout']; + var eventsJoined = '[' + events.join('],[') + ']'; + markdownRoot.querySelectorAll(eventsJoined).forEach(each => { + events.forEach(attr => { + if (each.getAttribute(attr)) { each.setAttribute(attr, null); } + }); + }); + + // Set the page title. + var title = markdownRoot.querySelector('h1, h2, h3, h4, h5, h6'); // First header + if (title) { + title = title.textContent.trim(); + } else { + title = markdownRoot.textContent.trim().split("\n", 1)[0].trim(); // First line + } + if (title.length > 50) { + title = title.substr(0, 50) + "..."; + } + document.title = title; + + // Finally insert the markdown. + document.body.appendChild(markdownRoot); +} + +function loadScriptThen(path, nextStep) { + browser.runtime.sendMessage({ scriptToInject: path }, (response) => { + if (response.success) { nextStep(); } + }); +} + +// Execute only if .md is unprocessed text. +var body = document.body; +if (body.childNodes.length == 1 && + body.children.length == 1 && + body.children[0].nodeName.toUpperCase() == 'PRE') +{ + var textContent = body.textContent; + body.textContent = ''; + + loadScriptThen('lib/highlightjs/highlight.pack.min.js', () => { + loadScriptThen('lib/markdown-it/dist/markdown-it.min.js', () => { + processMarkdown(textContent); + }) + }); +} diff --git a/lib/highlightjs b/lib/highlightjs new file mode 160000 index 0000000..c81db44 --- /dev/null +++ b/lib/highlightjs @@ -0,0 +1 @@ +Subproject commit c81db44e784c3854bec77808aaf1660d935ba8a1 diff --git a/lib/markdown-it b/lib/markdown-it new file mode 160000 index 0000000..ff643c5 --- /dev/null +++ b/lib/markdown-it @@ -0,0 +1 @@ +Subproject commit ff643c5bf52a510e1b3be806c21913beb9e3c456 diff --git a/lib/sss/LICENSE.md b/lib/sss/LICENSE.md new file mode 100644 index 0000000..853b46d --- /dev/null +++ b/lib/sss/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/sss/sss.css b/lib/sss/sss.css new file mode 100644 index 0000000..695c534 --- /dev/null +++ b/lib/sss/sss.css @@ -0,0 +1,89 @@ +body { + color: #333; + font-family: 'Segoe UI', 'Lucida Grande', Helvetica, sans-serif; + line-height: 1.5; + margin: 2em; + //max-width: 800px; // Ick +} + +h1, h2, h3, h4, h5, h6 { + font-weight: normal; + line-height: 1em; + margin: 20px 0; +} +h1 { + font-size: 2.25em; +} +h2 { + font-size: 1.75em; +} +h3 { + font-size: 1.5em; +} +h4, h5, h6 { + font-size: 1.25em; +} + +a { + color: #08C; + text-decoration: none; +} +a:hover, a:focus { + text-decoration: underline; +} +a:visited { + color: #058; +} + +img { + max-width: 100%; +} + +li + li { + margin-top: 3px; +} +dt { + font-weight: bold; +} + +code { + background: #EEE; + font-family: "Consolas", "Lucida Console", monospace; + padding: 1px 5px; +} +pre { + background: #EEE; + padding: 5px 10px; + white-space: pre-wrap; +} +pre code { + padding: 0; +} + +blockquote { + border-left: 5px solid #EEE; + margin: 0; + padding: 0 10px; +} + +table { + border-collapse: collapse; + width: 100%; +} +table + table { + margin-top: 1em; +} +thead { + background: #EEE; + text-align: left; +} +th, td { + border: 1px solid #EEE; + padding: 5px 10px; +} + +hr { + background: #EEE; + border: 0; + height: 1px; +} diff --git a/lib/sss/sss.print.css b/lib/sss/sss.print.css new file mode 100644 index 0000000..f32b64f --- /dev/null +++ b/lib/sss/sss.print.css @@ -0,0 +1,18 @@ +body { + color: #000; + font: 12pt serif; + margin: 0; + max-width: 100%; +} + +a { + color: #666; + text-decoration: underline; +} +a[href]:after { + content: ' (' attr(href) ')'; +} + +blockquote, ul, dl { + page-break-inside: avoid; +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..9b8c5cd --- /dev/null +++ b/manifest.json @@ -0,0 +1,28 @@ +{ + "manifest_version": 2, + "name": "markdown-viewer", + "version": "1.0", + "description": "Displays markdown documents beautified.", + "homepage_url": "https://github.com/KeithLRobertson/markdown-viewer", + + "permissions": [ + "activeTab", "tabs", "" + ], + + "icons": { + "32": "ext/32x20.png", + "48": "ext/48x30.png", + "66": "ext/66x40.png", + "208": "ext/208x128.png" + }, + + "background": { + "scripts": ["ext/background.js"] + }, + + "web_accessible_resources": [ + "lib/sss/sss.css", + "lib/sss/sss.print.css", + "lib/highlightjs/styles/default.css" + ] +} diff --git a/test/LICENSE b/test/LICENSE new file mode 100644 index 0000000..248e30d --- /dev/null +++ b/test/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2012-2013 Rousseau Thibaut + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/hello-world.md b/test/hello-world.md new file mode 100644 index 0000000..cc0be1e --- /dev/null +++ b/test/hello-world.md @@ -0,0 +1 @@ +# Hello World! diff --git a/test/sub/hello-sub.md b/test/sub/hello-sub.md new file mode 100644 index 0000000..5d21c2b --- /dev/null +++ b/test/sub/hello-sub.md @@ -0,0 +1 @@ +# Hello Sub-World! diff --git a/test/test-file.md b/test/test-file.md new file mode 100644 index 0000000..f5794c4 --- /dev/null +++ b/test/test-file.md @@ -0,0 +1,70 @@ +# Title 1 +## Title 2 +### Title 3 +#### Title 4 +##### Title 5 +###### Title 6 + +Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo +consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse +cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non +proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +Unicode: éè + +**bold** + +*italic* + +~~strike~~ + +multipe_underscores_in_text_without_parsing + +* item 1 +* item 2 +* item 3 + +1. item 1 +2. item 2 +3. item 3 + +| Header | Header | Header | +|--------|--------|--------| +| Cell | Cell | Cell | +| Cell | Cell | Cell | +| Cell | Cell | Cell | + +| Left-Aligned | Center Aligned | Right Aligned | +| :------------ |:---------------:| -----:| +| col 3 is | some wordy text | $1600 | +| col 2 is | centered | $12 | +| zebra stripes | are neat | $1 | + +https://addons.mozilla.org/fr/firefox/addon/markdown-viewer/ + +[link (markdown-viewer on AMO)](https://addons.mozilla.org/fr/firefox/addon/markdown-viewer/) + +[relative link](hello-world.md) + +[relative link to subdir](sub/hello-sub.md) + +```js +// Some Javascript code +function myFunction() { + console.log("Hello World!"); +} +``` + +![Small image](http://lorempixel.com/400/200/) + +![Large image (should be resized)](http://lorempixel.com/1200/200/) + +HTML is allowed + +But scripts (into script tags ot inline) aren't + + + +