Browse Source

Merge branch 'master' of github.com:simov/markdown-viewer into compilers

simov 5 years ago
parent
commit
0238b13648

+ 14 - 4
README.md

@@ -1,7 +1,7 @@
 
 # Markdown Viewer / Browser Extension
 
-**Install: [Chrome]** / **[Firefox]** / **[Opera]** / **Donate: [PayPal]**
+**Install: [Chrome]** / **[Firefox]** / **[Opera]**
 
 
 # Features
@@ -16,6 +16,7 @@
 - Syntax highlighted code blocks ([prism][prism])
 - Table of Contents (TOC)
 - [MathJax][mathjax] support
+- [Mermaid][mermaid] support
 - Emoji support (Icons provided free by [EmojiOne][emojione])
 - Remembers scroll position
 - Markdown Content-Type detection
@@ -83,10 +84,8 @@ Option          | Default | Description
 Option          | Default | Description
 :---            | :---    | :---
 **breaks**      | `false` | Enable GFM [line breaks][gfm]. This option requires the gfm option to be true.
-**commonmark**  | `false` | Toggle CommonMark mode.
 **footnotes**   | `false` | Toggle reference footnotes and inline footnotes.
 **gfm**         | `true`  | Enable GFM [GitHub Flavored Markdown][gfm].
-**pedantic**    | `false` | Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
 **sanitize**    | `false` | Sanitize the output. Ignore any HTML that has been input.
 
 
@@ -97,6 +96,7 @@ Option          | Default | Description
 **autoreload**  | `false` | Auto reload on file change
 **toc**         | `false` | Generate Table of Contents
 **mathjax**     | `false` | Render TeX and LaTeX math blocks
+**mermaid**     | `false` | Render Mermaid diagrams
 **emoji**       | `false` | Convert emoji :shortnames: into EmojiOne images
 **scroll**      | `true`  | Remember scroll position
 
@@ -129,6 +129,14 @@ The following rules apply to your content when `mathjax` is enabled:
 > The MathJax support currently works only on local file URLs and remote origins without strict *Content Security Policy (CSP)* set. For example it won't work for files hosted on the GitHub's `raw.githubusercontent.com` origin. However you can bypass this by enabling the [Disable CSP](#disable-content-security-policy) switch for that origin.
 
 
+## Mermaid
+
+Render Mermaid diagrams wrapped in `mmd` or `mermaid` fenced code blocks:
+
+    ```mmd
+    sequenceDiagram
+    ```
+
 ## Emoji
 
 - Emoji shortnames like: `:sparkles:` will be converted to :sparkles: using [EmojiOne][emojione] images.
@@ -360,6 +368,7 @@ SOFTWARE.
   [chrome]: https://chrome.google.com/webstore/detail/markdown-viewer/ckkdlimhmcjmikdlpkmbgfkaikojcbjk
   [firefox]: https://addons.mozilla.org/en-US/firefox/addon/markdown-viewer-chrome/
   [opera]: #opera
+  [edge]: #edge
   [paypal]: https://www.paypal.me/simeonvelichkov
   [donate]: https://img.shields.io/badge/paypal-donate-blue.svg?style=flat-square (Donate on Paypal)
 
@@ -373,6 +382,7 @@ SOFTWARE.
 
   [emojione]: https://emojione.com
   [mathjax]: https://www.mathjax.org
+  [mermaid]: https://mermaid-js.github.io/mermaid/
   [gfm]: https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown
   [themes0]: https://github.com/sindresorhus/github-markdown-css
   [themes1]: https://github.com/jasonm23/markdown-css-themes
@@ -391,7 +401,7 @@ SOFTWARE.
   [syntax-bitbucket]: https://bitbucket.org/simovelichkov/markdown-syntax
   [syntax-raw-bitbucket]: https://bitbucket.org/simovelichkov/markdown-syntax/raw/master/README.md
 
-  [file-urls]: https://i.imgur.com/BTmlNnX.png
+  [file-urls]: https://i.imgur.com/MpzsT5g.png
   [add-origin]: https://i.imgur.com/GnKmkRG.png
   [all-origins]: https://i.imgur.com/4GH3EuP.png
   [header-detection]: https://i.imgur.com/bdz3Reg.png

+ 4 - 6
background/compilers/remark.js

@@ -2,19 +2,15 @@
 md.compilers.remark = (() => {
   var defaults = {
     breaks: false,
-    commonmark: false,
     footnotes: false,
     gfm: true,
-    pedantic: false,
     sanitize: false,
   }
 
   var description = {
     breaks: 'Exposes newline characters inside paragraphs as breaks',
-    commonmark: 'Toggle CommonMark mode',
     footnotes: 'Toggle reference footnotes and inline footnotes',
     gfm: 'Toggle GFM (GitHub Flavored Markdown)',
-    pedantic: 'Don\'t fix any of the original markdown\nbugs or poor behavior',
     sanitize: 'Disable HTML tag rendering',
   }
 
@@ -23,8 +19,10 @@ md.compilers.remark = (() => {
     description,
     compile: (markdown) =>
       remark.unified()
-        .use(remark.parse, state.remark) // commonmark, gfm, pedantic, footnotes
-        .use(state.remark.breaks ? remark.breaks : undefined) // breaks
+        .use(remark.parse)
+        .use(state.remark.gfm ? remark.gfm : undefined)
+        .use(state.remark.breaks ? remark.breaks : undefined)
+        .use(state.remark.footnotes ? remark.footnotes : undefined)
         .use(remark.stringify)
         .use(remark.slug)
         .use(remark.frontmatter, ['yaml', 'toml'])

+ 2 - 1
background/index.js

@@ -5,6 +5,7 @@
   var detect = md.detect({storage, inject})
   var webrequest = md.webrequest({storage, detect})
   var mathjax = md.mathjax()
+  var xhr = md.xhr()
 
   var compilers = Object.keys(md.compilers)
     .reduce((all, compiler) => (
@@ -12,7 +13,7 @@
       all
     ), {})
 
-  var messages = md.messages({storage, compilers, mathjax, webrequest})
+  var messages = md.messages({storage, compilers, mathjax, xhr, webrequest})
 
   chrome.tabs.onUpdated.addListener(detect.tab)
   chrome.runtime.onMessage.addListener(messages)

+ 1 - 0
background/inject.js

@@ -6,6 +6,7 @@ md.inject = ({storage: {state}}) => (id) => {
       document.querySelector('pre').style.visibility = 'hidden'
       var theme = ${JSON.stringify(state.theme)}
       var raw = ${state.raw}
+      var themes = ${JSON.stringify(state.themes)}
       var content = ${JSON.stringify(state.content)}
       var compiler = '${state.compiler}'
     `,

+ 13 - 32
background/messages.js

@@ -1,8 +1,9 @@
 
-md.messages = ({storage: {defaults, state, set}, compilers, mathjax, webrequest}) => {
+md.messages = ({storage: {defaults, state, set}, compilers, mathjax, xhr, webrequest}) => {
 
   return (req, sender, sendResponse) => {
 
+    // content
     if (req.message === 'markdown') {
       var markdown = req.markdown
 
@@ -19,6 +20,11 @@ md.messages = ({storage: {defaults, state, set}, compilers, mathjax, webrequest}
 
       sendResponse({message: 'html', html})
     }
+    else if (req.message === 'autoreload') {
+      xhr.get(req.location, (err, body) => {
+        sendResponse({err, body})
+      })
+    }
 
     // popup
     else if (req.message === 'popup') {
@@ -39,6 +45,11 @@ md.messages = ({storage: {defaults, state, set}, compilers, mathjax, webrequest}
       notifyContent({message: 'raw', raw: req.raw})
       sendResponse()
     }
+    else if (req.message === 'popup.themes') {
+      set({themes: req.themes})
+      notifyContent({message: 'themes', themes: req.themes})
+      sendResponse()
+    }
     else if (req.message === 'popup.defaults') {
       var options = Object.assign({}, defaults)
       options.origins = state.origins
@@ -83,11 +94,6 @@ md.messages = ({storage: {defaults, state, set}, compilers, mathjax, webrequest}
         match: state.match,
       })
     }
-    else if (req.message === 'options.themes') {
-      sendResponse({
-        themes: state.themes,
-      })
-    }
     else if (req.message === 'options.header') {
       set({header: req.header})
       sendResponse()
@@ -116,32 +122,7 @@ md.messages = ({storage: {defaults, state, set}, compilers, mathjax, webrequest}
       sendResponse()
     }
 
-    // themes
-    else if (req.message === 'themes') {
-      set({themes: req.themes})
-
-      ;(() => {
-        var defaults = chrome.runtime.getManifest().web_accessible_resources
-          .filter((file) => file.indexOf('/themes/') === 0)
-          .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1'))
-        var custom = state.themes.map(({name}) => name)
-        var all = custom.concat(defaults)
-
-        if (!all.includes(state.theme.name)) {
-          var theme = {
-            name: 'github',
-            url: chrome.runtime.getURL('/themes/github.css')
-          }
-          set({theme})
-        }
-        else if (custom.includes(state.theme.name)) {
-          var theme = state.themes.find(({name}) => state.theme.name === name)
-          set({theme})
-        }
-      })()
-
-      sendResponse()
-    }
+    return true
   }
 
   function notifyContent (req, res) {

+ 21 - 5
background/storage.js

@@ -39,20 +39,21 @@ md.storage.defaults = (compilers) => {
   var match = '\\.(?:markdown|mdown|mkdn|md|mkd|mdwn|mdtxt|mdtext|text)(?:#.*|\\?.*)?$'
 
   var defaults = {
-    theme: {
-      name: 'github',
-      url: chrome.runtime.getURL('/themes/github.css')
-    },
+    theme: 'github',
     compiler: 'marked',
     raw: false,
     header: true,
     match,
+    themes: {
+      wide: false,
+    },
     content: {
       emoji: false,
       scroll: true,
       toc: false,
       mathjax: false,
       autoreload: false,
+      mermaid: false,
     },
     origins: {
       'file://': {
@@ -61,7 +62,6 @@ md.storage.defaults = (compilers) => {
         encoding: '',
       }
     },
-    themes: [],
   }
 
   Object.keys(compilers).forEach((compiler) => {
@@ -99,4 +99,20 @@ md.storage.migrations = (state) => {
   if (state.marked.tables !== undefined) {
     delete state.marked.tables
   }
+  // v3.9 -> v4.0
+  if (state.remark.commonmark !== undefined) {
+    delete state.remark.commonmark
+  }
+  if (state.remark.pedantic !== undefined) {
+    delete state.remark.pedantic
+  }
+  if (state.content.mermaid === undefined) {
+    state.content.mermaid = false
+  }
+  if (state.themes === undefined || state.themes instanceof Array) {
+    state.themes = {wide: false}
+  }
+  if (typeof state.theme === 'object') {
+    state.theme = state.theme.name
+  }
 }

+ 25 - 0
background/xhr.js

@@ -0,0 +1,25 @@
+
+md.xhr = () => {
+  var done
+
+  var xhr = new XMLHttpRequest()
+  xhr.onreadystatechange = () => {
+    if (xhr.readyState === 4) {
+      done(null, xhr.responseText)
+    }
+  }
+
+  var get = (url, _done) => {
+    done = _done
+    xhr.open('GET', url + '?preventCache=' + Date.now(), true)
+    try {
+      xhr.send()
+    }
+    catch (err) {
+      console.error(err)
+      done(err)
+    }
+  }
+
+  return {get}
+}

+ 64 - 13
build/prism/prism.json

@@ -8,55 +8,74 @@
     "abnf",
     "actionscript",
     "ada",
+    "agda",
+    "al",
+    "antlr4",
     "apacheconf",
     "apl",
     "applescript",
+    "aql",
     "arduino",
     "arff",
     "asciidoc",
-    "asm6502",
     "aspnet",
+    "asm6502",
     "autohotkey",
     "autoit",
     "bash",
     "basic",
     "batch",
+    "bbcode",
+    "birb",
     "bison",
     "bnf",
     "brainfuck",
+    "brightscript",
     "bro",
+    "bsl",
     "c",
     "csharp",
     "cpp",
     "cil",
-    "coffeescript",
-    "cmake",
     "clojure",
-    "crystal",
+    "cmake",
+    "coffeescript",
+    "concurnas",
     "csp",
+    "crystal",
     "css-extras",
+    "cypher",
     "d",
     "dart",
+    "dax",
+    "dhall",
     "diff",
     "django",
     "dns-zone-file",
     "docker",
     "ebnf",
+    "editorconfig",
     "eiffel",
     "ejs",
     "elixir",
     "elm",
+    "etlua",
     "erb",
     "erlang",
+    "excel-formula",
     "fsharp",
+    "factor",
+    "firestore-security-rules",
     "flow",
     "fortran",
+    "ftl",
+    "gml",
     "gcode",
+    "gdscript",
     "gedcom",
     "gherkin",
     "git",
     "glsl",
-    "gml",
     "go",
     "graphql",
     "groovy",
@@ -65,11 +84,13 @@
     "haskell",
     "haxe",
     "hcl",
+    "hlsl",
     "http",
     "hpkp",
     "hsts",
     "ichigojam",
     "icon",
+    "ignore",
     "inform7",
     "ini",
     "io",
@@ -82,19 +103,22 @@
     "jq",
     "jsdoc",
     "js-extras",
-    "js-templates",
     "json",
-    "jsonp",
     "json5",
+    "jsonp",
+    "jsstacktrace",
+    "js-templates",
     "julia",
     "keyman",
     "kotlin",
     "latex",
+    "latte",
     "less",
     "lilypond",
     "liquid",
     "lisp",
     "livescript",
+    "llvm",
     "lolcode",
     "lua",
     "makefile",
@@ -103,11 +127,15 @@
     "matlab",
     "mel",
     "mizar",
+    "mongodb",
     "monkey",
+    "moonscript",
     "n1ql",
     "n4js",
     "nand2tetris-hdl",
+    "naniscript",
     "nasm",
+    "neon",
     "nginx",
     "nim",
     "nix",
@@ -121,11 +149,13 @@
     "pascal",
     "pascaligo",
     "pcaxis",
+    "peoplecode",
     "perl",
     "php",
     "phpdoc",
     "php-extras",
     "plsql",
+    "powerquery",
     "powershell",
     "processing",
     "prolog",
@@ -134,18 +164,23 @@
     "pug",
     "puppet",
     "pure",
+    "purebasic",
+    "purescript",
     "python",
     "q",
+    "qml",
     "qore",
     "r",
+    "racket",
     "jsx",
     "tsx",
-    "renpy",
     "reason",
     "regex",
+    "renpy",
     "rest",
     "rip",
     "roboconf",
+    "robotframework",
     "ruby",
     "rust",
     "sas",
@@ -154,23 +189,34 @@
     "scala",
     "scheme",
     "shell-session",
+    "smali",
     "smalltalk",
     "smarty",
+    "sml",
+    "solidity",
+    "solution-file",
     "soy",
+    "sparql",
     "splunk-spl",
+    "sqf",
     "sql",
+    "stan",
+    "iecst",
     "stylus",
     "swift",
+    "t4-templating",
+    "t4-cs",
+    "t4-vb",
     "tap",
     "tcl",
+    "tt2",
     "textile",
     "toml",
-    "tt2",
+    "turtle",
     "twig",
     "typescript",
-    "t4-cs",
-    "t4-vb",
-    "t4-templating",
+    "typoscript",
+    "unrealscript",
     "vala",
     "vbnet",
     "velocity",
@@ -178,12 +224,16 @@
     "vhdl",
     "vim",
     "visual-basic",
+    "warpscript",
     "wasm",
     "wiki",
     "xeora",
+    "xml-doc",
     "xojo",
     "xquery",
-    "yaml"
+    "yaml",
+    "yang",
+    "zig"
   ],
   "markdown-viewer": [
     "actionscript",
@@ -237,6 +287,7 @@
     "scala",
     "scheme",
     "scss",
+    "shell-session",
     "smalltalk",
     "sql",
     "swift",

+ 2 - 0
build/remark/index.js

@@ -5,8 +5,10 @@ module.exports = {
   parse: require('remark-parse'),
   stringify: require('remark-stringify'),
   // plugins
+  gfm: require('remark-gfm'),
   breaks: require('remark-breaks'),
   html: require('remark-html'),
   slug: require('remark-slug'),
+  footnotes: require('remark-footnotes'),
   frontmatter: require('remark-frontmatter'),
 }

+ 9 - 0
build/themes/fix-github.js

@@ -0,0 +1,9 @@
+
+var fs = require('fs')
+
+var github = fs.readFileSync('./themes/github.css', 'utf8')
+fs.writeFileSync(
+  'themes/github.css',
+  github.replace(/\.markdown-body :root/g, ':root'),
+  'utf8'
+)

+ 1 - 0
build/themes/themes.sh

@@ -20,3 +20,4 @@ npx csso --input ../markdown-themes/solarized-dark.css --output themes/solarized
 npx csso --input ../markdown-themes/solarized-light.css --output themes/solarized-light.css
 npx csso --input ../markdown-themes/torpedo.css --output themes/torpedo.css
 npx csso --input ../markdown-themes/vostok.css --output themes/vostok.css
+node build/themes/fix-github.js

+ 20 - 4
content/index.css

@@ -14,13 +14,13 @@ pre#_markdown {
 .markdown-body {
   overflow: auto;
 
-  min-width: 882px;
-  max-width: 882px;
+  min-width: 838px;
+  max-width: 838px;
 
   background-color: #fff;
-  border: 1px solid #ddd;
+  border: 1px solid #e1e4e8;
 
-  padding: 48px;
+  padding: 32px;
   margin: 20px auto;
 }
 .markdown-body #_html>*:first-child {
@@ -53,6 +53,22 @@ pre#_markdown {
   .markdown-theme { width: 1145px !important; }
 }
 
+/*100% width*/
+.wide-theme {
+  box-sizing: border-box;
+  width: 100% !important;
+  max-width: 100% !important;
+  min-width: 100% !important;
+  border: none;
+  padding: 20px !important;
+  margin: 0 !important;
+}
+
+/*mermaid text bold effect*/
+svg[id^=mermaid] text {
+  stroke: none !important;
+}
+
 /*toc*/
 body {
   display: flex;

+ 97 - 31
content/index.js

@@ -4,6 +4,7 @@ var $ = document.querySelector.bind(document)
 var state = {
   theme,
   raw,
+  themes,
   content,
   compiler,
   html: '',
@@ -21,6 +22,10 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
     state.theme = req.theme
     m.redraw()
   }
+  else if (req.message === 'themes') {
+    state.themes = req.themes
+    m.redraw()
+  }
   else if (req.message === 'raw') {
     state.raw = req.raw
     m.redraw()
@@ -78,12 +83,14 @@ function mount () {
         if (state.theme) {
           dom.push(m('link#_theme', {
             rel: 'stylesheet', type: 'text/css',
-            href: state.theme.url,
+            href: chrome.runtime.getURL(`/themes/${state.theme}.css`),
           }))
         }
         if (state.html) {
           dom.push(m('#_html', {oncreate: oncreate.html,
-            class: /github(-dark)?/.test(state.theme.name) ? 'markdown-body' : 'markdown-theme'},
+            class: (/github(-dark)?/.test(state.theme) ? 'markdown-body' : 'markdown-theme') +
+            (state.themes.wide ? ' wide-theme' : '')
+          },
             m.trust(state.html)
           ))
           if (state.content.toc && state.toc) {
@@ -95,8 +102,23 @@ function mount () {
           if (state.content.mathjax) {
             dom.push(m('script', {type: 'text/x-mathjax-config'}, mathjax))
             dom.push(m('script', {
-              src: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js'
+              src: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.9/MathJax.js'
+            }))
+          }
+          if (state.content.mermaid) {
+            dom.push(m('script', {
+              src: 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.8.4/mermaid.min.js'
             }))
+            dom.push(m('script', {type: 'text/javascript'}, `
+              ;(() => {
+                var timeout = setInterval(() => {
+                  if (!!(window.mermaid && mermaid.init)) {
+                    clearInterval(timeout)
+                    mermaid.init({}, 'code.language-mmd, code.language-mermaid')
+                  }
+                }, 50)
+              })()
+            `))
           }
         }
       }
@@ -108,22 +130,46 @@ function mount () {
 
 var scroll = (() => {
   function race (done) {
-    var images = Array.from(document.querySelectorAll('img'))
-    if (!images.length) {
-      done()
-    }
-    var loaded = 0
-    images.forEach((img) => {
-      img.addEventListener('load', () => {
-        if (++loaded === images.length) {
-          done()
-        }
-      }, {once: true})
-    })
-    setTimeout(done, 100)
+    Promise.race([
+      Promise.all([
+        new Promise((resolve) => {
+          var diagrams = Array.from(document.querySelectorAll('code.language-mmd, code.language-mermaid'))
+          if (!state.content.mermaid || !diagrams.length) {
+            resolve()
+          }
+          else {
+            var timeout = setInterval(() => {
+              var svg = Array.from(document.querySelectorAll('code.language-mmd svg, code.language-mermaid svg'))
+              if (diagrams.length === svg.length) {
+                clearInterval(timeout)
+                resolve()
+              }
+            }, 50)
+          }
+        }),
+        new Promise((resolve) => {
+          var images = Array.from(document.querySelectorAll('img'))
+          if (!images.length) {
+            resolve()
+          }
+          else {
+            var loaded = 0
+            images.forEach((img) => {
+              img.addEventListener('load', () => {
+                if (++loaded === images.length) {
+                  resolve()
+                }
+              }, {once: true})
+            })
+          }
+        }),
+      ]),
+      new Promise((resolve) => setTimeout(resolve, 500))
+    ])
+    .then(done)
   }
   function debounce (container, done) {
-    var listener = /body/i.test(container.nodeName) ? window : container
+    var listener = /html/i.test(container.nodeName) ? window : container
     var timeout = null
     listener.addEventListener('scroll', () => {
       clearTimeout(timeout)
@@ -154,10 +200,10 @@ var scroll = (() => {
         if (!loaded) {
           loaded = true
           if (state.content.scroll) {
-            listen($('body'), 'md-')
+            listen($('html'), 'md-')
           }
           else if (location.hash && $(location.hash)) {
-            $('body').scrollTop = $(location.hash).offsetTop
+            $('html').scrollTop = $(location.hash).offsetTop
           }
         }
       })
@@ -213,26 +259,46 @@ if (state.content.autoreload) {
   ;(() => {
     var initial = ''
 
+    var response = (body) => {
+      if (!initial) {
+        initial = body
+      }
+      else if (initial !== body) {
+        location.reload(true)
+      }
+    }
+
     var xhr = new XMLHttpRequest()
     xhr.onreadystatechange = () => {
       if (xhr.readyState === 4) {
-        if (!initial) {
-          initial = xhr.responseText
-        }
-        else if (initial !== xhr.responseText) {
-          location.reload(true)
-        }
+        response(xhr.responseText)
       }
     }
 
     var get = () => {
-      xhr.open('GET', location.href + '?preventCache=' + Date.now(), true)
-      try {
-        xhr.send()
+      if (location.protocol === 'file:') {
+        chrome.runtime.sendMessage({
+          message: 'autoreload',
+          location: location.href
+        }, (res) => {
+          if (res.err) {
+            console.error(res.err)
+            clearInterval(state.interval)
+          }
+          else {
+            response(res.body)
+          }
+        })
       }
-      catch (err) {
-        console.error(err)
-        clearInterval(state.interval)
+      else {
+        xhr.open('GET', location.href + '?preventCache=' + Date.now(), true)
+        try {
+          xhr.send()
+        }
+        catch (err) {
+          console.error(err)
+          clearInterval(state.interval)
+        }
       }
     }
 

+ 2 - 1
manifest.firefox.json

@@ -1,7 +1,7 @@
 {
   "manifest_version": 2,
   "name"            : "Markdown Viewer",
-  "version"         : "3.8",
+  "version"         : "3.9",
   "description"     : "Markdown Viewer",
 
   "browser_action": {
@@ -32,6 +32,7 @@
       "/background/inject.js",
       "/background/messages.js",
       "/background/mathjax.js",
+      "/background/xhr.js",
 
       "/background/index.js"
     ]

+ 2 - 1
manifest.json

@@ -1,7 +1,7 @@
 {
   "manifest_version": 2,
   "name"            : "Markdown Viewer",
-  "version"         : "3.8",
+  "version"         : "3.9",
   "description"     : "Markdown Viewer",
 
   "browser_action": {
@@ -41,6 +41,7 @@
       "/background/inject.js",
       "/background/messages.js",
       "/background/mathjax.js",
+      "/background/xhr.js",
 
       "/background/index.js"
     ],

+ 2 - 1
manifest.test.json

@@ -1,7 +1,7 @@
 {
   "manifest_version": 2,
   "name"            : "Markdown Viewer",
-  "version"         : "3.8",
+  "version"         : "3.9",
   "description"     : "Markdown Viewer",
 
   "browser_action": {
@@ -29,6 +29,7 @@
       "/background/inject.js",
       "/background/messages.js",
       "/background/mathjax.js",
+      "/background/xhr.js",
 
       "/background/index.js"
     ],

+ 0 - 34
options/index.css

@@ -237,31 +237,6 @@ footer .icon-hidden {
 }
 
 
-/*add theme*/
-.m-add-theme:after { content: ''; display: block; clear: both; }
-.m-add-theme h4 {
-  float: left;
-  padding: 0 80px 0 0;
-}
-.m-add-theme .m-textfield {
-  height: auto !important;
-  padding: 0 !important;
-  margin: 0 10px !important;
-}
-.m-add-theme .m-textfield input {
-  padding-top: 3px;
-}
-.m-add-theme .m-name {
-  width: 270px;
-}
-.m-add-theme .m-url {
-  width: 500px;
-}
-.m-add-theme button {
-  float: right;
-}
-
-
 /*global*/
 .m-global {
   margin: 20px 0 0 0;
@@ -400,15 +375,6 @@ footer .icon-hidden {
   margin: 6px 0 0 25px;
 }
 
-/*list options - themes*/
-
-.m-list .m-content .m-option.m-theme .m-control {
-  width: 80%;
-  margin: 0 0 0 15px;
-}
-.m-list .m-content .m-option.m-theme .m-textfield {
-  width: 80%;
-}
 
 /*list footer*/
 

+ 0 - 1
options/index.html

@@ -30,6 +30,5 @@
   </footer>
 </body>
 <script src="/options/origins.js" type="text/javascript" charset="utf-8"></script>
-<script src="/options/themes.js" type="text/javascript" charset="utf-8"></script>
 <script src="/options/index.js" type="text/javascript" charset="utf-8"></script>
 </html>

+ 0 - 3
options/index.js

@@ -1,13 +1,10 @@
 
 var origins = Origins()
-var themes = Themes()
 
 m.mount(document.querySelector('main'), {
   view: () => [
     // allowed origins
     origins.render(),
-    // custom themes
-    // themes.render(),
   ]
 })
 

+ 0 - 207
options/themes.js

@@ -1,207 +0,0 @@
-
-var Themes = () => {
-  var defaults = {
-    // storage
-    themes: [],
-    // UI
-    timeout: null,
-    theme: {},
-    // static
-  }
-
-  var state = Object.assign({}, defaults)
-
-  chrome.runtime.sendMessage({message: 'options.themes'}, (res) => {
-    Object.assign(state, res)
-    m.redraw()
-  })
-
-  var events = {
-    name: (e) => {
-      state.theme.name = e.target.value
-    },
-
-    url: (e) => {
-      state.theme.url = e.target.value
-    },
-
-    add: () => {
-      if (!state.theme.name || !state.theme.url) {
-        return
-      }
-      var all = chrome.runtime.getManifest().web_accessible_resources
-        .filter((file) => file.indexOf('/themes/') === 0)
-        .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1'))
-        .concat(state.themes.map(({name}) => name))
-      if (all.includes(state.theme.name)) {
-        return
-      }
-      state.themes.push({
-        name: state.theme.name,
-        url: state.theme.url,
-      })
-      chrome.runtime.sendMessage({
-        message: 'themes',
-        themes: state.themes.map(({name, url}) => ({name, url}))
-      })
-      state.theme.name = ''
-      state.theme.url = ''
-      m.redraw()
-    },
-
-    update: {
-      name: (theme) => (e) => {
-        theme.name = e.target.value
-        clearTimeout(state.timeout)
-        state.timeout = setTimeout(() => {
-          chrome.runtime.sendMessage({
-            message: 'themes',
-            themes: state.themes.map(({name, url}) => ({name, url}))
-          })
-          m.redraw()
-        }, 750)
-      },
-
-      url: (theme) => (e) => {
-        theme.url = e.target.value
-        clearTimeout(state.timeout)
-        state.timeout = setTimeout(() => {
-          chrome.runtime.sendMessage({
-            message: 'themes',
-            themes: state.themes.map(({name, url}) => ({name, url}))
-          })
-        }, 750)
-      }
-    },
-
-    remove: (theme) => () => {
-      var index = state.themes.findIndex(({name}) => name === theme.name)
-      state.themes.splice(index, 1)
-      chrome.runtime.sendMessage({
-        message: 'themes',
-        themes: state.themes.map(({name, url}) => ({name, url}))
-      })
-      m.redraw()
-    }
-  }
-
-  var oncreate = {
-    ripple: (vnode) => {
-      mdc.ripple.MDCRipple.attachTo(vnode.dom)
-    },
-    textfield: (vnode) => {
-      mdc.textfield.MDCTextField.attachTo(vnode.dom)
-    }
-  }
-
-  var onupdate = {
-    cache: (theme) => (vnode) => {
-      if (vnode.dom.classList.contains('is-checked') !== state.themes[theme.name].cache) {
-        vnode.dom.classList.toggle('is-checked')
-      }
-    }
-  }
-
-  var render = () =>
-    m('.bs-callout m-themes',
-
-      // add theme
-      m('.m-add-theme',
-        m('h4.mdc-typography--headline5', 'Custom Themes'),
-        // name
-        m('.mdc-text-field m-textfield m-name', {
-          oncreate: oncreate.textfield,
-          },
-          m('input.mdc-text-field__input', {
-            type: 'text',
-            value: state.theme.name,
-            onchange: events.name,
-            placeholder: 'Name'
-          }),
-          m('.mdc-line-ripple')
-        ),
-        // url
-        m('.mdc-text-field m-textfield m-url', {
-          oncreate: oncreate.textfield,
-          },
-          m('input.mdc-text-field__input', {
-            type: 'text',
-            value: state.theme.url,
-            onchange: events.url,
-            placeholder: 'URL - file:///home.. | http://localhost..'
-          }),
-          m('.mdc-line-ripple')
-        ),
-        m('button.mdc-button mdc-button--raised m-button', {
-          oncreate: oncreate.ripple,
-          onclick: events.add
-          },
-          'Add'
-        )
-      ),
-
-      // themes list
-      (state.themes.length || null) &&
-      m('ul.m-list', state.themes.map((theme) =>
-        m('li.mdc-elevation--z2', {
-          class: theme.expanded ? 'm-expanded' : null,
-          },
-          m('.m-summary', {
-            onclick: (e) => theme.expanded = !theme.expanded
-            },
-            m('.m-title', theme.name),
-            m('i.material-icons', {
-              class: theme.expanded ? 'icon-arrow-up' : 'icon-arrow-down'
-            })
-          ),
-          m('.m-content',
-            // name
-            m('.m-option m-theme',
-              m('.m-name', m('span', 'Name')),
-              m('.m-control',
-                m('.mdc-text-field m-textfield', {
-                  oncreate: oncreate.textfield
-                  },
-                  m('input.mdc-text-field__input', {
-                    type: 'text',
-                    onkeyup: events.update.name(theme),
-                    value: theme.name,
-                  }),
-                  m('.mdc-line-ripple')
-                )
-              )
-            ),
-            // url
-            m('.m-option m-theme',
-              m('.m-name', m('span', 'URL')),
-              m('.m-control',
-                m('.mdc-text-field m-textfield', {
-                  oncreate: oncreate.textfield
-                  },
-                  m('input.mdc-text-field__input', {
-                    type: 'text',
-                    onkeyup: events.update.url(theme),
-                    value: theme.url,
-                  }),
-                  m('.mdc-line-ripple')
-                )
-              )
-            ),
-            // update/remove
-            m('.m-footer',
-              m('span',
-                m('button.mdc-button mdc-button--raised m-button', {
-                  oncreate: oncreate.ripple,
-                  onclick: events.remove(theme)
-                  },
-                  'Remove'
-                )
-              )
-            )
-          )
-        )
-      ))
-    )
-
-  return {state, render}
-}

+ 18 - 16
package.json

@@ -17,29 +17,31 @@
     "@material/switch": "^0.36.1",
     "@material/tabs": "^0.37.1",
     "@material/textfield": "^0.37.1",
-    "marked": "^0.8.0",
+    "marked": "^1.2.7",
     "mithril": "^1.1.6",
-    "prismjs": "^1.17.1",
-    "remark": "^11.0.2",
-    "remark-breaks": "^1.0.3",
-    "remark-frontmatter": "^1.3.2",
-    "remark-html": "^10.0.0",
-    "remark-slug": "^5.1.2"
+    "prismjs": "^1.22.0",
+    "remark": "^13.0.0",
+    "remark-breaks": "^2.0.1",
+    "remark-footnotes": "^3.0.0",
+    "remark-frontmatter": "^3.0.0",
+    "remark-gfm": "^1.0.0",
+    "remark-html": "^13.0.1",
+    "remark-slug": "^6.0.0"
   },
   "devDependencies": {
     "babel-cli": "^6.26.0",
     "babel-preset-env": "^1.7.0",
-    "browserify": "^16.2.3",
+    "browserify": "^17.0.0",
     "csso-cli": "^2.0.2",
     "iconv-lite": "^0.4.24",
-    "mocha": "^5.2.0",
-    "node-sass": "^4.11.0",
-    "puppeteer": "1.5.0",
-    "rollup": "^1.1.0",
-    "rollup-plugin-commonjs": "^9.2.0",
-    "rollup-plugin-node-resolve": "^4.0.0",
-    "terser": "^3.14.1",
-    "uglify-js": "^3.6.0"
+    "mocha": "^7.2.0",
+    "node-sass": "^5.0.0",
+    "puppeteer": "5.4.1",
+    "rollup": "^2.35.1",
+    "rollup-plugin-commonjs": "^10.1.0",
+    "rollup-plugin-node-resolve": "^5.2.0",
+    "terser": "^5.5.1",
+    "uglify-js": "^3.12.2"
   },
   "bin": {},
   "main": "",

+ 36 - 19
popup/index.js

@@ -4,13 +4,16 @@ var state = {
   options: {},
   content: {},
   theme: '',
-  themes: [],
-  custom: [],
+  themes: {},
+  _themes: [],
   raw: false,
   tab: '',
   tabs: ['theme', 'compiler', 'content'],
   compilers: [],
   description: {
+    themes: {
+      wide: '100% width',
+    },
     compiler: {},
     content: {
       autoreload: 'Auto reload on file change',
@@ -18,6 +21,7 @@ var state = {
       scroll: 'Remember scroll position',
       toc: 'Generate Table of Contents',
       mathjax: 'Render MathJax formulas',
+      mermaid: 'Mermaid diagrams',
     }
   }
 }
@@ -57,17 +61,16 @@ var events = {
     })
   },
 
-  theme: (e) => {
-    var name = state.themes[e.target.selectedIndex]
-
-    var defaults = chrome.runtime.getManifest().web_accessible_resources
-      .filter((file) => file.indexOf('/themes/') === 0)
-      .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1'))
-
-    state.theme = defaults.includes(name)
-      ? {name, url: chrome.runtime.getURL(`/themes/${name}.css`)}
-      : state.custom.find((theme) => theme.name === name)
+  themes: (e) => {
+    state.themes[e.target.name] = !state.themes[e.target.name]
+    chrome.runtime.sendMessage({
+      message: 'popup.themes',
+      themes: state.themes,
+    })
+  },
 
+  theme: (e) => {
+    state.theme = state._themes[e.target.selectedIndex]
     chrome.runtime.sendMessage({
       message: 'popup.theme',
       theme: state.theme
@@ -102,12 +105,11 @@ var init = (res) => {
   state.options = res.options
   state.content = res.content
   state.theme = res.theme
+  state.themes = res.themes
 
-  state.custom = res.themes
-  state.themes = res.themes.map(({name}) => name).concat(
-    chrome.runtime.getManifest().web_accessible_resources
-      .filter((file) => file.indexOf('/themes/') === 0)
-      .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1')))
+  state._themes = chrome.runtime.getManifest().web_accessible_resources
+    .filter((file) => file.indexOf('/themes/') === 0)
+    .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1'))
 
   state.raw = res.raw
   state.tab = localStorage.getItem('tab') || 'theme'
@@ -180,9 +182,24 @@ m.mount(document.querySelector('body'), {
           m('select.mdc-elevation--z2 m-select', {
             onchange: events.theme
             },
-            state.themes.map((theme) =>
-              m('option', {selected: state.theme.name === theme}, theme)
+            state._themes.map((theme) =>
+              m('option', {selected: state.theme === theme}, theme)
             )
+          ),
+          m('.scroll', Object.keys(state.themes).map((key) =>
+            m('label.mdc-switch m-switch', {
+              onupdate: onupdate('themes', key),
+              title: state.description.themes[key]
+              },
+              m('input.mdc-switch__native-control', {
+                type: 'checkbox',
+                name: key,
+                checked: state.themes[key],
+                onchange: events.themes
+              }),
+              m('.mdc-switch__background', m('.mdc-switch__knob')),
+              m('span.mdc-switch-label', key)
+            ))
           )
         ),
         // compiler

+ 0 - 429
test/custom-themes.js

@@ -1,429 +0,0 @@
-
-var t = require('assert')
-var defaults = require('./utils/defaults')
-
-
-module.exports = ({popup, advanced, content}) => {
-
-  before(async () => {
-    await defaults({popup, advanced, content})
-  })
-
-  describe('validate input', () => {
-    before(async () => {
-      // advanced
-      await advanced.bringToFront()
-    })
-
-    it('missing name and url', async () => {
-      // both empty
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = ''
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = ''
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      t.equal(
-        await advanced.evaluate(() => {
-          document.querySelector('.m-themes .m-list')
-        }),
-        null,
-        'should not add theme with missing name and url'
-      )
-    })
-
-    it('missing name', async () => {
-      // empty name
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = ''
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      t.equal(
-        await advanced.evaluate(() => {
-          document.querySelector('.m-themes .m-list')
-        }),
-        null,
-        'should not add theme with missing name'
-      )
-    })
-
-    it('missing url', async () => {
-      // empty url
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'hey'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = ''
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      t.equal(
-        await advanced.evaluate(() => {
-          document.querySelector('.m-themes .m-list')
-        }),
-        null,
-        'should not add theme with missing url'
-      )
-    })
-
-    it('duplicate name from the default themes', async () => {
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'github'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      t.equal(
-        await advanced.evaluate(() => {
-          document.querySelector('.m-themes .m-list')
-        }),
-        null,
-        'should not add theme with duplicate name'
-      )
-    })
-  })
-
-  describe('add', () => {
-
-    before(async () => {
-      await advanced.bringToFront()
-    })
-
-    it('add custom theme', async () => {
-      // add theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'a custom theme'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'file:///hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      await advanced.waitFor(300)
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelectorAll('.m-themes .m-list li').length
-        ),
-        1,
-        'the new theme should be added to the list of custom themes'
-      )
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-themes .m-list .m-title').innerText
-        ),
-        'a custom theme',
-        'the custom theme name should be set in the list'
-      )
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-add-theme [placeholder=Name]').innerText
-        ),
-        '',
-        'cleanup name input'
-      )
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-add-theme [placeholder~=URL]').innerText
-        ),
-        '',
-        'cleanup url input'
-      )
-    })
-
-    it('duplicate name from the custom themes', async () => {
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'a custom theme'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelectorAll('.m-themes .m-list').length
-        ),
-        1,
-        'should not add theme with duplicate name'
-      )
-    })
-
-    it('preserve state', async () => {
-      await advanced.reload()
-      await advanced.waitFor(300)
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelectorAll('.m-themes .m-list').length
-        ),
-        1,
-        'the new theme should be added to the list of custom themes'
-      )
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-themes .m-list .m-title').innerText
-        ),
-        'a custom theme',
-        'the custom theme name should be set in the list'
-      )
-    })
-  })
-
-  describe('choose', () => {
-
-    before(async () => {
-      // advanced
-      await advanced.bringToFront()
-      // add theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'a custom theme'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'file:///hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-    })
-
-    it('choose custom theme from the popup', async () => {
-      // popup
-      await popup.bringToFront()
-      await popup.reload()
-      // theme tab
-      await popup.click('.m-tabs a:nth-of-type(1)')
-
-      // select the first theme
-      await popup.select('.m-panel:nth-of-type(1) select', 'a custom theme')
-      t.equal(
-        await popup.evaluate(() =>
-          state.theme.name
-        ),
-        'a custom theme',
-        'custom theme should be selected'
-      )
-
-      await popup.reload()
-      await popup.waitFor(300)
-      t.equal(
-        await popup.evaluate(() =>
-          state.theme.name
-        ),
-        'a custom theme',
-        'custom theme should be selected'
-      )
-    })
-
-    it('theme should be added to the content', async () => {
-      await content.goto('http://localhost:3000/correct-content-type')
-      await content.bringToFront()
-      await content.waitFor(300)
-      t.equal(
-        await content.evaluate(() =>
-          document.querySelector('#_theme').getAttribute('href')
-        ),
-        'file:///hey',
-        'custom theme should be embedded'
-      )
-    })
-  })
-
-  describe('update url', () => {
-
-    before(async () => {
-      // advanced
-      await advanced.bringToFront()
-      // add theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'a custom theme'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'file:///hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-      // popup
-      await popup.bringToFront()
-      await popup.reload()
-      // theme tab
-      await popup.click('.m-tabs a:nth-of-type(1)')
-      // select the first theme
-      await popup.select('.m-panel:nth-of-type(1) select', 'a custom theme')
-    })
-
-    it('update custom theme url', async () => {
-      // advanced
-      await advanced.bringToFront()
-      // expand theme
-      if (!await advanced.evaluate(() => document.querySelector('.m-themes .m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
-        await advanced.click('.m-themes .m-list li:nth-of-type(1)')
-      }
-      // update theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-themes .m-option:nth-of-type(2) input').value = 'file:///hi'
-        document.querySelector('.m-themes .m-option:nth-of-type(2) input').dispatchEvent(new Event('keyup'))
-      })
-      // there is debounce timeout of 750ms in the options UI
-      await advanced.waitFor(800)
-
-      // reload
-      await advanced.reload()
-      await advanced.waitFor(300)
-
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-themes .m-option:nth-of-type(2) input').value
-        ),
-        'file:///hi',
-        'the custom theme URL should be updated'
-      )
-    })
-
-    it('check content', async () => {
-      await content.goto('http://localhost:3000/correct-content-type')
-      await content.bringToFront()
-      await content.waitFor(300)
-      t.equal(
-        await content.evaluate(() =>
-          document.querySelector('#_theme').getAttribute('href')
-        ),
-        'file:///hi',
-        'custom theme url should be updated'
-      )
-    })
-  })
-
-  describe('update name', () => {
-
-    before(async () => {
-      // advanced
-      await advanced.bringToFront()
-      // add theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'a custom theme'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'file:///hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-
-      // popup
-      await popup.bringToFront()
-      await popup.reload()
-      // theme tab
-      await popup.click('.m-tabs a:nth-of-type(1)')
-      // select the first theme
-      await popup.select('.m-panel:nth-of-type(1) select', 'a custom theme')
-
-      // advanced
-      await advanced.bringToFront()
-      // expand theme
-      if (!await advanced.evaluate(() => document.querySelector('.m-themes .m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
-        await advanced.click('.m-themes .m-list li:nth-of-type(1)')
-      }
-    })
-
-    it('update custom theme name', async () => {
-      // update theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-themes .m-option:nth-of-type(1) input').value = 'a very custom theme'
-        document.querySelector('.m-themes .m-option:nth-of-type(1) input').dispatchEvent(new Event('keyup'))
-      })
-      // there is debounce timeout of 750ms in the options UI
-      await advanced.waitFor(800)
-
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-themes .m-list .m-title').innerText
-        ),
-        'a very custom theme',
-        'the custom theme name should be updated in the list title'
-      )
-
-      // reload
-      await advanced.reload()
-      await advanced.waitFor(300)
-
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-themes .m-list .m-title').innerText
-        ),
-        'a very custom theme',
-        'the custom theme name should be updated in the list title'
-      )
-      t.equal(
-        await advanced.evaluate(() =>
-          document.querySelector('.m-themes .m-option:nth-of-type(1) input').value
-        ),
-        'a very custom theme',
-        'the custom theme name should be updated'
-      )
-    })
-
-    it('check popup', async () => {
-      // popup
-      await popup.bringToFront()
-      await popup.reload()
-      // theme tab
-      await popup.click('.m-tabs a:nth-of-type(1)')
-
-      t.equal(
-        await popup.evaluate(() =>
-          document.querySelector('.m-panel:nth-of-type(1) select option').innerText
-        ),
-        'a very custom theme',
-        'the custom theme should be updated'
-      )
-    })
-
-    it('check content', async () => {
-      await content.goto('http://localhost:3000/correct-content-type')
-      await content.bringToFront()
-      await content.waitFor(300)
-      t.ok(
-        /github\.css$/.test(
-          await content.evaluate(() =>
-            document.querySelector('#_theme').getAttribute('href')
-          )
-        ),
-        'defaults to github theme if the custom theme was active'
-      )
-    })
-  })
-
-  describe('remove', () => {
-
-    before(async () => {
-      // advanced
-      await advanced.bringToFront()
-      // add theme
-      await advanced.evaluate(() => {
-        document.querySelector('.m-add-theme [placeholder=Name]').value = 'a custom theme'
-        document.querySelector('.m-add-theme [placeholder=Name]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme [placeholder~=URL]').value = 'file:///hey'
-        document.querySelector('.m-add-theme [placeholder~=URL]').dispatchEvent(new Event('change'))
-        document.querySelector('.m-add-theme button').click()
-      })
-
-      // advanced
-      await advanced.bringToFront()
-      // expand theme
-      if (!await advanced.evaluate(() => document.querySelector('.m-themes .m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
-        await advanced.click('.m-themes .m-list li:nth-of-type(1)')
-      }
-    })
-
-    it('remove custom theme', async () => {
-      // remove
-      await advanced.evaluate(() => {
-        document.querySelector('.m-themes .m-list button').click()
-      })
-      t.equal(
-        await advanced.evaluate(() => {
-          document.querySelector('.m-themes .m-list')
-        }),
-        null,
-        'should not have any themes'
-      )
-    })
-  })
-
-}

+ 2 - 2
test/defaults-popup.js

@@ -55,10 +55,10 @@ module.exports = ({popup}) => {
   it('tab - theme', async () => {
     t.equal(
       await popup.evaluate(() =>
-        state.theme.name
+        state.theme
       ),
       'github',
-      'state.theme.name should equal github'
+      'state.theme should equal github'
     )
     t.strictEqual(
       await popup.evaluate(() =>

+ 1 - 2
test/index.js

@@ -5,7 +5,7 @@ var Server = require('./utils/server')
 
 var options = {
   headless: false,
-  // slowMo: 300,
+  slowMo: 25,
   args: [
     `--disable-extensions-except=${path.resolve(__dirname, '../')}`,
     `--load-extension=${path.resolve(__dirname, '../')}`,
@@ -17,7 +17,6 @@ var tests = [
   'defaults-options',
 
   'popup-options',
-  // 'custom-themes',
 
   'origin-add',
   'origin-match',

+ 1 - 1
test/origin-add.js

@@ -12,7 +12,7 @@ module.exports = ({popup, advanced, content}) => {
   describe('defaults', () => {
     it('localhost:3000', async () => {
       await advanced.bringToFront()
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
       t.equal(
         await advanced.evaluate(() =>
           document.querySelectorAll('.m-origins .m-list li').length

+ 59 - 19
test/origin-csp.js

@@ -8,19 +8,26 @@ module.exports = ({extensions, popup, advanced, content}) => {
   before(async () => {
     await defaults({popup, advanced, content})
 
+    await advanced.bringToFront()
+
     // enable path matching
     await advanced.evaluate(() => {
       document.querySelector('.m-list li:nth-of-type(1) input').value = 'csp-match-path'
       document.querySelector('.m-list li:nth-of-type(1) input').dispatchEvent(new Event('keyup'))
     })
     // there is debounce timeout of 750ms in the options UI
-    await advanced.waitFor(800)
+    await advanced.waitForTimeout(800)
   })
 
   describe('not correct content-type + non matching path', () => {
     before(async () => {
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // enable csp
       if (!await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
@@ -29,7 +36,7 @@ module.exports = ({extensions, popup, advanced, content}) => {
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-no-header-no-path')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('non matching urls should be skipped', async () => {
       t.strictEqual(
@@ -51,6 +58,11 @@ module.exports = ({extensions, popup, advanced, content}) => {
     before(async () => {
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // enable csp
       if (!await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
@@ -59,7 +71,7 @@ module.exports = ({extensions, popup, advanced, content}) => {
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-match-header')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('non matching urls cannot be checked for enabled csp', async () => {
       t.strictEqual(
@@ -77,10 +89,16 @@ module.exports = ({extensions, popup, advanced, content}) => {
     })
   })
 
-  describe('not correct content-type + matching path', () => {
+  // TEST: localStorage is no longer available even with disabled CSP
+  describe.skip('not correct content-type + matching path', () => {
     before(async () => {
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // enable csp
       if (!await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
@@ -89,7 +107,7 @@ module.exports = ({extensions, popup, advanced, content}) => {
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-match-path')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('webRequest.onHeadersReceived event is enabled', async () => {
       t.strictEqual(
@@ -102,21 +120,27 @@ module.exports = ({extensions, popup, advanced, content}) => {
     })
   })
 
-  describe('disable - enable - disable', () => {
+  // TEST: localStorage is no longer available even with disabled CSP
+  describe.skip('disable - enable - disable', () => {
     it('full cycle', async () => {
       // 1. disable
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // disable csp
       if (await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
       }
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
 
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-match-path')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() => {
@@ -138,12 +162,12 @@ module.exports = ({extensions, popup, advanced, content}) => {
       if (!await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
       }
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
 
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-match-path')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
@@ -160,12 +184,12 @@ module.exports = ({extensions, popup, advanced, content}) => {
       if (await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
       }
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
 
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-match-path')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() => {
@@ -186,12 +210,17 @@ module.exports = ({extensions, popup, advanced, content}) => {
     it('enable csp', async () => {
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // enable csp
       if (!await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
       }
       await advanced.reload()
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
 
       // expand origin
       await advanced.click('.m-list li:nth-of-type(1)')
@@ -207,12 +236,17 @@ module.exports = ({extensions, popup, advanced, content}) => {
     it('disable csp', async () => {
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // disable csp
       if (await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
       }
       await advanced.reload()
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
 
       // expand origin
       await advanced.click('.m-list li:nth-of-type(1)')
@@ -231,6 +265,11 @@ module.exports = ({extensions, popup, advanced, content}) => {
     before(async () => {
       await advanced.bringToFront()
 
+      // expand origin
+      if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
+        await advanced.click('.m-list li:nth-of-type(1)')
+      }
+
       // enable csp
       if (!await advanced.evaluate(() => origins.state.origins['http://localhost:3000'].csp)) {
         await advanced.click('.m-list li:nth-of-type(1) .m-switch')
@@ -245,7 +284,7 @@ module.exports = ({extensions, popup, advanced, content}) => {
           document.querySelector('extensions-manager').shadowRoot
             .querySelector('extensions-item-list').shadowRoot
             .querySelectorAll('extensions-item'))[0].shadowRoot
-            .querySelector('#enable-toggle').click()
+            .querySelector('#enableToggle').click()
       })
       // disable the extension
       await extensions.evaluate(() => {
@@ -253,9 +292,9 @@ module.exports = ({extensions, popup, advanced, content}) => {
           document.querySelector('extensions-manager').shadowRoot
             .querySelector('extensions-item-list').shadowRoot
             .querySelectorAll('extensions-item'))[0].shadowRoot
-            .querySelector('#enable-toggle').click()
+            .querySelector('#enableToggle').click()
       })
-      await extensions.waitFor(300)
+      await extensions.waitForTimeout(300)
       // check
       t.equal(
         await extensions.evaluate(() =>
@@ -272,9 +311,10 @@ module.exports = ({extensions, popup, advanced, content}) => {
       // go to page serving content with strict csp
       await content.goto('http://localhost:3000/csp-match-path')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
-    it('the tab is reloaded on event page wakeup', async () => {
+    // TEST: localStorage is no longer available even with disabled CSP
+    it.skip('the tab is reloaded on event page wakeup', async () => {
       t.strictEqual(
         await content.evaluate(() =>
           window.localStorage.toString()

+ 8 - 7
test/origin-encoding.js

@@ -14,7 +14,7 @@ module.exports = ({popup, advanced, content}) => {
       document.querySelector('.m-list li:nth-of-type(1) input').dispatchEvent(new Event('keyup'))
     })
     // there is debounce timeout of 750ms in the options UI
-    await advanced.waitFor(800)
+    await advanced.waitForTimeout(800)
   })
 
   describe('no content-type header set', () => {
@@ -26,7 +26,7 @@ module.exports = ({popup, advanced, content}) => {
       // with no content-type header set
       await content.goto('http://localhost:3000/encoding-no-content-type')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('do not override if content-type header is missing', async () => {
       t.equal(
@@ -51,7 +51,7 @@ module.exports = ({popup, advanced, content}) => {
       // with no charset set in the content-type header
       await content.goto('http://localhost:3000/encoding-no-charset')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('do not override if charset is missing in content-type header', async () => {
       t.equal(
@@ -76,7 +76,7 @@ module.exports = ({popup, advanced, content}) => {
       // with UTF-8 charset set in content-type header
       await content.goto('http://localhost:3000/encoding-wrong-charset')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('when encoding override is disabled', async () => {
       t.equal(
@@ -92,7 +92,8 @@ module.exports = ({popup, advanced, content}) => {
     })
   })
 
-  describe('override charset set in content-type header', () => {
+  // TEST: overriding content-type no longer works
+  describe.skip('override charset set in content-type header', () => {
     before(async () => {
       await advanced.bringToFront()
 
@@ -103,7 +104,7 @@ module.exports = ({popup, advanced, content}) => {
       // with UTF-8 charset set in content-type header
       await content.goto('http://localhost:3000/encoding-wrong-charset')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
     })
     it('use encoding set for the origin', async () => {
       t.equal(
@@ -123,7 +124,7 @@ module.exports = ({popup, advanced, content}) => {
     before(async () => {
       await advanced.bringToFront()
       await advanced.reload()
-      await advanced.waitFor(300)
+      await advanced.waitForTimeout(300)
       // expand origin
       await advanced.click('.m-list li:nth-of-type(1)')
     })

+ 7 - 7
test/origin-match.js

@@ -24,13 +24,13 @@ module.exports = ({popup, advanced, content}) => {
         document.querySelector('.m-list li:nth-of-type(1) input').dispatchEvent(new Event('keyup'))
       })
       // there is debounce timeout of 750ms in the options UI
-      await advanced.waitFor(800)
+      await advanced.waitForTimeout(800)
     })
     it('text/markdown', async () => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/correct-content-type')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -57,13 +57,13 @@ module.exports = ({popup, advanced, content}) => {
         document.querySelector('.m-list li:nth-of-type(1) input').dispatchEvent(new Event('keyup'))
       })
       // there is debounce timeout of 750ms in the options UI
-      await advanced.waitFor(800)
+      await advanced.waitForTimeout(800)
     })
     it('text/markdown', async () => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/correct-content-type')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -77,7 +77,7 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/x-markdown
       await content.goto('http://localhost:3000/correct-content-type-variation')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -104,14 +104,14 @@ module.exports = ({popup, advanced, content}) => {
         document.querySelector('.m-list li:nth-of-type(1) input').dispatchEvent(new Event('keyup'))
       })
       // there is debounce timeout of 750ms in the options UI
-      await advanced.waitFor(800)
+      await advanced.waitForTimeout(800)
     })
 
     it('text/plain', async () => {
       // go to page serving markdown as text/plain
       await content.goto('http://localhost:3000/wrong-content-type')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>

+ 71 - 59
test/popup-options.js

@@ -21,7 +21,7 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/correct-content-type')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -52,10 +52,11 @@ module.exports = ({popup, advanced, content}) => {
 
     it('display raw markdown', async () => {
       // raw button
-      await content.bringToFront()
+      await popup.bringToFront()
       await popup.click('button:nth-of-type(1)')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
 
       t.equal(
         await content.evaluate(() =>
@@ -99,7 +100,7 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/correct-content-type')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
@@ -114,10 +115,11 @@ module.exports = ({popup, advanced, content}) => {
 
     it('set github-dark theme', async () => {
       // select github-dark theme
-      await content.bringToFront()
+      await popup.bringToFront()
       await popup.select('.m-panel:nth-of-type(1) select', 'github-dark')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
 
       t.strictEqual(
         await content.evaluate(() =>
@@ -134,14 +136,14 @@ module.exports = ({popup, advanced, content}) => {
       // reload popup
       await popup.bringToFront()
       await popup.reload()
-      await popup.waitFor(300)
+      await popup.waitForTimeout(300)
 
       t.equal(
         await popup.evaluate(() =>
-          state.theme.name
+          state.theme
         ),
         'github-dark',
-        'state.theme.name should equal github-dark'
+        'state.theme should equal github-dark'
       )
       t.equal(
         await popup.evaluate(() =>
@@ -169,7 +171,7 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/compiler-options-marked')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -182,11 +184,13 @@ module.exports = ({popup, advanced, content}) => {
 
     it('gfm is disabled', async () => {
       // disable gfm
-      await content.bringToFront()
+      await popup.bringToFront()
       // gfm switch
       await popup.click('.m-panel:nth-of-type(2) .m-switch:nth-of-type(2)')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -201,7 +205,7 @@ module.exports = ({popup, advanced, content}) => {
       // reload popup
       await popup.bringToFront()
       await popup.reload()
-      await popup.waitFor(300)
+      await popup.waitForTimeout(300)
 
       t.equal(
         await popup.evaluate(() =>
@@ -243,7 +247,7 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/compiler-options-remark')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -270,10 +274,12 @@ module.exports = ({popup, advanced, content}) => {
 
     it('remark should render gfm task lists by default', async () => {
       // select remark compiler
-      await content.bringToFront()
+      await popup.bringToFront()
       await popup.select('.m-panel:nth-of-type(2) select', 'remark')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -302,14 +308,14 @@ module.exports = ({popup, advanced, content}) => {
       // redraw popup
       await popup.bringToFront()
       await popup.reload()
-      await popup.waitFor(300)
+      await popup.waitForTimeout(300)
 
-      // disable gfm
-      await content.bringToFront()
-      // gfm switch
+      // disable gfm - gfm switch
       await popup.click('.m-panel:nth-of-type(2) .m-switch[title~=GFM]')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
+      await content.waitForTimeout(300)
 
       t.equal(
         await content.evaluate(() =>
@@ -324,7 +330,7 @@ module.exports = ({popup, advanced, content}) => {
       // reload popup
       await popup.bringToFront()
       await popup.reload()
-      await popup.waitFor(300)
+      await popup.waitForTimeout(300)
 
       t.equal(
         await popup.evaluate(() =>
@@ -366,7 +372,7 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/content-options-toc')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
@@ -379,11 +385,13 @@ module.exports = ({popup, advanced, content}) => {
 
     it('enable toc', async () => {
       // enable toc
-      await content.bringToFront()
+      await popup.bringToFront()
       // toc switch
       await popup.click('.m-panel:nth-of-type(3) .m-switch:nth-of-type(3)')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
+      await content.waitForTimeout(300)
 
       t.deepStrictEqual(
         await content.evaluate(() =>
@@ -414,21 +422,21 @@ module.exports = ({popup, advanced, content}) => {
       // go to page serving markdown as text/markdown
       await content.goto('http://localhost:3000/content-options-scroll')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       // scroll down 200px
       await content.evaluate(() =>
-        document.querySelector('body').scrollTop = 200
+        document.querySelector('html').scrollTop = 200
       )
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       // reload page
       await content.reload()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
-          document.querySelector('body').scrollTop,
+          document.querySelector('html').scrollTop,
         ),
         200,
         'scrollTop should be 200px'
@@ -437,15 +445,17 @@ module.exports = ({popup, advanced, content}) => {
 
     it('scroll to top', async () => {
       // disable scroll option
-      await content.bringToFront()
+      await popup.bringToFront()
       // scroll switch
       await popup.click('.m-panel:nth-of-type(3) .m-switch:nth-of-type(2)')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
-          document.querySelector('body').scrollTop,
+          document.querySelector('html').scrollTop,
         ),
         0,
         'scrollTop should be 0px'
@@ -453,17 +463,17 @@ module.exports = ({popup, advanced, content}) => {
 
       // scroll down 200px
       await content.evaluate(() =>
-        document.querySelector('body').scrollTop = 200
+        document.querySelector('html').scrollTop = 200
       )
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       // reload page
       await content.reload()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
-          document.querySelector('body').scrollTop,
+          document.querySelector('html').scrollTop,
         ),
         0,
         'scrollTop should be 0px'
@@ -473,11 +483,11 @@ module.exports = ({popup, advanced, content}) => {
     it('scroll to anchor', async () => {
       // click on header link
       await content.click('h2 a')
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
-          document.querySelector('body').scrollTop + 1
+          document.querySelector('html').scrollTop + 1
         ),
         await content.evaluate(() =>
           document.querySelector('h2').offsetTop
@@ -487,13 +497,13 @@ module.exports = ({popup, advanced, content}) => {
 
       // scroll down 200px
       await content.evaluate(() =>
-        document.querySelector('body').scrollTop += 200
+        document.querySelector('html').scrollTop += 200
       )
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
-          document.querySelector('body').scrollTop + 1
+          document.querySelector('html').scrollTop + 1
         ),
         await content.evaluate(() =>
           document.querySelector('h2').offsetTop + 200
@@ -503,11 +513,11 @@ module.exports = ({popup, advanced, content}) => {
 
       // reload page
       await content.reload()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       t.strictEqual(
         await content.evaluate(() =>
-          document.querySelector('body').scrollTop
+          document.querySelector('html').scrollTop
         ),
         await content.evaluate(() =>
           document.querySelector('h2').offsetTop
@@ -528,19 +538,21 @@ module.exports = ({popup, advanced, content}) => {
 
       await content.goto('about:blank')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       // go to test page
       await content.goto('http://localhost:3000/popup-autoreload')
       await content.bringToFront()
-      await content.waitFor(300)
+      await content.waitForTimeout(300)
 
       // enable autoreload
-      await content.bringToFront()
+      await popup.bringToFront()
       // autoreload switch
       await popup.click('.m-panel:nth-of-type(3) .m-switch:nth-of-type(5)')
-      // content auto reloads
-      await content.waitFor(300)
+      // content auto reloads, but there is no way to have both tabs active
+      await content.bringToFront()
+      await content.reload()
+      await content.waitForTimeout(300)
 
       // TODO: wait for https://github.com/GoogleChrome/puppeteer/pull/2812
       // update autoreload interval
@@ -556,7 +568,7 @@ module.exports = ({popup, advanced, content}) => {
         'first request'
       )
       // the initial interval is 1000
-      await content.waitFor(1300)
+      await content.waitForTimeout(1300)
 
       t.equal(
         await content.evaluate(() =>
@@ -566,7 +578,7 @@ module.exports = ({popup, advanced, content}) => {
         'second request - xhr body is UTF-8 - should not trigger reload'
       )
       // the initial interval is 1000
-      await content.waitFor(1300)
+      await content.waitForTimeout(1300)
 
       t.equal(
         await content.evaluate(() =>
@@ -579,7 +591,7 @@ module.exports = ({popup, advanced, content}) => {
       // popup
       await popup.bringToFront()
       // the initial interval is 1000
-      await content.waitFor(1300)
+      await content.waitForTimeout(1300)
       await content.bringToFront()
       t.equal(
         await content.evaluate(() =>

+ 3 - 3
test/utils/defaults.js

@@ -4,7 +4,7 @@ module.exports = async ({popup, advanced, content}) => {
   await popup.bringToFront()
   // defaults button
   await popup.click('button:nth-of-type(2)')
-  await popup.waitFor(300)
+  await popup.waitForTimeout(300)
 
   // advanced
   await advanced.bringToFront()
@@ -27,7 +27,7 @@ module.exports = async ({popup, advanced, content}) => {
   await advanced.select('.m-select', 'http')
   await advanced.type('[type=text]', 'localhost:3000')
   await advanced.click('button')
-  await advanced.waitFor(300)
+  await advanced.waitForTimeout(300)
 
   // expand origin
   if (!await advanced.evaluate(() => document.querySelector('.m-list li:nth-of-type(1)').classList.contains('m-expanded'))) {
@@ -37,5 +37,5 @@ module.exports = async ({popup, advanced, content}) => {
   // content
   await content.bringToFront()
   await content.goto('about:blank')
-  await content.waitFor(300)
+  await content.waitForTimeout(300)
 }

File diff suppressed because it is too large
+ 0 - 0
themes/github-dark.css


File diff suppressed because it is too large
+ 0 - 0
themes/github.css


File diff suppressed because it is too large
+ 1 - 1
vendor/marked.min.js


+ 1 - 1
vendor/prism.min.css

@@ -1 +1 @@
-code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:rgba(255,255,255,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
+code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:rgba(255,255,255,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}

File diff suppressed because it is too large
+ 0 - 0
vendor/prism.min.js


File diff suppressed because it is too large
+ 0 - 0
vendor/remark.min.js


Some files were not shown because too many files changed in this diff