popup.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. var state = {
  2. compiler: '',
  3. options: {},
  4. content: {},
  5. theme: '',
  6. themes: [],
  7. custom: [],
  8. raw: false,
  9. tab: '',
  10. tabs: ['theme', 'compiler', 'content'],
  11. compilers: [],
  12. description: {
  13. compiler: {},
  14. content: {
  15. autoreload: 'Auto reload on file change',
  16. emoji: 'Convert emoji :shortnames: into EmojiOne images',
  17. scroll: 'Remember scroll position',
  18. toc: 'Generate Table of Contents',
  19. mathjax: 'Render MathJax formulas',
  20. }
  21. }
  22. }
  23. var events = {
  24. tab: (e) => {
  25. state.tab = e.target.hash.replace('#tab-', '')
  26. localStorage.setItem('tab', state.tab)
  27. return false
  28. },
  29. compiler: {
  30. name: (e) => {
  31. state.compiler = state.compilers[e.target.selectedIndex]
  32. chrome.runtime.sendMessage({
  33. message: 'popup.compiler.name',
  34. compiler: state.compiler,
  35. }, () => {
  36. chrome.runtime.sendMessage({message: 'popup'}, init)
  37. })
  38. },
  39. options: (e) => {
  40. state.options[e.target.name] = !state.options[e.target.name]
  41. chrome.runtime.sendMessage({
  42. message: 'popup.compiler.options',
  43. compiler: state.compiler,
  44. options: state.options,
  45. })
  46. }
  47. },
  48. content: (e) => {
  49. state.content[e.target.name] = !state.content[e.target.name]
  50. chrome.runtime.sendMessage({
  51. message: 'popup.content',
  52. content: state.content,
  53. })
  54. },
  55. theme: (e) => {
  56. var name = state.themes[e.target.selectedIndex]
  57. var defaults = chrome.runtime.getManifest().web_accessible_resources
  58. .filter((file) => file.indexOf('/themes/') === 0)
  59. .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1'))
  60. state.theme = defaults.includes(name)
  61. ? {name, url: chrome.runtime.getURL(`/themes/${name}.css`)}
  62. : state.custom.find((theme) => theme.name === name)
  63. chrome.runtime.sendMessage({
  64. message: 'popup.theme',
  65. theme: state.theme
  66. })
  67. },
  68. raw: () => {
  69. state.raw = !state.raw
  70. chrome.runtime.sendMessage({
  71. message: 'popup.raw',
  72. raw: state.raw
  73. })
  74. },
  75. defaults: () => {
  76. chrome.runtime.sendMessage({
  77. message: 'popup.defaults'
  78. }, () => {
  79. chrome.runtime.sendMessage({message: 'popup'}, init)
  80. localStorage.removeItem('tab')
  81. state._tabs.activeTabIndex = 0
  82. })
  83. },
  84. advanced: () => {
  85. chrome.runtime.sendMessage({message: 'popup.advanced'})
  86. }
  87. }
  88. var init = (res) => {
  89. state.compiler = res.compiler
  90. state.options = res.options
  91. state.content = res.content
  92. state.theme = res.theme
  93. state.custom = res.themes
  94. state.themes = res.themes.map(({name}) => name).concat(
  95. chrome.runtime.getManifest().web_accessible_resources
  96. .filter((file) => file.indexOf('/themes/') === 0)
  97. .map((file) => file.replace(/\/themes\/(.*)\.css/, '$1')))
  98. state.raw = res.raw
  99. state.tab = localStorage.getItem('tab') || 'theme'
  100. state.compilers = res.compilers
  101. state.description.compiler = res.description
  102. m.redraw()
  103. }
  104. chrome.runtime.sendMessage({message: 'popup'}, init)
  105. var oncreate = {
  106. ripple: (vnode) => {
  107. mdc.ripple.MDCRipple.attachTo(vnode.dom)
  108. },
  109. tabs: (vnode) => {
  110. state._tabs = mdc.tabs.MDCTabBar.attachTo(vnode.dom)
  111. setTimeout(() => {
  112. state._tabs.activeTabIndex = state.tabs.indexOf(state.tab)
  113. }, 250)
  114. }
  115. }
  116. var onupdate = (tab, key) => (vnode) => {
  117. var value = tab === 'compiler' ? state.options[key]
  118. : tab === 'content' ? state.content[key]
  119. : null
  120. if (vnode.dom.classList.contains('is-checked') !== value) {
  121. vnode.dom.classList.toggle('is-checked')
  122. }
  123. }
  124. m.mount(document.querySelector('body'), {
  125. view: (vnode) =>
  126. m('#popup',
  127. // raw
  128. m('button.mdc-button mdc-button--raised m-button', {
  129. oncreate: oncreate.ripple,
  130. onclick: events.raw
  131. },
  132. (state.raw ? 'Html' : 'Markdown')
  133. ),
  134. // defaults
  135. m('button.mdc-button mdc-button--raised m-button', {
  136. oncreate: oncreate.ripple,
  137. onclick: events.defaults
  138. },
  139. 'Defaults'
  140. ),
  141. // tabs
  142. m('nav.mdc-tab-bar m-tabs', {
  143. oncreate: oncreate.tabs,
  144. onclick: events.tab
  145. },
  146. state.tabs.map((tab) =>
  147. m('a.mdc-tab', {
  148. href: '#tab-' + tab,
  149. },
  150. tab
  151. )),
  152. m('span.mdc-tab-bar__indicator')
  153. ),
  154. m('.m-panels',
  155. // theme
  156. m('.m-panel', {
  157. class: state.tab === 'theme' ? 'is-active' : ''
  158. },
  159. m('select.mdc-elevation--z2 m-select', {
  160. onchange: events.theme
  161. },
  162. state.themes.map((theme) =>
  163. m('option', {selected: state.theme.name === theme}, theme)
  164. )
  165. )
  166. ),
  167. // compiler
  168. m('.m-panel', {
  169. class: state.tab === 'compiler' ? 'is-active' : ''
  170. },
  171. m('select.mdc-elevation--z2 m-select', {
  172. onchange: events.compiler.name
  173. },
  174. state.compilers.map((name) =>
  175. m('option', {selected: state.compiler === name}, name)
  176. )
  177. ),
  178. m('.scroll', {
  179. class: Object.keys(state.options)
  180. .filter((key) => typeof state.options[key] === 'boolean')
  181. .length > 8
  182. ? 'max' : ''
  183. },
  184. Object.keys(state.options)
  185. .filter((key) => typeof state.options[key] === 'boolean')
  186. .map((key) =>
  187. m('label.mdc-switch m-switch', {
  188. onupdate: onupdate('compiler', key),
  189. title: state.description.compiler[key]
  190. },
  191. m('input.mdc-switch__native-control', {
  192. type: 'checkbox',
  193. name: key,
  194. checked: state.options[key],
  195. onchange: events.compiler.options
  196. }),
  197. m('.mdc-switch__background', m('.mdc-switch__knob')),
  198. m('span.mdc-switch-label', key)
  199. )
  200. )
  201. )
  202. ),
  203. // content
  204. m('.m-panel', {
  205. class: state.tab === 'content' ? 'is-active' : ''
  206. },
  207. m('.scroll', Object.keys(state.content).map((key) =>
  208. m('label.mdc-switch m-switch', {
  209. onupdate: onupdate('content', key),
  210. title: state.description.content[key]
  211. },
  212. m('input.mdc-switch__native-control', {
  213. type: 'checkbox',
  214. name: key,
  215. checked: state.content[key],
  216. onchange: events.content
  217. }),
  218. m('.mdc-switch__background', m('.mdc-switch__knob')),
  219. m('span.mdc-switch-label', key)
  220. ))
  221. )
  222. )
  223. ),
  224. // advanced options
  225. m('button.mdc-button mdc-button--raised m-button', {
  226. oncreate: oncreate.ripple,
  227. onclick: events.advanced
  228. },
  229. 'Advanced Options'
  230. )
  231. )
  232. })