Selaa lähdekoodia

Add showdown parser

simov 8 vuotta sitten
vanhempi
sitoutus
436d14de85
9 muutettua tiedostoa jossa 273 lisäystä ja 107 poistoa
  1. 20 10
      background/background.js
  2. 0 24
      background/markdown.js
  3. 27 0
      background/marked.js
  4. 26 0
      background/showdown.js
  5. 3 1
      content/content.js
  6. 145 60
      content/popup.js
  7. 47 11
      css/popup.css
  8. 3 1
      manifest.json
  9. 2 0
      vendor/showdown.min.js

+ 20 - 10
background/background.js

@@ -6,7 +6,7 @@ chrome.storage.sync.get((res) => {
   var match = '\\.(?:markdown|mdown|mkdn|md|mkd|mdwn|mdtxt|mdtext|text)(?:#.*)?$'
 
   var defaults = {
-    compiler: md.defaults,
+    compiler: md.showdown.defaults,
     content: {
       toc: false,
       scroll: true
@@ -39,6 +39,10 @@ chrome.storage.sync.get((res) => {
   if (!options.content) {
     options.content = defaults.content
   }
+  // v2.7 -> v2.8
+  if (!options.compiler.name && !options.compiler.options) {
+    options.compiler = md.showdown.defaults
+  }
 
   chrome.storage.sync.set(options)
 
@@ -119,7 +123,8 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
             'document.querySelector("pre").style.visibility = "hidden"',
             'var theme = "' + res.theme + '"',
             'var raw = ' + res.raw,
-            'var content = ' + JSON.stringify(res.content)
+            'var content = ' + JSON.stringify(res.content),
+            'var compiler = "' + res.compiler.name + '"'
           ].join(';'), runAt: 'document_start'})
 
         chrome.tabs.insertCSS(id, {file: 'css/content.css', runAt: 'document_start'})
@@ -139,16 +144,21 @@ chrome.tabs.onUpdated.addListener((id, info, tab) => {
 
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
   if (req.message === 'markdown') {
-    md.compile(req.markdown, sendResponse)
+    md[req.compiler].compile(req.markdown, sendResponse)
   }
   else if (req.message === 'settings') {
-    chrome.storage.sync.get(['compiler', 'content', 'theme', 'raw'], (res) => {
-      delete res.compiler.langPrefix
-      sendResponse(res)
-    })
+    chrome.storage.sync.get(['compiler', 'content', 'theme', 'raw'], sendResponse)
+  }
+  else if (req.message === 'compiler.name') {
+    chrome.storage.sync.set({compiler: md[req.compiler.name].defaults}, sendResponse)
+    sendMessage({message: 'reload'})
+  }
+  else if (req.message === 'compiler.options') {
+    chrome.storage.sync.set({compiler: req.compiler}, sendResponse)
+    sendMessage({message: 'reload'})
   }
-  else if (req.message === 'compiler') {
-    req.compiler.langPrefix = 'language-' // prism
+  else if (req.message === 'compiler.flavor') {
+    req.compiler.options = md.showdown.flavor(req.compiler.flavor)
     chrome.storage.sync.set({compiler: req.compiler}, sendResponse)
     sendMessage({message: 'reload'})
   }
@@ -158,7 +168,7 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
   }
   else if (req.message === 'defaults') {
     chrome.storage.sync.set({
-      compiler: md.defaults,
+      compiler: md.showdown.defaults,
       content: {toc: false, scroll: true},
       theme: 'github', raw: false
     }, sendResponse)

+ 0 - 24
background/markdown.js

@@ -1,24 +0,0 @@
-
-var md = {
-  // marked
-  defaults: {
-    gfm: true,
-    tables: true,
-    breaks: false,
-    pedantic: false,
-    sanitize: false,
-    smartLists: false,
-    langPrefix: 'language-', // prism
-    smartypants: false
-  },
-  compile: (markdown, sendResponse) => {
-    chrome.storage.sync.get((res) => {
-      marked.setOptions(res.compiler)
-
-      marked(markdown, (err, html) => {
-        if (err) throw err
-        sendResponse({message: 'marked', marked: html})
-      })
-    })
-  }
-}

+ 27 - 0
background/marked.js

@@ -0,0 +1,27 @@
+
+var md = {}
+
+md.marked = {
+  defaults: {
+    name: 'marked',
+    options: {
+      gfm: true,
+      tables: true,
+      breaks: false,
+      pedantic: false,
+      sanitize: false,
+      smartLists: false,
+      langPrefix: 'language-', // prism
+      smartypants: false
+    }
+  },
+  compile: (markdown, sendResponse) => {
+    chrome.storage.sync.get('compiler', (res) => {
+      marked.setOptions(res.compiler.options)
+      marked(markdown, (err, html) => {
+        if (err) throw err
+        sendResponse({message: 'html', html})
+      })
+    })
+  }
+}

+ 26 - 0
background/showdown.js

@@ -0,0 +1,26 @@
+
+md.showdown = {
+  defaults: {
+    name: 'showdown',
+    options: null, // see below
+    flavor: 'github'
+  },
+  flavor: (name) => {
+    var options = showdown.getDefaultOptions()
+    var flavor = showdown.getFlavorOptions(name)
+    var result = {}
+    for (var key in options) {
+      result[key] = (flavor[key] !== undefined) ? flavor[key] : options[key]
+    }
+    return result
+  },
+  compile: (markdown, sendResponse) => {
+    chrome.storage.sync.get('compiler', (res) => {
+      var converter = new showdown.Converter(res.compiler.options)
+      var html = converter.makeHtml(markdown)
+      sendResponse({message: 'html', html})
+    })
+  }
+}
+
+md.showdown.defaults.options = md.showdown.flavor('github')

+ 3 - 1
content/content.js

@@ -5,6 +5,7 @@ var state = {
   theme,
   raw,
   content,
+  compiler,
   html: '',
   markdown: '',
   toc: ''
@@ -60,9 +61,10 @@ function mount () {
 
         chrome.runtime.sendMessage({
           message: 'markdown',
+          compiler: state.compiler,
           markdown: state.markdown
         }, (res) => {
-          state.html = res.marked
+          state.html = res.html
           m.redraw()
         })
       })

+ 145 - 60
content/popup.js

@@ -14,12 +14,35 @@ var events = {
     localStorage.setItem('tab', state.tab)
   },
 
-  compiler: (e) => {
-    state.compiler[e.target.name] = !state.compiler[e.target.name]
-    chrome.runtime.sendMessage({
-      message: 'compiler',
-      compiler: state.compiler
-    })
+  compiler: {
+    name: (e) => {
+      state.compiler.name = ui.compilers[e.target.selectedIndex]
+      chrome.runtime.sendMessage({
+        message: 'compiler.name',
+        compiler: state.compiler
+      }, () => {
+        // preserve options order
+        state.compiler.options = []
+        m.redraw()
+        chrome.runtime.sendMessage({message: 'settings'}, init)
+      })
+    },
+    options: (e) => {
+      state.compiler.options[e.target.name] = !state.compiler.options[e.target.name]
+      chrome.runtime.sendMessage({
+        message: 'compiler.options',
+        compiler: state.compiler
+      })
+    },
+    flavor: (e) => {
+      state.compiler.flavor = ui.flavors[e.target.selectedIndex]
+      chrome.runtime.sendMessage({
+        message: 'compiler.flavor',
+        compiler: state.compiler
+      }, () => {
+        chrome.runtime.sendMessage({message: 'settings'}, init)
+      })
+    }
   },
 
   content: (e) => {
@@ -49,7 +72,7 @@ var events = {
   defaults: () => {
     chrome.runtime.sendMessage({
       message: 'defaults'
-    }, (res) => {
+    }, () => {
       chrome.runtime.sendMessage({message: 'settings'}, init)
       localStorage.removeItem('tab')
     })
@@ -60,19 +83,56 @@ var events = {
   }
 }
 
-var description = {
-  compiler: {
-    gfm: 'Enable GFM\n(GitHub Flavored Markdown)',
-    tables: 'Enable GFM tables\n(requires the gfm option to be true)',
-    breaks: 'Enable GFM line breaks\n(requires the gfm option to be true)',
-    pedantic: 'Don\'t fix any of the original markdown\nbugs or poor behavior',
-    sanitize: 'Ignore any HTML\nthat has been input',
-    smartLists: 'Use smarter list behavior\nthan the original markdown',
-    smartypants: 'Use "smart" typographic punctuation\nfor things like quotes and dashes'
-  },
-  content: {
-    scroll: 'Remember scroll position',
-    toc: 'Generate Table of Contents'
+var ui = {
+  tabs: [
+    'theme', 'compiler', 'options', 'content'
+  ],
+  compilers: [
+    'showdown', 'marked'
+  ],
+  flavors: [
+    'github', 'original'
+  ],
+  description: {
+    compiler: {
+      marked: {
+        gfm: 'Enable GFM\n(GitHub Flavored Markdown)',
+        tables: 'Enable GFM tables\n(requires the gfm option to be true)',
+        breaks: 'Enable GFM line breaks\n(requires the gfm option to be true)',
+        pedantic: 'Don\'t fix any of the original markdown\nbugs or poor behavior',
+        sanitize: 'Ignore any HTML\nthat has been input',
+        smartLists: 'Use smarter list behavior\nthan the original markdown',
+        smartypants: 'Use "smart" typographic punctuation\nfor things like quotes and dashes'
+      },
+      showdown: {
+        disableForced4SpacesIndentedSublists: "Disables the requirement of indenting nested sublists by 4 spaces",
+        encodeEmails: "Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",
+        excludeTrailingPunctuationFromURLs: "Excludes trailing punctuation from links generated with autoLinking",
+        ghCodeBlocks: "Turn on/off GFM fenced code blocks support",
+        ghCompatibleHeaderId: "Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)",
+        ghMentions: "Enables github @mentions",
+        ghMentionsLink: "Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",
+        headerLevelStart: "The header blocks level start",
+        literalMidWordUnderscores: "Parse midword underscores as literal underscores",
+        noHeaderId: "Turn on/off generated header id",
+        omitExtraWLInCodeBlocks: "Omit the default extra whiteline added to code blocks",
+        parseImgDimensions: "Turn on/off image dimension parsing",
+        prefixHeaderId: "Specify a prefix to generated header ids",
+        requireSpaceBeforeHeadingText: "Makes adding a space between `#` and the header text mandatory (GFM Style)",
+        simpleLineBreaks: "Parses simple line breaks as <br> (GFM Style)",
+        simplifiedAutoLink: "Turn on/off GFM autolink style",
+        smartIndentationFix: "Tries to smartly fix indentation in es6 strings",
+        smoothLivePreview: "Prevents weird effects in live previews due to incomplete input",
+        strikethrough: "Turn on/off strikethrough support",
+        tables: "Turn on/off tables support",
+        tablesHeaderId: "Add an id to table headers",
+        tasklists: "Turn on/off GFM tasklist support"
+      }
+    },
+    content: {
+      scroll: 'Remember scroll position',
+      toc: 'Generate Table of Contents'
+    }
   }
 }
 
@@ -86,7 +146,7 @@ function init (res) {
     .map((file) => (file.replace(/\/themes\/(.*)\.css/, '$1')))
 
   state.raw = res.raw
-  state.tab = localStorage.getItem('tab') || 'compiler'
+  state.tab = localStorage.getItem('tab') || 'theme'
   m.redraw()
 }
 
@@ -96,7 +156,11 @@ function oncreate (vnode) {
   componentHandler.upgradeElements(vnode.dom)
 }
 var onupdate = (tab, key) => (vnode) => {
-  if (vnode.dom.classList.contains('is-checked') !== state[tab][key]) {
+  var value = tab === 'compiler' ? state[tab].options[key]
+    : tab === 'content' ? state[tab][key]
+    : null
+
+  if (vnode.dom.classList.contains('is-checked') !== value) {
     vnode.dom.classList.toggle('is-checked')
   }
 }
@@ -104,6 +168,7 @@ var onupdate = (tab, key) => (vnode) => {
 m.mount(document.querySelector('body'), {
   view: (vnode) =>
     m('#popup',
+      // defaults
       m('button.mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect',
         {oncreate, onclick: events.raw},
         (state.raw ? 'Html' : 'Markdown')),
@@ -111,58 +176,78 @@ m.mount(document.querySelector('body'), {
         {oncreate, onclick: events.defaults},
         'Defaults'),
 
+      // tabs
       m('.mdl-tabs mdl-js-tabs mdl-js-ripple-effect', {oncreate},
-        m('.mdl-tabs__tab-bar',
-          m('a.mdl-tabs__tab', {href: '#tab-theme', class: 'is-active'}, 'Theme')
+        m('.mdl-tabs__tab-bar', {onclick: events.tab}, ui.tabs.map((tab) =>
+          m('a.mdl-tabs__tab', {
+            href: '#tab-' + tab,
+            class: state.tab === tab ? 'is-active' : null
+          }, tab))
         ),
-        m('.mdl-tabs__panel #tab-theme', {class: 'is-active'},
+
+        // theme
+        m('.mdl-tabs__panel #tab-theme', {class: state.tab === 'theme' ? 'is-active' : null},
           m('select.mdl-shadow--2dp', {onchange: events.theme}, state.themes.map((theme) =>
             m('option', {selected: state.theme === theme}, theme)
           ))
-        )
-      ),
-
-      m('.mdl-tabs mdl-js-tabs mdl-js-ripple-effect', {oncreate},
-        m('.mdl-tabs__tab-bar', {onclick: events.tab},
-          m('a.mdl-tabs__tab', {href: '#tab-compiler',
-            class: state.tab === 'compiler' ? 'is-active' : null}, 'Compiler'),
-          m('a.mdl-tabs__tab', {href: '#tab-content',
-            class: state.tab === 'content' ? 'is-active' : null}, 'Content')
         ),
-        m('.mdl-tabs__panel #tab-compiler',
-          {class: state.tab === 'compiler' ? 'is-active' : null},
-          m('.mdl-grid', Object.keys(state.compiler).map((key) =>
-            m('.mdl-cell',
-              m('label.mdl-switch mdl-js-switch mdl-js-ripple-effect',
-                {oncreate, onupdate: onupdate('compiler', key), title: description.compiler[key]},
-                m('input[type="checkbox"].mdl-switch__input', {
-                  name: key,
-                  checked: state.compiler[key],
-                  onchange: events.compiler
-                }),
-                m('span.mdl-switch__label', key)
-              )
-            )
+
+        // compiler
+        m('.mdl-tabs__panel #tab-compiler', {class: state.tab === 'compiler' ? 'is-active' : null},
+          m('select.mdl-shadow--2dp', {onchange: events.compiler.name}, ui.compilers.map((name) =>
+            m('option', {selected: state.compiler.name === name}, name)
           ))
         ),
+
+        // options
+        m('.mdl-tabs__panel #tab-options',
+          {class: state.tab === 'options' ? 'is-active' : null}, [
+          (state.compiler.name === 'showdown' || null) &&
+          m('select.mdl-shadow--2dp', {onchange: events.compiler.flavor}, ui.flavors.map((name) =>
+            m('option', {selected: state.compiler.flavor === name}, name)
+          )),
+          m('.scroll', {class: state.compiler.name},
+            m('.mdl-grid', Object.keys(state.compiler.options || [])
+              .filter((key) => typeof state.compiler.options[key] === 'boolean')
+              .map((key) =>
+              m('.mdl-cell',
+                m('label.mdl-switch mdl-js-switch mdl-js-ripple-effect',
+                  {oncreate, onupdate: onupdate('compiler', key),
+                  title: ui.description.compiler[state.compiler.name][key]},
+                  m('input[type="checkbox"].mdl-switch__input', {
+                    name: key,
+                    checked: state.compiler.options[key],
+                    onchange: events.compiler.options
+                  }),
+                  m('span.mdl-switch__label', key)
+                )
+              ))
+            )
+          )
+        ]),
+
+        // content
         m('.mdl-tabs__panel #tab-content',
           {class: state.tab === 'content' ? 'is-active' : null},
-          m('.mdl-grid', Object.keys(state.content).map((key) =>
-            m('.mdl-cell',
-              m('label.mdl-switch mdl-js-switch mdl-js-ripple-effect',
-                {oncreate, onupdate: onupdate('content', key), title: description.content[key]},
-                m('input[type="checkbox"].mdl-switch__input', {
-                  name: key,
-                  checked: state.content[key],
-                  onchange: events.content
-                }),
-                m('span.mdl-switch__label', key)
+          m('.scroll',
+            m('.mdl-grid', Object.keys(state.content).map((key) =>
+              m('.mdl-cell',
+                m('label.mdl-switch mdl-js-switch mdl-js-ripple-effect',
+                  {oncreate, onupdate: onupdate('content', key), title: ui.description.content[key]},
+                  m('input[type="checkbox"].mdl-switch__input', {
+                    name: key,
+                    checked: state.content[key],
+                    onchange: events.content
+                  }),
+                  m('span.mdl-switch__label', key)
+                )
               )
-            )
-          ))
+            ))
+          )
         )
       ),
 
+      // advanced options
       m('button.mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect',
         {oncreate, onclick: events.advanced},
         'Advanced Options')

+ 47 - 11
css/popup.css

@@ -1,4 +1,6 @@
 
+html, body { height: auto; min-height: auto; }
+
 #popup { width: 350px; padding: 20px; -webkit-user-select: none; }
 #popup:after { content: ''; display: block; clear: both; }
 
@@ -6,7 +8,6 @@
 #popup .mdl-button,
 select { letter-spacing: 0.2px; }
 
-/*theme*/
 select {
   font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
   font-size: 14px; line-height: 17px;
@@ -15,29 +16,64 @@ select {
   border: none; border-radius: 2px;
   cursor: pointer;
   width: 350px; padding: 9px 12px;
+  margin: 20px 0 0 0;
 }
 
-/*defaults*/
+/*defaults button*/
 button:nth-child(2) { float: right; }
-/*advanced options*/
-button:nth-child(2n+3) { float: right; }
+/*advanced options button*/
+button:nth-child(2n+4) { float: right; margin-top: 20px; }
 
 /*tabs*/
 .mdl-tabs__tab-bar {
   justify-content: flex-start;
   border: 0;
   height: 36px;
-  margin: 10px 0;
-}
-.mdl-tabs:nth-of-type(2) .mdl-tabs__tab-bar {
-  margin-bottom: 2px;
+  margin: 20px 0 0 0;
 }
+/*tab buttons*/
 .mdl-tabs__tab {
   line-height: 36px;
   border-bottom: 1px solid #e0e0e0;
   height: 36px;
-  padding: 0 16px;
+  padding: 0 12px;
+}
+/*switch label*/
+.mdl-switch__label {
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: inline-block;
+  width: 261px;
+}
+/*options scroll*/
+.scroll {
+  margin-top: 15px;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+.scroll.showdown {
+  height: 324px;
+}
+.scroll .mdl-grid { padding: 0 0 8px 0; }
+
+/*custom scrollbars in WebKit*/
+::-webkit-scrollbar {
+  width: 10px;
+}
+::-webkit-scrollbar-track-piece {
+  background: #ececec;
+  -webkit-border-radius: 8px;
+}
+::-webkit-scrollbar-thumb {
+  height: 50px;
+  background: rgba(96,125,139,.5);
+  -webkit-border-radius: 8px;
+  outline: 2px solid #333;
+  outline-offset: -2px;
 }
-.mdl-tabs:nth-of-type(2) .mdl-tabs__panel {
-  min-height: 296px;
+::-webkit-scrollbar-thumb:hover {
+  height: 50px;
+  background-color: #607d8b;
+  -webkit-border-radius: 8px;
 }

+ 3 - 1
manifest.json

@@ -18,7 +18,9 @@
   "background" : {
     "scripts": [
       "/vendor/marked.js",
-      "/background/markdown.js",
+      "/vendor/showdown.min.js",
+      "/background/marked.js",
+      "/background/showdown.js",
       "/background/background.js"
     ],
     "persistent": false

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 2 - 0
vendor/showdown.min.js


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä