├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── README.md
├── app
├── eme.sh
├── eme
│ ├── config.js
│ ├── context-menu.js
│ ├── event.js
│ ├── menu.js
│ ├── shell.js
│ ├── utils.js
│ ├── utils
│ │ ├── electron-config.js
│ │ └── theme.js
│ └── window.js
├── index.html
├── index.js
├── math-typesetting.md
├── package.json
├── resources
│ └── icon.png
├── vendor
│ ├── css
│ │ ├── print.css
│ │ └── spinners.css
│ ├── github-markdown-css
│ │ └── github-markdown.css
│ ├── katex
│ │ ├── fonts
│ │ │ ├── KaTeX_AMS-Regular.eot
│ │ │ ├── KaTeX_AMS-Regular.ttf
│ │ │ ├── KaTeX_AMS-Regular.woff
│ │ │ ├── KaTeX_AMS-Regular.woff2
│ │ │ ├── KaTeX_Caligraphic-Bold.eot
│ │ │ ├── KaTeX_Caligraphic-Bold.ttf
│ │ │ ├── KaTeX_Caligraphic-Bold.woff
│ │ │ ├── KaTeX_Caligraphic-Bold.woff2
│ │ │ ├── KaTeX_Caligraphic-Regular.eot
│ │ │ ├── KaTeX_Caligraphic-Regular.ttf
│ │ │ ├── KaTeX_Caligraphic-Regular.woff
│ │ │ ├── KaTeX_Caligraphic-Regular.woff2
│ │ │ ├── KaTeX_Fraktur-Bold.eot
│ │ │ ├── KaTeX_Fraktur-Bold.ttf
│ │ │ ├── KaTeX_Fraktur-Bold.woff
│ │ │ ├── KaTeX_Fraktur-Bold.woff2
│ │ │ ├── KaTeX_Fraktur-Regular.eot
│ │ │ ├── KaTeX_Fraktur-Regular.ttf
│ │ │ ├── KaTeX_Fraktur-Regular.woff
│ │ │ ├── KaTeX_Fraktur-Regular.woff2
│ │ │ ├── KaTeX_Main-Bold.eot
│ │ │ ├── KaTeX_Main-Bold.ttf
│ │ │ ├── KaTeX_Main-Bold.woff
│ │ │ ├── KaTeX_Main-Bold.woff2
│ │ │ ├── KaTeX_Main-Italic.eot
│ │ │ ├── KaTeX_Main-Italic.ttf
│ │ │ ├── KaTeX_Main-Italic.woff
│ │ │ ├── KaTeX_Main-Italic.woff2
│ │ │ ├── KaTeX_Main-Regular.eot
│ │ │ ├── KaTeX_Main-Regular.ttf
│ │ │ ├── KaTeX_Main-Regular.woff
│ │ │ ├── KaTeX_Main-Regular.woff2
│ │ │ ├── KaTeX_Math-BoldItalic.eot
│ │ │ ├── KaTeX_Math-BoldItalic.ttf
│ │ │ ├── KaTeX_Math-BoldItalic.woff
│ │ │ ├── KaTeX_Math-BoldItalic.woff2
│ │ │ ├── KaTeX_Math-Italic.eot
│ │ │ ├── KaTeX_Math-Italic.ttf
│ │ │ ├── KaTeX_Math-Italic.woff
│ │ │ ├── KaTeX_Math-Italic.woff2
│ │ │ ├── KaTeX_Math-Regular.eot
│ │ │ ├── KaTeX_Math-Regular.ttf
│ │ │ ├── KaTeX_Math-Regular.woff
│ │ │ ├── KaTeX_Math-Regular.woff2
│ │ │ ├── KaTeX_SansSerif-Bold.eot
│ │ │ ├── KaTeX_SansSerif-Bold.ttf
│ │ │ ├── KaTeX_SansSerif-Bold.woff
│ │ │ ├── KaTeX_SansSerif-Bold.woff2
│ │ │ ├── KaTeX_SansSerif-Italic.eot
│ │ │ ├── KaTeX_SansSerif-Italic.ttf
│ │ │ ├── KaTeX_SansSerif-Italic.woff
│ │ │ ├── KaTeX_SansSerif-Italic.woff2
│ │ │ ├── KaTeX_SansSerif-Regular.eot
│ │ │ ├── KaTeX_SansSerif-Regular.ttf
│ │ │ ├── KaTeX_SansSerif-Regular.woff
│ │ │ ├── KaTeX_SansSerif-Regular.woff2
│ │ │ ├── KaTeX_Script-Regular.eot
│ │ │ ├── KaTeX_Script-Regular.ttf
│ │ │ ├── KaTeX_Script-Regular.woff
│ │ │ ├── KaTeX_Script-Regular.woff2
│ │ │ ├── KaTeX_Size1-Regular.eot
│ │ │ ├── KaTeX_Size1-Regular.ttf
│ │ │ ├── KaTeX_Size1-Regular.woff
│ │ │ ├── KaTeX_Size1-Regular.woff2
│ │ │ ├── KaTeX_Size2-Regular.eot
│ │ │ ├── KaTeX_Size2-Regular.ttf
│ │ │ ├── KaTeX_Size2-Regular.woff
│ │ │ ├── KaTeX_Size2-Regular.woff2
│ │ │ ├── KaTeX_Size3-Regular.eot
│ │ │ ├── KaTeX_Size3-Regular.ttf
│ │ │ ├── KaTeX_Size3-Regular.woff
│ │ │ ├── KaTeX_Size3-Regular.woff2
│ │ │ ├── KaTeX_Size4-Regular.eot
│ │ │ ├── KaTeX_Size4-Regular.ttf
│ │ │ ├── KaTeX_Size4-Regular.woff
│ │ │ ├── KaTeX_Size4-Regular.woff2
│ │ │ ├── KaTeX_Typewriter-Regular.eot
│ │ │ ├── KaTeX_Typewriter-Regular.ttf
│ │ │ ├── KaTeX_Typewriter-Regular.woff
│ │ │ └── KaTeX_Typewriter-Regular.woff2
│ │ ├── katex.js
│ │ ├── katex.min.css
│ │ └── src
│ │ │ ├── Lexer.js
│ │ │ ├── Options.js
│ │ │ ├── ParseError.js
│ │ │ ├── Parser.js
│ │ │ ├── Settings.js
│ │ │ ├── Style.js
│ │ │ ├── buildCommon.js
│ │ │ ├── buildHTML.js
│ │ │ ├── buildMathML.js
│ │ │ ├── buildTree.js
│ │ │ ├── delimiter.js
│ │ │ ├── domTree.js
│ │ │ ├── environments.js
│ │ │ ├── fontMetrics.js
│ │ │ ├── fontMetricsData.js
│ │ │ ├── functions.js
│ │ │ ├── mathMLTree.js
│ │ │ ├── parseData.js
│ │ │ ├── parseTree.js
│ │ │ ├── symbols.js
│ │ │ └── utils.js
│ ├── markdown-it-katex
│ │ └── index.js
│ └── mousetrap
│ │ ├── global-bind.js
│ │ └── mousetrap.js
├── welcome-guide.md
└── yarn.lock
├── build
└── icons
│ ├── 512x512.png
│ ├── icon.icns
│ ├── icon.ico
│ ├── markdown_file_association.icns
│ └── markdown_file_association.ico
├── electron-builder.json
├── media
└── preview.png
├── package.json
├── scripts
├── webpack.config.dev.js
├── webpack.config.js
└── webpack.config.prod.js
├── src
├── app.vue
├── components
│ ├── footer.vue
│ ├── header.vue
│ ├── main.vue
│ ├── preference-pane.vue
│ ├── svg-icon.vue
│ ├── tip.vue
│ └── writing-modes.vue
├── css
│ ├── codemirror
│ │ ├── base16-light.css
│ │ ├── editor-dialog.css
│ │ ├── editor-reset.css
│ │ ├── editor-scrollbar.css
│ │ └── tomorrow-night-bright.css
│ ├── highlight
│ │ ├── github.css
│ │ └── tomorrow-night-bright.css
│ ├── normalize.css
│ ├── reset.css
│ └── theme
│ │ ├── dark.css
│ │ └── light.css
├── index.ts
├── svg-shim.d.ts
├── svg
│ ├── align-horizontal-middle.svg
│ ├── arrow-left.svg
│ ├── arrow-right.svg
│ ├── eye.svg
│ ├── pencil.svg
│ └── settings.svg
├── utils
│ ├── common.ts
│ ├── dialog.js
│ ├── dom.js
│ ├── event.js
│ ├── fs-promise.js
│ ├── gist.js
│ ├── handle-error.js
│ ├── key.js
│ ├── make-html.js
│ ├── markdown.js
│ ├── os.js
│ ├── resolve-path.js
│ ├── sanitize.js
│ ├── tab.js
│ ├── tildify.ts
│ ├── tips.js
│ ├── transitions.js
│ └── wordcount.ts
├── views
│ └── about.vue
├── vue-shims.d.ts
└── vuex
│ ├── modules
│ ├── app.js
│ └── editor.js
│ ├── store.d.ts
│ └── store.js
├── tsconfig.json
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | plugins: ["@babel/plugin-syntax-object-rest-spread",
3 | "@babel/plugin-transform-runtime",
4 | "@babel/plugin-transform-spread",
5 | "@babel/plugin-proposal-object-rest-spread"]
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "xo-space/esnext",
3 | "plugins": [
4 | "vue"
5 | ],
6 | "rules": {
7 | "semi": ["error", "never"],
8 | "curly": ["error", "multi-line"],
9 | "no-warning-comments": 0,
10 | "max-lines": 0,
11 | "quote-props": ["error", "as-needed"],
12 | "guard-for-in": 0
13 | },
14 | "env": {
15 | "browser": "true"
16 | },
17 | "globals": {
18 | "Mousetrap": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.vue linguist-language=JavaScript
3 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2020 The EME Authors
2 |
3 | name: Build_Check
4 | on: [push]
5 |
6 | env:
7 | BUILD_TYPE: Debug
8 |
9 | jobs:
10 | build:
11 | name: Build_Check
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v1
15 | - name: Prepare Node.js
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: 10
19 | - name: Install Dependencies
20 | run: |
21 | yarn install
22 | - name: Build App
23 | run: |
24 | yarn build
25 | - name: Build
26 | env:
27 | GH_TOKEN: ${{ secrets.github_token }}
28 | run: |
29 | yarn linux
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | .DS_Store
4 | /app/libvue
5 | /dist
6 | stats.html
7 | stats.json
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 EGOIST 0x142857@gmail.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EME [](https://github.com/egoist/eme/releases) [](https://github.com/egoist/eme/releases) [](https://github.com/egoist/eme/releases/latest) [](https://circleci.com/gh/egoist/eme) [](#donate)
2 |
3 | 
4 |
5 | ## Download
6 |
7 | You can manually download the latest release [here](https://github.com/egoist/eme/releases)
8 |
9 | ## Features
10 |
11 | - It just suits, show editor or preview or both just as you wish.
12 | - Focus mode, writing without distractions.
13 | - Exportable, from Markdown to HTML/PDF... You name it.
14 | - Supporting math typesetting, good for students and professionals.
15 | - Auto-sync files to GitHub Gist after being saved, optional.
16 |
17 | ## WIKI
18 |
19 | - [Key bindings](https://github.com/egoist/eme/wiki/Key-bindings)
20 | - [...more](https://github.com/egoist/eme/wiki)
21 |
22 | ## Contribute
23 |
24 | Pull requests are always welcome! Check out the [these issues](https://github.com/egoist/eme/issues?q=is%3Aissue+is%3Aopen+label%3A%22contribution+welcome%22) to get started fast.
25 |
26 |
27 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
28 | 2. Install the dependencies: `yarn`
29 | 3. Build the code and watch for changes: `yarn watch`
30 | 4. In a new tab, start the application: `yarn start`
31 |
32 | If you want to build the binary for a specified platform, run the command:
33 |
34 | ```bash
35 | $ yarn mac # .dmg
36 | $ yarn win # .exe
37 | $ yarn linux # .deb
38 | ```
39 |
40 | After that, you'll see the binaries in the `./dist` folder!
41 |
42 | ## Donate
43 |
44 | If you are enjoying this app, please consider making a donation to keep it alive, I will try my best to dedicate more time or even full time to work on it. 😉
45 |
46 | - [Donate via Paypal](https://www.paypal.me/egoistian/10)
47 | - [Donate via Wechat](http://ww4.sinaimg.cn/large/a15b4afegw1f72ib6rj67j20u00tvgnj.jpg)
48 | - [Donate via Alipay](http://ww4.sinaimg.cn/large/a15b4afegw1f72ib54hybj20qo0nndh5.jpg)
49 | - [Donate via Bitcoin](http://ww4.sinaimg.cn/large/a15b4afegw1f72icbcu0gj202s02sdfl.jpg) `1NUSDCWti9FBJLiUxaLY1zJnwcSDc5Tfci`
50 |
51 | If you are not available for this, simply spreading the word for us would help too!
52 |
53 | ## License
54 |
55 | MIT © [EGOIST](https://github.com/egoist)
56 |
--------------------------------------------------------------------------------
/app/eme.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ "$(uname)" == 'Darwin' ]; then
4 | OS='Mac'
5 | else
6 | echo "Your platform ($(uname -a)) is not supported."
7 | exit 1
8 | fi
9 |
10 | while getopts ":wtfvh-:" opt; do
11 | case "$opt" in
12 | -)
13 | case "${OPTARG}" in
14 | wait)
15 | WAIT=1
16 | ;;
17 | help|version)
18 | REDIRECT_STDERR=1
19 | EXPECT_OUTPUT=1
20 | ;;
21 | foreground|test)
22 | EXPECT_OUTPUT=1
23 | ;;
24 | esac
25 | ;;
26 | w)
27 | WAIT=1
28 | ;;
29 | h|v)
30 | REDIRECT_STDERR=1
31 | EXPECT_OUTPUT=1
32 | ;;
33 | f|t)
34 | EXPECT_OUTPUT=1
35 | ;;
36 | esac
37 | done
38 |
39 | if [ $REDIRECT_STDERR ]; then
40 | exec 2> /dev/null
41 | fi
42 |
43 | if [ $EXPECT_OUTPUT ]; then
44 | export ELECTRON_ENABLE_LOGGING=1
45 | fi
46 |
47 | if [ $OS == 'Mac' ]; then
48 | EME_APP_NAME="EME.app"
49 | if [ -z "${EME_PATH}" ]; then
50 | if [ -x "/Applications/$EME_APP_NAME" ]; then
51 | EME_PATH="/Applications"
52 | elif [ -x "$HOME/Applications/$EME_APP_NAME" ]; then
53 | EME_PATH="$HOME/Applications"
54 | else
55 | if [ ! -x "$EME_PATH/$EME_APP_NAME" ]; then
56 | echo "Cannot locate EME.app, it is usually located /Applications. Set the EME_PATH environment variable to the directory containing EME.app. "
57 | exit 1
58 | fi
59 | fi
60 | fi
61 |
62 | if [ $EXPECT_OUTPUT ]; then
63 | "$EME_PATH/$EME_APP_NAME/Contents/MacOS/EME" --executed-from="$(pwd)" --pid=$$ $*
64 | exit $?
65 | else
66 | open -a "$EME_PATH/$EME_APP_NAME" -n --args --executed-from="$(pwd)" --pid=$$ --path-environment="$PATH" $*
67 | fi
68 | fi
69 |
--------------------------------------------------------------------------------
/app/eme/config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const os = require('os')
3 | const Config = require('./utils/electron-config')
4 |
5 | const cmdOrCtrl = os.platform() === 'darwin' ? 'command' : 'ctrl'
6 |
7 | const config = new Config({
8 | defaults: {
9 | recentFiles: [],
10 | lastAppState: {
11 | tabs: [],
12 | currentTabIndex: null
13 | },
14 | gists: {},
15 | settings: {
16 | theme: 'light',
17 | themeControl: 'system',
18 | writingMode: 'default',
19 | tokens: {
20 | github: ''
21 | },
22 | editor: {
23 | theme: 'base16-light',
24 | font: `"fira code", menlo, "lucida console"`,
25 | fontSize: 16,
26 | tabSize: 2,
27 | indentWithTabs: false
28 | },
29 | preview: {
30 | highlight: 'github',
31 | font: `"fira code", menlo, "lucida console"`,
32 | codeFont: 'inherit',
33 | fontSize: 16
34 | },
35 | autoSaveGist: false,
36 | keys: {
37 | openNewTab: `${cmdOrCtrl}+t`,
38 | openFile: `${cmdOrCtrl}+o`,
39 | openLastSession: `${cmdOrCtrl}+l`,
40 | switchWritingMode: `${cmdOrCtrl}+shift+m`,
41 | distractionFreeMode: `${cmdOrCtrl}+j`,
42 | vimMode: `${cmdOrCtrl}+i`,
43 | focusMode: `${cmdOrCtrl}+\\`,
44 | toggleNightMode: `${cmdOrCtrl}+shift+n`
45 | }
46 | }
47 | }
48 | })
49 |
50 | module.exports = config
51 |
--------------------------------------------------------------------------------
/app/eme/context-menu.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const {
3 | MenuItem,
4 | shell
5 | } = require('electron')
6 |
7 | module.exports = {
8 | append(params) {
9 | const text = params.selectionText
10 | return [
11 | new MenuItem({
12 | type: 'separator'
13 | }),
14 | new MenuItem({
15 | label: 'Search in Google',
16 | enabled: Boolean(text),
17 | click() {
18 | if (text) {
19 | shell.openExternal(`https://www.google.com/search?q=${text}`)
20 | }
21 | }
22 | })
23 | ]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/eme/event.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('events')
2 |
3 | const event = new EventEmitter()
4 |
5 | module.exports = event
6 |
--------------------------------------------------------------------------------
/app/eme/menu.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 @The EME Authors
2 |
3 | 'use strict'
4 |
5 | const axios = require('axios')
6 | const compare = require('semver-compare')
7 | const config = require('./config')
8 | const event = require('./event')
9 | const path = require('path')
10 | const tildify = require('tildify')
11 | const {
12 | app,
13 | dialog,
14 | Menu,
15 | shell
16 | } = require('electron')
17 | const { changeTheme, isSystemInNightMode } = require('./utils/theme')
18 | const { InstallShell } = require('./shell')
19 | const _ = require('./utils')
20 |
21 | const version = app.getVersion()
22 | const checkForUpdates = {
23 | label: 'Check for Updates',
24 | click(item, focusedWindow) {
25 | axios.get('https://api.github.com/repos/egoist/eme/releases/latest')
26 | .then(res => {
27 | const latestVersion = res.data.tag_name.substr(1)
28 | const hasUpdates = compare(latestVersion, version) === 1
29 | if (hasUpdates) {
30 | const answer = dialog.showMessageBox(focusedWindow, {
31 | type: 'question',
32 | message: 'Update',
33 | detail: `A newer version ${latestVersion} is available, are you willing to download the updates?`,
34 | buttons: ['OK', 'Cancel']
35 | })
36 | if (answer === 0) {
37 | shell.openExternal(`https://github.com/egoist/eme/releases/tag/v${latestVersion}`)
38 | }
39 | } else {
40 | dialog.showMessageBox(focusedWindow, {
41 | type: 'info',
42 | message: 'No Updates',
43 | detail: `Current version ${version} is already up to date!`,
44 | buttons: ['OK']
45 | })
46 | }
47 | })
48 | .catch(err => {
49 | dialog.showErrorBox('Update', `${err.name}: ${err.message}`)
50 | })
51 | }
52 | }
53 |
54 | module.exports = cb => {
55 | const openFileInWindow = async (win, file) => {
56 | if (win) {
57 | win.webContents.send('open-file', file)
58 | } else {
59 | win = cb.createWindow()
60 | win.webContents.on('did-finish-load', () => {
61 | win.webContents.send('open-file', file)
62 | })
63 | }
64 | }
65 |
66 | let recentFiles = config.get('recentFiles').map(file => ({
67 | label: tildify(file),
68 | file,
69 | click(item, focusedWindow) {
70 | openFileInWindow(focusedWindow, item.file)
71 | }
72 | }))
73 | if (recentFiles.length === 0) {
74 | recentFiles = [{
75 | label: '(empty)'
76 | }]
77 | }
78 |
79 | const settings = config.get('settings')
80 | const keys = settings.keys
81 |
82 | const template = [
83 | {
84 | label: 'File',
85 | submenu: [
86 | {
87 | label: 'New Tab',
88 | accelerator: keys.openNewTab,
89 | click(item, focusedWindow) {
90 | if (focusedWindow) {
91 | focusedWindow.webContents.send('new-tab')
92 | } else {
93 | cb.createWindow()
94 | }
95 | }
96 | },
97 | {
98 | label: 'Open',
99 | accelerator: keys.openFile,
100 | click(item, focusedWindow) {
101 | dialog.showOpenDialog(focusedWindow, {
102 | properties: ['openFile', 'multiSelections']
103 | }).then(result => {
104 | if (result && !result.canceled) {
105 | const locationsToOpen = result.filePaths
106 | locationsToOpen.forEach(singleLocation => {
107 | openFileInWindow(focusedWindow, singleLocation)
108 | })
109 | }
110 | }).catch(err => {
111 | console.log(err)
112 | })
113 | }
114 | },
115 | {
116 | label: 'Open Last Session',
117 | accelerator: keys.openLastSession,
118 | click(item, focusedWindow) {
119 | if (focusedWindow) focusedWindow.webContents.send('open-last-session')
120 | }
121 | },
122 | {
123 | label: 'Open Recent',
124 | submenu: recentFiles.concat([
125 | {
126 | type: 'separator'
127 | },
128 | {
129 | label: 'Clear all',
130 | click() {
131 | config.set('recentFiles', [])
132 | event.emit('reload-menu')
133 | }
134 | }
135 | ])
136 | },
137 | {
138 | type: 'separator'
139 | },
140 | {
141 | label: 'Save',
142 | accelerator: 'CmdOrCtrl+S',
143 | click(item, focusedWindow) {
144 | if (focusedWindow) focusedWindow.webContents.send('file-save')
145 | }
146 | },
147 | {
148 | label: 'Save As',
149 | accelerator: 'CmdOrCtrl+S+Shift',
150 | click(item, focusedWindow) {
151 | if (focusedWindow) focusedWindow.webContents.send('file-save-as')
152 | }
153 | },
154 | {
155 | label: 'Rename',
156 | accelerator: 'CmdOrCtrl+Shift+R',
157 | click(item, focusedWindow) {
158 | if (focusedWindow) focusedWindow.webContents.send('file-rename')
159 | }
160 | },
161 | {
162 | type: 'separator'
163 | },
164 | {
165 | label: 'Export as PDF',
166 | accelerator: 'CmdOrCtrl+Shift+P',
167 | click(item, focusedWindow) {
168 | if (focusedWindow) focusedWindow.webContents.send('show-save-pdf-dialog')
169 | }
170 | }
171 | ]
172 | },
173 | {
174 | label: 'Edit',
175 | submenu: [
176 | {
177 | role: 'undo'
178 | },
179 | {
180 | role: 'redo'
181 | },
182 | {
183 | type: 'separator'
184 | },
185 | {
186 | role: 'cut'
187 | },
188 | {
189 | role: 'copy'
190 | },
191 | {
192 | role: 'paste'
193 | },
194 | {
195 | role: 'pasteandmatchstyle'
196 | },
197 | {
198 | role: 'delete'
199 | },
200 | {
201 | role: 'selectall'
202 | }
203 | ]
204 | },
205 | {
206 | label: 'View',
207 | submenu: [
208 | {
209 | label: 'Reload',
210 | visible: Boolean(_.isDev),
211 | accelerator: 'CmdOrCtrl+R',
212 | click(item, focusedWindow) {
213 | if (focusedWindow) focusedWindow.reload()
214 | }
215 | },
216 | {
217 | role: 'togglefullscreen'
218 | },
219 | {
220 | type: 'separator'
221 | },
222 | {
223 | label: 'Theme',
224 | submenu: [
225 | {
226 | label: 'System Setting',
227 | type: 'radio',
228 | checked: settings.themeControl === 'system',
229 | click(item, focusedWindow) {
230 | var theme = isSystemInNightMode() ? 'dark' : 'light'
231 | changeTheme(focusedWindow, 'system', theme)
232 | }
233 | },
234 | {
235 | label: 'Light',
236 | type: 'radio',
237 | checked: settings.themeControl === 'manual' && settings.theme === 'light',
238 | click(item, focusedWindow) {
239 | changeTheme(focusedWindow, 'manual', 'light')
240 | }
241 | },
242 | {
243 | label: "Night",
244 | type: 'radio',
245 | checked: settings.themeControl === 'manual' && settings.theme === 'dark',
246 | click(item, focusedWindow) {
247 | changeTheme(focusedWindow, 'manual', 'dark')
248 | }
249 | }
250 | ],
251 | },
252 | {
253 | type: 'separator'
254 | },
255 | {
256 | label: 'Toggle Distraction Free mode',
257 | accelerator: keys.distractionFreeMode,
258 | click(item, focusedWindow) {
259 | if (focusedWindow) focusedWindow.webContents.send('toggle-distraction-free-mode')
260 | }
261 | },
262 | {
263 | label: 'Toggle Focus Mode',
264 | accelerator: keys.focusMode,
265 | click(item, focusedWindow) {
266 | if (focusedWindow) focusedWindow.webContents.send('toggle-focus-mode')
267 | }
268 | },
269 | {
270 | label: 'Toggle Vim Mode',
271 | accelerator: keys.vimMode,
272 | click(item, focusedWindow) {
273 | if (focusedWindow) focusedWindow.webContents.send('toggle-vim-mode')
274 | }
275 | },
276 | {
277 | type: 'separator'
278 | },
279 | {
280 | label: 'Switch Writing Mode',
281 | accelerator: keys.switchWritingMode,
282 | click(item, focusedWindow) {
283 | if (focusedWindow) focusedWindow.webContents.send('switch-writing-mode')
284 | }
285 | },
286 | {
287 | type: 'separator'
288 | },
289 | {
290 | label: 'Toggle Developer Tools',
291 | accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
292 | click(item, focusedWindow) {
293 | if (focusedWindow) focusedWindow.webContents.toggleDevTools()
294 | }
295 | }
296 | ]
297 | },
298 | {
299 | role: 'window',
300 | submenu: [
301 | {
302 | label: 'Close',
303 | accelerator: 'CmdOrCtrl+W',
304 | click(item, focusedWindow) {
305 | if (focusedWindow) focusedWindow.webContents.send('close-current-tab')
306 | }
307 | },
308 | {
309 | label: 'Minimize',
310 | accelerator: 'CmdOrCtrl+M',
311 | role: 'minimize'
312 | },
313 | {
314 | label: 'Zoom',
315 | role: 'zoom'
316 | },
317 | {
318 | type: 'separator'
319 | },
320 | {
321 | label: 'Bring All to Front',
322 | role: 'front'
323 | }
324 | ]
325 | },
326 | {
327 | role: 'help',
328 | submenu: [
329 | {
330 | label: 'Report bugs',
331 | click() {
332 | shell.openExternal('http://github.com/egoist/eme/issues')
333 | }
334 | },
335 | {
336 | type: 'separator'
337 | },
338 | {
339 | label: 'Welcome Guide',
340 | click(item, focusedWindow) {
341 | const file = path.join(__dirname, '../welcome-guide.md')
342 | openFileInWindow(focusedWindow, file)
343 | }
344 | },
345 | {
346 | label: 'Math Typesetting',
347 | click(item, focusedWindow) {
348 | const file = path.join(__dirname, '../math-typesetting.md')
349 | openFileInWindow(focusedWindow, file)
350 | }
351 | }
352 | ]
353 | }
354 | ]
355 |
356 | if (process.platform === 'darwin') {
357 | const name = app.name
358 | template.unshift({
359 | label: name,
360 | submenu: [
361 | {
362 | role: 'about'
363 | },
364 | {
365 | type: 'separator'
366 | },
367 | checkForUpdates,
368 | {
369 | type: 'separator'
370 | },
371 | {
372 | label: 'Install Shell Command',
373 | click() {
374 | new InstallShell().installShell()
375 | }
376 | },
377 | {
378 | type: 'separator'
379 | },
380 | {
381 | role: 'hide'
382 | },
383 | {
384 | role: 'hideothers'
385 | },
386 | {
387 | role: 'unhide'
388 | },
389 | {
390 | type: 'separator'
391 | },
392 | {
393 | label: 'Quit',
394 | accelerator: 'Command+Q',
395 | click(item, focusedWindow) {
396 | if (focusedWindow) {
397 | focusedWindow.webContents.send('close-and-exit')
398 | } else {
399 | app.exit(0)
400 | }
401 | }
402 | }
403 | ]
404 | })
405 | } else {
406 | template[template.length - 1].submenu.push(
407 | {
408 | type: 'separator'
409 | },
410 | checkForUpdates,
411 | {
412 | label: 'About',
413 | click() {
414 | cb.showAboutWindow()
415 | }
416 | }
417 | )
418 | }
419 |
420 | return Menu.buildFromTemplate(template)
421 | }
422 |
--------------------------------------------------------------------------------
/app/eme/shell.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 | const spawn = require('child_process').spawn
5 | const yargs = require('yargs')
6 | const {app} = require('electron')
7 |
8 | class InstallShell {
9 |
10 | installShell() {
11 | const commandName = 'eme'
12 | const commandPath = path.join(this.getResourcesDirectory(), 'app', 'eme.sh')
13 | const destinationPath = path.join(this.getInstallDirectory(), commandName)
14 | this.createSymLink(commandPath, destinationPath)
15 | }
16 |
17 | getInstallDirectory() {
18 | return '/usr/local/bin'
19 | }
20 |
21 | getResourcesDirectory() {
22 | return process.resourcesPath
23 | }
24 |
25 | createSymLink(commandPath, destinationPath) {
26 | try {
27 | spawn('rm', ['-f', destinationPath])
28 | spawn('mkdir', ['-p', path.dirname(destinationPath)])
29 | spawn('ln', ['-s', commandPath, destinationPath])
30 | } catch (e) {
31 | console.log(e)
32 | }
33 | }
34 | }
35 |
36 | function writeFullVersion() {
37 | console.log(
38 | `
39 | Atom : ${app.getVersion()}
40 | Electron: ${process.versions.electron}
41 | Chrome : ${process.versions.chrome}
42 | Node : ${process.versions.node}
43 | `
44 | )
45 | }
46 |
47 | function parseShellCommand() {
48 | const options = yargs(process.argv.slice(1))
49 | .usage(
50 | `
51 | EME - Elegant Markdown Editor
52 |
53 | Usage: eme [options] [path ...]
54 | `
55 | )
56 | .alias('h', 'help')
57 | .boolean('h')
58 | .describe('h', 'Print this usage message.')
59 | .alias('v', 'version')
60 | .boolean('v')
61 | .describe('v', 'Print the version information.')
62 |
63 | const argv = options.argv
64 | if (argv.version) {
65 | writeFullVersion()
66 | process.exit(0)
67 | }
68 |
69 | if (argv.help) {
70 | options.showHelp('log')
71 | process.exit(0)
72 | }
73 |
74 | const pathsToOpen = argv._
75 | const resourcePath = argv.executedFrom
76 | return {
77 | resourcePath, pathsToOpen
78 | }
79 | }
80 |
81 | module.exports = {
82 | InstallShell,
83 | parseShellCommand
84 | }
85 |
--------------------------------------------------------------------------------
/app/eme/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const _ = module.exports = {}
3 |
4 | _.isDev = process.env.NODE_ENV === 'development'
5 |
--------------------------------------------------------------------------------
/app/eme/utils/electron-config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const electron = require('electron')
3 | const Conf = require('conf')
4 |
5 | class ElectronConfig extends Conf {
6 | constructor(opts) {
7 | opts = Object.assign({name: 'config'}, opts)
8 | opts.cwd = (electron.app || electron.remote.app).getPath('userData')
9 | opts.configName = opts.name
10 | delete opts.name
11 | super(opts)
12 | }
13 | }
14 |
15 | module.exports = ElectronConfig
16 |
--------------------------------------------------------------------------------
/app/eme/utils/theme.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 @TwoCookingMice
2 |
3 | 'use strict'
4 |
5 | const { nativeTheme } = require('electron')
6 |
7 | function isSystemInNightMode() {
8 | return nativeTheme.shouldUseDarkColors
9 | }
10 |
11 | function changeTheme(win, themeControl, theme) {
12 | if (win) {
13 | win.webContents.send('change-theme', themeControl, theme)
14 | }
15 | }
16 |
17 | module.exports = { changeTheme, isSystemInNightMode }
18 |
--------------------------------------------------------------------------------
/app/eme/window.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const electron = require('electron')
4 | const {
5 | BrowserWindow,
6 | shell
7 | } = electron
8 | const config = require('./config')
9 |
10 | const isDev = process.env.NODE_ENV === 'development'
11 |
12 | class Window {
13 | constructor() {
14 | this.wins = 0
15 | }
16 |
17 | createWindow({
18 | homepage = `file://${path.join(__dirname, '../index.html')}`,
19 | windowState = {}
20 | } = {}) {
21 | const win = new BrowserWindow(Object.assign({
22 | name: 'EME',
23 | width: 800,
24 | height: 600,
25 | minWidth: 430,
26 | minHeight: 250,
27 | titleBarStyle: 'hidden-inset',
28 | webPreferences: {
29 | nodeIntegration: true
30 | }
31 | }, windowState))
32 |
33 | const web = win.webContents
34 |
35 | win.loadURL(homepage)
36 |
37 | win.webContents.on('new-window', (e, url) => {
38 | e.preventDefault()
39 | shell.openExternal(url)
40 | })
41 |
42 | win.on('close', () => {
43 | web.send('close-window')
44 | })
45 |
46 | win.on('closed', () => {
47 | this.wins--
48 | })
49 |
50 | win.on('focus', () => {
51 | web.send('win-focus')
52 | })
53 |
54 | win.on('enter-full-screen', () => {
55 | web.send('enter-full-screen')
56 | })
57 |
58 | win.on('leave-full-screen', () => {
59 | web.send('leave-full-screen')
60 | })
61 |
62 | if (isDev) {
63 | const installExtension = require('electron-devtools-installer')
64 | installExtension.default(installExtension.VUEJS_DEVTOOLS)
65 | .then(name => console.log(`Added Extension: ${name}`))
66 | .catch(err => console.log('An error occurred: ', err))
67 | }
68 |
69 | this.wins++
70 | win.$state = {
71 | unsaved: 0
72 | }
73 | win.$config = config
74 |
75 | return win
76 | }
77 | }
78 |
79 | module.exports = new Window()
80 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EME
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 @The EME Authors
2 |
3 | 'use strict'
4 | const buildMenu = require('./eme/menu')
5 | const config = require('./eme/config')
6 | const contextMenu = require('./eme/context-menu')
7 | const emeWindow = require('./eme/window')
8 | const event = require('./eme/event')
9 | const fs = require('fs')
10 | const os = require('os')
11 | const path = require('path')
12 | const pkg = require('./package.json')
13 | const windowStateKeeper = require('electron-window-state')
14 | const {
15 | app,
16 | BrowserWindow,
17 | Menu,
18 | ipcMain,
19 | dialog,
20 | nativeTheme
21 | } = require('electron')
22 | const { isDev } = require('./eme/utils')
23 | const { changeTheme, isSystemInNightMode } = require('./eme/utils/theme')
24 | const { parseShellCommand } = require('./eme/shell')
25 |
26 | require('electron-context-menu')(contextMenu)
27 |
28 | const platform = os.platform()
29 |
30 | const createMainWindow = () => {
31 | const windowState = windowStateKeeper({
32 | defaultWidth: 800,
33 | defaultHeight: 600
34 | })
35 | if (platform === 'linux') {
36 | windowState.icon = path.join(__dirname, 'resources/icon.png')
37 | }
38 | const win = emeWindow.createWindow({windowState})
39 | windowState.manage(win)
40 | return win
41 | }
42 |
43 | const showAboutWindow = () => {
44 | dialog.showMessageBox({
45 | title: pkg.productName,
46 | message: pkg.productName,
47 | type: 'info',
48 | detail: [
49 | `Version ${app.getVersion()}`,
50 | `Shell ${process.versions.electron}`,
51 | `Renderer ${process.versions.chrome}`,
52 | `Node ${process.versions.node}`
53 | ].join('\n'),
54 | buttons: ['OK'],
55 | noLink: true
56 | }, () => null)
57 | }
58 |
59 | const menuOptions = {
60 | createWindow: emeWindow.createWindow,
61 | showAboutWindow
62 | }
63 |
64 | const appMenu = buildMenu(menuOptions)
65 |
66 | const reloadMenu = () => {
67 | Menu.setApplicationMenu(buildMenu(menuOptions))
68 | }
69 |
70 | let mainWindow // eslint-disable-line
71 | let pdfWindow // eslint-disable-line
72 |
73 | let locationToOpen = null // Cache locationToOpen when app is not ready.
74 |
75 | app.on('ready', () => {
76 | const argv = parseShellCommand()
77 | Menu.setApplicationMenu(appMenu)
78 | mainWindow = createMainWindow()
79 |
80 | if (!isDev) {
81 | const {pathsToOpen, resourcePath} = argv
82 | const pathToOpen = pathsToOpen[0]
83 |
84 | if (pathsToOpen && resourcePath) {
85 | locationToOpen = path.resolve(resourcePath, pathToOpen)
86 | }
87 |
88 | // Open the file
89 | if (locationToOpen != null) {
90 | mainWindow.webContents.on('did-finish-load', () => {
91 | mainWindow.webContents.send('open-file', locationToOpen)
92 | locationToOpen = null
93 | })
94 | }
95 | }
96 |
97 | if (platform === 'darwin') {
98 | mainWindow.setSheetOffset(36)
99 | }
100 | })
101 |
102 | app.on('window-all-closed', () => {
103 | if (platform !== 'darwin') {
104 | app.quit()
105 | } else {
106 | mainWindow = null
107 | }
108 | })
109 |
110 | app.on('activate', (e, hasVisibleWindows) => {
111 | // If the application is not ready, then
112 | // just return and wait when the app is ready.
113 | if (!app.isReady()) {
114 | return
115 | }
116 |
117 | if (mainWindow == null) {
118 | mainWindow = createMainWindow()
119 | if (platform === 'darwin') {
120 | mainWindow.setSheetOffset(36)
121 | }
122 | }
123 |
124 | if (!hasVisibleWindows) {
125 | mainWindow.show()
126 | }
127 |
128 | // Open the file
129 | if (locationToOpen != null) {
130 | mainWindow.webContents.on('did-finish-load', () => {
131 | mainWindow.webContents.send('open-file', locationToOpen)
132 | locationToOpen = null
133 | })
134 | }
135 | })
136 |
137 | app.on('open-file', (e, filePath) => {
138 | e.preventDefault()
139 | console.log(filePath)
140 |
141 | if (mainWindow == null) {
142 | locationToOpen = filePath
143 | } else {
144 | mainWindow.webContents.send('open-file', filePath)
145 | }
146 | })
147 |
148 | ipcMain.on('close-focus-window', () => {
149 | BrowserWindow.getFocusedWindow().close()
150 | })
151 |
152 | // TODO: refactor
153 | ipcMain.on('print-to-pdf', (e, html) => {
154 | pdfWindow = new BrowserWindow({show: false})
155 | const tempPath = path.join(os.tmpdir(), `eme-export-pdf.${Date.now()}.html`)
156 | fs.writeFileSync(tempPath, html, 'utf8')
157 | console.log(tempPath)
158 | pdfWindow.loadURL(`file://${tempPath}`)
159 | })
160 |
161 | ipcMain.on('pdf-window-ready', (e, options) => {
162 | const page = pdfWindow.webContents
163 | page.on('did-finish-load', () => {
164 | page.printToPDF({
165 | pageSize: 'A4'
166 | }, (err, pdfData) => {
167 | if (err) {
168 | return console.log(err)
169 | }
170 | fs.writeFile(options.saveTo, pdfData, err => {
171 | pdfWindow.destroy()
172 | pdfWindow = undefined
173 | mainWindow.webContents.send('finish-exporting-pdf', err, options.saveTo)
174 | })
175 | })
176 | })
177 | })
178 |
179 | ipcMain.on('add-recent-file', (e, filePath) => {
180 | const files = config.get('recentFiles')
181 | const existing = files.indexOf(filePath)
182 | if (existing === -1) {
183 | // add to the head
184 | files.unshift(filePath)
185 | if (files.length > 10) {
186 | files.pop()
187 | }
188 | } else {
189 | // remove the existing one
190 | // add to the head
191 | files.splice(existing, 1)
192 | files.unshift(filePath)
193 | }
194 |
195 | config.set('recentFiles', files)
196 | reloadMenu()
197 | })
198 |
199 | ipcMain.on('remove-recent-file', (e, fileToRemove) => {
200 | config.set('recentFiles', config.get('recentFiles').filter(filePath => {
201 | return filePath !== fileToRemove
202 | }))
203 | reloadMenu()
204 | })
205 |
206 | ipcMain.on('log', (e, msg) => {
207 | console.log(JSON.stringify(msg, null, 2))
208 | })
209 |
210 | ipcMain.on('reload-menu', () => {
211 | reloadMenu()
212 | })
213 |
214 | event.on('reload-menu', () => {
215 | reloadMenu()
216 | })
217 |
218 | // Electron uses **nativeTheme** API to support native theme
219 | // on MacOS and Windows. We register a listener here to adjust
220 | // app's appearance when native theme of system changes.
221 | nativeTheme.on('updated', () => {
222 | const themeControl = config.get('settings').themeControl
223 | if (themeControl === 'system') {
224 | const theme = isSystemInNightMode() ? 'dark' : 'light'
225 | if (mainWindow) {
226 | changeTheme(mainWindow, themeControl, theme)
227 | }
228 | }
229 | })
230 |
--------------------------------------------------------------------------------
/app/math-typesetting.md:
--------------------------------------------------------------------------------
1 | # Math typesetting
2 |
3 | EME uses [KaTex](https://khan.github.io/KaTeX/) to render math symbols.
4 | You can write $\frac{inline}{expressions}$ by using single `$`.
5 | For a block, use double `$$`:
6 | $$
7 | \frac{this}{a} \frac{is}{block}
8 | $$
9 |
10 | ## Symbols
11 |
12 | This list of symbols is copied from [here](http://web.ift.uib.no/Teori/KURS/WRK/TeX/symALL.html).
13 |
14 | ### Greek Letters
15 |
16 | $$
17 | \alpha \theta o \tau
18 | \beta \vartheta \pi \upsilon
19 | \gamma \gamma \varpi \phi
20 | \delta \kappa \rho \varphi
21 | \epsilon \lambda \varrho \chi
22 | \varepsilon \mu \sigma \psi
23 | \zeta \nu \varsigma \omega
24 | \eta \xi
25 | $$
26 |
27 | $$
28 | \Gamma \Lambda \Sigma \Psi
29 | \Delta \Xi \Upsilon \Omega
30 | \Theta \Pi \Phi
31 | $$
32 |
33 | ### Binary Operation Symbols
34 |
35 | $$
36 | \pm \cap \diamond \oplus
37 | \mp \cup \bigtriangleup \ominus
38 | \times \uplus \bigtriangledown \otimes
39 | \div \sqcap \triangleleft \oslash
40 | \ast \sqcup \triangleright \odot
41 | \star \vee \lhd \bigcirc
42 | \circ \wedge \rhd \dagger
43 | \bullet \setminus \unlhd \ddagger
44 | \cdot \wr \unrhd \amalg
45 | + -
46 | $$
47 |
48 | ### Relation Symbols
49 |
50 | $$
51 | \leq \geq \equiv \models
52 | \prec \succ \sim \perp
53 | \preceq \succeq \simeq \mid
54 | \ll \gg \asymp \parallel
55 | \subset \supset \approx \bowtie
56 | \subseteq \supseteq \cong \Join
57 | \sqsubset \sqsupset \neq \smile
58 | \sqsubseteq \sqsupseteq \doteq \frown
59 | \in \ni \propto =
60 | \vdash \dashv < >
61 | :
62 | $$
63 |
64 | ### Punctuation Symbols
65 |
66 | $$
67 | , ; \colon \ldotp \cdotp
68 | $$
69 |
70 | ### Arrow Symbols
71 |
72 | $$
73 | \leftarrow \longleftarrow \uparrow
74 | \Leftarrow \Longleftarrow \Uparrow
75 | \rightarrow \longrightarrow \downarrow
76 | \Rightarrow \Longrightarrow \Downarrow
77 | \leftrightarrow \longleftrightarrow \updownarrow
78 | \Leftrightarrow \Longleftrightarrow \Updownarrow
79 | \mapsto \longmapsto \nearrow
80 | \hookleftarrow \hookrightarrow \searrow
81 | \leftharpoonup \rightharpoonup \swarrow
82 | \leftharpoondown \rightharpoondown \nwarrow
83 | \rightleftharpoons \leadsto
84 | $$
85 |
86 | ### Miscellaneous Symbols
87 |
88 | $$
89 | \ldots \cdots \vdots \ddots
90 | \aleph \prime \forall \infty
91 | \hbar \emptyset \exists \Box
92 | \imath \nabla \neg \Diamond
93 | \jmath \surd \flat \triangle
94 | \ell \top \natural \clubsuit
95 | \wp \bot \sharp \diamondsuit
96 | \Re \| \backslash \heartsuit
97 | \Im \angle \partial \spadesuit
98 | \mho . |
99 | $$
100 |
101 | ### Variable-sized Symbols
102 |
103 | $$
104 | \sum \bigcap \bigodot
105 | \prod \bigcup \bigotimes
106 | \coprod \bigsqcup \bigoplus
107 | \int \bigvee \biguplus
108 | \oint \bigwedge
109 | $$
110 |
111 | ### Log-like Symbols
112 |
113 | $$
114 | \arccos \cos \csc \exp \ker \limsup \min \sinh
115 | \arcsin \cosh \deg \gcd \lg \ln \Pr \sup
116 | $$
117 | $$
118 | \arctan \cot \det \hom \lim \log \sec \tan
119 | \arg \coth \dim \inf \liminf \max \sin \tanh
120 | $$
121 |
122 | ### Delimiters
123 |
124 | $$
125 | ( ) \uparrow \Uparrow
126 | [ ] \downarrow \Downarrow
127 | \{ \} \updownarrow \Updownarrow
128 | \lfloor \rfloor \lceil \rceil
129 | \langle \rangle / \backslash
130 | | \|
131 | $$
132 |
133 | ### Large Delimiters
134 |
135 | $$
136 | \rmoustache \lmoustache \rgroup \lgroup
137 | $$
138 |
139 | ### Math mode accents
140 |
141 | $$
142 | \hat{a} \acute{a} \bar{a} \dot{a} \breve{a}
143 | \check{a} \grave{a} \vec{a} \ddot{a} \tilde{a}
144 | $$
145 |
146 | ### Some other constructions
147 |
148 | $$
149 | \sqrt{abc} \sqrt[n]{abc} \frac{abc}{xyz} \underline{abc} \overline{abc}
150 | $$
151 |
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "EME",
3 | "productName": "EME",
4 | "version": "0.15.1",
5 | "description": "Elegant Markdown Editor",
6 | "author": {
7 | "name": "The EME Authors",
8 | "email": "0x142857@gmail.com"
9 | },
10 | "homepage": "https://github.com/egoist/eme",
11 | "main": "index.js",
12 | "scripts": {
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "license": "MIT",
16 | "dependencies": {
17 | "axios": "^0.18.1",
18 | "conf": "github:egoist-robot/conf",
19 | "electron-context-menu": "^0.4.0",
20 | "electron-window-state": "^3.0.3",
21 | "front-matter": "^4.0.1",
22 | "match-at": "^0.1.0",
23 | "semver-compare": "^1.0.0",
24 | "tildify": "^1.2.0",
25 | "yargs": "^5.0.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/resources/icon.png
--------------------------------------------------------------------------------
/app/vendor/css/print.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | -webkit-print-color-adjust: exact;
4 | }
5 | .page-break {
6 | display: block;
7 | page-break-before: always;
8 | }
9 |
--------------------------------------------------------------------------------
/app/vendor/css/spinners.css:
--------------------------------------------------------------------------------
1 | /* --- Basics --- */
2 |
3 | .loading {
4 | display: inline-block;
5 | overflow: hidden;
6 | height: 1.3em;
7 | margin-top: -0.3em;
8 | line-height: 1.5em;
9 | vertical-align: text-bottom;
10 | }
11 |
12 | .loading::after {
13 | display: inline-table;
14 | white-space: pre;
15 | text-align: left;
16 | }
17 |
18 | /* --- Types --- */
19 |
20 | /* default loading is ellip */
21 | .loading::after {
22 | content: "\A.\A..\A...";
23 | animation: spin4 2s steps(4) infinite;
24 | }
25 |
26 | .loading.line::after {
27 | content: "/\A–\A\\\A|";
28 | text-align: center;
29 | animation: spin4 1s steps(4) infinite;
30 | }
31 |
32 | .loading.line2::after {
33 | content: "╲\A│\A╱\A─";
34 | text-align: center;
35 | animation: spin4 1s steps(4) infinite;
36 | }
37 |
38 | .loading.plus::after {
39 | content: "┽\A╀\A┾\A╁";
40 | animation: spin4 1s steps(4) infinite;
41 | }
42 |
43 | .loading.dots::after {
44 | content: "⠋\A⠙\A⠹\A⠸\A⠼\A⠴\A⠦\A⠧\A⠇\A⠏";
45 | animation: spin10 1s steps(10) infinite;
46 | }
47 |
48 | .loading.dots2::after {
49 | content: "⠋\A⠙\A⠚\A⠞\A⠖\A⠦\A⠴\A⠲\A⠳";
50 | animation: spin9 1s steps(9) infinite;
51 | }
52 |
53 | .loading.dots3::after {
54 | content: "⋮\A⋰\A⋯\A⋱";
55 | text-align: center;
56 | animation: spin4 1s steps(4) infinite;
57 | }
58 |
59 | .loading.lifting::after {
60 | content: "꜈꜍\A꜉꜎\A꜊꜏\A꜋꜐\A꜌꜑";
61 | animation: spin5 .5s steps(5) infinite alternate;
62 | }
63 |
64 | .loading.hamburger::after {
65 | content: "☱\A☲\A☴";
66 | animation: spin3 .3s steps(3) infinite alternate;
67 | }
68 |
69 | .loading.bar::after {
70 | content: "▏\A▎\A▍\A▌\A▋\A▊\A▉";
71 | animation: spin7 1s steps(7) infinite alternate;
72 | }
73 |
74 | .loading.bar2::after {
75 | content: "▁\A▂\A▃\A▄\A▅\A▆\A▇\A█";
76 | animation: spin8 2s steps(8) infinite alternate;
77 | }
78 |
79 | .loading.circle::after {
80 | content: "◴\A◷\A◶\A◵";
81 | animation: spin4 1s steps(4) infinite;
82 | }
83 |
84 | .loading.open-circle::after {
85 | content: "◜\A◠\A◝\A◞\A◡\A◟";
86 | animation: spin6 .6s steps(6) infinite;
87 | }
88 |
89 | .loading.arrow::after {
90 | content: "←\A↖\A↑\A↗\A→\A↘\A↓\A↙";
91 | animation: spin8 1s steps(8) infinite;
92 | }
93 |
94 | .loading.triangle::after {
95 | content: "◢\A◣\A◤\A◥";
96 | animation: spin4 1s steps(4) infinite;
97 | }
98 |
99 | .loading.triangles::after {
100 | content: "▹▹▹▹▹\A ▸▹▹▹▹\A ▹▸▹▹▹\A ▹▹▸▹▹\A ▹▹▹▸▹\A ▹▹▹▹▸";
101 | animation: spin6 1s steps(6) infinite;
102 | }
103 |
104 | .loading.beam::after {
105 | content: "\A= \A == \A === \A ====\A ===\A ==\A =\A";
106 | animation: spin9 1.2s steps(9) infinite;
107 | font-family: monospace;
108 | }
109 |
110 | .loading.bullet::after {
111 | content: " ● \A ● \A ● \A ● \A ●\A ● \A ● \A ● \A ● \A ● ";
112 | animation: spin10 1s steps(10) infinite;
113 | }
114 |
115 | .loading.bullseye::after {
116 | content: "◎◎◎\A◉◎◎\A◎◉◎\A◎◎◉";
117 | animation: spin4 1s steps(4) infinite;
118 | }
119 |
120 | .loading.rhomb::after {
121 | content: "◇◇◇\A◈◇◇\A◇◈◇\A◇◇◈";
122 | animation: spin4 1s steps(4) infinite;
123 | }
124 |
125 | .loading.fish::after {
126 | content: ">))'>\A >))'>\A >))'>\A >))'>\A >))'>\A <'((<\A <'((<\A <'((<\A <'((<\A <'((<\A";
127 | animation: spin10 5s steps(10) infinite;
128 | }
129 |
130 | .loading.toggle::after {
131 | content: "⊶\A⊷";
132 | animation: spin2 1s steps(2) infinite;
133 | }
134 |
135 | .loading.countdown::after {
136 | content: "0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9";
137 | animation: spin10 10s steps(10) reverse;
138 | }
139 |
140 | .loading.time::after {
141 | content: "🕐\A🕑\A🕒\A🕓\A🕔\A🕕\A🕖\A🕗\A🕘\A🕙\A🕚\A🕛";
142 | animation: spin12 3s steps(12) infinite;
143 | width: 1.3em;
144 | }
145 |
146 | .loading.hearts::after {
147 | content: "💛\A💙\A💜\A💚";
148 | animation: spin4 2s steps(4) infinite;
149 | width: 1.3em;
150 | }
151 |
152 | .loading.earth::after {
153 | content: "🌍\A🌎\A🌏";
154 | animation: spin3 1s steps(3) infinite;
155 | width: 1.3em;
156 | }
157 |
158 | .loading.moon::after {
159 | content: "🌑\A🌒\A🌓\A🌔\A🌕\A🌖\A🌗\A🌘";
160 | animation: spin8 2s steps(8) infinite;
161 | width: 1.3em;
162 | }
163 |
164 | .loading.monkey::after {
165 | content: "🙈\A🙉\A🙊";
166 | animation: spin3 1.5s steps(3) infinite;
167 | width: 1.3em;
168 | }
169 |
170 | .loading.runner::after {
171 | content: "🚶\A🏃";
172 | animation: spin2 1s steps(2) infinite;
173 | width: 1.3em;
174 | }
175 |
176 | .loading.words::after {
177 | content: "Loading\A Still loading\A Mostly done\A A bit more \A Almost done\A Ready-ish";
178 | animation: spin6 12s steps(6) infinite;
179 | }
180 |
181 | /* --- Animations --- */
182 |
183 | @keyframes spin1 { to { transform: translateY( -1.5em); } }
184 | @keyframes spin2 { to { transform: translateY( -3.0em); } }
185 | @keyframes spin3 { to { transform: translateY( -4.5em); } }
186 | @keyframes spin4 { to { transform: translateY( -6.0em); } }
187 | @keyframes spin5 { to { transform: translateY( -7.5em); } }
188 | @keyframes spin6 { to { transform: translateY( -9.0em); } }
189 | @keyframes spin7 { to { transform: translateY(-10.5em); } }
190 | @keyframes spin8 { to { transform: translateY(-12.0em); } }
191 | @keyframes spin9 { to { transform: translateY(-13.5em); } }
192 | @keyframes spin10 { to { transform: translateY(-15.0em); } }
193 | @keyframes spin11 { to { transform: translateY(-16.5em); } }
194 | @keyframes spin12 { to { transform: translateY(-18.0em); } }
195 |
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_AMS-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_AMS-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_AMS-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_AMS-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_AMS-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_AMS-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_AMS-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_AMS-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Bold.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Bold.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Bold.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Bold.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Italic.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Italic.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Italic.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Italic.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Main-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Main-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Italic.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Italic.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Italic.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Italic.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Math-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Math-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Script-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Script-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Script-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Script-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Script-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Script-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Script-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Script-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size1-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size1-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size1-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size1-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size1-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size1-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size1-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size1-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size2-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size2-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size2-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size2-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size2-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size2-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size2-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size2-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size3-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size3-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size3-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size3-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size3-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size3-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size3-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size3-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size4-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size4-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size4-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size4-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size4-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size4-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Size4-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Size4-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.eot
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff
--------------------------------------------------------------------------------
/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/app/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2
--------------------------------------------------------------------------------
/app/vendor/katex/katex.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console:0 */
2 | /**
3 | * This is the main entry point for KaTeX. Here, we expose functions for
4 | * rendering expressions either to DOM nodes or to markup strings.
5 | *
6 | * We also expose the ParseError class to check if errors thrown from KaTeX are
7 | * errors in the expression, or errors in javascript handling.
8 | */
9 |
10 | var ParseError = require("./src/ParseError");
11 | var Settings = require("./src/Settings");
12 |
13 | var buildTree = require("./src/buildTree");
14 | var parseTree = require("./src/parseTree");
15 | var utils = require("./src/utils");
16 |
17 | /**
18 | * Parse and build an expression, and place that expression in the DOM node
19 | * given.
20 | */
21 | var render = function(expression, baseNode, options) {
22 | utils.clearNode(baseNode);
23 |
24 | var settings = new Settings(options);
25 |
26 | var tree = parseTree(expression, settings);
27 | var node = buildTree(tree, expression, settings).toNode();
28 |
29 | baseNode.appendChild(node);
30 | };
31 |
32 | // KaTeX's styles don't work properly in quirks mode. Print out an error, and
33 | // disable rendering.
34 | if (typeof document !== "undefined") {
35 | if (document.compatMode !== "CSS1Compat") {
36 | typeof console !== "undefined" && console.warn(
37 | "Warning: KaTeX doesn't work in quirks mode. Make sure your " +
38 | "website has a suitable doctype.");
39 |
40 | render = function() {
41 | throw new ParseError("KaTeX doesn't work in quirks mode.");
42 | };
43 | }
44 | }
45 |
46 | /**
47 | * Parse and build an expression, and return the markup for that.
48 | */
49 | var renderToString = function(expression, options) {
50 | var settings = new Settings(options);
51 |
52 | var tree = parseTree(expression, settings);
53 | return buildTree(tree, expression, settings).toMarkup();
54 | };
55 |
56 | /**
57 | * Parse an expression and return the parse tree.
58 | */
59 | var generateParseTree = function(expression, options) {
60 | var settings = new Settings(options);
61 | return parseTree(expression, settings);
62 | };
63 |
64 | module.exports = {
65 | render: render,
66 | renderToString: renderToString,
67 | /**
68 | * NOTE: This method is not currently recommended for public use.
69 | * The internal tree representation is unstable and is very likely
70 | * to change. Use at your own risk.
71 | */
72 | __parse: generateParseTree,
73 | ParseError: ParseError,
74 | };
75 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/Lexer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The Lexer class handles tokenizing the input in various ways. Since our
3 | * parser expects us to be able to backtrack, the lexer allows lexing from any
4 | * given starting point.
5 | *
6 | * Its main exposed function is the `lex` function, which takes a position to
7 | * lex from and a type of token to lex. It defers to the appropriate `_innerLex`
8 | * function.
9 | *
10 | * The various `_innerLex` functions perform the actual lexing of different
11 | * kinds.
12 | */
13 |
14 | var matchAt = require("match-at");
15 |
16 | var ParseError = require("./ParseError");
17 |
18 | // The main lexer class
19 | function Lexer(input) {
20 | this._input = input;
21 | }
22 |
23 | // The resulting token returned from `lex`.
24 | function Token(text, data, position) {
25 | this.text = text;
26 | this.data = data;
27 | this.position = position;
28 | }
29 |
30 | /* The following tokenRegex
31 | * - matches typical whitespace (but not NBSP etc.) using its first group
32 | * - matches symbol combinations which result in a single output character
33 | * - does not match any control character \x00-\x1f except whitespace
34 | * - does not match a bare backslash
35 | * - matches any ASCII character except those just mentioned
36 | * - does not match the BMP private use area \uE000-\uF8FF
37 | * - does not match bare surrogate code units
38 | * - matches any BMP character except for those just described
39 | * - matches any valid Unicode surrogate pair
40 | * - matches a backslash followed by one or more letters
41 | * - matches a backslash followed by any BMP character, including newline
42 | * Just because the Lexer matches something doesn't mean it's valid input:
43 | * If there is no matching function or symbol definition, the Parser will
44 | * still reject the input.
45 | */
46 | var tokenRegex = new RegExp(
47 | "([ \r\n\t]+)|(" + // whitespace
48 | "---?" + // special combinations
49 | "|[!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
50 | "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
51 | "|\\\\(?:[a-zA-Z]+|[^\uD800-\uDFFF])" + // function name
52 | ")"
53 | );
54 |
55 | var whitespaceRegex = /\s*/;
56 |
57 | /**
58 | * This function lexes a single normal token. It takes a position and
59 | * whether it should completely ignore whitespace or not.
60 | */
61 | Lexer.prototype._innerLex = function(pos, ignoreWhitespace) {
62 | var input = this._input;
63 | if (pos === input.length) {
64 | return new Token("EOF", null, pos);
65 | }
66 | var match = matchAt(tokenRegex, input, pos);
67 | if (match === null) {
68 | throw new ParseError(
69 | "Unexpected character: '" + input[pos] + "'",
70 | this, pos);
71 | } else if (match[2]) { // matched non-whitespace
72 | return new Token(match[2], null, pos + match[2].length);
73 | } else if (ignoreWhitespace) {
74 | return this._innerLex(pos + match[1].length, true);
75 | } else { // concatenate whitespace to a single space
76 | return new Token(" ", null, pos + match[1].length);
77 | }
78 | };
79 |
80 | // A regex to match a CSS color (like #ffffff or BlueViolet)
81 | var cssColor = /#[a-z0-9]+|[a-z]+/i;
82 |
83 | /**
84 | * This function lexes a CSS color.
85 | */
86 | Lexer.prototype._innerLexColor = function(pos) {
87 | var input = this._input;
88 |
89 | // Ignore whitespace
90 | var whitespace = matchAt(whitespaceRegex, input, pos)[0];
91 | pos += whitespace.length;
92 |
93 | var match;
94 | if ((match = matchAt(cssColor, input, pos))) {
95 | // If we look like a color, return a color
96 | return new Token(match[0], null, pos + match[0].length);
97 | } else {
98 | throw new ParseError("Invalid color", this, pos);
99 | }
100 | };
101 |
102 | // A regex to match a dimension. Dimensions look like
103 | // "1.2em" or ".4pt" or "1 ex"
104 | var sizeRegex = /(-?)\s*(\d+(?:\.\d*)?|\.\d+)\s*([a-z]{2})/;
105 |
106 | /**
107 | * This function lexes a dimension.
108 | */
109 | Lexer.prototype._innerLexSize = function(pos) {
110 | var input = this._input;
111 |
112 | // Ignore whitespace
113 | var whitespace = matchAt(whitespaceRegex, input, pos)[0];
114 | pos += whitespace.length;
115 |
116 | var match;
117 | if ((match = matchAt(sizeRegex, input, pos))) {
118 | var unit = match[3];
119 | // We only currently handle "em" and "ex" units
120 | if (unit !== "em" && unit !== "ex") {
121 | throw new ParseError("Invalid unit: '" + unit + "'", this, pos);
122 | }
123 | return new Token(match[0], {
124 | number: +(match[1] + match[2]),
125 | unit: unit,
126 | }, pos + match[0].length);
127 | }
128 |
129 | throw new ParseError("Invalid size", this, pos);
130 | };
131 |
132 | /**
133 | * This function lexes a string of whitespace.
134 | */
135 | Lexer.prototype._innerLexWhitespace = function(pos) {
136 | var input = this._input;
137 |
138 | var whitespace = matchAt(whitespaceRegex, input, pos)[0];
139 | pos += whitespace.length;
140 |
141 | return new Token(whitespace[0], null, pos);
142 | };
143 |
144 | /**
145 | * This function lexes a single token starting at `pos` and of the given mode.
146 | * Based on the mode, we defer to one of the `_innerLex` functions.
147 | */
148 | Lexer.prototype.lex = function(pos, mode) {
149 | if (mode === "math") {
150 | return this._innerLex(pos, true);
151 | } else if (mode === "text") {
152 | return this._innerLex(pos, false);
153 | } else if (mode === "color") {
154 | return this._innerLexColor(pos);
155 | } else if (mode === "size") {
156 | return this._innerLexSize(pos);
157 | } else if (mode === "whitespace") {
158 | return this._innerLexWhitespace(pos);
159 | }
160 | };
161 |
162 | module.exports = Lexer;
163 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/Options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains information about the options that the Parser carries
3 | * around with it while parsing. Data is held in an `Options` object, and when
4 | * recursing, a new `Options` object can be created with the `.with*` and
5 | * `.reset` functions.
6 | */
7 |
8 | /**
9 | * This is the main options class. It contains the style, size, color, and font
10 | * of the current parse level. It also contains the style and size of the parent
11 | * parse level, so size changes can be handled efficiently.
12 | *
13 | * Each of the `.with*` and `.reset` functions passes its current style and size
14 | * as the parentStyle and parentSize of the new options class, so parent
15 | * handling is taken care of automatically.
16 | */
17 | function Options(data) {
18 | this.style = data.style;
19 | this.color = data.color;
20 | this.size = data.size;
21 | this.phantom = data.phantom;
22 | this.font = data.font;
23 |
24 | if (data.parentStyle === undefined) {
25 | this.parentStyle = data.style;
26 | } else {
27 | this.parentStyle = data.parentStyle;
28 | }
29 |
30 | if (data.parentSize === undefined) {
31 | this.parentSize = data.size;
32 | } else {
33 | this.parentSize = data.parentSize;
34 | }
35 | }
36 |
37 | /**
38 | * Returns a new options object with the same properties as "this". Properties
39 | * from "extension" will be copied to the new options object.
40 | */
41 | Options.prototype.extend = function(extension) {
42 | var data = {
43 | style: this.style,
44 | size: this.size,
45 | color: this.color,
46 | parentStyle: this.style,
47 | parentSize: this.size,
48 | phantom: this.phantom,
49 | font: this.font,
50 | };
51 |
52 | for (var key in extension) {
53 | if (extension.hasOwnProperty(key)) {
54 | data[key] = extension[key];
55 | }
56 | }
57 |
58 | return new Options(data);
59 | };
60 |
61 | /**
62 | * Create a new options object with the given style.
63 | */
64 | Options.prototype.withStyle = function(style) {
65 | return this.extend({
66 | style: style,
67 | });
68 | };
69 |
70 | /**
71 | * Create a new options object with the given size.
72 | */
73 | Options.prototype.withSize = function(size) {
74 | return this.extend({
75 | size: size,
76 | });
77 | };
78 |
79 | /**
80 | * Create a new options object with the given color.
81 | */
82 | Options.prototype.withColor = function(color) {
83 | return this.extend({
84 | color: color,
85 | });
86 | };
87 |
88 | /**
89 | * Create a new options object with "phantom" set to true.
90 | */
91 | Options.prototype.withPhantom = function() {
92 | return this.extend({
93 | phantom: true,
94 | });
95 | };
96 |
97 | /**
98 | * Create a new options objects with the give font.
99 | */
100 | Options.prototype.withFont = function(font) {
101 | return this.extend({
102 | font: font,
103 | });
104 | };
105 |
106 | /**
107 | * Create a new options object with the same style, size, and color. This is
108 | * used so that parent style and size changes are handled correctly.
109 | */
110 | Options.prototype.reset = function() {
111 | return this.extend({});
112 | };
113 |
114 | /**
115 | * A map of color names to CSS colors.
116 | * TODO(emily): Remove this when we have real macros
117 | */
118 | var colorMap = {
119 | "katex-blue": "#6495ed",
120 | "katex-orange": "#ffa500",
121 | "katex-pink": "#ff00af",
122 | "katex-red": "#df0030",
123 | "katex-green": "#28ae7b",
124 | "katex-gray": "gray",
125 | "katex-purple": "#9d38bd",
126 | "katex-blueA": "#c7e9f1",
127 | "katex-blueB": "#9cdceb",
128 | "katex-blueC": "#58c4dd",
129 | "katex-blueD": "#29abca",
130 | "katex-blueE": "#1c758a",
131 | "katex-tealA": "#acead7",
132 | "katex-tealB": "#76ddc0",
133 | "katex-tealC": "#5cd0b3",
134 | "katex-tealD": "#55c1a7",
135 | "katex-tealE": "#49a88f",
136 | "katex-greenA": "#c9e2ae",
137 | "katex-greenB": "#a6cf8c",
138 | "katex-greenC": "#83c167",
139 | "katex-greenD": "#77b05d",
140 | "katex-greenE": "#699c52",
141 | "katex-goldA": "#f7c797",
142 | "katex-goldB": "#f9b775",
143 | "katex-goldC": "#f0ac5f",
144 | "katex-goldD": "#e1a158",
145 | "katex-goldE": "#c78d46",
146 | "katex-redA": "#f7a1a3",
147 | "katex-redB": "#ff8080",
148 | "katex-redC": "#fc6255",
149 | "katex-redD": "#e65a4c",
150 | "katex-redE": "#cf5044",
151 | "katex-maroonA": "#ecabc1",
152 | "katex-maroonB": "#ec92ab",
153 | "katex-maroonC": "#c55f73",
154 | "katex-maroonD": "#a24d61",
155 | "katex-maroonE": "#94424f",
156 | "katex-purpleA": "#caa3e8",
157 | "katex-purpleB": "#b189c6",
158 | "katex-purpleC": "#9a72ac",
159 | "katex-purpleD": "#715582",
160 | "katex-purpleE": "#644172",
161 | "katex-mintA": "#f5f9e8",
162 | "katex-mintB": "#edf2df",
163 | "katex-mintC": "#e0e5cc",
164 | "katex-grayA": "#fdfdfd",
165 | "katex-grayB": "#f7f7f7",
166 | "katex-grayC": "#eeeeee",
167 | "katex-grayD": "#dddddd",
168 | "katex-grayE": "#cccccc",
169 | "katex-grayF": "#aaaaaa",
170 | "katex-grayG": "#999999",
171 | "katex-grayH": "#555555",
172 | "katex-grayI": "#333333",
173 | "katex-kaBlue": "#314453",
174 | "katex-kaGreen": "#639b24",
175 | };
176 |
177 | /**
178 | * Gets the CSS color of the current options object, accounting for the
179 | * `colorMap`.
180 | */
181 | Options.prototype.getColor = function() {
182 | if (this.phantom) {
183 | return "transparent";
184 | } else {
185 | return colorMap[this.color] || this.color;
186 | }
187 | };
188 |
189 | module.exports = Options;
190 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/ParseError.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is the ParseError class, which is the main error thrown by KaTeX
3 | * functions when something has gone wrong. This is used to distinguish internal
4 | * errors from errors in the expression that the user provided.
5 | */
6 | function ParseError(message, lexer, position) {
7 | var error = "KaTeX parse error: " + message;
8 |
9 | if (lexer !== undefined && position !== undefined) {
10 | // If we have the input and a position, make the error a bit fancier
11 |
12 | // Prepend some information
13 | error += " at position " + position + ": ";
14 |
15 | // Get the input
16 | var input = lexer._input;
17 | // Insert a combining underscore at the correct position
18 | input = input.slice(0, position) + "\u0332" +
19 | input.slice(position);
20 |
21 | // Extract some context from the input and add it to the error
22 | var begin = Math.max(0, position - 15);
23 | var end = position + 15;
24 | error += input.slice(begin, end);
25 | }
26 |
27 | // Some hackery to make ParseError a prototype of Error
28 | // See http://stackoverflow.com/a/8460753
29 | var self = new Error(error);
30 | self.name = "ParseError";
31 | self.__proto__ = ParseError.prototype;
32 |
33 | self.position = position;
34 | return self;
35 | }
36 |
37 | // More hackery
38 | ParseError.prototype.__proto__ = Error.prototype;
39 |
40 | module.exports = ParseError;
41 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/Settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a module for storing settings passed into KaTeX. It correctly handles
3 | * default settings.
4 | */
5 |
6 | /**
7 | * Helper function for getting a default value if the value is undefined
8 | */
9 | function get(option, defaultValue) {
10 | return option === undefined ? defaultValue : option;
11 | }
12 |
13 | /**
14 | * The main Settings object
15 | *
16 | * The current options stored are:
17 | * - displayMode: Whether the expression should be typeset by default in
18 | * textstyle or displaystyle (default false)
19 | */
20 | function Settings(options) {
21 | // allow null options
22 | options = options || {};
23 | this.displayMode = get(options.displayMode, false);
24 | this.throwOnError = get(options.throwOnError, true);
25 | this.errorColor = get(options.errorColor, "#cc0000");
26 | }
27 |
28 | module.exports = Settings;
29 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/Style.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains information and classes for the various kinds of styles
3 | * used in TeX. It provides a generic `Style` class, which holds information
4 | * about a specific style. It then provides instances of all the different kinds
5 | * of styles possible, and provides functions to move between them and get
6 | * information about them.
7 | */
8 |
9 | /**
10 | * The main style class. Contains a unique id for the style, a size (which is
11 | * the same for cramped and uncramped version of a style), a cramped flag, and a
12 | * size multiplier, which gives the size difference between a style and
13 | * textstyle.
14 | */
15 | function Style(id, size, multiplier, cramped) {
16 | this.id = id;
17 | this.size = size;
18 | this.cramped = cramped;
19 | this.sizeMultiplier = multiplier;
20 | }
21 |
22 | /**
23 | * Get the style of a superscript given a base in the current style.
24 | */
25 | Style.prototype.sup = function() {
26 | return styles[sup[this.id]];
27 | };
28 |
29 | /**
30 | * Get the style of a subscript given a base in the current style.
31 | */
32 | Style.prototype.sub = function() {
33 | return styles[sub[this.id]];
34 | };
35 |
36 | /**
37 | * Get the style of a fraction numerator given the fraction in the current
38 | * style.
39 | */
40 | Style.prototype.fracNum = function() {
41 | return styles[fracNum[this.id]];
42 | };
43 |
44 | /**
45 | * Get the style of a fraction denominator given the fraction in the current
46 | * style.
47 | */
48 | Style.prototype.fracDen = function() {
49 | return styles[fracDen[this.id]];
50 | };
51 |
52 | /**
53 | * Get the cramped version of a style (in particular, cramping a cramped style
54 | * doesn't change the style).
55 | */
56 | Style.prototype.cramp = function() {
57 | return styles[cramp[this.id]];
58 | };
59 |
60 | /**
61 | * HTML class name, like "displaystyle cramped"
62 | */
63 | Style.prototype.cls = function() {
64 | return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
65 | };
66 |
67 | /**
68 | * HTML Reset class name, like "reset-textstyle"
69 | */
70 | Style.prototype.reset = function() {
71 | return resetNames[this.size];
72 | };
73 |
74 | // IDs of the different styles
75 | var D = 0;
76 | var Dc = 1;
77 | var T = 2;
78 | var Tc = 3;
79 | var S = 4;
80 | var Sc = 5;
81 | var SS = 6;
82 | var SSc = 7;
83 |
84 | // String names for the different sizes
85 | var sizeNames = [
86 | "displaystyle textstyle",
87 | "textstyle",
88 | "scriptstyle",
89 | "scriptscriptstyle",
90 | ];
91 |
92 | // Reset names for the different sizes
93 | var resetNames = [
94 | "reset-textstyle",
95 | "reset-textstyle",
96 | "reset-scriptstyle",
97 | "reset-scriptscriptstyle",
98 | ];
99 |
100 | // Instances of the different styles
101 | var styles = [
102 | new Style(D, 0, 1.0, false),
103 | new Style(Dc, 0, 1.0, true),
104 | new Style(T, 1, 1.0, false),
105 | new Style(Tc, 1, 1.0, true),
106 | new Style(S, 2, 0.7, false),
107 | new Style(Sc, 2, 0.7, true),
108 | new Style(SS, 3, 0.5, false),
109 | new Style(SSc, 3, 0.5, true),
110 | ];
111 |
112 | // Lookup tables for switching from one style to another
113 | var sup = [S, Sc, S, Sc, SS, SSc, SS, SSc];
114 | var sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc];
115 | var fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
116 | var fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
117 | var cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
118 |
119 | // We only export some of the styles. Also, we don't export the `Style` class so
120 | // no more styles can be generated.
121 | module.exports = {
122 | DISPLAY: styles[D],
123 | TEXT: styles[T],
124 | SCRIPT: styles[S],
125 | SCRIPTSCRIPT: styles[SS],
126 | };
127 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/buildTree.js:
--------------------------------------------------------------------------------
1 | var buildHTML = require("./buildHTML");
2 | var buildMathML = require("./buildMathML");
3 | var buildCommon = require("./buildCommon");
4 | var Options = require("./Options");
5 | var Settings = require("./Settings");
6 | var Style = require("./Style");
7 |
8 | var makeSpan = buildCommon.makeSpan;
9 |
10 | var buildTree = function(tree, expression, settings) {
11 | settings = settings || new Settings({});
12 |
13 | var startStyle = Style.TEXT;
14 | if (settings.displayMode) {
15 | startStyle = Style.DISPLAY;
16 | }
17 |
18 | // Setup the default options
19 | var options = new Options({
20 | style: startStyle,
21 | size: "size5",
22 | });
23 |
24 | // `buildHTML` sometimes messes with the parse tree (like turning bins ->
25 | // ords), so we build the MathML version first.
26 | var mathMLNode = buildMathML(tree, expression, options);
27 | var htmlNode = buildHTML(tree, options);
28 |
29 | var katexNode = makeSpan(["katex"], [
30 | mathMLNode, htmlNode,
31 | ]);
32 |
33 | if (settings.displayMode) {
34 | return makeSpan(["katex-display"], [katexNode]);
35 | } else {
36 | return katexNode;
37 | }
38 | };
39 |
40 | module.exports = buildTree;
41 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/domTree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These objects store the data about the DOM nodes we create, as well as some
3 | * extra data. They can then be transformed into real DOM nodes with the
4 | * `toNode` function or HTML markup using `toMarkup`. They are useful for both
5 | * storing extra properties on the nodes, as well as providing a way to easily
6 | * work with the DOM.
7 | *
8 | * Similar functions for working with MathML nodes exist in mathMLTree.js.
9 | */
10 |
11 | var utils = require("./utils");
12 |
13 | /**
14 | * Create an HTML className based on a list of classes. In addition to joining
15 | * with spaces, we also remove null or empty classes.
16 | */
17 | var createClass = function(classes) {
18 | classes = classes.slice();
19 | for (var i = classes.length - 1; i >= 0; i--) {
20 | if (!classes[i]) {
21 | classes.splice(i, 1);
22 | }
23 | }
24 |
25 | return classes.join(" ");
26 | };
27 |
28 | /**
29 | * This node represents a span node, with a className, a list of children, and
30 | * an inline style. It also contains information about its height, depth, and
31 | * maxFontSize.
32 | */
33 | function span(classes, children, height, depth, maxFontSize, style) {
34 | this.classes = classes || [];
35 | this.children = children || [];
36 | this.height = height || 0;
37 | this.depth = depth || 0;
38 | this.maxFontSize = maxFontSize || 0;
39 | this.style = style || {};
40 | this.attributes = {};
41 | }
42 |
43 | /**
44 | * Sets an arbitrary attribute on the span. Warning: use this wisely. Not all
45 | * browsers support attributes the same, and having too many custom attributes
46 | * is probably bad.
47 | */
48 | span.prototype.setAttribute = function(attribute, value) {
49 | this.attributes[attribute] = value;
50 | };
51 |
52 | /**
53 | * Convert the span into an HTML node
54 | */
55 | span.prototype.toNode = function() {
56 | var span = document.createElement("span");
57 |
58 | // Apply the class
59 | span.className = createClass(this.classes);
60 |
61 | // Apply inline styles
62 | for (var style in this.style) {
63 | if (Object.prototype.hasOwnProperty.call(this.style, style)) {
64 | span.style[style] = this.style[style];
65 | }
66 | }
67 |
68 | // Apply attributes
69 | for (var attr in this.attributes) {
70 | if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
71 | span.setAttribute(attr, this.attributes[attr]);
72 | }
73 | }
74 |
75 | // Append the children, also as HTML nodes
76 | for (var i = 0; i < this.children.length; i++) {
77 | span.appendChild(this.children[i].toNode());
78 | }
79 |
80 | return span;
81 | };
82 |
83 | /**
84 | * Convert the span into an HTML markup string
85 | */
86 | span.prototype.toMarkup = function() {
87 | var markup = "";
119 |
120 | // Add the markup of the children, also as markup
121 | for (var i = 0; i < this.children.length; i++) {
122 | markup += this.children[i].toMarkup();
123 | }
124 |
125 | markup += "";
126 |
127 | return markup;
128 | };
129 |
130 | /**
131 | * This node represents a document fragment, which contains elements, but when
132 | * placed into the DOM doesn't have any representation itself. Thus, it only
133 | * contains children and doesn't have any HTML properties. It also keeps track
134 | * of a height, depth, and maxFontSize.
135 | */
136 | function documentFragment(children, height, depth, maxFontSize) {
137 | this.children = children || [];
138 | this.height = height || 0;
139 | this.depth = depth || 0;
140 | this.maxFontSize = maxFontSize || 0;
141 | }
142 |
143 | /**
144 | * Convert the fragment into a node
145 | */
146 | documentFragment.prototype.toNode = function() {
147 | // Create a fragment
148 | var frag = document.createDocumentFragment();
149 |
150 | // Append the children
151 | for (var i = 0; i < this.children.length; i++) {
152 | frag.appendChild(this.children[i].toNode());
153 | }
154 |
155 | return frag;
156 | };
157 |
158 | /**
159 | * Convert the fragment into HTML markup
160 | */
161 | documentFragment.prototype.toMarkup = function() {
162 | var markup = "";
163 |
164 | // Simply concatenate the markup for the children together
165 | for (var i = 0; i < this.children.length; i++) {
166 | markup += this.children[i].toMarkup();
167 | }
168 |
169 | return markup;
170 | };
171 |
172 | /**
173 | * A symbol node contains information about a single symbol. It either renders
174 | * to a single text node, or a span with a single text node in it, depending on
175 | * whether it has CSS classes, styles, or needs italic correction.
176 | */
177 | function symbolNode(value, height, depth, italic, skew, classes, style) {
178 | this.value = value || "";
179 | this.height = height || 0;
180 | this.depth = depth || 0;
181 | this.italic = italic || 0;
182 | this.skew = skew || 0;
183 | this.classes = classes || [];
184 | this.style = style || {};
185 | this.maxFontSize = 0;
186 | }
187 |
188 | /**
189 | * Creates a text node or span from a symbol node. Note that a span is only
190 | * created if it is needed.
191 | */
192 | symbolNode.prototype.toNode = function() {
193 | var node = document.createTextNode(this.value);
194 | var span = null;
195 |
196 | if (this.italic > 0) {
197 | span = document.createElement("span");
198 | span.style.marginRight = this.italic + "em";
199 | }
200 |
201 | if (this.classes.length > 0) {
202 | span = span || document.createElement("span");
203 | span.className = createClass(this.classes);
204 | }
205 |
206 | for (var style in this.style) {
207 | if (this.style.hasOwnProperty(style)) {
208 | span = span || document.createElement("span");
209 | span.style[style] = this.style[style];
210 | }
211 | }
212 |
213 | if (span) {
214 | span.appendChild(node);
215 | return span;
216 | } else {
217 | return node;
218 | }
219 | };
220 |
221 | /**
222 | * Creates markup for a symbol node.
223 | */
224 | symbolNode.prototype.toMarkup = function() {
225 | // TODO(alpert): More duplication than I'd like from
226 | // span.prototype.toMarkup and symbolNode.prototype.toNode...
227 | var needsSpan = false;
228 |
229 | var markup = " 0) {
241 | styles += "margin-right:" + this.italic + "em;";
242 | }
243 | for (var style in this.style) {
244 | if (this.style.hasOwnProperty(style)) {
245 | styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
246 | }
247 | }
248 |
249 | if (styles) {
250 | needsSpan = true;
251 | markup += " style=\"" + utils.escape(styles) + "\"";
252 | }
253 |
254 | var escaped = utils.escape(this.value);
255 | if (needsSpan) {
256 | markup += ">";
257 | markup += escaped;
258 | markup += "";
259 | return markup;
260 | } else {
261 | return escaped;
262 | }
263 | };
264 |
265 | module.exports = {
266 | span: span,
267 | documentFragment: documentFragment,
268 | symbolNode: symbolNode,
269 | };
270 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/environments.js:
--------------------------------------------------------------------------------
1 | /* eslint no-constant-condition:0 */
2 | var fontMetrics = require("./fontMetrics");
3 | var parseData = require("./parseData");
4 | var ParseError = require("./ParseError");
5 |
6 | var ParseNode = parseData.ParseNode;
7 |
8 | /**
9 | * Parse the body of the environment, with rows delimited by \\ and
10 | * columns delimited by &, and create a nested list in row-major order
11 | * with one group per cell.
12 | */
13 | function parseArray(parser, result) {
14 | var row = [];
15 | var body = [row];
16 | var rowGaps = [];
17 | while (true) {
18 | var cell = parser.parseExpression(false, null);
19 | row.push(new ParseNode("ordgroup", cell, parser.mode));
20 | var next = parser.nextToken.text;
21 | if (next === "&") {
22 | parser.consume();
23 | } else if (next === "\\end") {
24 | break;
25 | } else if (next === "\\\\" || next === "\\cr") {
26 | var cr = parser.parseFunction();
27 | rowGaps.push(cr.value.size);
28 | row = [];
29 | body.push(row);
30 | } else {
31 | // TODO: Clean up the following hack once #385 got merged
32 | var pos = Math.min(parser.pos + 1, parser.lexer._input.length);
33 | throw new ParseError("Expected & or \\\\ or \\end",
34 | parser.lexer, pos);
35 | }
36 | }
37 | result.body = body;
38 | result.rowGaps = rowGaps;
39 | return new ParseNode(result.type, result, parser.mode);
40 | }
41 |
42 | /*
43 | * An environment definition is very similar to a function definition:
44 | * it is declared with a name or a list of names, a set of properties
45 | * and a handler containing the actual implementation.
46 | *
47 | * The properties include:
48 | * - numArgs: The number of arguments after the \begin{name} function.
49 | * - argTypes: (optional) Just like for a function
50 | * - allowedInText: (optional) Whether or not the environment is allowed inside
51 | * text mode (default false) (not enforced yet)
52 | * - numOptionalArgs: (optional) Just like for a function
53 | * A bare number instead of that object indicates the numArgs value.
54 | *
55 | * The handler function will receive two arguments
56 | * - context: information and references provided by the parser
57 | * - args: an array of arguments passed to \begin{name}
58 | * The context contains the following properties:
59 | * - envName: the name of the environment, one of the listed names.
60 | * - parser: the parser object
61 | * - lexer: the lexer object
62 | * - positions: the positions associated with these arguments from args.
63 | * The handler must return a ParseResult.
64 | */
65 |
66 | function defineEnvironment(names, props, handler) {
67 | if (typeof names === "string") {
68 | names = [names];
69 | }
70 | if (typeof props === "number") {
71 | props = { numArgs: props };
72 | }
73 | // Set default values of environments
74 | var data = {
75 | numArgs: props.numArgs || 0,
76 | argTypes: props.argTypes,
77 | greediness: 1,
78 | allowedInText: !!props.allowedInText,
79 | numOptionalArgs: props.numOptionalArgs || 0,
80 | handler: handler,
81 | };
82 | for (var i = 0; i < names.length; ++i) {
83 | module.exports[names[i]] = data;
84 | }
85 | }
86 |
87 | // Arrays are part of LaTeX, defined in lttab.dtx so its documentation
88 | // is part of the source2e.pdf file of LaTeX2e source documentation.
89 | defineEnvironment("array", {
90 | numArgs: 1,
91 | }, function(context, args) {
92 | var colalign = args[0];
93 | colalign = colalign.value.map ? colalign.value : [colalign];
94 | var cols = colalign.map(function(node) {
95 | var ca = node.value;
96 | if ("lcr".indexOf(ca) !== -1) {
97 | return {
98 | type: "align",
99 | align: ca,
100 | };
101 | } else if (ca === "|") {
102 | return {
103 | type: "separator",
104 | separator: "|",
105 | };
106 | }
107 | throw new ParseError(
108 | "Unknown column alignment: " + node.value,
109 | context.lexer, context.positions[1]);
110 | });
111 | var res = {
112 | type: "array",
113 | cols: cols,
114 | hskipBeforeAndAfter: true, // \@preamble in lttab.dtx
115 | };
116 | res = parseArray(context.parser, res);
117 | return res;
118 | });
119 |
120 | // The matrix environments of amsmath builds on the array environment
121 | // of LaTeX, which is discussed above.
122 | defineEnvironment([
123 | "matrix",
124 | "pmatrix",
125 | "bmatrix",
126 | "Bmatrix",
127 | "vmatrix",
128 | "Vmatrix",
129 | ], {
130 | }, function(context) {
131 | var delimiters = {
132 | "matrix": null,
133 | "pmatrix": ["(", ")"],
134 | "bmatrix": ["[", "]"],
135 | "Bmatrix": ["\\{", "\\}"],
136 | "vmatrix": ["|", "|"],
137 | "Vmatrix": ["\\Vert", "\\Vert"],
138 | }[context.envName];
139 | var res = {
140 | type: "array",
141 | hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath
142 | };
143 | res = parseArray(context.parser, res);
144 | if (delimiters) {
145 | res = new ParseNode("leftright", {
146 | body: [res],
147 | left: delimiters[0],
148 | right: delimiters[1],
149 | }, context.mode);
150 | }
151 | return res;
152 | });
153 |
154 | // A cases environment (in amsmath.sty) is almost equivalent to
155 | // \def\arraystretch{1.2}%
156 | // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
157 | defineEnvironment("cases", {
158 | }, function(context) {
159 | var res = {
160 | type: "array",
161 | arraystretch: 1.2,
162 | cols: [{
163 | type: "align",
164 | align: "l",
165 | pregap: 0,
166 | postgap: fontMetrics.metrics.quad,
167 | }, {
168 | type: "align",
169 | align: "l",
170 | pregap: 0,
171 | postgap: 0,
172 | }],
173 | };
174 | res = parseArray(context.parser, res);
175 | res = new ParseNode("leftright", {
176 | body: [res],
177 | left: "\\{",
178 | right: ".",
179 | }, context.mode);
180 | return res;
181 | });
182 |
183 | // An aligned environment is like the align* environment
184 | // except it operates within math mode.
185 | // Note that we assume \nomallineskiplimit to be zero,
186 | // so that \strut@ is the same as \strut.
187 | defineEnvironment("aligned", {
188 | }, function(context) {
189 | var res = {
190 | type: "array",
191 | cols: [],
192 | };
193 | res = parseArray(context.parser, res);
194 | var emptyGroup = new ParseNode("ordgroup", [], context.mode);
195 | var numCols = 0;
196 | res.value.body.forEach(function(row) {
197 | var i;
198 | for (i = 1; i < row.length; i += 2) {
199 | row[i].value.unshift(emptyGroup);
200 | }
201 | if (numCols < row.length) {
202 | numCols = row.length;
203 | }
204 | });
205 | for (var i = 0; i < numCols; ++i) {
206 | var align = "r";
207 | var pregap = 0;
208 | if (i % 2 === 1) {
209 | align = "l";
210 | } else if (i > 0) {
211 | pregap = 2; // one \qquad between columns
212 | }
213 | res.value.cols[i] = {
214 | type: "align",
215 | align: align,
216 | pregap: pregap,
217 | postgap: 0,
218 | };
219 | }
220 | return res;
221 | });
222 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/fontMetrics.js:
--------------------------------------------------------------------------------
1 | /* eslint no-unused-vars:0 */
2 |
3 | var Style = require("./Style");
4 |
5 | /**
6 | * This file contains metrics regarding fonts and individual symbols. The sigma
7 | * and xi variables, as well as the metricMap map contain data extracted from
8 | * TeX, TeX font metrics, and the TTF files. These data are then exposed via the
9 | * `metrics` variable and the getCharacterMetrics function.
10 | */
11 |
12 | // These font metrics are extracted from TeX by using
13 | // \font\a=cmmi10
14 | // \showthe\fontdimenX\a
15 | // where X is the corresponding variable number. These correspond to the font
16 | // parameters of the symbol fonts. In TeX, there are actually three sets of
17 | // dimensions, one for each of textstyle, scriptstyle, and scriptscriptstyle,
18 | // but we only use the textstyle ones, and scale certain dimensions accordingly.
19 | // See the TeXbook, page 441.
20 | var sigma1 = 0.025;
21 | var sigma2 = 0;
22 | var sigma3 = 0;
23 | var sigma4 = 0;
24 | var sigma5 = 0.431;
25 | var sigma6 = 1;
26 | var sigma7 = 0;
27 | var sigma8 = 0.677;
28 | var sigma9 = 0.394;
29 | var sigma10 = 0.444;
30 | var sigma11 = 0.686;
31 | var sigma12 = 0.345;
32 | var sigma13 = 0.413;
33 | var sigma14 = 0.363;
34 | var sigma15 = 0.289;
35 | var sigma16 = 0.150;
36 | var sigma17 = 0.247;
37 | var sigma18 = 0.386;
38 | var sigma19 = 0.050;
39 | var sigma20 = 2.390;
40 | var sigma21 = 1.01;
41 | var sigma21Script = 0.81;
42 | var sigma21ScriptScript = 0.71;
43 | var sigma22 = 0.250;
44 |
45 | // These font metrics are extracted from TeX by using
46 | // \font\a=cmex10
47 | // \showthe\fontdimenX\a
48 | // where X is the corresponding variable number. These correspond to the font
49 | // parameters of the extension fonts (family 3). See the TeXbook, page 441.
50 | var xi1 = 0;
51 | var xi2 = 0;
52 | var xi3 = 0;
53 | var xi4 = 0;
54 | var xi5 = 0.431;
55 | var xi6 = 1;
56 | var xi7 = 0;
57 | var xi8 = 0.04;
58 | var xi9 = 0.111;
59 | var xi10 = 0.166;
60 | var xi11 = 0.2;
61 | var xi12 = 0.6;
62 | var xi13 = 0.1;
63 |
64 | // This value determines how large a pt is, for metrics which are defined in
65 | // terms of pts.
66 | // This value is also used in katex.less; if you change it make sure the values
67 | // match.
68 | var ptPerEm = 10.0;
69 |
70 | // The space between adjacent `|` columns in an array definition. From
71 | // `\showthe\doublerulesep` in LaTeX.
72 | var doubleRuleSep = 2.0 / ptPerEm;
73 |
74 | /**
75 | * This is just a mapping from common names to real metrics
76 | */
77 | var metrics = {
78 | xHeight: sigma5,
79 | quad: sigma6,
80 | num1: sigma8,
81 | num2: sigma9,
82 | num3: sigma10,
83 | denom1: sigma11,
84 | denom2: sigma12,
85 | sup1: sigma13,
86 | sup2: sigma14,
87 | sup3: sigma15,
88 | sub1: sigma16,
89 | sub2: sigma17,
90 | supDrop: sigma18,
91 | subDrop: sigma19,
92 | axisHeight: sigma22,
93 | defaultRuleThickness: xi8,
94 | bigOpSpacing1: xi9,
95 | bigOpSpacing2: xi10,
96 | bigOpSpacing3: xi11,
97 | bigOpSpacing4: xi12,
98 | bigOpSpacing5: xi13,
99 | ptPerEm: ptPerEm,
100 | emPerEx: sigma5 / sigma6,
101 | doubleRuleSep: doubleRuleSep,
102 |
103 | // TODO(alpert): Missing parallel structure here. We should probably add
104 | // style-specific metrics for all of these.
105 | delim1: sigma20,
106 | getDelim2: function(style) {
107 | if (style.size === Style.TEXT.size) {
108 | return sigma21;
109 | } else if (style.size === Style.SCRIPT.size) {
110 | return sigma21Script;
111 | } else if (style.size === Style.SCRIPTSCRIPT.size) {
112 | return sigma21ScriptScript;
113 | }
114 | throw new Error("Unexpected style size: " + style.size);
115 | },
116 | };
117 |
118 | // This map contains a mapping from font name and character code to character
119 | // metrics, including height, depth, italic correction, and skew (kern from the
120 | // character to the corresponding \skewchar)
121 | // This map is generated via `make metrics`. It should not be changed manually.
122 | var metricMap = require("./fontMetricsData");
123 |
124 | /**
125 | * This function is a convenience function for looking up information in the
126 | * metricMap table. It takes a character as a string, and a style.
127 | *
128 | * Note: the `width` property may be undefined if fontMetricsData.js wasn't
129 | * built using `Make extended_metrics`.
130 | */
131 | var getCharacterMetrics = function(character, style) {
132 | var metrics = metricMap[style][character.charCodeAt(0)];
133 | if (metrics) {
134 | return {
135 | depth: metrics[0],
136 | height: metrics[1],
137 | italic: metrics[2],
138 | skew: metrics[3],
139 | width: metrics[4],
140 | };
141 | }
142 | };
143 |
144 | module.exports = {
145 | metrics: metrics,
146 | getCharacterMetrics: getCharacterMetrics,
147 | };
148 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/mathMLTree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These objects store data about MathML nodes. This is the MathML equivalent
3 | * of the types in domTree.js. Since MathML handles its own rendering, and
4 | * since we're mainly using MathML to improve accessibility, we don't manage
5 | * any of the styling state that the plain DOM nodes do.
6 | *
7 | * The `toNode` and `toMarkup` functions work simlarly to how they do in
8 | * domTree.js, creating namespaced DOM nodes and HTML text markup respectively.
9 | */
10 |
11 | var utils = require("./utils");
12 |
13 | /**
14 | * This node represents a general purpose MathML node of any type. The
15 | * constructor requires the type of node to create (for example, `"mo"` or
16 | * `"mspace"`, corresponding to `` and `` tags).
17 | */
18 | function MathNode(type, children) {
19 | this.type = type;
20 | this.attributes = {};
21 | this.children = children || [];
22 | }
23 |
24 | /**
25 | * Sets an attribute on a MathML node. MathML depends on attributes to convey a
26 | * semantic content, so this is used heavily.
27 | */
28 | MathNode.prototype.setAttribute = function(name, value) {
29 | this.attributes[name] = value;
30 | };
31 |
32 | /**
33 | * Converts the math node into a MathML-namespaced DOM element.
34 | */
35 | MathNode.prototype.toNode = function() {
36 | var node = document.createElementNS(
37 | "http://www.w3.org/1998/Math/MathML", this.type);
38 |
39 | for (var attr in this.attributes) {
40 | if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
41 | node.setAttribute(attr, this.attributes[attr]);
42 | }
43 | }
44 |
45 | for (var i = 0; i < this.children.length; i++) {
46 | node.appendChild(this.children[i].toNode());
47 | }
48 |
49 | return node;
50 | };
51 |
52 | /**
53 | * Converts the math node into an HTML markup string.
54 | */
55 | MathNode.prototype.toMarkup = function() {
56 | var markup = "<" + this.type;
57 |
58 | // Add the attributes
59 | for (var attr in this.attributes) {
60 | if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
61 | markup += " " + attr + "=\"";
62 | markup += utils.escape(this.attributes[attr]);
63 | markup += "\"";
64 | }
65 | }
66 |
67 | markup += ">";
68 |
69 | for (var i = 0; i < this.children.length; i++) {
70 | markup += this.children[i].toMarkup();
71 | }
72 |
73 | markup += "" + this.type + ">";
74 |
75 | return markup;
76 | };
77 |
78 | /**
79 | * This node represents a piece of text.
80 | */
81 | function TextNode(text) {
82 | this.text = text;
83 | }
84 |
85 | /**
86 | * Converts the text node into a DOM text node.
87 | */
88 | TextNode.prototype.toNode = function() {
89 | return document.createTextNode(this.text);
90 | };
91 |
92 | /**
93 | * Converts the text node into HTML markup (which is just the text itself).
94 | */
95 | TextNode.prototype.toMarkup = function() {
96 | return utils.escape(this.text);
97 | };
98 |
99 | module.exports = {
100 | MathNode: MathNode,
101 | TextNode: TextNode,
102 | };
103 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/parseData.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The resulting parse tree nodes of the parse tree.
3 | */
4 | function ParseNode(type, value, mode) {
5 | this.type = type;
6 | this.value = value;
7 | this.mode = mode;
8 | }
9 |
10 | module.exports = {
11 | ParseNode: ParseNode,
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/parseTree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Provides a single function for parsing an expression using a Parser
3 | * TODO(emily): Remove this
4 | */
5 |
6 | var Parser = require("./Parser");
7 |
8 | /**
9 | * Parses an expression using a Parser, then returns the parsed result.
10 | */
11 | var parseTree = function(toParse, settings) {
12 | var parser = new Parser(toParse, settings);
13 |
14 | return parser.parse();
15 | };
16 |
17 | module.exports = parseTree;
18 |
--------------------------------------------------------------------------------
/app/vendor/katex/src/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains a list of utility functions which are useful in other
3 | * files.
4 | */
5 |
6 | /**
7 | * Provide an `indexOf` function which works in IE8, but defers to native if
8 | * possible.
9 | */
10 | var nativeIndexOf = Array.prototype.indexOf;
11 | var indexOf = function(list, elem) {
12 | if (list == null) {
13 | return -1;
14 | }
15 | if (nativeIndexOf && list.indexOf === nativeIndexOf) {
16 | return list.indexOf(elem);
17 | }
18 | var i = 0;
19 | var l = list.length;
20 | for (; i < l; i++) {
21 | if (list[i] === elem) {
22 | return i;
23 | }
24 | }
25 | return -1;
26 | };
27 |
28 | /**
29 | * Return whether an element is contained in a list
30 | */
31 | var contains = function(list, elem) {
32 | return indexOf(list, elem) !== -1;
33 | };
34 |
35 | /**
36 | * Provide a default value if a setting is undefined
37 | */
38 | var deflt = function(setting, defaultIfUndefined) {
39 | return setting === undefined ? defaultIfUndefined : setting;
40 | };
41 |
42 | // hyphenate and escape adapted from Facebook's React under Apache 2 license
43 |
44 | var uppercase = /([A-Z])/g;
45 | var hyphenate = function(str) {
46 | return str.replace(uppercase, "-$1").toLowerCase();
47 | };
48 |
49 | var ESCAPE_LOOKUP = {
50 | "&": "&",
51 | ">": ">",
52 | "<": "<",
53 | "\"": """,
54 | "'": "'",
55 | };
56 |
57 | var ESCAPE_REGEX = /[&><"']/g;
58 |
59 | function escaper(match) {
60 | return ESCAPE_LOOKUP[match];
61 | }
62 |
63 | /**
64 | * Escapes text to prevent scripting attacks.
65 | *
66 | * @param {*} text Text value to escape.
67 | * @return {string} An escaped string.
68 | */
69 | function escape(text) {
70 | return ("" + text).replace(ESCAPE_REGEX, escaper);
71 | }
72 |
73 | /**
74 | * A function to set the text content of a DOM element in all supported
75 | * browsers. Note that we don't define this if there is no document.
76 | */
77 | var setTextContent;
78 | if (typeof document !== "undefined") {
79 | var testNode = document.createElement("span");
80 | if ("textContent" in testNode) {
81 | setTextContent = function(node, text) {
82 | node.textContent = text;
83 | };
84 | } else {
85 | setTextContent = function(node, text) {
86 | node.innerText = text;
87 | };
88 | }
89 | }
90 |
91 | /**
92 | * A function to clear a node.
93 | */
94 | function clearNode(node) {
95 | setTextContent(node, "");
96 | }
97 |
98 | module.exports = {
99 | contains: contains,
100 | deflt: deflt,
101 | escape: escape,
102 | hyphenate: hyphenate,
103 | indexOf: indexOf,
104 | setTextContent: setTextContent,
105 | clearNode: clearNode,
106 | };
107 |
--------------------------------------------------------------------------------
/app/vendor/markdown-it-katex/index.js:
--------------------------------------------------------------------------------
1 | /* Process inline math */
2 | /*
3 | Like markdown-it-simplemath, this is a stripped down, simplified version of:
4 | https://github.com/runarberg/markdown-it-math
5 |
6 | It differs in that it takes (a subset of) LaTeX as input and relies on KaTeX
7 | for rendering output.
8 | */
9 |
10 | /*jslint node: true */
11 | 'use strict';
12 |
13 | var katex = require('../katex/katex');
14 |
15 | // Test if potential opening or closing delimieter
16 | // Assumes that there is a "$" at state.src[pos]
17 | function isValidDelim(state, pos) {
18 | var prevChar, nextChar,
19 | max = state.posMax,
20 | can_open = true,
21 | can_close = true;
22 |
23 | prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
24 | nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
25 |
26 | // Check non-whitespace conditions for opening and closing, and
27 | // check that closing delimeter isn't followed by a number
28 | if (prevChar === 0x20/* " " */ || prevChar === 0x09/* \t */ ||
29 | (nextChar >= 0x30/* "0" */ && nextChar <= 0x39/* "9" */)) {
30 | can_close = false;
31 | }
32 | if (nextChar === 0x20/* " " */ || nextChar === 0x09/* \t */) {
33 | can_open = false;
34 | }
35 |
36 | return {
37 | can_open: can_open,
38 | can_close: can_close
39 | };
40 | }
41 |
42 | function math_inline(state, silent) {
43 | var start, match, token, res, pos, esc_count;
44 |
45 | if (state.src[state.pos] !== "$") { return false; }
46 |
47 | res = isValidDelim(state, state.pos);
48 | if (!res.can_open) {
49 | if (!silent) { state.pending += "$"; }
50 | state.pos += 1;
51 | return true;
52 | }
53 |
54 | // First check for and bypass all properly escaped delimieters
55 | // This loop will assume that the first leading backtick can not
56 | // be the first character in state.src, which is known since
57 | // we have found an opening delimieter already.
58 | start = state.pos + 1;
59 | match = start;
60 | while ( (match = state.src.indexOf("$", match)) !== -1) {
61 | // Found potential $, look for escapes, pos will point to
62 | // first non escape when complete
63 | pos = match - 1;
64 | while (state.src[pos] === "\\") { pos -= 1; }
65 |
66 | // Even number of escapes, potential closing delimiter found
67 | if ( ((match - pos) % 2) == 1 ) { break; }
68 | match += 1;
69 | }
70 |
71 | // No closing delimter found. Consume $ and continue.
72 | if (match === -1) {
73 | if (!silent) { state.pending += "$"; }
74 | state.pos = start;
75 | return true;
76 | }
77 |
78 | // Check if we have empty content, ie: $$. Do not parse.
79 | if (match - start === 0) {
80 | if (!silent) { state.pending += "$$"; }
81 | state.pos = start + 1;
82 | return true;
83 | }
84 |
85 | // Check for valid closing delimiter
86 | res = isValidDelim(state, match);
87 | if (!res.can_close) {
88 | if (!silent) { state.pending += "$"; }
89 | state.pos = start;
90 | return true;
91 | }
92 |
93 | if (!silent) {
94 | token = state.push('math_inline', 'math', 0);
95 | token.markup = "$";
96 | token.content = state.src.slice(start, match);
97 | }
98 |
99 | state.pos = match + 1;
100 | return true;
101 | }
102 |
103 | function math_block(state, start, end, silent){
104 | var firstLine, lastLine, next, lastPos, found = false, token,
105 | pos = state.bMarks[start] + state.tShift[start],
106 | max = state.eMarks[start]
107 |
108 | if(pos + 2 > max){ return false; }
109 | if(state.src.slice(pos,pos+2)!=='$$'){ return false; }
110 |
111 | pos += 2;
112 | firstLine = state.src.slice(pos,max);
113 |
114 | if(silent){ return true; }
115 | if(firstLine.trim().slice(-2)==='$$'){
116 | // Single line expression
117 | firstLine = firstLine.trim().slice(0, -2);
118 | found = true;
119 | }
120 |
121 | for(next = start; !found; ){
122 |
123 | next++;
124 |
125 | if(next >= end){ break; }
126 |
127 | pos = state.bMarks[next]+state.tShift[next];
128 | max = state.eMarks[next];
129 |
130 | if(pos < max && state.tShift[next] < state.blkIndent){
131 | // non-empty line with negative indent should stop the list:
132 | break;
133 | }
134 |
135 | if(state.src.slice(pos,max).trim().slice(-2)==='$$'){
136 | lastPos = state.src.slice(0,max).lastIndexOf('$$');
137 | lastLine = state.src.slice(pos,lastPos);
138 | found = true;
139 | }
140 |
141 | }
142 |
143 | state.line = next + 1;
144 |
145 | token = state.push('math_block', 'math', 0);
146 | token.block = true;
147 | token.content = (firstLine && firstLine.trim() ? firstLine + '\n' : '')
148 | + state.getLines(start + 1, next, state.tShift[start], true)
149 | + (lastLine && lastLine.trim() ? lastLine : '');
150 | token.map = [ start, state.line ];
151 | token.markup = '$$';
152 | return true;
153 | }
154 |
155 | module.exports = function math_plugin(md, options) {
156 | // Default options
157 |
158 | options = options || {};
159 |
160 | // set KaTeX as the renderer for markdown-it-simplemath
161 | var katexInline = function(latex){
162 | options.displayMode = false;
163 | try{
164 | return katex.renderToString(latex, options);
165 | }
166 | catch(error){
167 | if(options.throwOnError){ console.log(error); }
168 | return latex;
169 | }
170 | };
171 |
172 | var inlineRenderer = function(tokens, idx){
173 | return katexInline(tokens[idx].content);
174 | };
175 |
176 | var katexBlock = function(latex){
177 | options.displayMode = true;
178 | try{
179 | return "" + katex.renderToString(latex, options) + "
";
180 | }
181 | catch(error){
182 | if(options.throwOnError){ console.log(error); }
183 | return latex;
184 | }
185 | }
186 |
187 | var blockRenderer = function(tokens, idx){
188 | return katexBlock(tokens[idx].content) + '\n';
189 | }
190 |
191 | md.inline.ruler.after('escape', 'math_inline', math_inline);
192 | md.block.ruler.after('blockquote', 'math_block', math_block, {
193 | alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
194 | });
195 | md.renderer.rules.math_inline = inlineRenderer;
196 | md.renderer.rules.math_block = blockRenderer;
197 | };
198 |
--------------------------------------------------------------------------------
/app/vendor/mousetrap/global-bind.js:
--------------------------------------------------------------------------------
1 | (function(a){var c={},d=a.prototype.stopCallback;a.prototype.stopCallback=function(e,b,a,f){return this.paused?!0:c[a]||c[f]?!1:d.call(this,e,b,a)};a.prototype.bindGlobal=function(a,b,d){this.bind(a,b,d);if(a instanceof Array)for(b=0;bl||k.hasOwnProperty(l)&&(n[k[l]]=l)}e=n[h]?"keydown":"keypress"}"keypress"==e&&g.length&&(e="keydown");return{key:c,modifiers:g,action:e}}function C(a,b){return null===a||a===t?!1:a===b?!0:C(a.parentNode,b)}function c(a){function b(a){a=
4 | a||{};var b=!1,m;for(m in n)a[m]?b=!0:n[m]=0;b||(w=!1)}function h(a,b,m,f,c,h){var g,e,k=[],l=m.type;if(!d._callbacks[a])return[];"keyup"==l&&v(a)&&(b=[a]);for(g=0;g":".","?":"/","|":"\\"},A={option:"alt",command:"meta","return":"enter",
9 | escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},n;for(g=1;20>g;++g)k[111+g]="f"+g;for(g=0;9>=g;++g)k[g+96]=g;c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};this._directMap=
10 | {};return this};c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||C(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(k[b]=a[b]);n=null};c.init=function(){var a=c(t),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};
11 | c.init();r.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=c);"function"===typeof define&&define.amd&&define(function(){return c})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
12 |
--------------------------------------------------------------------------------
/app/welcome-guide.md:
--------------------------------------------------------------------------------
1 | ## Hola!
2 |
3 | The best time to learn Markdown is 10 years ago, the second is __now__!
4 |
5 | EME is trying to give you the best experience of writing in Markdown.
6 |
7 | Markdown shines ✨
8 |
9 | 
10 |
11 | ## Focus Mode
12 |
13 | The first feature that most markdown editors do not have is, focus the paragraph you are actually writing. It reduces a lot of distractions when you are working on a long post. Use Ctrl/CMD + \ to toggle it.
14 |
15 | ## Distraction Free Mode
16 |
17 | One step further, you may want to hide the header and footer when you are writing in full-screen. Use Ctrl/CMD + J to toggle it.
18 |
19 | ## Vim Mode
20 |
21 | For developers, you may miss the vim key binding, here it is. Use Ctrl/CMD + I to toggle it.
22 |
23 | ## Shortcuts
24 |
25 | Please view: https://github.com/egoist/eme/wiki/Key-bindings
26 |
--------------------------------------------------------------------------------
/build/icons/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/build/icons/512x512.png
--------------------------------------------------------------------------------
/build/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/build/icons/icon.icns
--------------------------------------------------------------------------------
/build/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/build/icons/icon.ico
--------------------------------------------------------------------------------
/build/icons/markdown_file_association.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/build/icons/markdown_file_association.icns
--------------------------------------------------------------------------------
/build/icons/markdown_file_association.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/build/icons/markdown_file_association.ico
--------------------------------------------------------------------------------
/electron-builder.json:
--------------------------------------------------------------------------------
1 | {
2 | "appId": "com.egoistian.eme",
3 | "asar": true,
4 | "copyright": "Copyright @2020 The EME Authors",
5 | "win": {
6 | "icon": "build/icons/icon.ico",
7 | "target": "squirrel"
8 | },
9 | "linux": {
10 | "category": "public.app-category.utilities",
11 | "target": [
12 | "deb",
13 | "tar.xz"
14 | ]
15 | },
16 | "mac": {
17 | "icon": "build/icons/icon.icns"
18 | },
19 |
20 | "fileAssociations": {
21 | "ext": "md",
22 | "icon": "build/icons/markdown_file_association.icns",
23 | "name": "Markdown File",
24 | "role": "Editor"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/media/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/egoist/eme/c7c2b4fdb0df703473792d4ad7a369d8c0adccaf/media/preview.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eme",
3 | "description": "elegant markdown editor",
4 | "main": "app/index.js",
5 | "scripts": {
6 | "test": "npm run lint",
7 | "lint": "npm run lint:app && npm run lint:src",
8 | "lint:app": "eslint app/index.js app/eme --ext .js --fix",
9 | "lint:src": "eslint src/* src/* --ext .js --ext .vue --fix",
10 | "postinstall": "electron-builder install-app-deps",
11 | "app": "cross-env NODE_ENV=development electron app/ --inspect=5800",
12 | "build": "rm -rf app/dist && webpack --progress --config scripts/webpack.config.prod.js --profile --json > stats.json",
13 | "watch": "webpack --config scripts/webpack.config.dev.js --watch",
14 | "dist": "npm run mac && npm run linux && npm run win",
15 | "mac": "electron-builder --mac --config electron-builder.json",
16 | "linux": "electron-builder --linux --config electron-builder.json",
17 | "win": "electron-builder --config electron-builder.json",
18 | "stats": "webpack-bundle-analyzer stats.json"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/egoist/eme.git"
23 | },
24 | "keywords": [
25 | "markdown",
26 | "editor"
27 | ],
28 | "devDependencies": {
29 | "autoprefixer": "^9.3.0",
30 | "@babel/core": "^7.10.0",
31 | "@babel/plugin-proposal-object-rest-spread": "^7.10.0",
32 | "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
33 | "@babel/plugin-transform-runtime": "^7.10.0",
34 | "@babel/plugin-transform-spread": "^7.10.0",
35 | "babel-eslint": "^6.1.2",
36 | "babel-loader": "^8.1.0",
37 | "cross-env": "^2.0.0",
38 | "css-loader": "^0.23.1",
39 | "devtron": "^1.2.1",
40 | "electron": "^7.2.4",
41 | "electron-builder": "^22.0.0",
42 | "electron-builder-squirrel-windows": "^11.6.1",
43 | "electron-devtools-installer": "^2.0.0",
44 | "electron-packager": "^11.2.1",
45 | "eslint": "^3.12.2",
46 | "eslint-config-xo-space": "^0.15.0",
47 | "eslint-plugin-babel": "^4.0.0",
48 | "eslint-plugin-vue": "^2.0.0",
49 | "extract-text-webpack-plugin": "^2.1.2",
50 | "file-loader": "^0.9.0",
51 | "json-loader": "^0.5.4",
52 | "minimist": "^1.2.3",
53 | "postcss-import": "^12.0.0",
54 | "postcss-loader": "^1.2.1",
55 | "postcss-mixins": "^5.0.0",
56 | "postcss-nested": "^1.0.0",
57 | "postcss-simple-vars": "^3.0.0",
58 | "scripy": "^0.3.0",
59 | "shelljs": "^0.7.4",
60 | "style-loader": "^0.13.1",
61 | "svg-inline-loader": "^0.6.1",
62 | "ts-loader": "^3.5.0",
63 | "typescript": "^3.4.5",
64 | "url-loader": "^0.5.7",
65 | "vue": "^2.6.10",
66 | "vue-hot-reload-api": "^2.0.6",
67 | "vue-html-loader": "^1.2.3",
68 | "vue-loader": "^12.0.3",
69 | "vue-style-loader": "^1.0.0",
70 | "vue-template-compiler": "^2.6.10",
71 | "webpack": "^2.7.0",
72 | "webpack-bundle-analyzer": "^3.3.2",
73 | "webpack-hot-middleware": "^2.12.1"
74 | },
75 | "dependencies": {
76 | "@wordpress/wordcount": "^2.2.0",
77 | "babel-runtime": "^6.11.6",
78 | "codemirror": "^5.17.0",
79 | "color-preset": "^0.1.1",
80 | "fetch-enhance": "^0.1.1",
81 | "highlight.js": "^9.5.0",
82 | "hint.css": "2.3.2",
83 | "markdown-it": "^7.0.0",
84 | "markdown-it-front-matter": "^0.1.2",
85 | "markdown-it-task-lists": "^1.4.0",
86 | "object-picker": "^0.2.0",
87 | "pify": "^2.3.0",
88 | "sanitize-html": "^1.13.0",
89 | "sumchecker": "^2.0.1",
90 | "unique-random-array": "^2.0.0",
91 | "vuex": "^2.1.1"
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/scripts/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const webpack = require('webpack')
3 | const config = require('./webpack.config')
4 |
5 | config.devtool = 'cheap-module-inline-source-map'
6 | config.plugins = config.plugins.concat([
7 | new webpack.NoErrorsPlugin(),
8 | /*eslint-disable */
9 | new webpack.DefinePlugin({
10 | __DEV__: true,
11 | 'process.env.NODE_ENV': JSON.stringify('development')
12 | })
13 | ])
14 |
15 | module.exports = config
16 |
--------------------------------------------------------------------------------
/scripts/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const webpack = require('webpack')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const appPkg = require('../app/package')
6 |
7 | const postcss = [
8 | require('postcss-nested'),
9 | require('postcss-import')(),
10 | require('postcss-simple-vars'),
11 | require('postcss-mixins'),
12 | require('autoprefixer')({
13 | overrideBrowsersList: ['last 2 Chrome versions']
14 | })
15 | ]
16 |
17 | module.exports = {
18 | entry: {
19 | app: './src/index.ts',
20 | vendor: ['vue', 'vuex']
21 | },
22 | output: {
23 | path: process.cwd() + '/app/libvue',
24 | filename: '[name].js'
25 | },
26 | resolve: {
27 | extensions: ['.js', '.vue', '.css', '.json', '.ts'],
28 | alias: {
29 | 'vue$': 'vue/dist/vue.esm.js',
30 | src: path.join(__dirname, '../src'),
31 | utils: path.join(__dirname, '../src/utils'),
32 | components: path.join(__dirname, '../src/components'),
33 | css: path.join(__dirname, '../src/css'),
34 | directives: path.join(__dirname, '../src/directives'),
35 | store: path.join(__dirname, '../src/vuex/store')
36 | }
37 | },
38 | module: {
39 | rules: [
40 | {
41 | test: /\.tsx?$/,
42 | loaders: 'ts-loader',
43 | exclude: /node_modules/,
44 | options: {
45 | appendTsSuffixTo: [/\.vue$/],
46 | }
47 | },
48 | {
49 | test: /\.vue$/,
50 | loader: 'vue-loader',
51 | options: {
52 | autoprefixer: false,
53 | postcss,
54 | loaders: {
55 | css: ExtractTextPlugin.extract({
56 | fallback: 'vue-style-loader',
57 | use: 'css-loader?sourceMap'
58 | }
59 | )
60 | }
61 | }
62 | },
63 | {
64 | test: /\.js$/,
65 | loader: 'babel-loader',
66 | exclude: file => (
67 | /node_modules/.test(file)
68 | )
69 | },
70 | {
71 | test: /\.svg$/,
72 | exclude: /node_modules/,
73 | loader: 'svg-inline-loader'
74 | },
75 | {
76 | test: /\.css$/,
77 | exclude: /node_modules/,
78 | loader: ExtractTextPlugin.extract(
79 | {
80 | fallback: 'style-loader',
81 | use: 'css-loader!postcss-loader'
82 | }
83 | )
84 | },
85 | ],
86 | },
87 | target: 'electron-renderer',
88 | plugins: [
89 | new webpack.ExternalsPlugin('commonjs2', [
90 | './vendor/markdown-it-katex',
91 | '../package.json'
92 | ].concat(Object.keys(appPkg.dependencies))),
93 | new ExtractTextPlugin('[name].css'),
94 | new webpack.optimize.CommonsChunkPlugin({
95 | name: 'vendor',
96 | filename: 'vendor.js'
97 | })
98 | ],
99 |
100 | devtool: '#eval-source-map'
101 | }
102 |
--------------------------------------------------------------------------------
/scripts/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const webpack = require('webpack')
3 | const config = require('./webpack.config')
4 |
5 | config.plugins = config.plugins.concat([
6 | new webpack.optimize.UglifyJsPlugin({
7 | sourceMap: true,
8 | compressor: {
9 | warnings: false
10 | },
11 | comments: false
12 | }),
13 | new webpack.DefinePlugin({
14 | '__DEV__': false,
15 | 'process.env.NODE_ENV': JSON.stringify('production')
16 | })
17 | ])
18 |
19 | module.exports = config
20 |
--------------------------------------------------------------------------------
/src/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
18 |
19 |
29 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
105 |
--------------------------------------------------------------------------------
/src/components/footer.vue:
--------------------------------------------------------------------------------
1 |
79 |
80 |
81 |
115 |
116 |
117 |
160 |
--------------------------------------------------------------------------------
/src/components/header.vue:
--------------------------------------------------------------------------------
1 |
148 |
149 |
150 |
194 |
195 |
196 |
295 |
--------------------------------------------------------------------------------
/src/components/svg-icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
45 |
--------------------------------------------------------------------------------
/src/components/tip.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
33 |
34 |
35 |
60 |
--------------------------------------------------------------------------------
/src/components/writing-modes.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
77 |
--------------------------------------------------------------------------------
/src/css/codemirror/base16-light.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Name: Base16 Default Light
4 | Author: Chris Kempson (http://chriskempson.com)
5 |
6 | CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror)
7 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16)
8 |
9 | */
10 |
11 | .cm-s-base16-light.CodeMirror {
12 | background: #f5f5f5;
13 | color: #202020;
14 | }
15 | .cm-s-base16-light div.CodeMirror-selected {
16 | background: #e0e0e0;
17 | }
18 | .cm-s-base16-light .CodeMirror-line::selection, .cm-s-base16-light .CodeMirror-line> span::selection, .cm-s-base16-light .CodeMirror-line> span> span::selection {
19 | background: #e0e0e0;
20 | }
21 | .cm-s-base16-light .CodeMirror-line::-moz-selection, .cm-s-base16-light .CodeMirror-line> span::-moz-selection, .cm-s-base16-light .CodeMirror-line> span> span::-moz-selection {
22 | background: #e0e0e0;
23 | }
24 | .cm-s-base16-light .CodeMirror-gutters {
25 | background: #f5f5f5;
26 | border-right: 0px;
27 | }
28 | .cm-s-base16-light .CodeMirror-guttermarker {
29 | color: #ac4142;
30 | }
31 | .cm-s-base16-light .CodeMirror-guttermarker-subtle {
32 | color: #b0b0b0;
33 | }
34 | .cm-s-base16-light .CodeMirror-linenumber {
35 | color: #b0b0b0;
36 | }
37 | .cm-s-base16-light .CodeMirror-cursor {
38 | border-left: 1px solid #505050;
39 | }
40 | .cm-s-base16-light span.cm-comment {
41 | color: #8f5536;
42 | }
43 | .cm-s-base16-light span.cm-atom {
44 | color: #aa759f;
45 | }
46 | .cm-s-base16-light span.cm-number {
47 | color: #aa759f;
48 | }
49 | .cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute {
50 | color: #90a959;
51 | }
52 | .cm-s-base16-light span.cm-keyword {
53 | color: #ac4142;
54 | }
55 | .cm-s-base16-light span.cm-string {
56 | color: #f4bf75;
57 | }
58 | .cm-s-base16-light span.cm-variable {
59 | color: #90a959;
60 | }
61 | .cm-s-base16-light span.cm-variable-2 {
62 | color: #6a9fb5;
63 | }
64 | .cm-s-base16-light span.cm-def {
65 | color: #d28445;
66 | }
67 | .cm-s-base16-light span.cm-bracket {
68 | color: #202020;
69 | }
70 | .cm-s-base16-light span.cm-tag {
71 | color: #ac4142;
72 | }
73 | .cm-s-base16-light span.cm-link {
74 | color: #aa759f;
75 | }
76 | .cm-s-base16-light span.cm-error {
77 | background: #ac4142;
78 | color: #505050;
79 | }
80 | .cm-s-base16-light .CodeMirror-activeline-background {
81 | background: #DDDCDC;
82 | }
83 | .cm-s-base16-light .CodeMirror-matchingbracket {
84 | text-decoration: underline;
85 | color: black !important;
86 | }
87 |
--------------------------------------------------------------------------------
/src/css/codemirror/editor-dialog.css:
--------------------------------------------------------------------------------
1 | .CodeMirror-dialog {
2 | position: absolute;
3 | left: 0;
4 | right: 0;
5 | background: inherit;
6 | z-index: 15;
7 | padding: 0 .8em;
8 | height: 1.75rem;
9 | line-height: 1.75rem;
10 | overflow: hidden;
11 | color: inherit;
12 | }
13 |
14 | .CodeMirror-dialog-top {
15 | top: 0;
16 | border-bottom: 1px solid #ddd;
17 | }
18 |
19 | .CodeMirror-dialog-bottom {
20 | bottom: 0;
21 | }
22 |
23 | .CodeMirror-dialog input {
24 | border: none;
25 | outline: none;
26 | background: transparent;
27 | width: 20em;
28 | color: inherit;
29 | font-family: inherit;
30 | }
31 |
32 | .CodeMirror-dialog button {
33 | font-size: 70%;
34 | }
35 |
--------------------------------------------------------------------------------
/src/css/codemirror/editor-reset.css:
--------------------------------------------------------------------------------
1 | .editor {
2 | &.focus-mode {
3 | * {
4 | color: #ccc !important;
5 | }
6 | }
7 | .cm-s-base16-light {
8 | .CodeMirror-activeline-background {
9 | background-color: transparent;
10 | }
11 | .CodeMirror-activeline {
12 | * {
13 | color: #333 !important;
14 | }
15 | }
16 | }
17 | .CodeMirror-dialog input {
18 | font-family: inherit !important;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/css/codemirror/editor-scrollbar.css:
--------------------------------------------------------------------------------
1 | .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
2 | position: absolute;
3 | background: #f0f0f0;
4 | -moz-box-sizing: border-box;
5 | box-sizing: border-box;
6 | border: 1px solid #e2e2e2;
7 | border-radius: 2px;
8 | }
9 |
10 | .CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
11 | position: absolute;
12 | z-index: 6;
13 | background: #eee;
14 | }
15 |
16 | .CodeMirror-simplescroll-horizontal {
17 | bottom: 0; left: 0;
18 | height: 8px;
19 | }
20 | .CodeMirror-simplescroll-horizontal div {
21 | bottom: 0;
22 | height: 100%;
23 | }
24 |
25 | .CodeMirror-simplescroll-vertical {
26 | right: 0; top: 0;
27 | width: 8px;
28 | background-color: transparent;
29 | visibility: hidden;
30 | cursor: default;
31 | }
32 | .editor:hover .CodeMirror-simplescroll-vertical {
33 | visibility: visible;
34 | }
35 | .CodeMirror-simplescroll-vertical div {
36 | right: 0;
37 | width: 100%;
38 | }
39 |
40 |
41 | .CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
42 | display: none;
43 | }
44 |
45 | .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
46 | position: absolute;
47 | background: #bcd;
48 | border-radius: 3px;
49 | }
50 |
51 | .CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
52 | position: absolute;
53 | z-index: 6;
54 | }
55 |
56 | .CodeMirror-overlayscroll-horizontal {
57 | bottom: 0; left: 0;
58 | height: 6px;
59 | }
60 | .CodeMirror-overlayscroll-horizontal div {
61 | bottom: 0;
62 | height: 100%;
63 | }
64 |
65 | .CodeMirror-overlayscroll-vertical {
66 | right: 0; top: 0;
67 | width: 6px;
68 | }
69 | .CodeMirror-overlayscroll-vertical div {
70 | right: 0;
71 | width: 100%;
72 | }
73 |
--------------------------------------------------------------------------------
/src/css/codemirror/tomorrow-night-bright.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Name: Tomorrow Night - Bright
4 | Author: Chris Kempson
5 |
6 | Port done by Gerard Braad
7 |
8 | */
9 |
10 | .cm-s-tomorrow-night-bright.CodeMirror { background: #000000; color: #eaeaea; }
11 | .cm-s-tomorrow-night-bright div.CodeMirror-selected { background: #424242; }
12 | .cm-s-tomorrow-night-bright .CodeMirror-gutters { background: #000000; border-right: 0px; }
13 | .cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; }
14 | .cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; }
15 | .cm-s-tomorrow-night-bright .CodeMirror-linenumber { color: #424242; }
16 | .cm-s-tomorrow-night-bright .CodeMirror-cursor { border-left: 1px solid #6A6A6A; }
17 |
18 | .cm-s-tomorrow-night-bright span.cm-comment { color: #d27b53; }
19 | .cm-s-tomorrow-night-bright span.cm-atom { color: #a16a94; }
20 | .cm-s-tomorrow-night-bright span.cm-number { color: #a16a94; }
21 |
22 | .cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute { color: #99cc99; }
23 | .cm-s-tomorrow-night-bright span.cm-keyword { color: #d54e53; }
24 | .cm-s-tomorrow-night-bright span.cm-string { color: #e7c547; }
25 |
26 | .cm-s-tomorrow-night-bright span.cm-variable { color: #b9ca4a; }
27 | .cm-s-tomorrow-night-bright span.cm-variable-2 { color: #7aa6da; }
28 | .cm-s-tomorrow-night-bright span.cm-def { color: #e78c45; }
29 | .cm-s-tomorrow-night-bright span.cm-bracket { color: #eaeaea; }
30 | .cm-s-tomorrow-night-bright span.cm-tag { color: #d54e53; }
31 | .cm-s-tomorrow-night-bright span.cm-link { color: #a16a94; }
32 | .cm-s-tomorrow-night-bright span.cm-error { background: #d54e53; color: #6A6A6A; }
33 |
34 | .cm-s-tomorrow-night-bright .CodeMirror-activeline-background { background: #2a2a2a; }
35 | .cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
36 |
--------------------------------------------------------------------------------
/src/css/highlight/github.css:
--------------------------------------------------------------------------------
1 | .github {
2 | /*
3 |
4 | github.com style (c) Vasily Polovnyov
5 |
6 | */
7 |
8 | .hljs {
9 | display: block;
10 | overflow-x: auto;
11 | padding: 0.5em;
12 | color: #333;
13 | background: #f8f8f8;
14 | }
15 |
16 | .hljs-comment,
17 | .hljs-quote {
18 | color: #998;
19 | font-style: italic;
20 | }
21 |
22 | .hljs-keyword,
23 | .hljs-selector-tag,
24 | .hljs-subst {
25 | color: #333;
26 | font-weight: bold;
27 | }
28 |
29 | .hljs-number,
30 | .hljs-literal,
31 | .hljs-variable,
32 | .hljs-template-variable,
33 | .hljs-tag .hljs-attr {
34 | color: #008080;
35 | }
36 |
37 | .hljs-string,
38 | .hljs-doctag {
39 | color: #d14;
40 | }
41 |
42 | .hljs-title,
43 | .hljs-section,
44 | .hljs-selector-id {
45 | color: #900;
46 | font-weight: bold;
47 | }
48 |
49 | .hljs-subst {
50 | font-weight: normal;
51 | }
52 |
53 | .hljs-type,
54 | .hljs-class .hljs-title {
55 | color: #458;
56 | font-weight: bold;
57 | }
58 |
59 | .hljs-tag,
60 | .hljs-name,
61 | .hljs-attribute {
62 | color: #000080;
63 | font-weight: normal;
64 | }
65 |
66 | .hljs-regexp,
67 | .hljs-link {
68 | color: #009926;
69 | }
70 |
71 | .hljs-symbol,
72 | .hljs-bullet {
73 | color: #990073;
74 | }
75 |
76 | .hljs-built_in,
77 | .hljs-builtin-name {
78 | color: #0086b3;
79 | }
80 |
81 | .hljs-meta {
82 | color: #999;
83 | font-weight: bold;
84 | }
85 |
86 | .hljs-deletion {
87 | background: #fdd;
88 | }
89 |
90 | .hljs-addition {
91 | background: #dfd;
92 | }
93 |
94 | .hljs-emphasis {
95 | font-style: italic;
96 | }
97 |
98 | .hljs-strong {
99 | font-weight: bold;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/css/highlight/tomorrow-night-bright.css:
--------------------------------------------------------------------------------
1 | .tomorrow-night-bright {
2 | /* Tomorrow Night Bright Theme */
3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
5 |
6 | /* Tomorrow Comment */
7 | .hljs-comment,
8 | .hljs-quote {
9 | color: #969896;
10 | }
11 |
12 | /* Tomorrow Red */
13 | .hljs-variable,
14 | .hljs-template-variable,
15 | .hljs-tag,
16 | .hljs-name,
17 | .hljs-selector-id,
18 | .hljs-selector-class,
19 | .hljs-regexp,
20 | .hljs-deletion {
21 | color: #d54e53;
22 | }
23 |
24 | /* Tomorrow Orange */
25 | .hljs-number,
26 | .hljs-built_in,
27 | .hljs-builtin-name,
28 | .hljs-literal,
29 | .hljs-type,
30 | .hljs-params,
31 | .hljs-meta,
32 | .hljs-link {
33 | color: #e78c45;
34 | }
35 |
36 | /* Tomorrow Yellow */
37 | .hljs-attribute {
38 | color: #e7c547;
39 | }
40 |
41 | /* Tomorrow Green */
42 | .hljs-string,
43 | .hljs-symbol,
44 | .hljs-bullet,
45 | .hljs-addition {
46 | color: #b9ca4a;
47 | }
48 |
49 | /* Tomorrow Blue */
50 | .hljs-title,
51 | .hljs-section {
52 | color: #7aa6da;
53 | }
54 |
55 | /* Tomorrow Purple */
56 | .hljs-keyword,
57 | .hljs-selector-tag {
58 | color: #c397d8;
59 | }
60 |
61 | .hljs {
62 | display: block;
63 | overflow-x: auto;
64 | background: black;
65 | color: #eaeaea;
66 | padding: 0.5em;
67 | }
68 |
69 | .hljs-emphasis {
70 | font-style: italic;
71 | }
72 |
73 | .hljs-strong {
74 | font-weight: bold;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/css/normalize.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 14px;
3 | }
4 |
5 | * {
6 | box-sizing: border-box;
7 | }
8 |
9 | body {
10 | margin: 0;
11 | font-size: 1rem;
12 | line-height: 1.5;
13 | overflow: hidden;
14 | text-rendering: geometricPrecision;
15 | font-family: -apple-system, BlinkMacSystemFont,
16 | 'avenir next', avenir,
17 | helvetica, 'helvetica neue',
18 | Ubuntu,
19 | 'segoe ui', arial,
20 | sans-serif;
21 | }
22 |
23 | html,
24 | body,
25 | #app,
26 | #eme {
27 | height: 100%;
28 | width: 100%;
29 | }
30 |
--------------------------------------------------------------------------------
/src/css/reset.css:
--------------------------------------------------------------------------------
1 | .CodeMirror {
2 | font-family: inherit !important;
3 | }
4 |
5 | .markdown-body {
6 | word-break: break-word;
7 | font-family: inherit;
8 |
9 | *::-webkit-scrollbar {
10 | background-color: transparent;
11 | height: 8px;
12 | }
13 |
14 | *::-webkit-scrollbar-thumb {
15 | background-color: #f0f0f0;
16 | border-radius: 2px;
17 | border: 1px solid #e2e2e2;
18 | }
19 |
20 | kbd {
21 | line-height: 1em;
22 | font-size: .85em;
23 | }
24 | }
25 |
26 | .markdown-body ul{
27 | padding-left: 20px;
28 | }
29 |
30 | .markdown-body {
31 | .task-list {
32 | padding-left: 0;
33 | .task-list-item {
34 | input {
35 | margin: 0;
36 | }
37 | }
38 | }
39 | }
40 |
41 | input, textarea {
42 | &:focus {
43 | outline: none;
44 | }
45 | }
46 |
47 | /* modified gridly */
48 | .row { display: flex; }
49 | .col { flex: 1; }
50 | @media (max-width: 600px) {
51 | .row { flex-direction: column; }
52 | .col { flex: 0 0 auto; }
53 | }
54 | @media (min-width: 600px) {
55 | .col-tenth { flex: 0 0 10%; }
56 | .col-fifth { flex: 0 0 20%; }
57 | .col-quarter { flex: 0 0 25%; }
58 | .col-third { flex: 0 0 33.3333334%; }
59 | .col-half { flex: 0 0 50%; }
60 | }
61 |
--------------------------------------------------------------------------------
/src/css/theme/dark.css:
--------------------------------------------------------------------------------
1 | @import "color-preset";
2 |
3 | $dark: black;
4 | $light-dark: #333;
5 | $light: #ccc;
6 |
7 | .theme-dark {
8 | background-color: $dark;
9 | .header {
10 | background-color: $dark;
11 | border-bottom-color: $light-dark;
12 | .tab {
13 | border-bottom-color: $light-dark;
14 | border-left-color: $light-dark;
15 | .tab-title {
16 | color: $grey-500;
17 | }
18 | &.current-tab {
19 | background-color: $dark;
20 | .tab-title {
21 | color: white;
22 | }
23 | }
24 | }
25 | &:not(.single-tab) {
26 | .tab:last-child {
27 | border-right-color: $light-dark;
28 | }
29 | }
30 | &.single-tab {
31 | .tab {
32 | .tab-title {
33 | color: white;
34 | }
35 | &.current-tab {
36 | border-color: $light-dark;
37 | }
38 | }
39 | &:hover {
40 | .tab {
41 | &.current-tab {
42 | border-right-color: $light-dark;
43 | }
44 | }
45 | }
46 | }
47 | .settings-trigger {
48 | $color: $light;
49 | $colorActive: $grey-200;
50 |
51 | svg {
52 | color: $color;
53 | circle {
54 | color: $color;
55 | }
56 | }
57 | &:hover {
58 | svg {
59 | color: $colorActive;
60 | circle {
61 | color: $colorActive;
62 | }
63 | }
64 | }
65 | }
66 | }
67 |
68 | .editor {
69 | border-right-color: $light-dark;
70 | .CodeMirror {
71 | background-color: $dark !important;
72 | }
73 | }
74 |
75 | .footer, .footer * {
76 | color: $grey-100;
77 | }
78 |
79 | .footer {
80 | border-top-color: $light-dark;
81 | background-color: $dark;
82 | .footer-icon path {
83 | fill: $grey-100;
84 | }
85 | .footer-right .footer-icon-group .footer-icon-item {
86 | border-color: $light-dark;
87 | .footer-icon path {
88 | fill: #2f2e2e;
89 | }
90 | &:hover {
91 | .footer-icon path {
92 | fill: #ccc;
93 | }
94 | }
95 | &.active {
96 | .footer-icon path {
97 | fill: white;
98 | }
99 | }
100 | }
101 | }
102 |
103 |
104 | .tab-indicator {
105 | .dot {
106 | background-color: $light;
107 | }
108 | .cross {
109 | color: $light;
110 | }
111 | }
112 |
113 | .CodeMirror-simplescroll-horizontal div,
114 | .CodeMirror-simplescroll-vertical div {
115 | background-color: black;
116 | border-color: $grey-800;
117 | }
118 |
119 | .markdown-body {
120 | color: $light;
121 | h1, h2 {
122 | border-bottom-color: $light-dark;
123 | }
124 | kbd {
125 | background-color: transparent;
126 | color: $light;
127 | }
128 |
129 | pre {
130 | background-color: transparent;
131 | code {
132 | color: #50E3C2;
133 | }
134 | }
135 |
136 | *::-webkit-scrollbar-thumb {
137 | background-color: transparent;
138 | border-color: $grey-800;
139 | }
140 |
141 | code {
142 | color: white;
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/css/theme/light.css:
--------------------------------------------------------------------------------
1 | .theme-light {
2 | .header {
3 | background-color: white;
4 | border-bottom-color: #ddd;
5 | .tab {
6 | border-left-color: #ddd;
7 | border-bottom-color: #ddd;
8 | .tab-title {
9 | color: #999;
10 | }
11 | &.current-tab {
12 | border-left-color: #1976D2;
13 | background-color: white;
14 | .tab-title {
15 | color: #333;
16 | }
17 | }
18 | }
19 | &:not(.single-tab) {
20 | .tab:last-child {
21 | border-right-color: #ddd;
22 | }
23 | }
24 | &.single-tab {
25 | .tab {
26 | &.current-tab {
27 | border-color: #ddd;
28 | }
29 | }
30 | &:hover {
31 | .tab {
32 | &.current-tab {
33 | border-right-color: #ddd;
34 | }
35 | }
36 | }
37 | }
38 | .settings-trigger {
39 | $color: #b1b1b1;
40 | $colorActive: #666;
41 |
42 | svg {
43 | color: $color;
44 | circle {
45 | color: $color;
46 | }
47 | }
48 | &:hover {
49 | svg {
50 | color: $colorActive;
51 | circle {
52 | color: $colorActive;
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
59 | .editor {
60 | border-right-color: #e3e3e3;
61 | .CodeMirror {
62 | background-color: white !important;
63 | }
64 | }
65 |
66 | .footer, .footer * {
67 | color: #666;
68 | }
69 | .footer {
70 | border-top-color: #c2c0c2;
71 | box-shadow: inset 0 1px 0 #f5f4f5;
72 | background-image: linear-gradient(to bottom,#e8e6e8 0,#d1cfd1 100%);
73 | .footer-icon path {
74 | fill: #666;
75 | }
76 | .footer-right {
77 | .footer-icon-group {
78 | .footer-icon-item {
79 | border-color: #c2c0c2 #c2c0c2 #a19fa1;
80 | box-shadow: 0 1px 1px rgba(0,0,0,.06);
81 | background-color: #fcfcfc;
82 | background-image: linear-gradient(to bottom,#fcfcfc 0,#f1f1f1 100%);
83 | &.active {
84 | background-color: #6d6c6d;
85 | background-image: none;
86 | color: white;
87 | border-color: transparent;
88 | .footer-icon {
89 | path {
90 | fill: white;
91 | }
92 | }
93 | }
94 | }
95 | }
96 | }
97 | }
98 |
99 | .tab-indicator {
100 | .dot {
101 | background-color: #4b89ff;
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import store from './vuex/store';
3 | import app from './app.vue';
4 |
5 | let v = new Vue({
6 | el: '#eme',
7 | store,
8 | components: {app}
9 | });
10 |
--------------------------------------------------------------------------------
/src/svg-shim.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.svg" {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/src/svg/align-horizontal-middle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/svg/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/svg/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/svg/eye.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/svg/pencil.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/src/svg/settings.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/utils/common.ts:
--------------------------------------------------------------------------------
1 | //Copyright 2019 The EME authors
2 |
3 | export function truncate(str: string, maxLength: number): string {
4 | if(str.length > maxLength) return str.substr(Math.max(0, maxLength - 3)) + "...";
5 | return str;
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/dialog.js:
--------------------------------------------------------------------------------
1 | import {remote} from 'electron'
2 |
3 | export default (() => {
4 | const dialog = {}
5 | Object.keys(remote.dialog).forEach(method => {
6 | dialog[method] = (...args) => {
7 | const result = remote.dialog[method](...args)
8 | return result
9 | }
10 | })
11 | return dialog
12 | })()
13 |
--------------------------------------------------------------------------------
/src/utils/dom.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const _ = module.exports = {}
4 |
5 | _.$ = document.querySelector.bind(document)
6 |
7 | _.$$ = document.querySelectorAll.bind(document)
8 |
--------------------------------------------------------------------------------
/src/utils/event.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'events'
2 |
3 | const event = new EventEmitter()
4 |
5 | export default event
6 |
--------------------------------------------------------------------------------
/src/utils/fs-promise.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import pify from 'pify'
3 |
4 | export default pify(fs)
5 |
--------------------------------------------------------------------------------
/src/utils/gist.js:
--------------------------------------------------------------------------------
1 | import fetch from 'fetch-enhance'
2 | import store from 'store'
3 |
4 | export function createOrUpdateGist(payload, id) {
5 | const token = store.state.app.settings.tokens.github
6 | const headers = new Headers()
7 |
8 | if (token) {
9 | headers.append('Authorization', `token ${token}`)
10 | }
11 | const shouldUpdate = id && token
12 | const method = shouldUpdate ? 'PATCH' : 'POST'
13 | const url = `https://api.github.com/gists${shouldUpdate ? `/${id}` : ''}`
14 | return fetch(url, {
15 | method,
16 | headers,
17 | body: JSON.stringify(payload)
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/handle-error.js:
--------------------------------------------------------------------------------
1 | import dialog from 'utils/dialog'
2 | import {remote} from 'electron'
3 |
4 | export default err => {
5 | return dialog.showMessageBox(remote.getCurrentWindow(), {
6 | message: err.name || 'Error',
7 | detail: err.message,
8 | buttons: ['OK']
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/src/utils/key.js:
--------------------------------------------------------------------------------
1 | import {platform} from './os'
2 |
3 | export const cmdOrCtrl = platform === 'darwin' ? 'command' : 'ctrl'
4 |
5 |
--------------------------------------------------------------------------------
/src/utils/make-html.js:
--------------------------------------------------------------------------------
1 | export default ({html, css, data}) => {
2 | return `
3 |
4 |
5 |
6 |
7 | ${css ? css.map(style => ``).join('\n') : ''}
8 | EME
9 |
10 |
11 | ${Array.isArray(html) ?
12 | html
13 | .map(html => {
14 | const className = data.attrs.align || ''
15 | return `
16 | ${html}
17 |
`
18 | })
19 | .join('') :
20 | `${html}
`}
21 |
22 |
25 |
41 |
42 | `
43 | }
44 |
--------------------------------------------------------------------------------
/src/utils/markdown.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase, max-params */
2 | import MarkdownIt from 'markdown-it'
3 | import taskList from 'markdown-it-task-lists'
4 | import hljs from 'highlight.js/lib/highlight'
5 | import frontMatter from 'markdown-it-front-matter'
6 | import katex from './vendor/markdown-it-katex'
7 |
8 | const langs = [
9 | 'cpp',
10 | 'coffeescript',
11 | 'css',
12 | 'dockerfile',
13 | 'elixir',
14 | 'elm',
15 | 'erlang',
16 | 'go',
17 | 'haskell',
18 | 'ini',
19 | 'javascript',
20 | 'less',
21 | 'lua',
22 | 'makefile',
23 | 'livescript',
24 | 'markdown',
25 | 'matlab',
26 | 'nginx',
27 | 'ocaml',
28 | 'perl',
29 | 'php',
30 | 'python',
31 | 'ruby',
32 | 'scala',
33 | 'rust',
34 | 'scss',
35 | 'sql',
36 | 'stylus',
37 | 'swift',
38 | 'typescript',
39 | 'xml',
40 | 'yaml'
41 | ]
42 |
43 | langs.forEach(lang => {
44 | hljs.registerLanguage(lang, require(`highlight.js/lib/languages/${lang}`))
45 | })
46 |
47 | const md = new MarkdownIt({
48 | html: true,
49 | xhtmlOut: false,
50 | breaks: true,
51 | langPrefix: 'language-',
52 | linkify: true,
53 | typographer: true,
54 | quotes: '“”‘’',
55 | highlight(str, lang) {
56 | if (lang && hljs.getLanguage(lang)) {
57 | try {
58 | return hljs.highlight(lang, str).value
59 | } catch (__) {}
60 | }
61 |
62 | return ''
63 | }
64 | })
65 |
66 | md.use(taskList)
67 | md.use(katex)
68 | md.use(frontMatter, fm => console.log(fm))
69 |
70 | // add target _blank
71 | const defaultRender = md.renderer.rules.link_open || function (tokens, idx, options, env, self) {
72 | return self.renderToken(tokens, idx, options)
73 | }
74 | md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
75 | const aIndex = tokens[idx].attrIndex('target')
76 |
77 | if (aIndex < 0) {
78 | tokens[idx].attrPush(['target', '_blank'])
79 | } else {
80 | tokens[idx].attrs[aIndex][1] = '_blank'
81 | }
82 |
83 | return defaultRender(tokens, idx, options, env, self)
84 | }
85 |
86 | export default md
87 |
--------------------------------------------------------------------------------
/src/utils/os.js:
--------------------------------------------------------------------------------
1 | import os from 'os'
2 |
3 | const platform = os.platform()
4 |
5 | export {
6 | platform
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/resolve-path.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import {remote} from 'electron'
3 |
4 | export function appPath(...args) {
5 | return path.join(remote.app.getAppPath(), ...args)
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/sanitize.js:
--------------------------------------------------------------------------------
1 | import sanitizeHtml from 'sanitize-html'
2 |
3 | function sanitizer(html) {
4 | return sanitizeHtml(html, sanitizer.config)
5 | }
6 |
7 | sanitizer.config = {
8 | allowedTags: sanitizeHtml.defaults.allowedTags.concat([
9 | 'dd', 'del', 'div', 'dl', 'dt', 'h1', 'h2', 'iframe', 'img', 'input', 'ins', 'meta', 'path', 'pre', 's', 'span', 'sub', 'sup', 'svg'
10 | ]),
11 | allowedAttributes: {
12 | h1: ['id'],
13 | h2: ['id'],
14 | h3: ['id'],
15 | h4: ['id'],
16 | h5: ['id'],
17 | h6: ['id'],
18 | a: ['href', 'id', 'name', 'target', 'title', 'aria-hidden'],
19 | img: ['alt', 'id', 'src', 'width', 'height', 'align', 'valign', 'title', 'style'],
20 | p: ['align'],
21 | meta: ['name', 'content'],
22 | iframe: ['src', 'frameborder', 'allowfullscreen'],
23 | input: ['checked', 'class', 'disabled', 'type'],
24 | div: ['id'],
25 | pre: [],
26 | td: ['colspan', 'rowspan', 'style'],
27 | th: ['colspan', 'rowspan', 'style'],
28 | del: ['cite', 'datetime'],
29 | ins: ['cite', 'datetime'],
30 | path: ['d'],
31 | svg: ['aria-hidden', 'height', 'version', 'viewbox', 'width'],
32 | span: ['class', 'style'],
33 | ul: ['class'],
34 | li: ['class']
35 | },
36 | exclusiveFilter(frame) {
37 | // Allow Task List items
38 | if (frame.tag === 'input') {
39 | const isTaskItem = (frame.attribs.class && frame.attribs.class.indexOf('task-list-item-checkbox') > -1)
40 | const isCheckbox = (frame.attribs.type && frame.attribs.type === 'checkbox')
41 | const isDisabled = Object.prototype.hasOwnProperty.call(frame.attribs, 'disabled')
42 | return !(isTaskItem && isCheckbox && isDisabled)
43 | }
44 |
45 | // Allow YouTube iframes
46 | if (frame.tag !== 'iframe') return false
47 | return !String(frame.attribs.src).match(/^(https?:)?\/\/(www\.)?youtube\.com/)
48 | },
49 | transformTags: {
50 | td: sanitizeCellStyle,
51 | th: sanitizeCellStyle
52 | }
53 | }
54 |
55 | // Allow table cell alignment
56 | function sanitizeCellStyle(tagName, attribs) {
57 | // if we don't add the 'style' to the allowedAttributes above, it will be
58 | // stripped out by the time we get here, so we have to filter out
59 | // everything but `text-align` in case something else tries to sneak in
60 | function cell(alignment) {
61 | const attributes = attribs
62 | if (alignment) {
63 | attributes.style = 'text-align:' + alignment
64 | } else {
65 | delete attributes.style
66 | }
67 | return {
68 | tagName,
69 | attribs: attributes
70 | }
71 | }
72 |
73 | // look for CSS `text-align` directives
74 | const alignmentRegEx = /text-align\s*:\s*(left|center|right)[\s;$]*/igm
75 | const result = alignmentRegEx.exec(attribs.style || '')
76 | return result ? cell(result[1]) : cell()
77 | }
78 |
79 | export default sanitizer
80 |
--------------------------------------------------------------------------------
/src/utils/tab.js:
--------------------------------------------------------------------------------
1 |
2 | let id = 0
3 |
4 | export const tabId = {
5 | create() {
6 | return id++
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/utils/tildify.ts:
--------------------------------------------------------------------------------
1 | // Tildify absolute paths
2 |
3 | import * as path from 'path';
4 | import * as os from 'os';
5 |
6 | export default function (currentPath: string): string {
7 | const normalizedPath = path.normalize(currentPath) + path.sep;
8 | const homeDir = os.homedir();
9 |
10 | return (normalizedPath.indexOf(homeDir) === 0 ?
11 | normalizedPath.replace(homeDir + path.sep, `~${path.sep}`):
12 | normalizedPath.slice(0, -1));
13 | }
14 |
--------------------------------------------------------------------------------
/src/utils/tips.js:
--------------------------------------------------------------------------------
1 | import {platform} from './os'
2 |
3 | const metaKey = platform === 'darwin' ? 'Command' : 'Ctrl'
4 |
5 | export default [
6 | `Use ${metaKey} \\ to toggle focus mode`,
7 | `You can drag and drop a file in the editor`,
8 | `Double click the header to create new tab`,
9 | `Use ${metaKey} Shift M to switch writing mode`,
10 | `Try distraction free mode ${metaKey} J when you enter full screen`,
11 | `Arrow keys are available in \`Preview Only\` mode to navigate between slides`,
12 | `At nights use ${metaKey} Shift N to make your eyes feel better`
13 | ]
14 |
--------------------------------------------------------------------------------
/src/utils/transitions.js:
--------------------------------------------------------------------------------
1 | import store from 'src/vuex/store'
2 |
3 | const transition = {
4 | type: 'animation',
5 | beforeEnter() {
6 | store.commit('SLIDE_SWITCHING', true)
7 | },
8 | afterLeave() {
9 | store.commit('SLIDE_SWITCHING', false)
10 | }
11 | }
12 |
13 | const types = [
14 | 'bounce',
15 | 'slide',
16 | 'fade',
17 | 'zoom'
18 | ]
19 |
20 | /**
21 | * TODO Replace Vue.transition(`${type}-${direction}`) with a component that uses the new or element as its root
22 | * @deprecated in Vue2, use transition elements instead: https://vuejs.org/v2/guide/transitions.html#Reusable-Transitions
23 | */
24 | export default Vue => {
25 | const makeTransition = (type, direction) => {
26 | const directions = direction === 'left' ?
27 | ['Right', 'Left'] :
28 | ['Left', 'Right']
29 |
30 | Vue.transition(`${type}-${direction}`, {
31 | ...transition,
32 | enterClass: `${type}In${directions[0]}`,
33 | leaveClass: `${type}Out${directions[1]}`
34 | })
35 | }
36 |
37 | for (const type of types) {
38 | makeTransition(type, 'left')
39 | makeTransition(type, 'right')
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/utils/wordcount.ts:
--------------------------------------------------------------------------------
1 | // A word counter for markdown contents
2 | // A wrapper for '@wordpress/wordcount'
3 |
4 | import { count } from '@wordpress/wordcount';
5 |
6 | export default function (content: string): number {
7 | let cnContent: string = filterEn(content);
8 | let enContent: string = filterCn(content);
9 |
10 | return count(cnContent, 'characters_excluding_spaces', {}) +
11 | count(enContent, 'words', {});
12 | }
13 |
14 | function filterCn(content: string): string {
15 | return content.replace(/[\u4E00-\u9FA5]/g, ' ');
16 | }
17 |
18 | function filterEn(content: string): string {
19 | let res = content.match(/[\u4E00-\u9FA5]/g || []);
20 | if (res == null) return '';
21 | return res.join(' ');
22 | }
23 |
--------------------------------------------------------------------------------
/src/views/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
40 |
41 |
42 |
43 |
About EME
44 |
45 |
46 |
![]()
47 |
EME
48 |
49 | Version: {{ pkg.version }}
50 |
51 |
52 |
53 |
54 | Copyright © {{ year }} UNIPA, Inc.
55 |
56 |
57 |
58 |
59 |
71 |
--------------------------------------------------------------------------------
/src/vue-shims.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.vue" {
2 | import Vue from "vue";
3 | export default Vue;
4 | }
5 |
--------------------------------------------------------------------------------
/src/vuex/modules/app.js:
--------------------------------------------------------------------------------
1 | import {remote} from 'electron'
2 |
3 | const currentWindow = remote.getCurrentWindow()
4 |
5 | const state = {
6 | showPreferencePane: false,
7 | settings: JSON.parse(JSON.stringify(currentWindow.$config.get('settings')))
8 | }
9 |
10 | const mutations = {
11 | TOGGLE_PREFERENCE_PANE(state) {
12 | state.showPreferencePane = !state.showPreferencePane
13 | },
14 | UPDATE_SETTINGS(state, settings) {
15 | state.settings = settings
16 | },
17 | CHANGE_THEME(state, theme) {
18 | if (theme == 'light') {
19 | state.settings.theme = 'light'
20 | state.settings.preview.highlight = 'github'
21 | state.settings.editor.theme = 'base16-light'
22 | } else {
23 | state.settings.theme = 'dark'
24 | state.settings.preview.highlight = 'tomorrow-night-bright'
25 | state.settings.editor.theme = 'tomorrow-night-bright'
26 | }
27 | },
28 | CHANGE_THEME_CONTROL(state, themeControl) {
29 | state.settings.themeControl = themeControl
30 | }
31 | }
32 |
33 | export default {
34 | state,
35 | mutations
36 | }
37 |
--------------------------------------------------------------------------------
/src/vuex/modules/editor.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import {remote} from 'electron'
3 | import md from 'utils/markdown'
4 | import fm from 'front-matter'
5 | import sanitize from 'utils/sanitize'
6 |
7 | const win = remote.getCurrentWindow()
8 | win.$state.unsaved = 0
9 |
10 | const focusEditor = (tabs, index) => {
11 | const tab = tabs[index]
12 | if (tab && tab.editor) {
13 | tab.editor.refresh()
14 | tab.editor.focus()
15 | }
16 | }
17 |
18 | const renderHTML = tab => {
19 | const render = tab => md.render(tab.content).replace(/src="([^"]+)"/g, (m, p1) => {
20 | if (p1[0] === '.') {
21 | p1 = path.join(path.dirname(tab.filePath), p1)
22 | return `src="${p1}"`
23 | }
24 | return m
25 | })
26 |
27 | const data = fm(tab.content)
28 |
29 | return {
30 | attrs: data.attributes,
31 | html: sanitize(render({content: data.body, filePath: tab.filePath}))
32 | }
33 | }
34 |
35 | const state = {
36 | tabs: [],
37 | draggingTab: false,
38 | currentTabIndex: 0
39 | }
40 |
41 | const mutations = {
42 | INIT_NEW_TAB(state, payload) {
43 | const tab = {
44 | ...payload,
45 | ...renderHTML(payload)
46 | }
47 | state.tabs.push(tab)
48 | state.currentTabIndex++
49 | },
50 | UPDATE_CONTENT(state, {index, content}) {
51 | const tab = state.tabs[index]
52 | tab.content = content
53 | // render html in non-writing mode
54 | if (tab.writingMode !== 'editor') {
55 | const parsed = renderHTML({
56 | filePath: tab.filePath,
57 | content
58 | })
59 | tab.html = parsed.html
60 | tab.attrs = parsed.attrs
61 | }
62 | },
63 | UPDATE_FILE_PATH(state, {index, filePath}) {
64 | const tab = state.tabs[index]
65 | tab.filePath = filePath
66 | document.title = `${path.basename(filePath)} - EME`
67 | },
68 | UPDATE_CONTENT_WITH_FILEPATH(state, {
69 | index,
70 | content,
71 | filePath,
72 | gist,
73 | watcher
74 | }) {
75 | const tab = state.tabs[index]
76 | const parsed = renderHTML({
77 | content,
78 | filePath
79 | })
80 | tab.content = content
81 | tab.html = parsed.html
82 | tab.attrs = parsed.attrs
83 | tab.filePath = filePath
84 | tab.gist = gist
85 | tab.watcher = watcher
86 | document.title = `${path.basename(filePath)} - EME`
87 | },
88 | UPDATE_SAVE_STATUS(state, {index, saved}) {
89 | const tab = state.tabs[index]
90 | const fileName = tab.filePath ?
91 | path.basename(tab.filePath) :
92 | 'untitled'
93 |
94 | if (saved) {
95 | if (saved !== tab.saved) win.$state.unsaved--
96 | document.title = `${fileName} - EME`
97 | } else {
98 | if (saved !== tab.saved) win.$state.unsaved++
99 | document.title = `${fileName} * - EME`
100 | }
101 |
102 | tab.saved = saved
103 | },
104 | UPDATE_RENAME_STATUS(state, {index, rename}) {
105 | state.tabs[index].rename = rename
106 | },
107 | UPDATE_DRAGGING_STATUS(state, dragging) {
108 | state.draggingTab = dragging
109 | },
110 | UPDATE_EDITOR_SPLIT(state, {index, split}) {
111 | state.tabs[index].split = split
112 | },
113 | SET_EDITOR(state, {index, editor}) {
114 | state.currentTabIndex = index
115 | state.tabs[index].editor = editor
116 | },
117 | SET_CURRENT_TAB(state, index) {
118 | state.currentTabIndex = index
119 | },
120 | CLOSE_TAB(state, indexToClose) {
121 | const tab = state.tabs[indexToClose]
122 | if (tab && tab.watcher) {
123 | tab.watcher.close()
124 | }
125 | if (state.currentTabIndex !== 0 && indexToClose <= state.currentTabIndex) {
126 | state.currentTabIndex--
127 | }
128 | setTimeout(() => {
129 | focusEditor(state.tabs, state.currentTabIndex)
130 | }, 0)
131 | state.tabs.splice(indexToClose, 1)
132 | },
133 | SET_WRITING_MODE(state, {index, mode}) {
134 | const tab = state.tabs[index]
135 | if (tab.split === 100) {
136 | if (mode === 'default') tab.split = 50
137 | else if (mode === 'preview') tab.split = 0
138 | } else if (tab.split === 50) {
139 | if (mode === 'editor') tab.split = 100
140 | else if (mode === 'preview') tab.split = 0
141 | } else if (tab.split === 0) {
142 | if (mode === 'editor') tab.split = 100
143 | else if (mode === 'default') tab.split = 50
144 | }
145 |
146 | // if previous mode is writing mode
147 | // render html before switching
148 | if (tab.writingMode === 'editor') {
149 | const parsed = renderHTML(tab)
150 | tab.html = parsed.html
151 | tab.attrs = parsed.attrs
152 | }
153 | tab.writingMode = mode
154 |
155 | setTimeout(() => {
156 | tab.editor.refresh()
157 | if (mode !== 'preview') tab.editor.focus()
158 | }, 0)
159 | },
160 | START_EXPORTING(state, {index}) {
161 | const tab = state.tabs[index]
162 | tab.exporting = true
163 | },
164 | FINISH_EXPORTING_PDF(state, {index, pdf}) {
165 | const tab = state.tabs[index]
166 | tab.pdf = pdf
167 | tab.exporting = false
168 | },
169 | REORDER_TABS(state, {oldIndex, newIndex}) {
170 | const tabs = state.tabs
171 | if (newIndex >= tabs.length) {
172 | let k = newIndex - tabs.length
173 | while ((k--) + 1) {
174 | tabs.push(undefined)
175 | }
176 | }
177 | tabs.splice(newIndex, 0, tabs.splice(oldIndex, 1)[0])
178 | state.currentTabIndex = newIndex
179 | setTimeout(() => {
180 | focusEditor(state.tabs, state.currentTabIndex)
181 | }, 0)
182 | },
183 | TOGGLE_FOCUS_MODE(state) {
184 | const tab = state.tabs[state.currentTabIndex]
185 | tab.isFocusMode = !tab.isFocusMode
186 | },
187 | TOGGLE_VIM_MODE(state) {
188 | const tab = state.tabs[state.currentTabIndex]
189 | tab.isVimMode = !tab.isVimMode
190 | },
191 | UPDATE_FILE_GIST(state, gistId) {
192 | const tab = state.tabs[state.currentTabIndex]
193 | tab.gist = gistId
194 | }
195 | }
196 |
197 | export default {
198 | state,
199 | mutations
200 | }
201 |
--------------------------------------------------------------------------------
/src/vuex/store.d.ts:
--------------------------------------------------------------------------------
1 | declare let store: Vuex.Store;
2 |
3 | export default store;
4 |
--------------------------------------------------------------------------------
/src/vuex/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | Vue.use(Vuex)
5 |
6 | import app from './modules/app'
7 | import editor from './modules/editor'
8 |
9 | const store = new Vuex.Store({
10 | modules: {
11 | app,
12 | editor
13 | }
14 | })
15 |
16 | export default store
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist/",
4 | "allowJs": true,
5 | "allowSyntheticDefaultImports": true,
6 | "sourceMap": true,
7 | "strict": true,
8 | "noImplicitReturns": true,
9 | "module": "es2015",
10 | "moduleResolution": "node",
11 | "target": "es5",
12 | "experimentalDecorators": true
13 | },
14 | "include": [
15 | "./src/**/*",
16 | "./src/*"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------