diff --git a/assets/scripts/editor.js b/assets/scripts/editor.js
index d964984..d448657 100644
--- a/assets/scripts/editor.js
+++ b/assets/scripts/editor.js
@@ -46,6 +46,7 @@ let app = new Vue({
lineNumbers: false,
lineWrapping: true,
styleActiveLine: true,
+ autoCloseBrackets: true,
theme: this.currentEditorTheme,
mode: 'text/x-markdown'
}
diff --git a/assets/scripts/renderers/wx-renderer.js b/assets/scripts/renderers/wx-renderer.js
index b2fa4d0..8a441ff 100644
--- a/assets/scripts/renderers/wx-renderer.js
+++ b/assets/scripts/renderers/wx-renderer.js
@@ -98,7 +98,6 @@ let WxRenderer = function (opts) {
styleMapping = this.buildTheme(this.opts.theme);
let renderer = new marked.Renderer();
- FuriganaMD.register(renderer);
renderer.heading = (text, level) => {
switch (level) {
diff --git a/index.html b/index.html
index 3ecdaac..26730c3 100644
--- a/index.html
+++ b/index.html
@@ -128,7 +128,7 @@
-
+
diff --git a/libs/scripts/FuriganaMD.js b/libs/scripts/FuriganaMD.js
deleted file mode 100644
index 93abc5a..0000000
--- a/libs/scripts/FuriganaMD.js
+++ /dev/null
@@ -1,241 +0,0 @@
-/**
- * 注音功能来自于:https://github.com/amclees/furigana-markdown
- * 详见上述文档
- */
-(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global.FuriganaMD = factory());
-}(this, (function () {
- 'use strict';
-
- // This function escapes special characters for use in a regex constructor.
- function escapeForRegex(string) {
- return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
- }
-
- function emptyStringFilter(block) {
- return block !== '';
- }
-
- const kanjiRange = '\\u4e00-\\u9faf';
- const kanjiBlockRegex = new RegExp(`[${kanjiRange}]+`, 'g');
- const nonKanjiBlockRegex = new RegExp(`[^${kanjiRange}]+`, 'g');
- const kanaWithAnnotations = '\\u3041-\\u3095\\u3099-\\u309c\\u3081-\\u30fa\\u30fc';
- const furiganaSeperators = '..。・';
- const seperatorRegex = new RegExp(`[${furiganaSeperators}]`, 'g');
-
- const singleKanjiRegex = new RegExp(`^[${kanjiRange}]$`);
-
- function isKanji(character) {
- return character.match(singleKanjiRegex);
- }
-
- const innerRegexString = '(?:[^\\u0000-\\u007F]|\\w)+';
-
- let regexList = [];
- let previousFuriganaForms = '';
-
- function updateRegexList(furiganaForms) {
- previousFuriganaForms = furiganaForms;
- let formArray = furiganaForms.split('|');
- if (formArray.length === 0) {
- formArray = ['[]:^:()'];
- }
- regexList = formArray.map(form => {
- let furiganaComponents = form.split(':');
- if (furiganaComponents.length !== 3) {
- furiganaComponents = ['[]', '^', '()'];
- }
- const mainBrackets = furiganaComponents[0];
- const seperator = furiganaComponents[1];
- const furiganaBrackets = furiganaComponents[2];
- return new RegExp(
- escapeForRegex(mainBrackets[0]) +
- '(' + innerRegexString + ')' +
- escapeForRegex(mainBrackets[1]) +
- escapeForRegex(seperator) +
- escapeForRegex(furiganaBrackets[0]) +
- '(' + innerRegexString + ')' +
- escapeForRegex(furiganaBrackets[1]),
- 'g'
- );
- });
- }
-
- let autoRegexList = [];
- let previousAutoBracketSets = '';
-
- function updateAutoRegexList(autoBracketSets) {
- previousAutoBracketSets = autoBracketSets;
- autoRegexList = autoBracketSets.split('|').map(brackets => {
- /*
- Sample built regex:
- /(^|[^\u4e00-\u9faf]|)([\u4e00-\u9faf]+)([\u3041-\u3095\u3099-\u309c\u3081-\u30fa\u30fc]*)【((?:[^【】\u4e00-\u9faf]|w)+)】/g
- */
- return new RegExp(
- `(^|[^${kanjiRange}]|)` +
- `([${kanjiRange}]+)` +
- `([${kanaWithAnnotations}]*)` +
- escapeForRegex(brackets[0]) +
- `((?:[^${escapeForRegex(brackets)}\\u0000-\\u007F]|\\w|[${furiganaSeperators}])+)` +
- escapeForRegex(brackets[1]),
- 'g'
- );
- });
- }
-
- let replacementTemplate = '';
- let replacementBrackets = '';
-
- function updateReplacementTemplate(furiganaFallbackBrackets) {
- if (furiganaFallbackBrackets.length !== 2) {
- furiganaFallbackBrackets = '【】';
- }
- replacementBrackets = furiganaFallbackBrackets;
- replacementTemplate = `$1`;
- }
-
- updateReplacementTemplate('【】');
-
- function addFurigana(text, options) {
- if (options.furiganaForms !== previousFuriganaForms) {
- updateRegexList(options.furiganaForms);
- }
- if (options.furiganaFallbackBrackets !== replacementBrackets) {
- updateReplacementTemplate(options.furiganaFallbackBrackets);
- }
- regexList.forEach(regex => {
- text = text.replace(regex, (match, wordText, furiganaText, offset, mainText) => {
- if (match.indexOf('\\') === -1 && mainText[offset - 1] !== '\\') {
- if ((!options.furiganaPatternMatching) || wordText.search(kanjiBlockRegex) === -1 || wordText[0].search(kanjiBlockRegex) === -1) {
- return replacementTemplate.replace('$1', wordText).replace('$2', furiganaText);
- } else {
- let originalFuriganaText = (' ' + furiganaText).slice(1);
- let nonKanji = wordText.split(kanjiBlockRegex).filter(emptyStringFilter);
- let kanji = wordText.split(nonKanjiBlockRegex).filter(emptyStringFilter);
- let replacementText = '';
- let lastUsedKanjiIndex = 0;
- if (nonKanji.length === 0) {
- return replacementTemplate.replace('$1', wordText).replace('$2', furiganaText);
- }
-
- nonKanji.forEach((currentNonKanji, index) => {
- if (furiganaText === undefined) {
- if (index < kanji.length) {
- replacementText += kanji[index];
- }
-
- replacementText += currentNonKanji;
- return;
- }
- let splitFurigana = furiganaText.split(new RegExp(escapeForRegex(currentNonKanji) + '(.*)')).filter(emptyStringFilter);
-
- lastUsedKanjiIndex = index;
- replacementText += replacementTemplate.replace('$1', kanji[index]).replace('$2', splitFurigana[0]);
- replacementText += currentNonKanji;
-
- furiganaText = splitFurigana[1];
- });
- if (furiganaText !== undefined && lastUsedKanjiIndex + 1 < kanji.length) {
- replacementText += replacementTemplate.replace('$1', kanji[lastUsedKanjiIndex + 1]).replace('$2', furiganaText);
- } else if (furiganaText !== undefined) {
- return replacementTemplate.replace('$1', wordText).replace('$2', originalFuriganaText);
- } else if (lastUsedKanjiIndex + 1 < kanji.length) {
- replacementText += kanji[lastUsedKanjiIndex + 1];
- }
- return replacementText;
- }
- } else {
- return match;
- }
- });
- });
-
- if (!options.furiganaStrictMode) {
- if (options.furiganaAutoBracketSets !== previousAutoBracketSets) {
- updateAutoRegexList(options.furiganaAutoBracketSets);
- }
- autoRegexList.forEach(regex => {
- text = text.replace(regex, (match, preWordTerminator, wordKanji, wordKanaSuffix, furiganaText, offset, mainText) => {
- if (match.indexOf('\\') === -1) {
- if (options.furiganaPatternMatching) {
- let rubies = [];
-
- let furigana = furiganaText;
-
- let stem = (' ' + wordKanaSuffix).slice(1);
- for (let i = furiganaText.length - 1; i >= 0; i--) {
- if (wordKanaSuffix.length === 0) {
- furigana = furiganaText.substring(0, i + 1);
- break;
- }
- if (furiganaText[i] !== wordKanaSuffix.slice(-1)) {
- furigana = furiganaText.substring(0, i + 1);
- break;
- }
- wordKanaSuffix = wordKanaSuffix.slice(0, -1);
- }
-
- if (furiganaSeperators.split('').reduce(
- (noSeperator, seperator) => {
- return noSeperator && (furigana.indexOf(seperator) === -1);
- },
- true
- )) {
- rubies = [replacementTemplate.replace('$1', wordKanji).replace('$2', furigana)];
- } else {
- let kanaParts = furigana.split(seperatorRegex);
- let kanji = wordKanji.split('');
- if (kanaParts.length === 0 || kanaParts.length > kanji.length) {
- rubies = [replacementTemplate.replace('$1', wordKanji).replace('$2', furigana)];
- } else {
- for (let i = 0; i < kanaParts.length - 1; i++) {
- if (kanji.length === 0) {
- break;
- }
- rubies.push(replacementTemplate.replace('$1', kanji.shift()).replace('$2', kanaParts[i]));
- }
- let lastKanaPart = kanaParts.pop();
- rubies.push(replacementTemplate.replace('$1', kanji.join('')).replace('$2', lastKanaPart));
- }
- }
-
- return preWordTerminator + rubies.join('') + stem;
- } else {
- return preWordTerminator + replacementTemplate.replace('$1', wordKanji).replace('$2', furiganaText) + wordKanaSuffix;
- }
- } else {
- return match;
- }
- });
- });
- }
- return text;
- }
-
- function handleEscapedSpecialBrackets(text) {
- // By default 【 and 】 cannot be escaped in markdown, this will remove backslashes from in front of them to give that effect.
- return text.replace(/\\([【】])/g, '$1');
- }
-
- let FuriganaMD = {};
- FuriganaMD.register = function (renderer) {
- renderer.text = function (text) {
- let options = {
- furigana: true,
- furiganaForms: "()::{}",
- furiganaFallbackBrackets: "{}",
- furiganaStrictMode: false,
- furiganaAutoBracketSets: "{}",
- furiganaPatternMatching: true,
- };
- // console.log('override text render',text);
- // console.log('after add',addFurigana(text, options));
- return handleEscapedSpecialBrackets(addFurigana(text, options));
- };
- };
-
- return FuriganaMD;
-
-})));
diff --git a/libs/scripts/closebrackets.js b/libs/scripts/closebrackets.js
new file mode 100644
index 0000000..4415c39
--- /dev/null
+++ b/libs/scripts/closebrackets.js
@@ -0,0 +1,191 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var defaults = {
+ pairs: "()[]{}''\"\"",
+ closeBefore: ")]}'\":;>",
+ triples: "",
+ explode: "[]{}"
+ };
+
+ var Pos = CodeMirror.Pos;
+
+ CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.removeKeyMap(keyMap);
+ cm.state.closeBrackets = null;
+ }
+ if (val) {
+ ensureBound(getOption(val, "pairs"))
+ cm.state.closeBrackets = val;
+ cm.addKeyMap(keyMap);
+ }
+ });
+
+ function getOption(conf, name) {
+ if (name == "pairs" && typeof conf == "string") return conf;
+ if (typeof conf == "object" && conf[name] != null) return conf[name];
+ return defaults[name];
+ }
+
+ var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
+ function ensureBound(chars) {
+ for (var i = 0; i < chars.length; i++) {
+ var ch = chars.charAt(i), key = "'" + ch + "'"
+ if (!keyMap[key]) keyMap[key] = handler(ch)
+ }
+ }
+ ensureBound(defaults.pairs + "`")
+
+ function handler(ch) {
+ return function(cm) { return handleChar(cm, ch); };
+ }
+
+ function getConfig(cm) {
+ var deflt = cm.state.closeBrackets;
+ if (!deflt || deflt.override) return deflt;
+ var mode = cm.getModeAt(cm.getCursor());
+ return mode.closeBrackets || deflt;
+ }
+
+ function handleBackspace(cm) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var cur = ranges[i].head;
+ cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
+ }
+ }
+
+ function handleEnter(cm) {
+ var conf = getConfig(cm);
+ var explode = conf && getOption(conf, "explode");
+ if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ cm.operation(function() {
+ var linesep = cm.lineSeparator() || "\n";
+ cm.replaceSelection(linesep + linesep, null);
+ cm.execCommand("goCharLeft");
+ ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var line = ranges[i].head.line;
+ cm.indentLine(line, null, true);
+ cm.indentLine(line + 1, null, true);
+ }
+ });
+ }
+
+ function contractSelection(sel) {
+ var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
+ return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
+ head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
+ }
+
+ function handleChar(cm, ch) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var pos = pairs.indexOf(ch);
+ if (pos == -1) return CodeMirror.Pass;
+
+ var closeBefore = getOption(conf,"closeBefore");
+
+ var triples = getOption(conf, "triples");
+
+ var identical = pairs.charAt(pos + 1) == ch;
+ var ranges = cm.listSelections();
+ var opening = pos % 2 == 0;
+
+ var type;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], cur = range.head, curType;
+ var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+ if (opening && !range.empty()) {
+ curType = "surround";
+ } else if ((identical || !opening) && next == ch) {
+ if (identical && stringStartsAfter(cm, cur))
+ curType = "both";
+ else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+ curType = "skipThree";
+ else
+ curType = "skip";
+ } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+ cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+ if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
+ curType = "addFour";
+ } else if (identical) {
+ var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+ if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+ else return CodeMirror.Pass;
+ } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
+ curType = "both";
+ } else {
+ return CodeMirror.Pass;
+ }
+ if (!type) type = curType;
+ else if (type != curType) return CodeMirror.Pass;
+ }
+
+ var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
+ var right = pos % 2 ? ch : pairs.charAt(pos + 1);
+ cm.operation(function() {
+ if (type == "skip") {
+ cm.execCommand("goCharRight");
+ } else if (type == "skipThree") {
+ for (var i = 0; i < 3; i++)
+ cm.execCommand("goCharRight");
+ } else if (type == "surround") {
+ var sels = cm.getSelections();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = left + sels[i] + right;
+ cm.replaceSelections(sels, "around");
+ sels = cm.listSelections().slice();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = contractSelection(sels[i]);
+ cm.setSelections(sels);
+ } else if (type == "both") {
+ cm.replaceSelection(left + right, null);
+ cm.triggerElectric(left + right);
+ cm.execCommand("goCharLeft");
+ } else if (type == "addFour") {
+ cm.replaceSelection(left + left + left + left, "before");
+ cm.execCommand("goCharRight");
+ }
+ });
+ }
+
+ function charsAround(cm, pos) {
+ var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+ Pos(pos.line, pos.ch + 1));
+ return str.length == 2 ? str : null;
+ }
+
+ function stringStartsAfter(cm, pos) {
+ var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+ return /\bstring/.test(token.type) && token.start == pos.ch &&
+ (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+ }
+});