add UMD support and add linting utility for JavaScript ESLint
parent
d9e66391d0
commit
df1e551c0f
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
2,
|
||||||
|
"tab"
|
||||||
|
],
|
||||||
|
"strict": 0,
|
||||||
|
"no-console": 0,
|
||||||
|
"quotes": [
|
||||||
|
2,
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
2,
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node":true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended"
|
||||||
|
}
|
@ -1,2 +1,6 @@
|
|||||||
localtesting/*
|
localtesting/*
|
||||||
node_modules/
|
node_modules/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
dist/
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,334 +0,0 @@
|
|||||||
/* BASICS */
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
/* Set height, width, borders, and global font properties here */
|
|
||||||
font-family: monospace;
|
|
||||||
height: 300px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PADDING */
|
|
||||||
|
|
||||||
.CodeMirror-lines {
|
|
||||||
padding: 4px 0; /* Vertical padding around content */
|
|
||||||
}
|
|
||||||
.CodeMirror pre {
|
|
||||||
padding: 0 4px; /* Horizontal padding of content */
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
|
||||||
background-color: white; /* The little square between H and V scrollbars */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GUTTER */
|
|
||||||
|
|
||||||
.CodeMirror-gutters {
|
|
||||||
border-right: 1px solid #ddd;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.CodeMirror-linenumbers {}
|
|
||||||
.CodeMirror-linenumber {
|
|
||||||
padding: 0 3px 0 5px;
|
|
||||||
min-width: 20px;
|
|
||||||
text-align: right;
|
|
||||||
color: #999;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-guttermarker { color: black; }
|
|
||||||
.CodeMirror-guttermarker-subtle { color: #999; }
|
|
||||||
|
|
||||||
/* CURSOR */
|
|
||||||
|
|
||||||
.CodeMirror-cursor {
|
|
||||||
border-left: 1px solid black;
|
|
||||||
border-right: none;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
/* Shown when moving in bi-directional text */
|
|
||||||
.CodeMirror div.CodeMirror-secondarycursor {
|
|
||||||
border-left: 1px solid silver;
|
|
||||||
}
|
|
||||||
.cm-fat-cursor .CodeMirror-cursor {
|
|
||||||
width: auto;
|
|
||||||
border: 0;
|
|
||||||
background: #7e7;
|
|
||||||
}
|
|
||||||
.cm-fat-cursor div.CodeMirror-cursors {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-animate-fat-cursor {
|
|
||||||
width: auto;
|
|
||||||
border: 0;
|
|
||||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
|
||||||
-moz-animation: blink 1.06s steps(1) infinite;
|
|
||||||
animation: blink 1.06s steps(1) infinite;
|
|
||||||
background-color: #7e7;
|
|
||||||
}
|
|
||||||
@-moz-keyframes blink {
|
|
||||||
0% {}
|
|
||||||
50% { background-color: transparent; }
|
|
||||||
100% {}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes blink {
|
|
||||||
0% {}
|
|
||||||
50% { background-color: transparent; }
|
|
||||||
100% {}
|
|
||||||
}
|
|
||||||
@keyframes blink {
|
|
||||||
0% {}
|
|
||||||
50% { background-color: transparent; }
|
|
||||||
100% {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can style cursor different in overwrite (non-insert) mode */
|
|
||||||
.CodeMirror-overwrite .CodeMirror-cursor {}
|
|
||||||
|
|
||||||
.cm-tab { display: inline-block; text-decoration: inherit; }
|
|
||||||
|
|
||||||
.CodeMirror-ruler {
|
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DEFAULT THEME */
|
|
||||||
|
|
||||||
.cm-s-default .cm-header {color: blue;}
|
|
||||||
.cm-s-default .cm-quote {color: #090;}
|
|
||||||
.cm-negative {color: #d44;}
|
|
||||||
.cm-positive {color: #292;}
|
|
||||||
.cm-header, .cm-strong {font-weight: bold;}
|
|
||||||
.cm-em {font-style: italic;}
|
|
||||||
.cm-link {text-decoration: underline;}
|
|
||||||
.cm-strikethrough {text-decoration: line-through;}
|
|
||||||
|
|
||||||
.cm-s-default .cm-keyword {color: #708;}
|
|
||||||
.cm-s-default .cm-atom {color: #219;}
|
|
||||||
.cm-s-default .cm-number {color: #164;}
|
|
||||||
.cm-s-default .cm-def {color: #00f;}
|
|
||||||
.cm-s-default .cm-variable,
|
|
||||||
.cm-s-default .cm-punctuation,
|
|
||||||
.cm-s-default .cm-property,
|
|
||||||
.cm-s-default .cm-operator {}
|
|
||||||
.cm-s-default .cm-variable-2 {color: #05a;}
|
|
||||||
.cm-s-default .cm-variable-3 {color: #085;}
|
|
||||||
.cm-s-default .cm-comment {color: #a50;}
|
|
||||||
.cm-s-default .cm-string {color: #a11;}
|
|
||||||
.cm-s-default .cm-string-2 {color: #f50;}
|
|
||||||
.cm-s-default .cm-meta {color: #555;}
|
|
||||||
.cm-s-default .cm-qualifier {color: #555;}
|
|
||||||
.cm-s-default .cm-builtin {color: #30a;}
|
|
||||||
.cm-s-default .cm-bracket {color: #997;}
|
|
||||||
.cm-s-default .cm-tag {color: #170;}
|
|
||||||
.cm-s-default .cm-attribute {color: #00c;}
|
|
||||||
.cm-s-default .cm-hr {color: #999;}
|
|
||||||
.cm-s-default .cm-link {color: #00c;}
|
|
||||||
|
|
||||||
.cm-s-default .cm-error {color: #f00;}
|
|
||||||
.cm-invalidchar {color: #f00;}
|
|
||||||
|
|
||||||
.CodeMirror-composing { border-bottom: 2px solid; }
|
|
||||||
|
|
||||||
/* Default styles for common addons */
|
|
||||||
|
|
||||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
|
||||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
|
||||||
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
|
||||||
.CodeMirror-activeline-background {background: #e8f2ff;}
|
|
||||||
|
|
||||||
/* STOP */
|
|
||||||
|
|
||||||
/* The rest of this file contains styles related to the mechanics of
|
|
||||||
the editor. You probably shouldn't touch them. */
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-scroll {
|
|
||||||
overflow: scroll !important; /* Things will break if this is overridden */
|
|
||||||
/* 30px is the magic margin used to hide the element's real scrollbars */
|
|
||||||
/* See overflow: hidden in .CodeMirror */
|
|
||||||
margin-bottom: -30px; margin-right: -30px;
|
|
||||||
padding-bottom: 30px;
|
|
||||||
height: 100%;
|
|
||||||
outline: none; /* Prevent dragging from highlighting the element */
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.CodeMirror-sizer {
|
|
||||||
position: relative;
|
|
||||||
border-right: 30px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
|
||||||
before actuall scrolling happens, thus preventing shaking and
|
|
||||||
flickering artifacts. */
|
|
||||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 6;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.CodeMirror-vscrollbar {
|
|
||||||
right: 0; top: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
.CodeMirror-hscrollbar {
|
|
||||||
bottom: 0; left: 0;
|
|
||||||
overflow-y: hidden;
|
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
|
||||||
.CodeMirror-scrollbar-filler {
|
|
||||||
right: 0; bottom: 0;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-filler {
|
|
||||||
left: 0; bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-gutters {
|
|
||||||
position: absolute; left: 0; top: 0;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter {
|
|
||||||
white-space: normal;
|
|
||||||
height: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
margin-bottom: -30px;
|
|
||||||
/* Hack to make IE7 behave */
|
|
||||||
*zoom:1;
|
|
||||||
*display:inline;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-wrapper {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 4;
|
|
||||||
background: none !important;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0; bottom: 0;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-elt {
|
|
||||||
position: absolute;
|
|
||||||
cursor: default;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutter-wrapper {
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-lines {
|
|
||||||
cursor: text;
|
|
||||||
min-height: 1px; /* prevents collapsing before first draw */
|
|
||||||
}
|
|
||||||
.CodeMirror pre {
|
|
||||||
/* Reset some styles that the rest of the page might have set */
|
|
||||||
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
|
||||||
border-width: 0;
|
|
||||||
background: transparent;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
margin: 0;
|
|
||||||
white-space: pre;
|
|
||||||
word-wrap: normal;
|
|
||||||
line-height: inherit;
|
|
||||||
color: inherit;
|
|
||||||
z-index: 2;
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
.CodeMirror-wrap pre {
|
|
||||||
word-wrap: break-word;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-linebackground {
|
|
||||||
position: absolute;
|
|
||||||
left: 0; right: 0; top: 0; bottom: 0;
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-linewidget {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-widget {}
|
|
||||||
|
|
||||||
.CodeMirror-code {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Force content-box sizing for the elements where we expect it */
|
|
||||||
.CodeMirror-scroll,
|
|
||||||
.CodeMirror-sizer,
|
|
||||||
.CodeMirror-gutter,
|
|
||||||
.CodeMirror-gutters,
|
|
||||||
.CodeMirror-linenumber {
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-measure {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-cursor { position: absolute; }
|
|
||||||
.CodeMirror-measure pre { position: static; }
|
|
||||||
|
|
||||||
div.CodeMirror-cursors {
|
|
||||||
visibility: hidden;
|
|
||||||
position: relative;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
div.CodeMirror-dragcursors {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-focused div.CodeMirror-cursors {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-selected { background: #d9d9d9; }
|
|
||||||
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
|
||||||
.CodeMirror-crosshair { cursor: crosshair; }
|
|
||||||
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
|
|
||||||
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
|
|
||||||
|
|
||||||
.cm-searching {
|
|
||||||
background: #ffa;
|
|
||||||
background: rgba(255, 255, 0, .4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
|
|
||||||
.CodeMirror span { *vertical-align: text-bottom; }
|
|
||||||
|
|
||||||
/* Used to force a border model for a node */
|
|
||||||
.cm-force-border { padding-right: .1px; }
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
/* Hide the cursor when printing */
|
|
||||||
.CodeMirror div.CodeMirror-cursors {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See issue #2901 */
|
|
||||||
.cm-tab-wrap-hack:after { content: ''; }
|
|
||||||
|
|
||||||
/* Help users use markselection to safely style text background */
|
|
||||||
span.CodeMirror-selectedtext { background: none; }
|
|
@ -1,3 +0,0 @@
|
|||||||
.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment) {
|
|
||||||
background: rgba(255, 0, 0, .15);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,51 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://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) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/,
|
|
||||||
emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)[.)])(\s*)$/,
|
|
||||||
unorderedListRE = /[*+-]\s/;
|
|
||||||
|
|
||||||
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
|
|
||||||
if (cm.getOption("disableInput")) return CodeMirror.Pass;
|
|
||||||
var ranges = cm.listSelections(), replacements = [];
|
|
||||||
for (var i = 0; i < ranges.length; i++) {
|
|
||||||
var pos = ranges[i].head;
|
|
||||||
var eolState = cm.getStateAfter(pos.line);
|
|
||||||
var inList = eolState.list !== false;
|
|
||||||
var inQuote = eolState.quote !== 0;
|
|
||||||
|
|
||||||
var line = cm.getLine(pos.line), match = listRE.exec(line);
|
|
||||||
if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
|
|
||||||
cm.execCommand("newlineAndIndent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (emptyListRE.test(line)) {
|
|
||||||
cm.replaceRange("", {
|
|
||||||
line: pos.line, ch: 0
|
|
||||||
}, {
|
|
||||||
line: pos.line, ch: pos.ch + 1
|
|
||||||
});
|
|
||||||
replacements[i] = "\n";
|
|
||||||
} else {
|
|
||||||
var indent = match[1], after = match[5];
|
|
||||||
var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
|
|
||||||
? match[2]
|
|
||||||
: (parseInt(match[3], 10) + 1) + match[4];
|
|
||||||
|
|
||||||
replacements[i] = "\n" + indent + bullet + after;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cm.replaceSelections(replacements);
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,41 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://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) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
|
|
||||||
if (old == CodeMirror.Init) old = false;
|
|
||||||
if (!old == !val) return;
|
|
||||||
if (val) setFullscreen(cm);
|
|
||||||
else setNormal(cm);
|
|
||||||
});
|
|
||||||
|
|
||||||
function setFullscreen(cm) {
|
|
||||||
var wrap = cm.getWrapperElement();
|
|
||||||
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
|
|
||||||
width: wrap.style.width, height: wrap.style.height};
|
|
||||||
wrap.style.width = "";
|
|
||||||
wrap.style.height = "auto";
|
|
||||||
wrap.className += " CodeMirror-fullscreen";
|
|
||||||
document.documentElement.style.overflow = "hidden";
|
|
||||||
cm.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNormal(cm) {
|
|
||||||
var wrap = cm.getWrapperElement();
|
|
||||||
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
|
|
||||||
document.documentElement.style.overflow = "";
|
|
||||||
var info = cm.state.fullScreenRestore;
|
|
||||||
wrap.style.width = info.width; wrap.style.height = info.height;
|
|
||||||
window.scrollTo(info.scrollLeft, info.scrollTop);
|
|
||||||
cm.refresh();
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,130 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
|
||||||
|
|
||||||
(function(mod) {
|
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
||||||
mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
|
|
||||||
else if (typeof define == "function" && define.amd) // AMD
|
|
||||||
define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
|
|
||||||
else // Plain browser env
|
|
||||||
mod(CodeMirror);
|
|
||||||
})(function(CodeMirror) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i
|
|
||||||
|
|
||||||
CodeMirror.defineMode("gfm", function(config, modeConfig) {
|
|
||||||
var codeDepth = 0;
|
|
||||||
function blankLine(state) {
|
|
||||||
state.code = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var gfmOverlay = {
|
|
||||||
startState: function() {
|
|
||||||
return {
|
|
||||||
code: false,
|
|
||||||
codeBlock: false,
|
|
||||||
ateSpace: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
copyState: function(s) {
|
|
||||||
return {
|
|
||||||
code: s.code,
|
|
||||||
codeBlock: s.codeBlock,
|
|
||||||
ateSpace: s.ateSpace
|
|
||||||
};
|
|
||||||
},
|
|
||||||
token: function(stream, state) {
|
|
||||||
state.combineTokens = null;
|
|
||||||
|
|
||||||
// Hack to prevent formatting override inside code blocks (block and inline)
|
|
||||||
if (state.codeBlock) {
|
|
||||||
if (stream.match(/^```+/)) {
|
|
||||||
state.codeBlock = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
stream.skipToEnd();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (stream.sol()) {
|
|
||||||
state.code = false;
|
|
||||||
}
|
|
||||||
if (stream.sol() && stream.match(/^```+/)) {
|
|
||||||
stream.skipToEnd();
|
|
||||||
state.codeBlock = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// If this block is changed, it may need to be updated in Markdown mode
|
|
||||||
if (stream.peek() === '`') {
|
|
||||||
stream.next();
|
|
||||||
var before = stream.pos;
|
|
||||||
stream.eatWhile('`');
|
|
||||||
var difference = 1 + stream.pos - before;
|
|
||||||
if (!state.code) {
|
|
||||||
codeDepth = difference;
|
|
||||||
state.code = true;
|
|
||||||
} else {
|
|
||||||
if (difference === codeDepth) { // Must be exact
|
|
||||||
state.code = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} else if (state.code) {
|
|
||||||
stream.next();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Check if space. If so, links can be formatted later on
|
|
||||||
if (stream.eatSpace()) {
|
|
||||||
state.ateSpace = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (stream.sol() || state.ateSpace) {
|
|
||||||
state.ateSpace = false;
|
|
||||||
if (modeConfig.gitHubSpice !== false) {
|
|
||||||
if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
|
|
||||||
// User/Project@SHA
|
|
||||||
// User@SHA
|
|
||||||
// SHA
|
|
||||||
state.combineTokens = true;
|
|
||||||
return "link";
|
|
||||||
} else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
|
|
||||||
// User/Project#Num
|
|
||||||
// User#Num
|
|
||||||
// #Num
|
|
||||||
state.combineTokens = true;
|
|
||||||
return "link";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stream.match(urlRE) &&
|
|
||||||
stream.string.slice(stream.start - 2, stream.start) != "](" &&
|
|
||||||
(stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) {
|
|
||||||
// URLs
|
|
||||||
// Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
|
|
||||||
// And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
|
|
||||||
// And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL
|
|
||||||
state.combineTokens = true;
|
|
||||||
return "link";
|
|
||||||
}
|
|
||||||
stream.next();
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
blankLine: blankLine
|
|
||||||
};
|
|
||||||
|
|
||||||
var markdownConfig = {
|
|
||||||
underscoresBreakWords: false,
|
|
||||||
taskLists: true,
|
|
||||||
fencedCodeBlocks: '```',
|
|
||||||
strikethrough: true
|
|
||||||
};
|
|
||||||
for (var attr in modeConfig) {
|
|
||||||
markdownConfig[attr] = modeConfig[attr];
|
|
||||||
}
|
|
||||||
markdownConfig.name = "markdown";
|
|
||||||
return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);
|
|
||||||
|
|
||||||
}, "markdown");
|
|
||||||
|
|
||||||
CodeMirror.defineMIME("text/x-gfm", "gfm");
|
|
||||||
});
|
|
@ -1,792 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
|
||||||
|
|
||||||
(function(mod) {
|
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
||||||
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
|
|
||||||
else if (typeof define == "function" && define.amd) // AMD
|
|
||||||
define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
|
|
||||||
else // Plain browser env
|
|
||||||
mod(CodeMirror);
|
|
||||||
})(function(CodeMirror) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
|
|
||||||
|
|
||||||
var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
|
|
||||||
var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
|
|
||||||
|
|
||||||
function getMode(name) {
|
|
||||||
if (CodeMirror.findModeByName) {
|
|
||||||
var found = CodeMirror.findModeByName(name);
|
|
||||||
if (found) name = found.mime || found.mimes[0];
|
|
||||||
}
|
|
||||||
var mode = CodeMirror.getMode(cmCfg, name);
|
|
||||||
return mode.name == "null" ? null : mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should characters that affect highlighting be highlighted separate?
|
|
||||||
// Does not include characters that will be output (such as `1.` and `-` for lists)
|
|
||||||
if (modeCfg.highlightFormatting === undefined)
|
|
||||||
modeCfg.highlightFormatting = false;
|
|
||||||
|
|
||||||
// Maximum number of nested blockquotes. Set to 0 for infinite nesting.
|
|
||||||
// Excess `>` will emit `error` token.
|
|
||||||
if (modeCfg.maxBlockquoteDepth === undefined)
|
|
||||||
modeCfg.maxBlockquoteDepth = 0;
|
|
||||||
|
|
||||||
// Should underscores in words open/close em/strong?
|
|
||||||
if (modeCfg.underscoresBreakWords === undefined)
|
|
||||||
modeCfg.underscoresBreakWords = true;
|
|
||||||
|
|
||||||
// Use `fencedCodeBlocks` to configure fenced code blocks. false to
|
|
||||||
// disable, string to specify a precise regexp that the fence should
|
|
||||||
// match, and true to allow three or more backticks or tildes (as
|
|
||||||
// per CommonMark).
|
|
||||||
|
|
||||||
// Turn on task lists? ("- [ ] " and "- [x] ")
|
|
||||||
if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
|
|
||||||
|
|
||||||
// Turn on strikethrough syntax
|
|
||||||
if (modeCfg.strikethrough === undefined)
|
|
||||||
modeCfg.strikethrough = false;
|
|
||||||
|
|
||||||
var codeDepth = 0;
|
|
||||||
|
|
||||||
var header = 'header'
|
|
||||||
, code = 'comment'
|
|
||||||
, quote = 'quote'
|
|
||||||
, list1 = 'variable-2'
|
|
||||||
, list2 = 'variable-3'
|
|
||||||
, list3 = 'keyword'
|
|
||||||
, hr = 'hr'
|
|
||||||
, image = 'tag'
|
|
||||||
, formatting = 'formatting'
|
|
||||||
, linkinline = 'link'
|
|
||||||
, linkemail = 'link'
|
|
||||||
, linktext = 'link'
|
|
||||||
, linkhref = 'string'
|
|
||||||
, em = 'em'
|
|
||||||
, strong = 'strong'
|
|
||||||
, strikethrough = 'strikethrough';
|
|
||||||
|
|
||||||
var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
|
|
||||||
, ulRE = /^[*\-+]\s+/
|
|
||||||
, olRE = /^[0-9]+([.)])\s+/
|
|
||||||
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
|
|
||||||
, atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
|
|
||||||
, setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
|
|
||||||
, textRE = /^[^#!\[\]*_\\<>` "'(~]+/
|
|
||||||
, fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
|
|
||||||
")[ \\t]*([\\w+#]*)");
|
|
||||||
|
|
||||||
function switchInline(stream, state, f) {
|
|
||||||
state.f = state.inline = f;
|
|
||||||
return f(stream, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchBlock(stream, state, f) {
|
|
||||||
state.f = state.block = f;
|
|
||||||
return f(stream, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lineIsEmpty(line) {
|
|
||||||
return !line || !/\S/.test(line.string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks
|
|
||||||
|
|
||||||
function blankLine(state) {
|
|
||||||
// Reset linkTitle state
|
|
||||||
state.linkTitle = false;
|
|
||||||
// Reset EM state
|
|
||||||
state.em = false;
|
|
||||||
// Reset STRONG state
|
|
||||||
state.strong = false;
|
|
||||||
// Reset strikethrough state
|
|
||||||
state.strikethrough = false;
|
|
||||||
// Reset state.quote
|
|
||||||
state.quote = 0;
|
|
||||||
// Reset state.indentedCode
|
|
||||||
state.indentedCode = false;
|
|
||||||
if (!htmlFound && state.f == htmlBlock) {
|
|
||||||
state.f = inlineNormal;
|
|
||||||
state.block = blockNormal;
|
|
||||||
}
|
|
||||||
// Reset state.trailingSpace
|
|
||||||
state.trailingSpace = 0;
|
|
||||||
state.trailingSpaceNewLine = false;
|
|
||||||
// Mark this line as blank
|
|
||||||
state.prevLine = state.thisLine
|
|
||||||
state.thisLine = null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function blockNormal(stream, state) {
|
|
||||||
|
|
||||||
var sol = stream.sol();
|
|
||||||
|
|
||||||
var prevLineIsList = state.list !== false,
|
|
||||||
prevLineIsIndentedCode = state.indentedCode;
|
|
||||||
|
|
||||||
state.indentedCode = false;
|
|
||||||
|
|
||||||
if (prevLineIsList) {
|
|
||||||
if (state.indentationDiff >= 0) { // Continued list
|
|
||||||
if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
|
|
||||||
state.indentation -= state.indentationDiff;
|
|
||||||
}
|
|
||||||
state.list = null;
|
|
||||||
} else if (state.indentation > 0) {
|
|
||||||
state.list = null;
|
|
||||||
state.listDepth = Math.floor(state.indentation / 4);
|
|
||||||
} else { // No longer a list
|
|
||||||
state.list = false;
|
|
||||||
state.listDepth = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var match = null;
|
|
||||||
if (state.indentationDiff >= 4) {
|
|
||||||
stream.skipToEnd();
|
|
||||||
if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
|
|
||||||
state.indentation -= 4;
|
|
||||||
state.indentedCode = true;
|
|
||||||
return code;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else if (stream.eatSpace()) {
|
|
||||||
return null;
|
|
||||||
} else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
|
|
||||||
state.header = match[1].length;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
|
||||||
state.f = state.inline;
|
|
||||||
return getType(state);
|
|
||||||
} else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
|
|
||||||
!prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
|
|
||||||
state.header = match[0].charAt(0) == '=' ? 1 : 2;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
|
||||||
state.f = state.inline;
|
|
||||||
return getType(state);
|
|
||||||
} else if (stream.eat('>')) {
|
|
||||||
state.quote = sol ? 1 : state.quote + 1;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "quote";
|
|
||||||
stream.eatSpace();
|
|
||||||
return getType(state);
|
|
||||||
} else if (stream.peek() === '[') {
|
|
||||||
return switchInline(stream, state, footnoteLink);
|
|
||||||
} else if (stream.match(hrRE, true)) {
|
|
||||||
state.hr = true;
|
|
||||||
return hr;
|
|
||||||
} else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
|
|
||||||
var listType = null;
|
|
||||||
if (stream.match(ulRE, true)) {
|
|
||||||
listType = 'ul';
|
|
||||||
} else {
|
|
||||||
stream.match(olRE, true);
|
|
||||||
listType = 'ol';
|
|
||||||
}
|
|
||||||
state.indentation = stream.column() + stream.current().length;
|
|
||||||
state.list = true;
|
|
||||||
state.listDepth++;
|
|
||||||
if (modeCfg.taskLists && stream.match(taskListRE, false)) {
|
|
||||||
state.taskList = true;
|
|
||||||
}
|
|
||||||
state.f = state.inline;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
|
|
||||||
return getType(state);
|
|
||||||
} else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
|
|
||||||
state.fencedChars = match[1]
|
|
||||||
// try switching mode
|
|
||||||
state.localMode = getMode(match[2]);
|
|
||||||
if (state.localMode) state.localState = state.localMode.startState();
|
|
||||||
state.f = state.block = local;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "code-block";
|
|
||||||
state.code = true;
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return switchInline(stream, state, state.inline);
|
|
||||||
}
|
|
||||||
|
|
||||||
function htmlBlock(stream, state) {
|
|
||||||
var style = htmlMode.token(stream, state.htmlState);
|
|
||||||
if ((htmlFound && state.htmlState.tagStart === null &&
|
|
||||||
(!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
|
|
||||||
(state.md_inside && stream.current().indexOf(">") > -1)) {
|
|
||||||
state.f = inlineNormal;
|
|
||||||
state.block = blockNormal;
|
|
||||||
state.htmlState = null;
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
function local(stream, state) {
|
|
||||||
if (stream.sol() && state.fencedChars && stream.match(state.fencedChars, false)) {
|
|
||||||
state.localMode = state.localState = null;
|
|
||||||
state.f = state.block = leavingLocal;
|
|
||||||
return null;
|
|
||||||
} else if (state.localMode) {
|
|
||||||
return state.localMode.token(stream, state.localState);
|
|
||||||
} else {
|
|
||||||
stream.skipToEnd();
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function leavingLocal(stream, state) {
|
|
||||||
stream.match(state.fencedChars);
|
|
||||||
state.block = blockNormal;
|
|
||||||
state.f = inlineNormal;
|
|
||||||
state.fencedChars = null;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "code-block";
|
|
||||||
state.code = true;
|
|
||||||
var returnType = getType(state);
|
|
||||||
state.code = false;
|
|
||||||
return returnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inline
|
|
||||||
function getType(state) {
|
|
||||||
var styles = [];
|
|
||||||
|
|
||||||
if (state.formatting) {
|
|
||||||
styles.push(formatting);
|
|
||||||
|
|
||||||
if (typeof state.formatting === "string") state.formatting = [state.formatting];
|
|
||||||
|
|
||||||
for (var i = 0; i < state.formatting.length; i++) {
|
|
||||||
styles.push(formatting + "-" + state.formatting[i]);
|
|
||||||
|
|
||||||
if (state.formatting[i] === "header") {
|
|
||||||
styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add `formatting-quote` and `formatting-quote-#` for blockquotes
|
|
||||||
// Add `error` instead if the maximum blockquote nesting depth is passed
|
|
||||||
if (state.formatting[i] === "quote") {
|
|
||||||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
|
|
||||||
styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote);
|
|
||||||
} else {
|
|
||||||
styles.push("error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.taskOpen) {
|
|
||||||
styles.push("meta");
|
|
||||||
return styles.length ? styles.join(' ') : null;
|
|
||||||
}
|
|
||||||
if (state.taskClosed) {
|
|
||||||
styles.push("property");
|
|
||||||
return styles.length ? styles.join(' ') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.linkHref) {
|
|
||||||
styles.push(linkhref, "url");
|
|
||||||
} else { // Only apply inline styles to non-url text
|
|
||||||
if (state.strong) { styles.push(strong); }
|
|
||||||
if (state.em) { styles.push(em); }
|
|
||||||
if (state.strikethrough) { styles.push(strikethrough); }
|
|
||||||
|
|
||||||
if (state.linkText) { styles.push(linktext); }
|
|
||||||
|
|
||||||
if (state.code) { styles.push(code); }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
|
|
||||||
|
|
||||||
if (state.quote) {
|
|
||||||
styles.push(quote);
|
|
||||||
|
|
||||||
// Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
|
|
||||||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
|
|
||||||
styles.push(quote + "-" + state.quote);
|
|
||||||
} else {
|
|
||||||
styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.list !== false) {
|
|
||||||
var listMod = (state.listDepth - 1) % 3;
|
|
||||||
if (!listMod) {
|
|
||||||
styles.push(list1);
|
|
||||||
} else if (listMod === 1) {
|
|
||||||
styles.push(list2);
|
|
||||||
} else {
|
|
||||||
styles.push(list3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.trailingSpaceNewLine) {
|
|
||||||
styles.push("trailing-space-new-line");
|
|
||||||
} else if (state.trailingSpace) {
|
|
||||||
styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return styles.length ? styles.join(' ') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleText(stream, state) {
|
|
||||||
if (stream.match(textRE, true)) {
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function inlineNormal(stream, state) {
|
|
||||||
var style = state.text(stream, state);
|
|
||||||
if (typeof style !== 'undefined')
|
|
||||||
return style;
|
|
||||||
|
|
||||||
if (state.list) { // List marker (*, +, -, 1., etc)
|
|
||||||
state.list = null;
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.taskList) {
|
|
||||||
var taskOpen = stream.match(taskListRE, true)[1] !== "x";
|
|
||||||
if (taskOpen) state.taskOpen = true;
|
|
||||||
else state.taskClosed = true;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "task";
|
|
||||||
state.taskList = false;
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.taskOpen = false;
|
|
||||||
state.taskClosed = false;
|
|
||||||
|
|
||||||
if (state.header && stream.match(/^#+$/, true)) {
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "header";
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get sol() value now, before character is consumed
|
|
||||||
var sol = stream.sol();
|
|
||||||
|
|
||||||
var ch = stream.next();
|
|
||||||
|
|
||||||
if (ch === '\\') {
|
|
||||||
stream.next();
|
|
||||||
if (modeCfg.highlightFormatting) {
|
|
||||||
var type = getType(state);
|
|
||||||
return type ? type + " formatting-escape" : "formatting-escape";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches link titles present on next line
|
|
||||||
if (state.linkTitle) {
|
|
||||||
state.linkTitle = false;
|
|
||||||
var matchCh = ch;
|
|
||||||
if (ch === '(') {
|
|
||||||
matchCh = ')';
|
|
||||||
}
|
|
||||||
matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
|
|
||||||
var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
|
|
||||||
if (stream.match(new RegExp(regex), true)) {
|
|
||||||
return linkhref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this block is changed, it may need to be updated in GFM mode
|
|
||||||
if (ch === '`') {
|
|
||||||
var previousFormatting = state.formatting;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "code";
|
|
||||||
var t = getType(state);
|
|
||||||
var before = stream.pos;
|
|
||||||
stream.eatWhile('`');
|
|
||||||
var difference = 1 + stream.pos - before;
|
|
||||||
if (!state.code) {
|
|
||||||
codeDepth = difference;
|
|
||||||
state.code = true;
|
|
||||||
return getType(state);
|
|
||||||
} else {
|
|
||||||
if (difference === codeDepth) { // Must be exact
|
|
||||||
state.code = false;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
state.formatting = previousFormatting;
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
} else if (state.code) {
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
|
|
||||||
stream.match(/\[[^\]]*\]/);
|
|
||||||
state.inline = state.f = linkHref;
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
|
|
||||||
state.linkText = true;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
var type = getType(state);
|
|
||||||
state.linkText = false;
|
|
||||||
state.inline = state.f = linkHref;
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
|
|
||||||
state.f = state.inline = linkInline;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
var type = getType(state);
|
|
||||||
if (type){
|
|
||||||
type += " ";
|
|
||||||
} else {
|
|
||||||
type = "";
|
|
||||||
}
|
|
||||||
return type + linkinline;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
|
|
||||||
state.f = state.inline = linkInline;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
var type = getType(state);
|
|
||||||
if (type){
|
|
||||||
type += " ";
|
|
||||||
} else {
|
|
||||||
type = "";
|
|
||||||
}
|
|
||||||
return type + linkemail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
|
|
||||||
var end = stream.string.indexOf(">", stream.pos);
|
|
||||||
if (end != -1) {
|
|
||||||
var atts = stream.string.substring(stream.start, end);
|
|
||||||
if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
|
|
||||||
}
|
|
||||||
stream.backUp(1);
|
|
||||||
state.htmlState = CodeMirror.startState(htmlMode);
|
|
||||||
return switchBlock(stream, state, htmlBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === '<' && stream.match(/^\/\w*?>/)) {
|
|
||||||
state.md_inside = false;
|
|
||||||
return "tag";
|
|
||||||
}
|
|
||||||
|
|
||||||
var ignoreUnderscore = false;
|
|
||||||
if (!modeCfg.underscoresBreakWords) {
|
|
||||||
if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
|
|
||||||
var prevPos = stream.pos - 2;
|
|
||||||
if (prevPos >= 0) {
|
|
||||||
var prevCh = stream.string.charAt(prevPos);
|
|
||||||
if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
|
|
||||||
ignoreUnderscore = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
|
|
||||||
if (sol && stream.peek() === ' ') {
|
|
||||||
// Do nothing, surrounded by newline and space
|
|
||||||
} else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "strong";
|
|
||||||
var t = getType(state);
|
|
||||||
state.strong = false;
|
|
||||||
return t;
|
|
||||||
} else if (!state.strong && stream.eat(ch)) { // Add STRONG
|
|
||||||
state.strong = ch;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "strong";
|
|
||||||
return getType(state);
|
|
||||||
} else if (state.em === ch) { // Remove EM
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "em";
|
|
||||||
var t = getType(state);
|
|
||||||
state.em = false;
|
|
||||||
return t;
|
|
||||||
} else if (!state.em) { // Add EM
|
|
||||||
state.em = ch;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "em";
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
} else if (ch === ' ') {
|
|
||||||
if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
|
|
||||||
if (stream.peek() === ' ') { // Surrounded by spaces, ignore
|
|
||||||
return getType(state);
|
|
||||||
} else { // Not surrounded by spaces, back up pointer
|
|
||||||
stream.backUp(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modeCfg.strikethrough) {
|
|
||||||
if (ch === '~' && stream.eatWhile(ch)) {
|
|
||||||
if (state.strikethrough) {// Remove strikethrough
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
|
|
||||||
var t = getType(state);
|
|
||||||
state.strikethrough = false;
|
|
||||||
return t;
|
|
||||||
} else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
|
|
||||||
state.strikethrough = true;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
} else if (ch === ' ') {
|
|
||||||
if (stream.match(/^~~/, true)) { // Probably surrounded by space
|
|
||||||
if (stream.peek() === ' ') { // Surrounded by spaces, ignore
|
|
||||||
return getType(state);
|
|
||||||
} else { // Not surrounded by spaces, back up pointer
|
|
||||||
stream.backUp(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === ' ') {
|
|
||||||
if (stream.match(/ +$/, false)) {
|
|
||||||
state.trailingSpace++;
|
|
||||||
} else if (state.trailingSpace) {
|
|
||||||
state.trailingSpaceNewLine = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function linkInline(stream, state) {
|
|
||||||
var ch = stream.next();
|
|
||||||
|
|
||||||
if (ch === ">") {
|
|
||||||
state.f = state.inline = inlineNormal;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
var type = getType(state);
|
|
||||||
if (type){
|
|
||||||
type += " ";
|
|
||||||
} else {
|
|
||||||
type = "";
|
|
||||||
}
|
|
||||||
return type + linkinline;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.match(/^[^>]+/, true);
|
|
||||||
|
|
||||||
return linkinline;
|
|
||||||
}
|
|
||||||
|
|
||||||
function linkHref(stream, state) {
|
|
||||||
// Check if space, and return NULL if so (to avoid marking the space)
|
|
||||||
if(stream.eatSpace()){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var ch = stream.next();
|
|
||||||
if (ch === '(' || ch === '[') {
|
|
||||||
state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link-string";
|
|
||||||
state.linkHref = true;
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
return 'error';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLinkHrefInside(endChar) {
|
|
||||||
return function(stream, state) {
|
|
||||||
var ch = stream.next();
|
|
||||||
|
|
||||||
if (ch === endChar) {
|
|
||||||
state.f = state.inline = inlineNormal;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link-string";
|
|
||||||
var returnState = getType(state);
|
|
||||||
state.linkHref = false;
|
|
||||||
return returnState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream.match(inlineRE(endChar), true)) {
|
|
||||||
stream.backUp(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.linkHref = true;
|
|
||||||
return getType(state);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function footnoteLink(stream, state) {
|
|
||||||
if (stream.match(/^[^\]]*\]:/, false)) {
|
|
||||||
state.f = footnoteLinkInside;
|
|
||||||
stream.next(); // Consume [
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
state.linkText = true;
|
|
||||||
return getType(state);
|
|
||||||
}
|
|
||||||
return switchInline(stream, state, inlineNormal);
|
|
||||||
}
|
|
||||||
|
|
||||||
function footnoteLinkInside(stream, state) {
|
|
||||||
if (stream.match(/^\]:/, true)) {
|
|
||||||
state.f = state.inline = footnoteUrl;
|
|
||||||
if (modeCfg.highlightFormatting) state.formatting = "link";
|
|
||||||
var returnType = getType(state);
|
|
||||||
state.linkText = false;
|
|
||||||
return returnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.match(/^[^\]]+/, true);
|
|
||||||
|
|
||||||
return linktext;
|
|
||||||
}
|
|
||||||
|
|
||||||
function footnoteUrl(stream, state) {
|
|
||||||
// Check if space, and return NULL if so (to avoid marking the space)
|
|
||||||
if(stream.eatSpace()){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Match URL
|
|
||||||
stream.match(/^[^\s]+/, true);
|
|
||||||
// Check for link title
|
|
||||||
if (stream.peek() === undefined) { // End of line, set flag to check next line
|
|
||||||
state.linkTitle = true;
|
|
||||||
} else { // More content on line, check if link title
|
|
||||||
stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
|
|
||||||
}
|
|
||||||
state.f = state.inline = inlineNormal;
|
|
||||||
return linkhref + " url";
|
|
||||||
}
|
|
||||||
|
|
||||||
var savedInlineRE = [];
|
|
||||||
function inlineRE(endChar) {
|
|
||||||
if (!savedInlineRE[endChar]) {
|
|
||||||
// Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
|
|
||||||
endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
|
|
||||||
// Match any non-endChar, escaped character, as well as the closing
|
|
||||||
// endChar.
|
|
||||||
savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
|
|
||||||
}
|
|
||||||
return savedInlineRE[endChar];
|
|
||||||
}
|
|
||||||
|
|
||||||
var mode = {
|
|
||||||
startState: function() {
|
|
||||||
return {
|
|
||||||
f: blockNormal,
|
|
||||||
|
|
||||||
prevLine: null,
|
|
||||||
thisLine: null,
|
|
||||||
|
|
||||||
block: blockNormal,
|
|
||||||
htmlState: null,
|
|
||||||
indentation: 0,
|
|
||||||
|
|
||||||
inline: inlineNormal,
|
|
||||||
text: handleText,
|
|
||||||
|
|
||||||
formatting: false,
|
|
||||||
linkText: false,
|
|
||||||
linkHref: false,
|
|
||||||
linkTitle: false,
|
|
||||||
em: false,
|
|
||||||
strong: false,
|
|
||||||
header: 0,
|
|
||||||
hr: false,
|
|
||||||
taskList: false,
|
|
||||||
list: false,
|
|
||||||
listDepth: 0,
|
|
||||||
quote: 0,
|
|
||||||
trailingSpace: 0,
|
|
||||||
trailingSpaceNewLine: false,
|
|
||||||
strikethrough: false,
|
|
||||||
fencedChars: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
copyState: function(s) {
|
|
||||||
return {
|
|
||||||
f: s.f,
|
|
||||||
|
|
||||||
prevLine: s.prevLine,
|
|
||||||
thisLine: s.this,
|
|
||||||
|
|
||||||
block: s.block,
|
|
||||||
htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
|
|
||||||
indentation: s.indentation,
|
|
||||||
|
|
||||||
localMode: s.localMode,
|
|
||||||
localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
|
|
||||||
|
|
||||||
inline: s.inline,
|
|
||||||
text: s.text,
|
|
||||||
formatting: false,
|
|
||||||
linkTitle: s.linkTitle,
|
|
||||||
code: s.code,
|
|
||||||
em: s.em,
|
|
||||||
strong: s.strong,
|
|
||||||
strikethrough: s.strikethrough,
|
|
||||||
header: s.header,
|
|
||||||
hr: s.hr,
|
|
||||||
taskList: s.taskList,
|
|
||||||
list: s.list,
|
|
||||||
listDepth: s.listDepth,
|
|
||||||
quote: s.quote,
|
|
||||||
indentedCode: s.indentedCode,
|
|
||||||
trailingSpace: s.trailingSpace,
|
|
||||||
trailingSpaceNewLine: s.trailingSpaceNewLine,
|
|
||||||
md_inside: s.md_inside,
|
|
||||||
fencedChars: s.fencedChars
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
token: function(stream, state) {
|
|
||||||
|
|
||||||
// Reset state.formatting
|
|
||||||
state.formatting = false;
|
|
||||||
|
|
||||||
if (stream != state.thisLine) {
|
|
||||||
var forceBlankLine = state.header || state.hr;
|
|
||||||
|
|
||||||
// Reset state.header and state.hr
|
|
||||||
state.header = 0;
|
|
||||||
state.hr = false;
|
|
||||||
|
|
||||||
if (stream.match(/^\s*$/, true) || forceBlankLine) {
|
|
||||||
blankLine(state);
|
|
||||||
if (!forceBlankLine) return null
|
|
||||||
state.prevLine = null
|
|
||||||
}
|
|
||||||
|
|
||||||
state.prevLine = state.thisLine
|
|
||||||
state.thisLine = stream
|
|
||||||
|
|
||||||
// Reset state.taskList
|
|
||||||
state.taskList = false;
|
|
||||||
|
|
||||||
// Reset state.trailingSpace
|
|
||||||
state.trailingSpace = 0;
|
|
||||||
state.trailingSpaceNewLine = false;
|
|
||||||
|
|
||||||
state.f = state.block;
|
|
||||||
var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
|
|
||||||
var difference = Math.floor((indentation - state.indentation) / 4) * 4;
|
|
||||||
if (difference > 4) difference = 4;
|
|
||||||
var adjustedIndentation = state.indentation + difference;
|
|
||||||
state.indentationDiff = adjustedIndentation - state.indentation;
|
|
||||||
state.indentation = adjustedIndentation;
|
|
||||||
if (indentation > 0) return null;
|
|
||||||
}
|
|
||||||
return state.f(stream, state);
|
|
||||||
},
|
|
||||||
|
|
||||||
innerMode: function(state) {
|
|
||||||
if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
|
|
||||||
if (state.localState) return {state: state.localState, mode: state.localMode};
|
|
||||||
return {state: state, mode: mode};
|
|
||||||
},
|
|
||||||
|
|
||||||
blankLine: blankLine,
|
|
||||||
|
|
||||||
getType: getType,
|
|
||||||
|
|
||||||
fold: "markdown"
|
|
||||||
};
|
|
||||||
return mode;
|
|
||||||
}, "xml");
|
|
||||||
|
|
||||||
CodeMirror.defineMIME("text/x-markdown", "markdown");
|
|
||||||
|
|
||||||
});
|
|
@ -1,85 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
|
||||||
|
|
||||||
// Utility function that allows modes to be combined. The mode given
|
|
||||||
// as the base argument takes care of most of the normal mode
|
|
||||||
// functionality, but a second (typically simple) mode is used, which
|
|
||||||
// can override the style of text. Both modes get to parse all of the
|
|
||||||
// text, but when both assign a non-null style to a piece of code, the
|
|
||||||
// overlay wins, unless the combine argument was true and not overridden,
|
|
||||||
// or state.overlay.combineTokens was true, in which case the styles are
|
|
||||||
// combined.
|
|
||||||
|
|
||||||
(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) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
CodeMirror.overlayMode = function(base, overlay, combine) {
|
|
||||||
return {
|
|
||||||
startState: function() {
|
|
||||||
return {
|
|
||||||
base: CodeMirror.startState(base),
|
|
||||||
overlay: CodeMirror.startState(overlay),
|
|
||||||
basePos: 0, baseCur: null,
|
|
||||||
overlayPos: 0, overlayCur: null,
|
|
||||||
streamSeen: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
copyState: function(state) {
|
|
||||||
return {
|
|
||||||
base: CodeMirror.copyState(base, state.base),
|
|
||||||
overlay: CodeMirror.copyState(overlay, state.overlay),
|
|
||||||
basePos: state.basePos, baseCur: null,
|
|
||||||
overlayPos: state.overlayPos, overlayCur: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
token: function(stream, state) {
|
|
||||||
if (stream != state.streamSeen ||
|
|
||||||
Math.min(state.basePos, state.overlayPos) < stream.start) {
|
|
||||||
state.streamSeen = stream;
|
|
||||||
state.basePos = state.overlayPos = stream.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream.start == state.basePos) {
|
|
||||||
state.baseCur = base.token(stream, state.base);
|
|
||||||
state.basePos = stream.pos;
|
|
||||||
}
|
|
||||||
if (stream.start == state.overlayPos) {
|
|
||||||
stream.pos = stream.start;
|
|
||||||
state.overlayCur = overlay.token(stream, state.overlay);
|
|
||||||
state.overlayPos = stream.pos;
|
|
||||||
}
|
|
||||||
stream.pos = Math.min(state.basePos, state.overlayPos);
|
|
||||||
|
|
||||||
// state.overlay.combineTokens always takes precedence over combine,
|
|
||||||
// unless set to null
|
|
||||||
if (state.overlayCur == null) return state.baseCur;
|
|
||||||
else if (state.baseCur != null &&
|
|
||||||
state.overlay.combineTokens ||
|
|
||||||
combine && state.overlay.combineTokens == null)
|
|
||||||
return state.baseCur + " " + state.overlayCur;
|
|
||||||
else return state.overlayCur;
|
|
||||||
},
|
|
||||||
|
|
||||||
indent: base.indent && function(state, textAfter) {
|
|
||||||
return base.indent(state.base, textAfter);
|
|
||||||
},
|
|
||||||
electricChars: base.electricChars,
|
|
||||||
|
|
||||||
innerMode: function(state) { return {state: state.base, mode: base}; },
|
|
||||||
|
|
||||||
blankLine: function(state) {
|
|
||||||
if (base.blankLine) base.blankLine(state.base);
|
|
||||||
if (overlay.blankLine) overlay.blankLine(state.overlay);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
@ -1,53 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://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) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
CodeMirror.commands.tabAndIndentMarkdownList = function(cm) {
|
|
||||||
var ranges = cm.listSelections();
|
|
||||||
var pos = ranges[0].head;
|
|
||||||
var eolState = cm.getStateAfter(pos.line);
|
|
||||||
var inList = eolState.list !== false;
|
|
||||||
|
|
||||||
if (inList) {
|
|
||||||
cm.execCommand('indentMore');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cm.options.indentWithTabs){
|
|
||||||
cm.execCommand('insertTab');
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
var spaces = Array(cm.options.tabSize + 1).join(" ");
|
|
||||||
cm.replaceSelection(spaces);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CodeMirror.commands.shiftTabAndUnindentMarkdownList = function(cm) {
|
|
||||||
var ranges = cm.listSelections();
|
|
||||||
var pos = ranges[0].head;
|
|
||||||
var eolState = cm.getStateAfter(pos.line);
|
|
||||||
var inList = eolState.list !== false;
|
|
||||||
|
|
||||||
if (inList) {
|
|
||||||
cm.execCommand('indentLess');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cm.options.indentWithTabs){
|
|
||||||
cm.execCommand('insertTab');
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
var spaces = Array(cm.options.tabSize + 1).join(" ");
|
|
||||||
cm.replaceSelection(spaces);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,385 +0,0 @@
|
|||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: http://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) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
CodeMirror.defineMode("xml", function(config, parserConfig) {
|
|
||||||
var indentUnit = config.indentUnit;
|
|
||||||
var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
|
|
||||||
var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
|
|
||||||
if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
|
|
||||||
|
|
||||||
var Kludges = parserConfig.htmlMode ? {
|
|
||||||
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
|
|
||||||
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
|
|
||||||
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
|
|
||||||
'track': true, 'wbr': true, 'menuitem': true},
|
|
||||||
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
|
|
||||||
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
|
|
||||||
'th': true, 'tr': true},
|
|
||||||
contextGrabbers: {
|
|
||||||
'dd': {'dd': true, 'dt': true},
|
|
||||||
'dt': {'dd': true, 'dt': true},
|
|
||||||
'li': {'li': true},
|
|
||||||
'option': {'option': true, 'optgroup': true},
|
|
||||||
'optgroup': {'optgroup': true},
|
|
||||||
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
|
|
||||||
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
|
|
||||||
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
|
|
||||||
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
|
|
||||||
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
|
|
||||||
'rp': {'rp': true, 'rt': true},
|
|
||||||
'rt': {'rp': true, 'rt': true},
|
|
||||||
'tbody': {'tbody': true, 'tfoot': true},
|
|
||||||
'td': {'td': true, 'th': true},
|
|
||||||
'tfoot': {'tbody': true},
|
|
||||||
'th': {'td': true, 'th': true},
|
|
||||||
'thead': {'tbody': true, 'tfoot': true},
|
|
||||||
'tr': {'tr': true}
|
|
||||||
},
|
|
||||||
doNotIndent: {"pre": true},
|
|
||||||
allowUnquoted: true,
|
|
||||||
allowMissing: true,
|
|
||||||
caseFold: true
|
|
||||||
} : {
|
|
||||||
autoSelfClosers: {},
|
|
||||||
implicitlyClosed: {},
|
|
||||||
contextGrabbers: {},
|
|
||||||
doNotIndent: {},
|
|
||||||
allowUnquoted: false,
|
|
||||||
allowMissing: false,
|
|
||||||
caseFold: false
|
|
||||||
};
|
|
||||||
var alignCDATA = parserConfig.alignCDATA;
|
|
||||||
|
|
||||||
// Return variables for tokenizers
|
|
||||||
var type, setStyle;
|
|
||||||
|
|
||||||
function inText(stream, state) {
|
|
||||||
function chain(parser) {
|
|
||||||
state.tokenize = parser;
|
|
||||||
return parser(stream, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ch = stream.next();
|
|
||||||
if (ch == "<") {
|
|
||||||
if (stream.eat("!")) {
|
|
||||||
if (stream.eat("[")) {
|
|
||||||
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
|
||||||
else return null;
|
|
||||||
} else if (stream.match("--")) {
|
|
||||||
return chain(inBlock("comment", "-->"));
|
|
||||||
} else if (stream.match("DOCTYPE", true, true)) {
|
|
||||||
stream.eatWhile(/[\w\._\-]/);
|
|
||||||
return chain(doctype(1));
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else if (stream.eat("?")) {
|
|
||||||
stream.eatWhile(/[\w\._\-]/);
|
|
||||||
state.tokenize = inBlock("meta", "?>");
|
|
||||||
return "meta";
|
|
||||||
} else {
|
|
||||||
type = stream.eat("/") ? "closeTag" : "openTag";
|
|
||||||
state.tokenize = inTag;
|
|
||||||
return "tag bracket";
|
|
||||||
}
|
|
||||||
} else if (ch == "&") {
|
|
||||||
var ok;
|
|
||||||
if (stream.eat("#")) {
|
|
||||||
if (stream.eat("x")) {
|
|
||||||
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
|
|
||||||
} else {
|
|
||||||
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
|
|
||||||
}
|
|
||||||
return ok ? "atom" : "error";
|
|
||||||
} else {
|
|
||||||
stream.eatWhile(/[^&<]/);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inText.isInText = true;
|
|
||||||
|
|
||||||
function inTag(stream, state) {
|
|
||||||
var ch = stream.next();
|
|
||||||
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
|
||||||
state.tokenize = inText;
|
|
||||||
type = ch == ">" ? "endTag" : "selfcloseTag";
|
|
||||||
return "tag bracket";
|
|
||||||
} else if (ch == "=") {
|
|
||||||
type = "equals";
|
|
||||||
return null;
|
|
||||||
} else if (ch == "<") {
|
|
||||||
state.tokenize = inText;
|
|
||||||
state.state = baseState;
|
|
||||||
state.tagName = state.tagStart = null;
|
|
||||||
var next = state.tokenize(stream, state);
|
|
||||||
return next ? next + " tag error" : "tag error";
|
|
||||||
} else if (/[\'\"]/.test(ch)) {
|
|
||||||
state.tokenize = inAttribute(ch);
|
|
||||||
state.stringStartCol = stream.column();
|
|
||||||
return state.tokenize(stream, state);
|
|
||||||
} else {
|
|
||||||
stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
|
|
||||||
return "word";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function inAttribute(quote) {
|
|
||||||
var closure = function(stream, state) {
|
|
||||||
while (!stream.eol()) {
|
|
||||||
if (stream.next() == quote) {
|
|
||||||
state.tokenize = inTag;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "string";
|
|
||||||
};
|
|
||||||
closure.isInAttribute = true;
|
|
||||||
return closure;
|
|
||||||
}
|
|
||||||
|
|
||||||
function inBlock(style, terminator) {
|
|
||||||
return function(stream, state) {
|
|
||||||
while (!stream.eol()) {
|
|
||||||
if (stream.match(terminator)) {
|
|
||||||
state.tokenize = inText;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stream.next();
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function doctype(depth) {
|
|
||||||
return function(stream, state) {
|
|
||||||
var ch;
|
|
||||||
while ((ch = stream.next()) != null) {
|
|
||||||
if (ch == "<") {
|
|
||||||
state.tokenize = doctype(depth + 1);
|
|
||||||
return state.tokenize(stream, state);
|
|
||||||
} else if (ch == ">") {
|
|
||||||
if (depth == 1) {
|
|
||||||
state.tokenize = inText;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
state.tokenize = doctype(depth - 1);
|
|
||||||
return state.tokenize(stream, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "meta";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function Context(state, tagName, startOfLine) {
|
|
||||||
this.prev = state.context;
|
|
||||||
this.tagName = tagName;
|
|
||||||
this.indent = state.indented;
|
|
||||||
this.startOfLine = startOfLine;
|
|
||||||
if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
|
|
||||||
this.noIndent = true;
|
|
||||||
}
|
|
||||||
function popContext(state) {
|
|
||||||
if (state.context) state.context = state.context.prev;
|
|
||||||
}
|
|
||||||
function maybePopContext(state, nextTagName) {
|
|
||||||
var parentTagName;
|
|
||||||
while (true) {
|
|
||||||
if (!state.context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
parentTagName = state.context.tagName;
|
|
||||||
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
|
||||||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
popContext(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function baseState(type, stream, state) {
|
|
||||||
if (type == "openTag") {
|
|
||||||
state.tagStart = stream.column();
|
|
||||||
return tagNameState;
|
|
||||||
} else if (type == "closeTag") {
|
|
||||||
return closeTagNameState;
|
|
||||||
} else {
|
|
||||||
return baseState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function tagNameState(type, stream, state) {
|
|
||||||
if (type == "word") {
|
|
||||||
state.tagName = stream.current();
|
|
||||||
setStyle = "tag";
|
|
||||||
return attrState;
|
|
||||||
} else {
|
|
||||||
setStyle = "error";
|
|
||||||
return tagNameState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function closeTagNameState(type, stream, state) {
|
|
||||||
if (type == "word") {
|
|
||||||
var tagName = stream.current();
|
|
||||||
if (state.context && state.context.tagName != tagName &&
|
|
||||||
Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
|
|
||||||
popContext(state);
|
|
||||||
if (state.context && state.context.tagName == tagName) {
|
|
||||||
setStyle = "tag";
|
|
||||||
return closeState;
|
|
||||||
} else {
|
|
||||||
setStyle = "tag error";
|
|
||||||
return closeStateErr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setStyle = "error";
|
|
||||||
return closeStateErr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeState(type, _stream, state) {
|
|
||||||
if (type != "endTag") {
|
|
||||||
setStyle = "error";
|
|
||||||
return closeState;
|
|
||||||
}
|
|
||||||
popContext(state);
|
|
||||||
return baseState;
|
|
||||||
}
|
|
||||||
function closeStateErr(type, stream, state) {
|
|
||||||
setStyle = "error";
|
|
||||||
return closeState(type, stream, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
function attrState(type, _stream, state) {
|
|
||||||
if (type == "word") {
|
|
||||||
setStyle = "attribute";
|
|
||||||
return attrEqState;
|
|
||||||
} else if (type == "endTag" || type == "selfcloseTag") {
|
|
||||||
var tagName = state.tagName, tagStart = state.tagStart;
|
|
||||||
state.tagName = state.tagStart = null;
|
|
||||||
if (type == "selfcloseTag" ||
|
|
||||||
Kludges.autoSelfClosers.hasOwnProperty(tagName)) {
|
|
||||||
maybePopContext(state, tagName);
|
|
||||||
} else {
|
|
||||||
maybePopContext(state, tagName);
|
|
||||||
state.context = new Context(state, tagName, tagStart == state.indented);
|
|
||||||
}
|
|
||||||
return baseState;
|
|
||||||
}
|
|
||||||
setStyle = "error";
|
|
||||||
return attrState;
|
|
||||||
}
|
|
||||||
function attrEqState(type, stream, state) {
|
|
||||||
if (type == "equals") return attrValueState;
|
|
||||||
if (!Kludges.allowMissing) setStyle = "error";
|
|
||||||
return attrState(type, stream, state);
|
|
||||||
}
|
|
||||||
function attrValueState(type, stream, state) {
|
|
||||||
if (type == "string") return attrContinuedState;
|
|
||||||
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;}
|
|
||||||
setStyle = "error";
|
|
||||||
return attrState(type, stream, state);
|
|
||||||
}
|
|
||||||
function attrContinuedState(type, stream, state) {
|
|
||||||
if (type == "string") return attrContinuedState;
|
|
||||||
return attrState(type, stream, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
startState: function() {
|
|
||||||
return {tokenize: inText,
|
|
||||||
state: baseState,
|
|
||||||
indented: 0,
|
|
||||||
tagName: null, tagStart: null,
|
|
||||||
context: null};
|
|
||||||
},
|
|
||||||
|
|
||||||
token: function(stream, state) {
|
|
||||||
if (!state.tagName && stream.sol())
|
|
||||||
state.indented = stream.indentation();
|
|
||||||
|
|
||||||
if (stream.eatSpace()) return null;
|
|
||||||
type = null;
|
|
||||||
var style = state.tokenize(stream, state);
|
|
||||||
if ((style || type) && style != "comment") {
|
|
||||||
setStyle = null;
|
|
||||||
state.state = state.state(type || style, stream, state);
|
|
||||||
if (setStyle)
|
|
||||||
style = setStyle == "error" ? style + " error" : setStyle;
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
},
|
|
||||||
|
|
||||||
indent: function(state, textAfter, fullLine) {
|
|
||||||
var context = state.context;
|
|
||||||
// Indent multi-line strings (e.g. css).
|
|
||||||
if (state.tokenize.isInAttribute) {
|
|
||||||
if (state.tagStart == state.indented)
|
|
||||||
return state.stringStartCol + 1;
|
|
||||||
else
|
|
||||||
return state.indented + indentUnit;
|
|
||||||
}
|
|
||||||
if (context && context.noIndent) return CodeMirror.Pass;
|
|
||||||
if (state.tokenize != inTag && state.tokenize != inText)
|
|
||||||
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
|
||||||
// Indent the starts of attribute names.
|
|
||||||
if (state.tagName) {
|
|
||||||
if (multilineTagIndentPastTag)
|
|
||||||
return state.tagStart + state.tagName.length + 2;
|
|
||||||
else
|
|
||||||
return state.tagStart + indentUnit * multilineTagIndentFactor;
|
|
||||||
}
|
|
||||||
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
|
||||||
var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
|
|
||||||
if (tagAfter && tagAfter[1]) { // Closing tag spotted
|
|
||||||
while (context) {
|
|
||||||
if (context.tagName == tagAfter[2]) {
|
|
||||||
context = context.prev;
|
|
||||||
break;
|
|
||||||
} else if (Kludges.implicitlyClosed.hasOwnProperty(context.tagName)) {
|
|
||||||
context = context.prev;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (tagAfter) { // Opening tag spotted
|
|
||||||
while (context) {
|
|
||||||
var grabbers = Kludges.contextGrabbers[context.tagName];
|
|
||||||
if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
|
|
||||||
context = context.prev;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (context && !context.startOfLine)
|
|
||||||
context = context.prev;
|
|
||||||
if (context) return context.indent + indentUnit;
|
|
||||||
else return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
electricInput: /<\/[\s\w:]+>$/,
|
|
||||||
blockCommentStart: "<!--",
|
|
||||||
blockCommentEnd: "-->",
|
|
||||||
|
|
||||||
configuration: parserConfig.htmlMode ? "html" : "xml",
|
|
||||||
helperType: parserConfig.htmlMode ? "html" : "xml"
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
CodeMirror.defineMIME("text/xml", "xml");
|
|
||||||
CodeMirror.defineMIME("application/xml", "xml");
|
|
||||||
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
|
|
||||||
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
|
||||||
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
@ -1,92 +0,0 @@
|
|||||||
// Initialize data globally to reduce memory consumption
|
|
||||||
var num_loaded = 0;
|
|
||||||
var aff_loading = false;
|
|
||||||
var dic_loading = false;
|
|
||||||
var aff_data = "";
|
|
||||||
var dic_data = "";
|
|
||||||
var typo;
|
|
||||||
|
|
||||||
|
|
||||||
CodeMirror.defineMode("spell-checker", function(config, parserConfig) {
|
|
||||||
// Load AFF/DIC data
|
|
||||||
if(!aff_loading){
|
|
||||||
aff_loading = true;
|
|
||||||
var xhr_aff = new XMLHttpRequest();
|
|
||||||
xhr_aff.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff", true);
|
|
||||||
xhr_aff.onload = function (e) {
|
|
||||||
if (xhr_aff.readyState === 4 && xhr_aff.status === 200) {
|
|
||||||
aff_data = xhr_aff.responseText;
|
|
||||||
num_loaded++;
|
|
||||||
|
|
||||||
if(num_loaded == 2){
|
|
||||||
typo = new Typo("en_US", aff_data, dic_data, {
|
|
||||||
platform: 'any'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr_aff.send(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!dic_loading){
|
|
||||||
dic_loading = true;
|
|
||||||
var xhr_dic = new XMLHttpRequest();
|
|
||||||
xhr_dic.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic", true);
|
|
||||||
xhr_dic.onload = function (e) {
|
|
||||||
if (xhr_dic.readyState === 4 && xhr_dic.status === 200) {
|
|
||||||
dic_data = xhr_dic.responseText;
|
|
||||||
num_loaded++;
|
|
||||||
|
|
||||||
if(num_loaded == 2){
|
|
||||||
typo = new Typo("en_US", aff_data, dic_data, {
|
|
||||||
platform: 'any'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr_dic.send(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Define what separates a word
|
|
||||||
var rx_word = "!\"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ ";
|
|
||||||
|
|
||||||
|
|
||||||
// Create the overlay and such
|
|
||||||
var overlay = {
|
|
||||||
token: function(stream, state) {
|
|
||||||
var ch = stream.peek();
|
|
||||||
var word = "";
|
|
||||||
|
|
||||||
if(rx_word.includes(ch)) {
|
|
||||||
stream.next();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
while((ch = stream.peek()) != null && !rx_word.includes(ch)) {
|
|
||||||
word += ch;
|
|
||||||
stream.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typo && !typo.check(word))
|
|
||||||
return "spell-error"; // CSS class: cm-spell-error
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var mode = CodeMirror.getMode(
|
|
||||||
config, config.backdrop || "text/plain"
|
|
||||||
);
|
|
||||||
|
|
||||||
return CodeMirror.overlayMode(mode, overlay, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Because some browsers don't support this functionality yet
|
|
||||||
if(!String.prototype.includes) {
|
|
||||||
String.prototype.includes = function() {'use strict';
|
|
||||||
return String.prototype.indexOf.apply(this, arguments) !== -1;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,766 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typo is a JavaScript implementation of a spellchecker using hunspell-style
|
|
||||||
* dictionaries.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typo constructor.
|
|
||||||
*
|
|
||||||
* @param {String} [dictionary] The locale code of the dictionary being used. e.g.,
|
|
||||||
* "en_US". This is only used to auto-load dictionaries.
|
|
||||||
* @param {String} [affData] The data from the dictionary's .aff file. If omitted
|
|
||||||
* and the first argument is supplied, in "chrome" platform,
|
|
||||||
* the .aff file will be loaded automatically from
|
|
||||||
* lib/typo/dictionaries/[dictionary]/[dictionary].aff
|
|
||||||
* In other platform, it will be loaded from
|
|
||||||
* [setting.path]/dictionaries/[dictionary]/[dictionary].aff
|
|
||||||
* @param {String} [wordsData] The data from the dictionary's .dic file. If omitted,
|
|
||||||
* and the first argument is supplied, in "chrome" platform,
|
|
||||||
* the .dic file will be loaded automatically from
|
|
||||||
* lib/typo/dictionaries/[dictionary]/[dictionary].dic
|
|
||||||
* In other platform, it will be loaded from
|
|
||||||
* [setting.path]/dictionaries/[dictionary]/[dictionary].dic
|
|
||||||
* @param {Object} [settings] Constructor settings. Available properties are:
|
|
||||||
* {String} [platform]: "chrome" for Chrome Extension or other
|
|
||||||
* value for the usual web.
|
|
||||||
* {String} [dictionaryPath]: path to load dictionary from in non-chrome
|
|
||||||
* environment.
|
|
||||||
* {Object} [flags]: flag information.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @returns {Typo} A Typo object.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var Typo = function (dictionary, affData, wordsData, settings) {
|
|
||||||
settings = settings || {};
|
|
||||||
|
|
||||||
/** Determines the method used for auto-loading .aff and .dic files. **/
|
|
||||||
this.platform = settings.platform || "chrome";
|
|
||||||
|
|
||||||
this.dictionary = null;
|
|
||||||
|
|
||||||
this.rules = {};
|
|
||||||
this.dictionaryTable = {};
|
|
||||||
|
|
||||||
this.compoundRules = [];
|
|
||||||
this.compoundRuleCodes = {};
|
|
||||||
|
|
||||||
this.replacementTable = [];
|
|
||||||
|
|
||||||
this.flags = settings.flags || {};
|
|
||||||
|
|
||||||
if (dictionary) {
|
|
||||||
this.dictionary = dictionary;
|
|
||||||
|
|
||||||
if (this.platform == "chrome") {
|
|
||||||
if (!affData) affData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".aff"));
|
|
||||||
if (!wordsData) wordsData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".dic"));
|
|
||||||
} else {
|
|
||||||
var path = settings.dictionaryPath || '';
|
|
||||||
|
|
||||||
if (!affData) affData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".aff");
|
|
||||||
if (!wordsData) wordsData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".dic");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rules = this._parseAFF(affData);
|
|
||||||
|
|
||||||
// Save the rule codes that are used in compound rules.
|
|
||||||
this.compoundRuleCodes = {};
|
|
||||||
|
|
||||||
for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
|
|
||||||
var rule = this.compoundRules[i];
|
|
||||||
|
|
||||||
for (var j = 0, _jlen = rule.length; j < _jlen; j++) {
|
|
||||||
this.compoundRuleCodes[rule[j]] = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we add this ONLYINCOMPOUND flag to this.compoundRuleCodes, then _parseDIC
|
|
||||||
// will do the work of saving the list of words that are compound-only.
|
|
||||||
if ("ONLYINCOMPOUND" in this.flags) {
|
|
||||||
this.compoundRuleCodes[this.flags.ONLYINCOMPOUND] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dictionaryTable = this._parseDIC(wordsData);
|
|
||||||
|
|
||||||
// Get rid of any codes from the compound rule codes that are never used
|
|
||||||
// (or that were special regex characters). Not especially necessary...
|
|
||||||
for (var i in this.compoundRuleCodes) {
|
|
||||||
if (this.compoundRuleCodes[i].length == 0) {
|
|
||||||
delete this.compoundRuleCodes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the full regular expressions for each compound rule.
|
|
||||||
// I have a feeling (but no confirmation yet) that this method of
|
|
||||||
// testing for compound words is probably slow.
|
|
||||||
for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
|
|
||||||
var ruleText = this.compoundRules[i];
|
|
||||||
|
|
||||||
var expressionText = "";
|
|
||||||
|
|
||||||
for (var j = 0, _jlen = ruleText.length; j < _jlen; j++) {
|
|
||||||
var character = ruleText[j];
|
|
||||||
|
|
||||||
if (character in this.compoundRuleCodes) {
|
|
||||||
expressionText += "(" + this.compoundRuleCodes[character].join("|") + ")";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expressionText += character;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.compoundRules[i] = new RegExp(expressionText, "i");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Typo.prototype = {
|
|
||||||
/**
|
|
||||||
* Loads a Typo instance from a hash of all of the Typo properties.
|
|
||||||
*
|
|
||||||
* @param object obj A hash of Typo properties, probably gotten from a JSON.parse(JSON.stringify(typo_instance)).
|
|
||||||
*/
|
|
||||||
|
|
||||||
load : function (obj) {
|
|
||||||
for (var i in obj) {
|
|
||||||
this[i] = obj[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the contents of a file.
|
|
||||||
*
|
|
||||||
* @param {String} path The path (relative) to the file.
|
|
||||||
* @param {String} [charset="ISO8859-1"] The expected charset of the file
|
|
||||||
* @returns string The file data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_readFile : function (path, charset) {
|
|
||||||
if (!charset) charset = "ISO8859-1";
|
|
||||||
|
|
||||||
var req = new XMLHttpRequest();
|
|
||||||
req.open("GET", path, false);
|
|
||||||
|
|
||||||
if (req.overrideMimeType)
|
|
||||||
req.overrideMimeType("text/plain; charset=" + charset);
|
|
||||||
|
|
||||||
req.send(null);
|
|
||||||
|
|
||||||
return req.responseText;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the rules out from a .aff file.
|
|
||||||
*
|
|
||||||
* @param {String} data The contents of the affix file.
|
|
||||||
* @returns object The rules from the file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_parseAFF : function (data) {
|
|
||||||
var rules = {};
|
|
||||||
|
|
||||||
// Remove comment lines
|
|
||||||
data = this._removeAffixComments(data);
|
|
||||||
|
|
||||||
var lines = data.split("\n");
|
|
||||||
|
|
||||||
for (var i = 0, _len = lines.length; i < _len; i++) {
|
|
||||||
var line = lines[i];
|
|
||||||
|
|
||||||
var definitionParts = line.split(/\s+/);
|
|
||||||
|
|
||||||
var ruleType = definitionParts[0];
|
|
||||||
|
|
||||||
if (ruleType == "PFX" || ruleType == "SFX") {
|
|
||||||
var ruleCode = definitionParts[1];
|
|
||||||
var combineable = definitionParts[2];
|
|
||||||
var numEntries = parseInt(definitionParts[3], 10);
|
|
||||||
|
|
||||||
var entries = [];
|
|
||||||
|
|
||||||
for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
|
|
||||||
var line = lines[j];
|
|
||||||
|
|
||||||
var lineParts = line.split(/\s+/);
|
|
||||||
var charactersToRemove = lineParts[2];
|
|
||||||
|
|
||||||
var additionParts = lineParts[3].split("/");
|
|
||||||
|
|
||||||
var charactersToAdd = additionParts[0];
|
|
||||||
if (charactersToAdd === "0") charactersToAdd = "";
|
|
||||||
|
|
||||||
var continuationClasses = this.parseRuleCodes(additionParts[1]);
|
|
||||||
|
|
||||||
var regexToMatch = lineParts[4];
|
|
||||||
|
|
||||||
var entry = {};
|
|
||||||
entry.add = charactersToAdd;
|
|
||||||
|
|
||||||
if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses;
|
|
||||||
|
|
||||||
if (regexToMatch !== ".") {
|
|
||||||
if (ruleType === "SFX") {
|
|
||||||
entry.match = new RegExp(regexToMatch + "$");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entry.match = new RegExp("^" + regexToMatch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (charactersToRemove != "0") {
|
|
||||||
if (ruleType === "SFX") {
|
|
||||||
entry.remove = new RegExp(charactersToRemove + "$");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entry.remove = charactersToRemove;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entries.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
rules[ruleCode] = { "type" : ruleType, "combineable" : (combineable == "Y"), "entries" : entries };
|
|
||||||
|
|
||||||
i += numEntries;
|
|
||||||
}
|
|
||||||
else if (ruleType === "COMPOUNDRULE") {
|
|
||||||
var numEntries = parseInt(definitionParts[1], 10);
|
|
||||||
|
|
||||||
for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
|
|
||||||
var line = lines[j];
|
|
||||||
|
|
||||||
var lineParts = line.split(/\s+/);
|
|
||||||
this.compoundRules.push(lineParts[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
i += numEntries;
|
|
||||||
}
|
|
||||||
else if (ruleType === "REP") {
|
|
||||||
var lineParts = line.split(/\s+/);
|
|
||||||
|
|
||||||
if (lineParts.length === 3) {
|
|
||||||
this.replacementTable.push([ lineParts[1], lineParts[2] ]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// ONLYINCOMPOUND
|
|
||||||
// COMPOUNDMIN
|
|
||||||
// FLAG
|
|
||||||
// KEEPCASE
|
|
||||||
// NEEDAFFIX
|
|
||||||
|
|
||||||
this.flags[ruleType] = definitionParts[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rules;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes comment lines and then cleans up blank lines and trailing whitespace.
|
|
||||||
*
|
|
||||||
* @param {String} data The data from an affix file.
|
|
||||||
* @return {String} The cleaned-up data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_removeAffixComments : function (data) {
|
|
||||||
// Remove comments
|
|
||||||
data = data.replace(/#.*$/mg, "");
|
|
||||||
|
|
||||||
// Trim each line
|
|
||||||
data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
|
|
||||||
|
|
||||||
// Remove blank lines.
|
|
||||||
data = data.replace(/\n{2,}/g, "\n");
|
|
||||||
|
|
||||||
// Trim the entire string
|
|
||||||
data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the words out from the .dic file.
|
|
||||||
*
|
|
||||||
* @param {String} data The data from the dictionary file.
|
|
||||||
* @returns object The lookup table containing all of the words and
|
|
||||||
* word forms from the dictionary.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_parseDIC : function (data) {
|
|
||||||
data = this._removeDicComments(data);
|
|
||||||
|
|
||||||
var lines = data.split("\n");
|
|
||||||
var dictionaryTable = {};
|
|
||||||
|
|
||||||
function addWord(word, rules) {
|
|
||||||
// Some dictionaries will list the same word multiple times with different rule sets.
|
|
||||||
if (!(word in dictionaryTable) || typeof dictionaryTable[word] != 'object') {
|
|
||||||
dictionaryTable[word] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
dictionaryTable[word].push(rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first line is the number of words in the dictionary.
|
|
||||||
for (var i = 1, _len = lines.length; i < _len; i++) {
|
|
||||||
var line = lines[i];
|
|
||||||
|
|
||||||
var parts = line.split("/", 2);
|
|
||||||
|
|
||||||
var word = parts[0];
|
|
||||||
|
|
||||||
// Now for each affix rule, generate that form of the word.
|
|
||||||
if (parts.length > 1) {
|
|
||||||
var ruleCodesArray = this.parseRuleCodes(parts[1]);
|
|
||||||
|
|
||||||
// Save the ruleCodes for compound word situations.
|
|
||||||
if (!("NEEDAFFIX" in this.flags) || ruleCodesArray.indexOf(this.flags.NEEDAFFIX) == -1) {
|
|
||||||
addWord(word, ruleCodesArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0, _jlen = ruleCodesArray.length; j < _jlen; j++) {
|
|
||||||
var code = ruleCodesArray[j];
|
|
||||||
|
|
||||||
var rule = this.rules[code];
|
|
||||||
|
|
||||||
if (rule) {
|
|
||||||
var newWords = this._applyRule(word, rule);
|
|
||||||
|
|
||||||
for (var ii = 0, _iilen = newWords.length; ii < _iilen; ii++) {
|
|
||||||
var newWord = newWords[ii];
|
|
||||||
|
|
||||||
addWord(newWord, []);
|
|
||||||
|
|
||||||
if (rule.combineable) {
|
|
||||||
for (var k = j + 1; k < _jlen; k++) {
|
|
||||||
var combineCode = ruleCodesArray[k];
|
|
||||||
|
|
||||||
var combineRule = this.rules[combineCode];
|
|
||||||
|
|
||||||
if (combineRule) {
|
|
||||||
if (combineRule.combineable && (rule.type != combineRule.type)) {
|
|
||||||
var otherNewWords = this._applyRule(newWord, combineRule);
|
|
||||||
|
|
||||||
for (var iii = 0, _iiilen = otherNewWords.length; iii < _iiilen; iii++) {
|
|
||||||
var otherNewWord = otherNewWords[iii];
|
|
||||||
addWord(otherNewWord, []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code in this.compoundRuleCodes) {
|
|
||||||
this.compoundRuleCodes[code].push(word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
addWord(word.trim(), []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dictionaryTable;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes comment lines and then cleans up blank lines and trailing whitespace.
|
|
||||||
*
|
|
||||||
* @param {String} data The data from a .dic file.
|
|
||||||
* @return {String} The cleaned-up data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_removeDicComments : function (data) {
|
|
||||||
// I can't find any official documentation on it, but at least the de_DE
|
|
||||||
// dictionary uses tab-indented lines as comments.
|
|
||||||
|
|
||||||
// Remove comments
|
|
||||||
data = data.replace(/^\t.*$/mg, "");
|
|
||||||
|
|
||||||
return data;
|
|
||||||
|
|
||||||
// Trim each line
|
|
||||||
data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
|
|
||||||
|
|
||||||
// Remove blank lines.
|
|
||||||
data = data.replace(/\n{2,}/g, "\n");
|
|
||||||
|
|
||||||
// Trim the entire string
|
|
||||||
data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
parseRuleCodes : function (textCodes) {
|
|
||||||
if (!textCodes) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
else if (!("FLAG" in this.flags)) {
|
|
||||||
return textCodes.split("");
|
|
||||||
}
|
|
||||||
else if (this.flags.FLAG === "long") {
|
|
||||||
var flags = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = textCodes.length; i < _len; i += 2) {
|
|
||||||
flags.push(textCodes.substr(i, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
else if (this.flags.FLAG === "num") {
|
|
||||||
return textCode.split(",");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies an affix rule to a word.
|
|
||||||
*
|
|
||||||
* @param {String} word The base word.
|
|
||||||
* @param {Object} rule The affix rule.
|
|
||||||
* @returns {String[]} The new words generated by the rule.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_applyRule : function (word, rule) {
|
|
||||||
var entries = rule.entries;
|
|
||||||
var newWords = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = entries.length; i < _len; i++) {
|
|
||||||
var entry = entries[i];
|
|
||||||
|
|
||||||
if (!entry.match || word.match(entry.match)) {
|
|
||||||
var newWord = word;
|
|
||||||
|
|
||||||
if (entry.remove) {
|
|
||||||
newWord = newWord.replace(entry.remove, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule.type === "SFX") {
|
|
||||||
newWord = newWord + entry.add;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newWord = entry.add + newWord;
|
|
||||||
}
|
|
||||||
|
|
||||||
newWords.push(newWord);
|
|
||||||
|
|
||||||
if ("continuationClasses" in entry) {
|
|
||||||
for (var j = 0, _jlen = entry.continuationClasses.length; j < _jlen; j++) {
|
|
||||||
var continuationRule = this.rules[entry.continuationClasses[j]];
|
|
||||||
|
|
||||||
if (continuationRule) {
|
|
||||||
newWords = newWords.concat(this._applyRule(newWord, continuationRule));
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
else {
|
|
||||||
// This shouldn't happen, but it does, at least in the de_DE dictionary.
|
|
||||||
// I think the author mistakenly supplied lower-case rule codes instead
|
|
||||||
// of upper-case.
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newWords;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a word or a capitalization variant exists in the current dictionary.
|
|
||||||
* The word is trimmed and several variations of capitalizations are checked.
|
|
||||||
* If you want to check a word without any changes made to it, call checkExact()
|
|
||||||
*
|
|
||||||
* @see http://blog.stevenlevithan.com/archives/faster-trim-javascript re:trimming function
|
|
||||||
*
|
|
||||||
* @param {String} aWord The word to check.
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
check : function (aWord) {
|
|
||||||
// Remove leading and trailing whitespace
|
|
||||||
var trimmedWord = aWord.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
|
||||||
|
|
||||||
if (this.checkExact(trimmedWord)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The exact word is not in the dictionary.
|
|
||||||
if (trimmedWord.toUpperCase() === trimmedWord) {
|
|
||||||
// The word was supplied in all uppercase.
|
|
||||||
// Check for a capitalized form of the word.
|
|
||||||
var capitalizedWord = trimmedWord[0] + trimmedWord.substring(1).toLowerCase();
|
|
||||||
|
|
||||||
if (this.hasFlag(capitalizedWord, "KEEPCASE")) {
|
|
||||||
// Capitalization variants are not allowed for this word.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.checkExact(capitalizedWord)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lowercaseWord = trimmedWord.toLowerCase();
|
|
||||||
|
|
||||||
if (lowercaseWord !== trimmedWord) {
|
|
||||||
if (this.hasFlag(lowercaseWord, "KEEPCASE")) {
|
|
||||||
// Capitalization variants are not allowed for this word.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a lowercase form
|
|
||||||
if (this.checkExact(lowercaseWord)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a word exists in the current dictionary.
|
|
||||||
*
|
|
||||||
* @param {String} word The word to check.
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
checkExact : function (word) {
|
|
||||||
var ruleCodes = this.dictionaryTable[word];
|
|
||||||
|
|
||||||
if (typeof ruleCodes === 'undefined') {
|
|
||||||
// Check if this might be a compound word.
|
|
||||||
if ("COMPOUNDMIN" in this.flags && word.length >= this.flags.COMPOUNDMIN) {
|
|
||||||
for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
|
|
||||||
if (word.match(this.compoundRules[i])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (var i = 0, _len = ruleCodes.length; i < _len; i++) {
|
|
||||||
if (!this.hasFlag(word, "ONLYINCOMPOUND", ruleCodes[i])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks up whether a given word is flagged with a given flag.
|
|
||||||
*
|
|
||||||
* @param {String} word The word in question.
|
|
||||||
* @param {String} flag The flag in question.
|
|
||||||
* @return {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
hasFlag : function (word, flag, wordFlags) {
|
|
||||||
if (flag in this.flags) {
|
|
||||||
if (typeof wordFlags === 'undefined') {
|
|
||||||
var wordFlags = Array.prototype.concat.apply([], this.dictionaryTable[word]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wordFlags && wordFlags.indexOf(this.flags[flag]) !== -1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of suggestions for a misspelled word.
|
|
||||||
*
|
|
||||||
* @see http://www.norvig.com/spell-correct.html for the basis of this suggestor.
|
|
||||||
* This suggestor is primitive, but it works.
|
|
||||||
*
|
|
||||||
* @param {String} word The misspelling.
|
|
||||||
* @param {Number} [limit=5] The maximum number of suggestions to return.
|
|
||||||
* @returns {String[]} The array of suggestions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
alphabet : "",
|
|
||||||
|
|
||||||
suggest : function (word, limit) {
|
|
||||||
if (!limit) limit = 5;
|
|
||||||
|
|
||||||
if (this.check(word)) return [];
|
|
||||||
|
|
||||||
// Check the replacement table.
|
|
||||||
for (var i = 0, _len = this.replacementTable.length; i < _len; i++) {
|
|
||||||
var replacementEntry = this.replacementTable[i];
|
|
||||||
|
|
||||||
if (word.indexOf(replacementEntry[0]) !== -1) {
|
|
||||||
var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]);
|
|
||||||
|
|
||||||
if (this.check(correctedWord)) {
|
|
||||||
return [ correctedWord ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
self.alphabet = "abcdefghijklmnopqrstuvwxyz";
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (!self.alphabet) {
|
|
||||||
// Use the alphabet as implicitly defined by the words in the dictionary.
|
|
||||||
var alphaHash = {};
|
|
||||||
|
|
||||||
for (var i in self.dictionaryTable) {
|
|
||||||
for (var j = 0, _len = i.length; j < _len; j++) {
|
|
||||||
alphaHash[i[j]] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i in alphaHash) {
|
|
||||||
self.alphabet += i;
|
|
||||||
}
|
|
||||||
|
|
||||||
var alphaArray = self.alphabet.split("");
|
|
||||||
alphaArray.sort();
|
|
||||||
self.alphabet = alphaArray.join("");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function edits1(words) {
|
|
||||||
var rv = [];
|
|
||||||
|
|
||||||
for (var ii = 0, _iilen = words.length; ii < _iilen; ii++) {
|
|
||||||
var word = words[ii];
|
|
||||||
|
|
||||||
var splits = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = word.length + 1; i < _len; i++) {
|
|
||||||
splits.push([ word.substring(0, i), word.substring(i, word.length) ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var deletes = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = splits.length; i < _len; i++) {
|
|
||||||
var s = splits[i];
|
|
||||||
|
|
||||||
if (s[1]) {
|
|
||||||
deletes.push(s[0] + s[1].substring(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var transposes = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = splits.length; i < _len; i++) {
|
|
||||||
var s = splits[i];
|
|
||||||
|
|
||||||
if (s[1].length > 1) {
|
|
||||||
transposes.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var replaces = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = splits.length; i < _len; i++) {
|
|
||||||
var s = splits[i];
|
|
||||||
|
|
||||||
if (s[1]) {
|
|
||||||
for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
|
|
||||||
replaces.push(s[0] + self.alphabet[j] + s[1].substring(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var inserts = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = splits.length; i < _len; i++) {
|
|
||||||
var s = splits[i];
|
|
||||||
|
|
||||||
if (s[1]) {
|
|
||||||
for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
|
|
||||||
replaces.push(s[0] + self.alphabet[j] + s[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = rv.concat(deletes);
|
|
||||||
rv = rv.concat(transposes);
|
|
||||||
rv = rv.concat(replaces);
|
|
||||||
rv = rv.concat(inserts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function known(words) {
|
|
||||||
var rv = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < words.length; i++) {
|
|
||||||
if (self.check(words[i])) {
|
|
||||||
rv.push(words[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function correct(word) {
|
|
||||||
// Get the edit-distance-1 and edit-distance-2 forms of this word.
|
|
||||||
var ed1 = edits1([word]);
|
|
||||||
var ed2 = edits1(ed1);
|
|
||||||
|
|
||||||
var corrections = known(ed1).concat(known(ed2));
|
|
||||||
|
|
||||||
// Sort the edits based on how many different ways they were created.
|
|
||||||
var weighted_corrections = {};
|
|
||||||
|
|
||||||
for (var i = 0, _len = corrections.length; i < _len; i++) {
|
|
||||||
if (!(corrections[i] in weighted_corrections)) {
|
|
||||||
weighted_corrections[corrections[i]] = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
weighted_corrections[corrections[i]] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sorted_corrections = [];
|
|
||||||
|
|
||||||
for (var i in weighted_corrections) {
|
|
||||||
sorted_corrections.push([ i, weighted_corrections[i] ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sorter(a, b) {
|
|
||||||
if (a[1] < b[1]) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sorted_corrections.sort(sorter).reverse();
|
|
||||||
|
|
||||||
var rv = [];
|
|
||||||
|
|
||||||
for (var i = 0, _len = Math.min(limit, sorted_corrections.length); i < _len; i++) {
|
|
||||||
if (!self.hasFlag(sorted_corrections[i][0], "NOSUGGEST")) {
|
|
||||||
rv.push(sorted_corrections[i][0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return correct(word);
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue