diff --git a/background/index.js b/background/index.js index 54a1e7b..4633693 100644 --- a/background/index.js +++ b/background/index.js @@ -1,39 +1,42 @@ +// Import all required scripts +importScripts( + '/vendor/markdown-it.min.js', + '/vendor/marked.min.js', + '/vendor/remark.min.js', + '/background/md.js', + '/background/compilers/markdown-it.js', + '/background/compilers/marked.js', + '/background/compilers/remark.js', + '/background/storage.js', + '/background/webrequest.js', + '/background/detect.js', + '/background/inject.js', + '/background/messages.js', + '/background/mathjax.js', + '/background/xhr.js', + '/background/icon.js' +); -importScripts('/vendor/markdown-it.min.js') -importScripts('/vendor/marked.min.js') -importScripts('/vendor/remark.min.js') -importScripts('/background/compilers/markdown-it.js') -importScripts('/background/compilers/marked.js') -importScripts('/background/compilers/remark.js') - -importScripts('/background/storage.js') -importScripts('/background/webrequest.js') -importScripts('/background/detect.js') -importScripts('/background/inject.js') -importScripts('/background/messages.js') -importScripts('/background/mathjax.js') -importScripts('/background/xhr.js') -importScripts('/background/icon.js') - -;(() => { - var storage = md.storage(md) - var inject = md.inject({storage}) - var detect = md.detect({storage, inject}) - var webrequest = md.webrequest({storage}) - var mathjax = md.mathjax() - var xhr = md.xhr() - var icon = md.icon({storage}) +// Then initialize everything +(() => { + var storage = md.storage(md); + var inject = md.inject({storage}); + var detect = md.detect({storage, inject}); + var webrequest = md.webrequest({storage}); + var mathjax = md.mathjax(); + var xhr = md.xhr(); + var icon = md.icon({storage}); var compilers = Object.keys(md.compilers) .reduce((all, compiler) => ( all[compiler] = md.compilers[compiler]({storage}), all - ), {}) + ), {}); - var messages = md.messages({storage, compilers, mathjax, xhr, webrequest, icon}) + var messages = md.messages({storage, compilers, mathjax, xhr, webrequest, icon}); - chrome.tabs.onUpdated.addListener(detect.tab) - chrome.runtime.onMessage.addListener(messages) + chrome.tabs.onUpdated.addListener(detect.tab); + chrome.runtime.onMessage.addListener(messages); - icon() -})() + icon(); +})(); diff --git a/background/inject.js b/background/inject.js index da617b3..fb8b7f0 100644 --- a/background/inject.js +++ b/background/inject.js @@ -34,6 +34,7 @@ md.inject = ({storage: {state}}) => (id) => { state.content.syntax && ['/vendor/prism.min.js', '/vendor/prism-autoloader.min.js', '/content/prism.js'], state.content.emoji && '/content/emoji.js', state.content.mermaid && ['/vendor/mermaid.min.js', '/vendor/panzoom.min.js', '/content/mermaid.js'], + state.content.plotly && ['/vendor/plotly.min.js', '/content/plotly.js'], state.content.mathjax && ['/content/mathjax.js', '/vendor/mathjax/tex-mml-chtml.js'], '/content/index.js', '/content/scroll.js', diff --git a/background/md.js b/background/md.js new file mode 100644 index 0000000..a2987ed --- /dev/null +++ b/background/md.js @@ -0,0 +1,5 @@ +// Create the md namespace +var md = {}; + +// Export the namespace +self.md = md; diff --git a/background/storage.js b/background/storage.js index 8f5e403..8de4d73 100644 --- a/background/storage.js +++ b/background/storage.js @@ -53,6 +53,7 @@ md.storage.defaults = (compilers) => { mermaid: false, syntax: true, toc: false, + plotly: false, }, origins: { 'file://': { diff --git a/build/package.sh b/build/package.sh index 5349c33..4936f21 100755 --- a/build/package.sh +++ b/build/package.sh @@ -31,6 +31,7 @@ sh mdc/build.sh sh mermaid/build.sh sh mithril/build.sh sh panzoom/build.sh +sh plotly/build.sh sh prism/build.sh sh remark/build.sh sh themes/build.sh $browser diff --git a/build/plotly/build.sh b/build/plotly/build.sh new file mode 100755 index 0000000..ab65f05 --- /dev/null +++ b/build/plotly/build.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# set current working directory to directory of the shell script +cd "$(dirname "$0")" + +# before +npm ci 2> /dev/null || npm i +mkdir -p tmp + +# copy plotly.min.js +cp node_modules/plotly.js-dist-min/plotly.min.js ../../vendor/ + +# after +rm -rf node_modules/ tmp/ \ No newline at end of file diff --git a/build/plotly/package.json b/build/plotly/package.json new file mode 100644 index 0000000..26492dd --- /dev/null +++ b/build/plotly/package.json @@ -0,0 +1,12 @@ +{ + "name": "markdown-viewer", + "version": "0.0.0", + "description": "Markdown Viewer / Browser Extension", + "private": true, + "dependencies": { + "plotly.js-dist-min": "2.30.1" + }, + "engines": { + "node": ">=18.0.0" + } +} \ No newline at end of file diff --git a/content/index.css b/content/index.css index 63b8dd9..a41fb1a 100644 --- a/content/index.css +++ b/content/index.css @@ -1,4 +1,3 @@ - /*---------------------------------------------------------------------------*/ /*global*/ @@ -359,3 +358,10 @@ img.emojione { /* prevent img stretch */ width: auto; } + +.plotly-container { + width: 100%; + min-height: 400px; + resize: vertical; + overflow: hidden; +} diff --git a/content/index.js b/content/index.js index b7cf50e..12e0de9 100644 --- a/content/index.js +++ b/content/index.js @@ -1,4 +1,3 @@ - var $ = document.querySelector.bind(document) var state = { @@ -110,6 +109,10 @@ var update = (update) => { if (state.content.mathjax) { setTimeout(() => mj.render(), 60) } + + if (state.content.plotly) { + setTimeout(() => plt.render(), 80) + } } var render = (md) => { @@ -129,6 +132,12 @@ var render = (md) => { '' ) } + if (state.content.plotly) { + state.html = state.html.replace( + //gi, + '' + ) + } if (state.content.toc) { state.toc = toc.render(state.html) } diff --git a/content/plotly.js b/content/plotly.js new file mode 100644 index 0000000..9fe8442 --- /dev/null +++ b/content/plotly.js @@ -0,0 +1,48 @@ +var plt = (() => { + var loaded = false + + var walk = (regex, string, result = [], match = regex.exec(string)) => + !match ? result : walk(regex, string, result.concat(match[1])) + + function decodeHtml(html) { + var txt = document.createElement('textarea') + txt.innerHTML = html + return txt.value + } + + return { + render: () => { + var definitions = walk(/
([\s\S]+?)<\/code><\/pre>/gi, state.html)
+      console.log('Definitions found:', definitions)
+      
+      Array.from(document.querySelectorAll('pre code.plotly')).forEach((plot, index) => {
+        try {
+          let rawData = definitions[index]
+          console.log('Before decode:', rawData)
+          
+          // Decode HTML entities
+          rawData = decodeHtml(rawData.trim())
+          console.log('After decode:', rawData)
+          console.log('First character code:', rawData.charCodeAt(0))
+          
+          // Remove any BOM or invisible characters
+          rawData = rawData.replace(/^\uFEFF/, '')
+          rawData = rawData.replace(/^\s+|\s+$/g, '')
+          console.log('After cleanup:', rawData)
+          
+          const plotData = JSON.parse(rawData)
+          const plotDiv = document.createElement('div')
+          plotDiv.className = 'plotly-container'
+          plot.parentElement.replaceWith(plotDiv)
+          Plotly.newPlot(plotDiv, plotData.data, plotData.layout || {})
+        }
+        catch (err) {
+          console.error('Failed to render Plotly plot:', err)
+          console.error('Raw data that caused error:', definitions[index])
+        }
+      })
+      
+      loaded = true
+    }
+  }
+})()
\ No newline at end of file
diff --git a/content/scroll.js b/content/scroll.js
index 8e833a5..4acc815 100644
--- a/content/scroll.js
+++ b/content/scroll.js
@@ -1,4 +1,3 @@
-
 var scroll = (() => {
   function onload (done) {
     Promise.all([
@@ -67,6 +66,21 @@ var scroll = (() => {
             }
           }, 50)
         }
+      }),
+      new Promise((resolve) => {
+        var plots = Array.from(document.querySelectorAll('code.plotly'))
+        if (!state.content.plotly || !plots.length) {
+          resolve()
+        }
+        else {
+          var timeout = setInterval(() => {
+            var containers = Array.from(document.querySelectorAll('.plotly-container'))
+            if (plots.length === containers.length) {
+              clearInterval(timeout)
+              resolve()
+            }
+          }, 50)
+        }
       })
     ]).then(done)
   }
diff --git a/manifest.chrome.json b/manifest.chrome.json
index 7f52256..13c3fa0 100644
--- a/manifest.chrome.json
+++ b/manifest.chrome.json
@@ -2,7 +2,7 @@
   "manifest_version": 3,
   "name"            : "Markdown Viewer",
   "version"         : "5.3",
-  "description"     : "Dark Mode • Themes • Autoreload • Mermaid Diagrams • MathJax • ToC • Syntax Highlighting",
+  "description"     : "Dark Mode • Themes • Autoreload • Mermaid Diagrams • MathJax • Plotly Charts • ToC • Syntax Highlighting",
 
   "homepage_url": "https://chromewebstore.google.com/detail/markdown-viewer/ckkdlimhmcjmikdlpkmbgfkaikojcbjk",
 
diff --git a/manifest.firefox.json b/manifest.firefox.json
index 8a0a721..6715901 100644
--- a/manifest.firefox.json
+++ b/manifest.firefox.json
@@ -26,16 +26,14 @@
     "default_popup": "/popup/index.html"
   },
 
-  "background" : {
+  "background": {
     "scripts": [
       "/vendor/markdown-it.min.js",
       "/vendor/marked.min.js",
       "/vendor/remark.min.js",
-
       "/background/compilers/markdown-it.js",
       "/background/compilers/marked.js",
       "/background/compilers/remark.js",
-
       "/background/storage.js",
       "/background/webrequest.js",
       "/background/detect.js",
@@ -44,7 +42,6 @@
       "/background/mathjax.js",
       "/background/xhr.js",
       "/background/icon.js",
-
       "/background/index.js"
     ]
   },
diff --git a/popup/index.js b/popup/index.js
index fe01bae..32669aa 100644
--- a/popup/index.js
+++ b/popup/index.js
@@ -1,4 +1,3 @@
-
 var Popup = () => {
 
   var state = {
@@ -65,6 +64,7 @@ var Popup = () => {
         mathjax: 'Render MathJax formulas',
         mermaid: 'Mermaid diagrams',
         syntax: 'Syntax highlighting for fenced code blocks',
+        plotly: 'Plotly charts',
       }
     },
     settings: {}
@@ -145,6 +145,10 @@ var Popup = () => {
   }
 
   var init = (res) => {
+    if (!res) {
+      console.log('Failed to get initial state');
+      return;
+    }
     state.compiler = res.compiler
     state.options = res.options
     state.content = res.content
@@ -171,7 +175,10 @@ var Popup = () => {
     tabs: (vnode) => {
       state._tabs = mdc.tabs.MDCTabBar.attachTo(vnode.dom)
       setTimeout(() => {
-        state._tabs.activeTabIndex = state.tabs.indexOf(state.tab)
+        const tabIndex = state.tabs.indexOf(state.tab)
+        if (tabIndex >= 0) {
+          state._tabs.activeTabIndex = tabIndex
+        }
       }, 250)
     }
   }