From 8fb666767fd92e2a3c317e0aa1f4027645d47103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20D=C3=B6bertin?= Date: Tue, 14 Apr 2015 14:57:17 +0200 Subject: [PATCH 01/46] Add custom CodeMirror wrapper based on Mirrormark --- assets/css/codemirror-5.1.0.css | 323 + assets/css/markdown.css | 94 - assets/css/mirrormark.package.min.css | 1 - assets/css/visualmarkdown.css | 230 + assets/js/codemirror-5.1.0.js | 8685 +++++++++++++++++ .../js/codemirror-addon-continuelist-5.1.0.js | 51 + assets/js/codemirror-mode-markdown-5.1.0.js | 765 ++ assets/js/lodash.js | 89 + assets/js/markdown.js | 364 - assets/js/mirrormark.js | 324 + assets/js/mirrormark.package.min.js | 7 - ...eenfull.min.js => screenfull-2.0.0.min.js} | 0 assets/js/visualmarkdowneditor.js | 440 + assets/js/visualmarkdownfield.js | 201 + markdown.php | 17 +- 15 files changed, 11119 insertions(+), 472 deletions(-) create mode 100644 assets/css/codemirror-5.1.0.css delete mode 100644 assets/css/markdown.css delete mode 100644 assets/css/mirrormark.package.min.css create mode 100644 assets/css/visualmarkdown.css create mode 100644 assets/js/codemirror-5.1.0.js create mode 100644 assets/js/codemirror-addon-continuelist-5.1.0.js create mode 100644 assets/js/codemirror-mode-markdown-5.1.0.js create mode 100644 assets/js/lodash.js delete mode 100644 assets/js/markdown.js create mode 100644 assets/js/mirrormark.js delete mode 100644 assets/js/mirrormark.package.min.js rename assets/js/{screenfull.min.js => screenfull-2.0.0.min.js} (100%) create mode 100644 assets/js/visualmarkdowneditor.js create mode 100644 assets/js/visualmarkdownfield.js diff --git a/assets/css/codemirror-5.1.0.css b/assets/css/codemirror-5.1.0.css new file mode 100644 index 0000000..e531d29 --- /dev/null +++ b/assets/css/codemirror-5.1.0.css @@ -0,0 +1,323 @@ +/* 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 div.CodeMirror-cursor { + border-left: 1px solid black; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.CodeMirror.cm-fat-cursor div.CodeMirror-cursor { + width: auto; + border: 0; + background: #7e7; +} +.CodeMirror.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; +} +@-moz-keyframes blink { + 0% { background: #7e7; } + 50% { background: none; } + 100% { background: #7e7; } +} +@-webkit-keyframes blink { + 0% { background: #7e7; } + 50% { background: none; } + 100% { background: #7e7; } +} +@keyframes blink { + 0% { background: #7e7; } + 50% { background: none; } + 100% { background: #7e7; } +} + +/* Can style cursor different in overwrite (non-insert) mode */ +div.CodeMirror-overwrite div.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-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-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.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-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +/* 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; + height: 100%; +} +.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-measure pre { position: static; } + +.CodeMirror div.CodeMirror-cursor { + position: absolute; + border-right: none; + width: 0; +} + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror ::selection { background: #d7d4f0; } +.CodeMirror ::-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; } diff --git a/assets/css/markdown.css b/assets/css/markdown.css deleted file mode 100644 index 9f9d85d..0000000 --- a/assets/css/markdown.css +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Visual Markdown Editor Field for Kirby 2 - * - * @version 1.1.0 - * @author Jonas Döbertin - * @copyright Jonas Döbertin - * @link https://github.com/JonasDoebertin/kirby-visual-markdown - * @license GNU GPL v3.0 - */ - - -/** - * WRAPPER - */ -.markdownfield-wrapper { - border: 2px solid #ddd; -} - - .markdownfield-wrapper-focused { - border-color: #8dae28; - } - - .markdownfield-wrapper-over { - border-color: #000000; - } - - -/** - * EDITOR - */ -.markdownfield-wrapper .CodeMirror-scroll { - padding: 2em; - margin-bottom: 0; - margin-right: -2em; -} - - -/** - * ICON - */ -.field-with-markdown.field-with-icon .CodeMirror-scroll { - margin-right: 1em; -} - -.field-with-markdown.field-with-icon .field-icon { - z-index: 0; -} - - -/** - * TOOLBAR - */ -.markdownfield-wrapper .mirrormark-toolbar { - border-bottom: 1px solid #efefef; - position: relative; - z-index: 100; -} - - .markdownfield-wrapper .mirrormark-toolbar .markdownfield-icon-text { - font-weight: bold; - text-transform: uppercase; - font-size: 18px; - } - - -/** - * FULLSCREEN MODE - */ -.markdownfield-wrapper-fullscreen { - border-width: 0; - height: 100% !important; - overflow: scroll !important; -} - - .markdownfield-wrapper-fullscreen .mirrormark-toolbar, - .markdownfield-wrapper-fullscreen .CodeMirror { - max-width: 60em; - } - - .markdownfield-wrapper-fullscreen .mirrormark-toolbar { - margin: auto; - position: fixed; - top: 0; - left: 0; - right: 0; - } - - .markdownfield-wrapper-fullscreen .CodeMirror { - padding: 2em 0 0; - } - - .field-with-markdown.field-with-icon .markdownfield-wrapper-fullscreen .CodeMirror-scroll { - margin-right: -2em; - } diff --git a/assets/css/mirrormark.package.min.css b/assets/css/mirrormark.package.min.css deleted file mode 100644 index 2ba2fc1..0000000 --- a/assets/css/mirrormark.package.min.css +++ /dev/null @@ -1 +0,0 @@ -.CodeMirror-gutter,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box}.CodeMirror{font-family:monospace;height:300px;color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;box-sizing:content-box}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror div.CodeMirror-cursor{border-left:1px solid #000}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.CodeMirror.cm-fat-cursor div.CodeMirror-cursor{width:auto;border:0;background:#7e7}.CodeMirror.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}@-moz-keyframes blink{0%,100%{background:#7e7}50%{background:0 0}}@-webkit-keyframes blink{0%,100%{background:#7e7}50%{background:0 0}}@keyframes blink{0%,100%{background:#7e7}50%{background:0 0}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-ruler{border-left:1px solid #ccc;position:absolute}.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-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,.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-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-invalidchar,.cm-s-default .cm-error{color:red}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}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:0;position:relative;box-sizing:content-box}.CodeMirror-sizer{position:relative;border-right:30px solid transparent;box-sizing:content-box}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{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%;box-sizing:content-box;display:inline-block;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;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-code{outline:0}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-measure pre{position:static}.CodeMirror div.CodeMirror-cursor{position:absolute;border-right:none;width:0}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror ::selection,.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror ::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.cm-s-mirrormark{font:16px/1.7 Menlo,Consolas,Monaco,'Andale Mono',monospace;box-sizing:border-box;height:auto;margin:auto;position:relative;z-index:0}.cm-s-mirrormark .CodeMirror-scroll{height:auto!important;overflow:visible!important;padding:4%;box-sizing:border-box}.cm-s-mirrormark .cm-header-1{font:28px Menlo,Consolas,Monaco,'Andale Mono',monospace;font-weight:700}.cm-s-mirrormark .cm-header-2{font:18px Menlo,Consolas,Monaco,'Andale Mono',monospace;font-weight:700}.cm-s-mirrormark .cm-link,.cm-s-mirrormark .cm-quote,.cm-s-mirrormark .cm-string,.cm-s-mirrormark .cm-tag{color:#888}.mirrormark-toolbar{background:#fff;border-bottom:1px solid rgba(0,0,0,.1);display:block;width:100%;margin-top:0;margin-bottom:0;padding-left:0;padding-right:0}.mirrormark-toolbar li{display:inline-block;position:relative;z-index:1}.mirrormark-toolbar li.fullScreen{float:right}.mirrormark-toolbar li a{background-color:#fff;color:#888;cursor:pointer;display:block;font-size:16px;height:40px;line-height:40px;text-align:center;transition:color .2s linear;width:40px}.mirrormark-toolbar li.has-nested:after{content:'\2304';color:#888;position:absolute;bottom:3px;right:3px;font-size:11px;transform:scale(1.3,1)}.mirrormark-toolbar li.has-nested:hover:after,.mirrormark-toolbar li:hover a{color:#000}.mirrormark-toolbar li:hover ul{display:block}.mirrormark-toolbar ul{background:#e5e5e5;display:none;position:absolute;top:41px;width:150px;z-index:1000}.mirrormark-toolbar ul li{float:none;width:100%}.mirrormark-toolbar ul li a{background:0 0;color:#888!important;font-family:"Helvetica Neue",sans-serif;font-size:12px;height:auto;line-height:normal;padding:.25rem .75rem;text-align:left;width:auto}.mirrormark-toolbar ul li:hover a{color:#000!important}:-webkit-full-screen{width:100%!important;height:100%!important}:-moz-full-screen .CodeMirror-scroll{height:100%!important;overflow:scroll!important}:-webkit-full-screen .CodeMirror-scroll{height:100%!important;overflow:scroll!important} diff --git a/assets/css/visualmarkdown.css b/assets/css/visualmarkdown.css new file mode 100644 index 0000000..f38c961 --- /dev/null +++ b/assets/css/visualmarkdown.css @@ -0,0 +1,230 @@ +/** + * Visual Markdown Editor Field for Kirby 2 + * + * @version 1.1.0 + * @author Jonas Döbertin + * @copyright Jonas Döbertin + * @link https://github.com/JonasDoebertin/kirby-visual-markdown + * @license GNU GPL v3.0 + */ + + +/** + * WRAPPER + */ +.markdownfield-wrapper { + border: 2px solid #ddd; +} + + .markdownfield-wrapper-focused { + border-color: #8dae28; + } + + .markdownfield-wrapper-over { + border-color: #000000; + } + + +/** + * CODEMIRROR THEME + */ +.cm-s-visualmarkdown { + font: 16px/1.7 Menlo, Consolas, Monaco, 'Andale Mono', monospace; + box-sizing: border-box; + height: auto; + margin: auto; + position: relative; + z-index: 0 +} + + .cm-s-visualmarkdown .CodeMirror-scroll { + height: auto !important; + overflow: visible !important; + box-sizing: border-box; + padding: 2em; + margin-bottom: 0; + margin-right: -2em; + } + + /** + * SYNTAX ELEMENTS + */ + .cm-s-visualmarkdown .cm-header-1 { + font: 28px Menlo, Consolas, Monaco, 'Andale Mono', monospace; + font-weight: bold; + } + + .cm-s-visualmarkdown .cm-header-2 { + font: 22px Menlo, Consolas, Monaco, 'Andale Mono', monospace; + font-weight: bold; + } + + .cm-s-visualmarkdown .cm-quote, + .cm-s-visualmarkdown .cm-link, + .cm-s-visualmarkdown .cm-tag, + .cm-s-visualmarkdown .cm-string { + color: #888; + } + + +/** + * TOOLBAR THEME + */ +.markdownfield-wrapper .visualmarkdown-toolbar { + background: #ffffff; + border-bottom: 1px solid #efefef; + display: block; + width: 100%; + margin-top: 0; + margin-bottom: 0; + padding-left: 0; + padding-right: 0; + position: relative; + z-index: 100; +} + + .markdownfield-wrapper .visualmarkdown-toolbar li { + display: inline-block; + position: relative; + z-index: 1 + } + + .markdownfield-wrapper .visualmarkdown-toolbar li.fullScreen { + float: right; + } + + .markdownfield-wrapper .visualmarkdown-toolbar li.divider + li { + padding-left: 0.5em; + border-left: 1px solid #efefef; + margin-left: 0.5em; + } + + .markdownfield-wrapper .visualmarkdown-toolbar li a { + background-color: #fff; + color: #888; + cursor: pointer; + display: block; + font-size: 16px; + height: 40px; + line-height: 40px; + text-align: center; + transition: color .2s linear; + width: 40px; + } + + .markdownfield-wrapper .visualmarkdown-toolbar li:hover a { + color: #000; + } + + .markdownfield-wrapper .visualmarkdown-toolbar li a.markdownfield-icon-text { + font-weight: bold; + text-transform: uppercase; + font-size: 18px; + } + + /*.markdownfield-wrapper .visualmarkdown-toolbar li.has-nested:after { + content: '\2304'; + color: #888; + position: absolute; + bottom: 3px; + right: 3px; + font-size: 11px; + transform:scale(1.3, 1) + }*/ + + /*.markdownfield-wrapper .visualmarkdown-toolbar li.has-nested:hover:after { + color: #000; + }*/ + + /*.markdownfield-wrapper .visualmarkdown-toolbar li:hover ul { + display: block; + }*/ + + /* Second Level */ + /*.visualmarkdown-toolbar ul { + background: #e5e5e5; + display: none; + position: absolute; + top: 41px; + width: 150px; + z-index: 1000; + } + + .visualmarkdown-toolbar ul li { + float: none; + width: 100%; + } + + .visualmarkdown-toolbar ul li a { + background: transparent; + color: #888 !important; + font-family: "Helvetica Neue", sans-serif; + font-size: 12px; + height: auto; + line-height: normal; + padding: .25rem .75rem; + text-align: left; + width: auto; + } + + .visualmarkdown-toolbar ul li:hover a { + color: black !important; + }*/ + + +/** + * FIELD ICON + */ +.field-with-visualmarkdown.field-with-icon .CodeMirror-scroll { + margin-right: 1em; +} + +.field-with-visualmarkdown.field-with-icon .field-icon { + z-index: 0; +} + + +/** + * FULLSCREEN MODE + */ +:-webkit-full-screen { + width: 100% !important; + height: 100% !important; +} + +:-moz-full-screen .CodeMirror-scroll { + height: 100% !important; + overflow: scroll !important; +} + +:-webkit-full-screen .CodeMirror-scroll { + height: 100% !important; + overflow: scroll !important; +} + +.markdownfield-wrapper-fullscreen { + border-width: 0; + height: 100% !important; + overflow: scroll !important; +} + + .markdownfield-wrapper-fullscreen .visualmarkdown-toolbar, + .markdownfield-wrapper-fullscreen .CodeMirror { + max-width: 60em; + } + + .markdownfield-wrapper-fullscreen .visualmarkdown-toolbar { + margin: auto; + position: fixed; + top: 0; + left: 0; + right: 0; + } + + .markdownfield-wrapper-fullscreen .CodeMirror { + padding: 2em 0 0; + } + + .field-with-markdown.field-with-icon .markdownfield-wrapper-fullscreen .CodeMirror-scroll { + margin-right: -2em; + } diff --git a/assets/js/codemirror-5.1.0.js b/assets/js/codemirror-5.1.0.js new file mode 100644 index 0000000..33f8f8f --- /dev/null +++ b/assets/js/codemirror-5.1.0.js @@ -0,0 +1,8685 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// This is CodeMirror (http://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + module.exports = mod(); + else if (typeof define == "function" && define.amd) // AMD + return define([], mod); + else // Plain browser env + this.CodeMirror = mod(); +})(function() { + "use strict"; + + // BROWSER SNIFFING + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + + var gecko = /gecko\/\d/i.test(navigator.userAgent); + var ie_upto10 = /MSIE \d/.test(navigator.userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent); + var ie = ie_upto10 || ie_11up; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); + var webkit = /WebKit\//.test(navigator.userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); + var chrome = /Chrome\//.test(navigator.userAgent); + var presto = /Opera\//.test(navigator.userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent); + var phantom = /PhantomJS/.test(navigator.userAgent); + + var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent); + var mac = ios || /Mac/.test(navigator.platform); + var windows = /win/i.test(navigator.platform); + + var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) presto_version = Number(presto_version[1]); + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + // EDITOR CONSTRUCTOR + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + setGuttersForLineNumbers(options); + + var doc = options.value; + if (typeof doc == "string") doc = new Doc(doc, options.mode); + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input); + display.wrapper.CodeMirror = this; + updateGutters(this); + themeChanged(this); + if (options.lineWrapping) + this.display.wrapper.className += " CodeMirror-wrap"; + if (options.autofocus && !mobile) display.input.focus(); + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + var cm = this; + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20); + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || cm.hasFocus()) + setTimeout(bind(onFocus, this), 20); + else + onBlur(this); + + for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) + optionHandlers[opt](this, options[opt], Init); + maybeUpdateLineNumberWidth(this); + if (options.finishInit) options.finishInit(this); + for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + display.lineDiv.style.textRendering = "auto"; + } + + // DISPLAY CONSTRUCTOR + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = elt("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) d.scroller.draggable = true; + + if (place) { + if (place.appendChild) place.appendChild(d.wrapper); + else place(d.wrapper); + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + input.init(d); + } + + // STATE UPDATES + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function(line) { + if (line.stateAfter) line.stateAfter = null; + if (line.styles) line.styles = null; + }); + cm.doc.frontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) regChange(cm); + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function(){updateScrollbars(cm);}, 100); + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function(line) { + if (lineIsHidden(cm.doc, line)) return 0; + + var widgetsHeight = 0; + if (line.widgets) for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) widgetsHeight += line.widgets[i].height; + } + + if (wrapping) + return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th; + else + return widgetsHeight + th; + }; + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function(line) { + var estHeight = est(line); + if (estHeight != line.height) updateLineHeight(line, estHeight); + }); + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + function guttersChanged(cm) { + updateGutters(cm); + regChange(cm); + setTimeout(function(){alignHorizontally(cm);}, 20); + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function updateGutters(cm) { + var gutters = cm.display.gutters, specs = cm.options.gutters; + removeChildren(gutters); + for (var i = 0; i < specs.length; ++i) { + var gutterClass = specs[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + cm.display.lineGutter = gElt; + gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = i ? "" : "none"; + updateGutterSpace(cm); + } + + function updateGutterSpace(cm) { + var width = cm.display.gutters.offsetWidth; + cm.display.sizer.style.marginLeft = width + "px"; + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) return 0; + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found = merged.find(0, true); + len -= cur.text.length - found.from.ch; + cur = found.to.line; + len += cur.text.length - found.to.ch; + } + return len; + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function(line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // Make sure the gutters options contains the element + // "CodeMirror-linenumbers" when the lineNumbers option is true. + function setGuttersForLineNumbers(options) { + var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + if (found == -1 && options.lineNumbers) { + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + } else if (found > -1 && !options.lineNumbers) { + options.gutters = options.gutters.slice(0); + options.gutters.splice(found, 1); + } + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + }; + } + + function NativeScrollbars(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + place(vert); place(horiz); + + on(vert, "scroll", function() { + if (vert.clientHeight) scroll(vert.scrollTop, "vertical"); + }); + on(horiz, "scroll", function() { + if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal"); + }); + + this.checkedOverlay = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; + } + + NativeScrollbars.prototype = copyObj({ + update: function(measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedOverlay && measure.clientHeight > 0) { + if (sWidth == 0) this.overlayHack(); + this.checkedOverlay = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}; + }, + setScrollLeft: function(pos) { + if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos; + }, + setScrollTop: function(pos) { + if (this.vert.scrollTop != pos) this.vert.scrollTop = pos; + }, + overlayHack: function() { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.minHeight = this.vert.style.minWidth = w; + var self = this; + var barMouseDown = function(e) { + if (e_target(e) != self.vert && e_target(e) != self.horiz) + operation(self.cm, onMouseDown)(e); + }; + on(this.vert, "mousedown", barMouseDown); + on(this.horiz, "mousedown", barMouseDown); + }, + clear: function() { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + } + }, NativeScrollbars.prototype); + + function NullScrollbars() {} + + NullScrollbars.prototype = copyObj({ + update: function() { return {bottom: 0, right: 0}; }, + setScrollLeft: function() {}, + setScrollTop: function() {}, + clear: function() {} + }, NullScrollbars.prototype); + + CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); + } + + cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function() { + if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0); + }); + node.setAttribute("cm-not-content", "true"); + }, function(pos, axis) { + if (axis == "horizontal") setScrollLeft(cm, pos); + else setScrollTop(cm, pos); + }, cm); + if (cm.display.scrollbars.addClass) + addClass(cm.display.wrapper, cm.display.scrollbars.addClass); + } + + function updateScrollbars(cm, measure) { + if (!measure) measure = measureForScrollbars(cm); + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + updateHeightsInViewport(cm); + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else d.scrollbarFiller.style.display = ""; + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else d.gutterFiller.style.display = ""; + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)}; + } + + // LINE NUMBERS + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return; + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) if (!view[i].hidden) { + if (cm.options.fixedGutter && view[i].gutter) + view[i].gutter.style.left = left; + var align = view[i].alignable; + if (align) for (var j = 0; j < align.length; j++) + align[j].style.left = left; + } + if (cm.options.fixedGutter) + display.gutters.style.left = (comp + gutterW) + "px"; + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) return false; + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm); + return true; + } + return false; + } + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)); + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; + } + + // DISPLAY DRAWING + + function DisplayUpdate(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + } + + DisplayUpdate.prototype.signal = function(emitter, type) { + if (hasHandler(emitter, type)) + this.events.push(arguments); + }; + DisplayUpdate.prototype.finish = function() { + for (var i = 0; i < this.events.length; i++) + signal.apply(null, this.events[i]); + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false; + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + return false; + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom); + if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo); + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + return false; + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var focused = activeElt(); + if (toUpdate > 4) display.lineDiv.style.display = "none"; + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) display.lineDiv.style.display = ""; + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + if (focused && activeElt() != focused && focused.offsetHeight) focused.focus(); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true; + } + + function postUpdateDisplay(cm, update) { + var force = update.force, viewport = update.viewport; + for (var first = true;; first = false) { + if (first && cm.options.lineWrapping && update.oldDisplayWidth != displayWidth(cm)) { + force = true; + } else { + force = false; + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + break; + } + if (!updateDisplayIfNeeded(cm, update)) break; + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + setDocumentHeight(cm, barMeasure); + updateScrollbars(cm, barMeasure); + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + setDocumentHeight(cm, barMeasure); + updateScrollbars(cm, barMeasure); + update.finish(); + } + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + var total = measure.docHeight + cm.display.barHeight; + cm.display.heightForcer.style.top = total + "px"; + cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px"; + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], height; + if (cur.hidden) continue; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + } + var diff = cur.line.height - height; + if (height < 2) height = textHeight(display); + if (diff > .001 || diff < -.001) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) for (var j = 0; j < cur.rest.length; j++) + updateWidgetHeight(cur.rest[j]); + } + } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) for (var i = 0; i < line.widgets.length; ++i) + line.widgets[i].height = line.widgets[i].node.offsetHeight; + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; + width[cm.options.gutters[i]] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth}; + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + node.style.display = "none"; + else + node.parentNode.removeChild(node); + return next; + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) { + } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) cur = rm(cur); + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false; + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) cur = rm(cur); + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") updateLineText(cm, lineView); + else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims); + else if (type == "class") updateLineClasses(lineView); + else if (type == "widget") updateLineWidgets(cm, lineView, dims); + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + lineView.text.parentNode.replaceChild(lineView.node, lineView.text); + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) lineView.node.style.zIndex = 2; + } + return lineView.node; + } + + function updateLineBackground(lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) cls += " CodeMirror-linebackground"; + if (lineView.background) { + if (cls) lineView.background.className = cls; + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built; + } + return buildLineContent(cm, lineView); + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) lineView.node = built.pre; + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(lineView) { + updateLineBackground(lineView); + if (lineView.line.wrapClass) + ensureLineWrapped(lineView).className = lineView.line.wrapClass; + else if (lineView.node != lineView.text) + lineView.node.className = ""; + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + + "px; width: " + dims.gutterTotalWidth + "px"); + cm.display.input.setUneditable(gutterWrap); + wrap.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + gutterWrap.className += " " + lineView.line.gutterClass; + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " + + cm.display.lineNumInnerWidth + "px")); + if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) + gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); + } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) lineView.alignable = null; + for (var node = lineView.node.firstChild, next; node; node = next) { + var next = node.nextSibling; + if (node.className == "CodeMirror-linewidget") + lineView.node.removeChild(node); + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) lineView.bgClass = built.bgClass; + if (built.textClass) lineView.textClass = built.textClass; + + updateLineClasses(lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node; + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) + insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) return; + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true"); + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + wrap.insertBefore(node, lineView.gutter || lineView.text); + else + wrap.appendChild(node); + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; + } + } + + // POSITION OBJECT + + // A Pos instance represents a position within the text. + var Pos = CodeMirror.Pos = function(line, ch) { + if (!(this instanceof Pos)) return new Pos(line, ch); + this.line = line; this.ch = ch; + }; + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; }; + + function copyPos(x) {return Pos(x.line, x.ch);} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b; } + + // INPUT HANDLING + + function ensureFocus(cm) { + if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } + } + + function isReadOnly(cm) { + return cm.options.readOnly || cm.doc.cantEdit; + } + + // This will be set to an array of strings when copying, so that, + // when pasting, we know what kind of selections the copied text + // was made out of. + var lastCopied = null; + + function applyTextInput(cm, inserted, deleted, sel) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) sel = doc.sel; + + var textLines = splitLines(inserted), multiPaste = null; + // When pasing N lines into N selections, insert one line per selection + if (cm.state.pasteIncoming && sel.ranges.length > 1) { + if (lastCopied && lastCopied.join("\n") == inserted) + multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines); + else if (textLines.length == sel.ranges.length) + multiPaste = map(textLines, function(l) { return [l]; }); + } + + // Normal behavior is to insert the new text into every selection + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + from = Pos(from.line, from.ch - deleted); + else if (cm.state.overwrite && !cm.state.pasteIncoming) // Handle overwrite + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); + } + var updateInput = cm.curOp.updateInput; + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, + origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + // When an 'electric' character is inserted, immediately trigger a reindent + if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && + cm.options.smartIndent && range.head.ch < 100 && + (!i || sel.ranges[i - 1].head.line != range.head.line)) { + var mode = cm.getModeAt(range.head); + var end = changeEnd(changeEvent); + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indentLine(cm, end.line, "smart"); + break; + } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch))) + indentLine(cm, end.line, "smart"); + } + } + } + ensureCursorVisible(cm); + cm.curOp.updateInput = updateInput; + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = false; + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges}; + } + + function disableBrowserMagic(field) { + field.setAttribute("autocorrect", "off"); + field.setAttribute("autocapitalize", "off"); + field.setAttribute("spellcheck", "false"); + } + + // TEXTAREA INPUT STYLE + + function TextareaInput(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Tracks when input.reset has punted to just putting a short + // string into the textarea instead of the full selection. + this.inaccurateSelection = false; + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + }; + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) te.style.width = "1000px"; + else te.setAttribute("wrap", "off"); + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) te.style.border = "1px solid black"; + disableBrowserMagic(te); + return div; + } + + TextareaInput.prototype = copyObj({ + init: function(display) { + var input = this, cm = this.cm; + + // Wraps and hides input textarea + var div = this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + var te = this.textarea = div.firstChild; + display.wrapper.insertBefore(div, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) te.style.width = "0px"; + + on(te, "input", function() { + if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null; + input.poll(); + }); + + on(te, "paste", function() { + // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206 + // Add a char to the end of textarea before paste occur so that + // selection doesn't span to the end of textarea. + if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) { + var start = te.selectionStart, end = te.selectionEnd; + te.value += "$"; + // The selection end needs to be set before the start, otherwise there + // can be an intermediate non-empty selection between the two, which + // can override the middle-click paste buffer on linux and cause the + // wrong thing to get pasted. + te.selectionEnd = end; + te.selectionStart = start; + cm.state.fakedLastChar = true; + } + cm.state.pasteIncoming = true; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (cm.somethingSelected()) { + lastCopied = cm.getSelections(); + if (input.inaccurateSelection) { + input.prevInput = ""; + input.inaccurateSelection = false; + te.value = lastCopied.join("\n"); + selectInput(te); + } + } else { + var ranges = copyableRanges(cm); + lastCopied = ranges.text; + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") cm.state.cutIncoming = true; + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function(e) { + if (eventInWidget(display, e)) return; + cm.state.pasteIncoming = true; + input.focus(); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function(e) { + if (!eventInWidget(display, e)) e_preventDefault(e); + }); + }, + + prepareSelection: function() { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result; + }, + + showSelection: function(drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }, + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + reset: function(typing) { + if (this.contextMenuPending) return; + var minimal, selected, cm = this.cm, doc = cm.doc; + if (cm.somethingSelected()) { + this.prevInput = ""; + var range = doc.sel.primary(); + minimal = hasCopyEvent && + (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000); + var content = minimal ? "-" : selected || cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) selectInput(this.textarea); + if (ie && ie_version >= 9) this.hasSelection = content; + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) this.hasSelection = null; + } + this.inaccurateSelection = minimal; + }, + + getField: function() { return this.textarea; }, + + supportsTouch: function() { return false; }, + + focus: function() { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }, + + blur: function() { this.textarea.blur(); }, + + resetPosition: function() { + this.wrapper.style.top = this.wrapper.style.left = 0; + }, + + receivedFocus: function() { this.slowPoll(); }, + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + slowPoll: function() { + var input = this; + if (input.pollingFast) return; + input.polling.set(this.cm.options.pollInterval, function() { + input.poll(); + if (input.cm.state.focused) input.slowPoll(); + }); + }, + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + fastPoll: function() { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }, + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + poll: function() { + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (!cm.state.focused || (hasSelection(input) && !prevInput) || + isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq) + return false; + // See paste handler for more on the fakedLastChar kludge + if (cm.state.pasteIncoming && cm.state.fakedLastChar) { + input.value = input.value.substring(0, input.value.length - 1); + cm.state.fakedLastChar = false; + } + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) return false; + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false; + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + if (text.charCodeAt(0) == 0x200b) { + if (!prevInput) prevInput = "\u200b"; + } else if (prevInput == "\u200b") { + text = text.slice(1); + prevInput = ""; + } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; + + var self = this; + runInOp(cm, function() { + applyTextInput(cm, text.slice(same), prevInput.length - same); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = ""; + else self.prevInput = text; + }); + return true; + }, + + ensurePolled: function() { + if (this.pollingFast && this.poll()) this.pollingFast = false; + }, + + onKeyPress: function() { + if (ie && ie_version >= 9) this.hasSelection = null; + this.fastPoll(); + }, + + onContextMenu: function(e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) return; // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); + + var oldCSS = te.style.cssText; + input.wrapper.style.position = "absolute"; + te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " + + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + + "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) window.scrollTo(null, oldScrollY); + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) te.value = input.prevInput = " "; + input.contextMenuPending = true; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = te.value = "\u200b" + (selected ? te.value : ""); + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + input.contextMenuPending = false; + input.wrapper.style.position = "relative"; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) prepareSelectAllHack(); + var i = 0, poll = function() { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && input.prevInput == "\u200b") + operation(cm, commands.selectAll)(cm); + else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500); + else display.input.reset(); + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) prepareSelectAllHack(); + if (captureRightClick) { + e_stop(e); + var mouseup = function() { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }, + + setUneditable: nothing, + + needsContentAttribute: false + }, TextareaInput.prototype); + + // CONTENTEDITABLE INPUT STYLE + + function ContentEditableInput(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.gracePeriod = false; + } + + ContentEditableInput.prototype = copyObj({ + init: function(display) { + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = "true"; + disableBrowserMagic(div); + + on(div, "paste", function(e) { + var pasted = e.clipboardData && e.clipboardData.getData("text/plain"); + if (pasted) { + e.preventDefault(); + cm.replaceSelection(pasted, null, "paste"); + } + }); + + on(div, "compositionstart", function(e) { + var data = e.data; + input.composing = {sel: cm.doc.sel, data: data, startData: data}; + if (!data) return; + var prim = cm.doc.sel.primary(); + var line = cm.getLine(prim.head.line); + var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)); + if (found > -1 && found <= prim.head.ch) + input.composing.sel = simpleSelection(Pos(prim.head.line, found), + Pos(prim.head.line, found + data.length)); + }); + on(div, "compositionupdate", function(e) { + input.composing.data = e.data; + }); + on(div, "compositionend", function(e) { + var ours = input.composing; + if (!ours) return; + if (e.data != ours.startData && !/\u200b/.test(e.data)) + ours.data = e.data; + // Need a small delay to prevent other code (input event, + // selection polling) from doing damage when fired right after + // compositionend. + setTimeout(function() { + if (!ours.handled) + input.applyComposition(ours); + if (input.composing == ours) + input.composing = null; + }, 50); + }); + + on(div, "touchstart", function() { + input.forceCompositionEnd(); + }); + + on(div, "input", function() { + if (input.composing) return; + if (!input.pollContent()) + runInOp(input.cm, function() {regChange(cm);}); + }); + + function onCopyCut(e) { + if (cm.somethingSelected()) { + lastCopied = cm.getSelections(); + if (e.type == "cut") cm.replaceSelection("", null, "cut"); + } else { + var ranges = copyableRanges(cm); + lastCopied = ranges.text; + if (e.type == "cut") { + cm.operation(function() { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + // iOS exposes the clipboard API, but seems to discard content inserted into it + if (e.clipboardData && !ios) { + e.preventDefault(); + e.clipboardData.clearData(); + e.clipboardData.setData("text/plain", lastCopied.join("\n")); + } else { + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function() { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + }, 50); + } + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }, + + prepareSelection: function() { + var result = prepareSelection(this.cm, false); + result.focus = this.cm.state.focused; + return result; + }, + + showSelection: function(info) { + if (!info || !this.cm.display.view.length) return; + if (info.focus) this.showPrimarySelection(); + this.showMultipleSelections(info); + }, + + showPrimarySelection: function() { + var sel = window.getSelection(), prim = this.cm.doc.sel.primary(); + var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && + cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) + return; + + var start = posToDOM(this.cm, prim.from()); + var end = posToDOM(this.cm, prim.to()); + if (!start && !end) return; + + var view = this.cm.display.view; + var old = sel.rangeCount && sel.getRangeAt(0); + if (!start) { + start = {node: view[0].measure.map[2], offset: 0}; + } else if (!end) { // FIXME dangerously hacky + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + try { var rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + sel.removeAllRanges(); + sel.addRange(rng); + if (old && sel.anchorNode == null) sel.addRange(old); + else if (gecko) this.startGracePeriod(); + } + this.rememberSelection(); + }, + + startGracePeriod: function() { + var input = this; + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function() { + input.gracePeriod = false; + if (input.selectionChanged()) + input.cm.operation(function() { input.cm.curOp.selectionChanged = true; }); + }, 20); + }, + + showMultipleSelections: function(info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }, + + rememberSelection: function() { + var sel = window.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }, + + selectionInEditor: function() { + var sel = window.getSelection(); + if (!sel.rangeCount) return false; + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node); + }, + + focus: function() { + if (this.cm.options.readOnly != "nocursor") this.div.focus(); + }, + blur: function() { this.div.blur(); }, + getField: function() { return this.div; }, + + supportsTouch: function() { return true; }, + + receivedFocus: function() { + var input = this; + if (this.selectionInEditor()) + this.pollSelection(); + else + runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }); + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }, + + selectionChanged: function() { + var sel = window.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset; + }, + + pollSelection: function() { + if (!this.composing && !this.gracePeriod && this.selectionChanged()) { + var sel = window.getSelection(), cm = this.cm; + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) runInOp(cm, function() { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) cm.curOp.selectionChanged = true; + }); + } + }, + + pollContent: function() { + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false; + + var fromIndex; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + var fromLine = lineNo(display.view[0].line); + var fromNode = display.view[0].node; + } else { + var fromLine = lineNo(display.view[fromIndex].line); + var fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + if (toIndex == display.view.length - 1) { + var toLine = display.viewTo - 1; + var toNode = display.view[toIndex].node; + } else { + var toLine = lineNo(display.view[toIndex + 1].line) - 1; + var toNode = display.view[toIndex + 1].node.previousSibling; + } + + var newText = splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else break; + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + ++cutFront; + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + ++cutEnd; + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd); + newText[0] = newText[0].slice(cutFront); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true; + } + }, + + ensurePolled: function() { + this.forceCompositionEnd(); + }, + reset: function() { + this.forceCompositionEnd(); + }, + forceCompositionEnd: function() { + if (!this.composing || this.composing.handled) return; + this.applyComposition(this.composing); + this.composing.handled = true; + this.div.blur(); + this.div.focus(); + }, + applyComposition: function(composing) { + if (composing.data && composing.data != composing.startData) + operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel); + }, + + setUneditable: function(node) { + node.setAttribute("contenteditable", "false"); + }, + + onKeyPress: function(e) { + e.preventDefault(); + operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); + }, + + onContextMenu: nothing, + resetPosition: nothing, + + needsContentAttribute: true + }, ContentEditableInput.prototype); + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) return null; + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, "left"); + result.offset = result.collapse == "right" ? result.end : result.start; + return result; + } + + function badPos(pos, bad) { if (bad) pos.bad = true; return pos; } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true); + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) return null; + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break; + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + return locateNodeInLineView(lineView, node, offset); + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true); + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad); + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) offset = textNode.nodeValue.length; + } + while (topNode.parentNode != wrapper) topNode = topNode.parentNode; + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)]; + return Pos(line, ch); + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) return badPos(found, bad); + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + return badPos(Pos(found.line, found.ch - dist), bad); + else + dist += after.textContent.length; + } + for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + return badPos(Pos(found.line, found.ch + dist), bad); + else + dist += after.textContent.length; + } + } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false; + function recognizeMarker(id) { return function(marker) { return marker.id == id; }; } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText != null) { + if (cmText == "") cmText = node.textContent.replace(/\u200b/g, ""); + text += cmText; + return; + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find())) + text += getBetween(cm.doc, range.from, range.to).join("\n"); + return; + } + if (node.getAttribute("contenteditable") == "false") return; + for (var i = 0; i < node.childNodes.length; i++) + walk(node.childNodes[i]); + if (/^(pre|div|p)$/i.test(node.nodeName)) + closing = true; + } else if (node.nodeType == 3) { + var val = node.nodeValue; + if (!val) return; + if (closing) { + text += "\n"; + closing = false; + } + text += val; + } + } + for (;;) { + walk(from); + if (from == to) break; + from = from.nextSibling; + } + return text; + } + + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // SELECTION / CURSOR + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + function Selection(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + } + + Selection.prototype = { + primary: function() { return this.ranges[this.primIndex]; }, + equals: function(other) { + if (other == this) return true; + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false; + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false; + } + return true; + }, + deepCopy: function() { + for (var out = [], i = 0; i < this.ranges.length; i++) + out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); + return new Selection(out, this.primIndex); + }, + somethingSelected: function() { + for (var i = 0; i < this.ranges.length; i++) + if (!this.ranges[i].empty()) return true; + return false; + }, + contains: function(pos, end) { + if (!end) end = pos; + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + return i; + } + return -1; + } + }; + + function Range(anchor, head) { + this.anchor = anchor; this.head = head; + } + + Range.prototype = { + from: function() { return minPos(this.anchor, this.head); }, + to: function() { return maxPos(this.anchor, this.head); }, + empty: function() { + return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch; + } + }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(ranges, primIndex) { + var prim = ranges[primIndex]; + ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + if (cmp(prev.to(), cur.from()) >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) --primIndex; + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex); + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0); + } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));} + function clipPos(doc, pos) { + if (pos.line < doc.first) return Pos(doc.first, 0); + var last = doc.first + doc.size - 1; + if (pos.line > last) return Pos(last, getLine(doc, last).text.length); + return clipToLen(pos, getLine(doc, pos.line).text.length); + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) return Pos(pos.line, linelen); + else if (ch < 0) return Pos(pos.line, 0); + else return pos; + } + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} + function clipPosArray(doc, array) { + for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]); + return out; + } + + // SELECTION UPDATES + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(doc, range, head, other) { + if (doc.cm && doc.cm.display.shift || doc.extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head); + } else { + return new Range(other || head, head); + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options) { + setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + for (var out = [], i = 0; i < doc.sel.ranges.length; i++) + out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null); + var newSel = normalizeSelection(out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); + } + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); + if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1); + else return sel; + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + sel = filterSelectionChange(doc, sel); + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm) + ensureCursorVisible(doc.cm); + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) return; + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) out = sel.ranges.slice(0, i); + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(out, sel.primIndex) : sel; + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, bias, mayClear) { + var flipped = false, curPos = pos; + var dir = bias || 1; + doc.cantEdit = false; + search: for (;;) { + var line = getLine(doc, curPos.line); + if (line.markedSpans) { + for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) && + (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) break; + else {--i; continue;} + } + } + if (!m.atomic) continue; + var newPos = m.find(dir < 0 ? -1 : 1); + if (cmp(newPos, curPos) == 0) { + newPos.ch += dir; + if (newPos.ch < 0) { + if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1)); + else newPos = null; + } else if (newPos.ch > line.text.length) { + if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0); + else newPos = null; + } + if (!newPos) { + if (flipped) { + // Driven in a corner -- no valid cursor position found at all + // -- try again *with* clearing, if we didn't already + if (!mayClear) return skipAtomic(doc, pos, bias, true); + // Otherwise, turn off editing until further notice, and return the start of the doc + doc.cantEdit = true; + return Pos(doc.first, 0); + } + flipped = true; newPos = pos; dir = -dir; + } + } + curPos = newPos; + continue search; + } + } + } + return curPos; + } + } + + // SELECTION DRAWING + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (primary === false && i == doc.sel.primIndex) continue; + var range = doc.sel.ranges[i]; + var collapsed = range.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + drawSelectionCursor(cm, range, curFragment); + if (!collapsed) + drawSelectionRange(cm, range, selFragment); + } + return result; + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, range, output) { + var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + + function add(left, top, width, bottom) { + if (top < 0) top = 0; + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + + "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) + + "px; height: " + (bottom - top) + "px")); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias); + } + + iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { + var leftPos = coords(from, "left"), rightPos, left, right; + if (from == to) { + rightPos = leftPos; + left = right = leftPos.left; + } else { + rightPos = coords(to - 1, "right"); + if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } + left = leftPos.left; + right = rightPos.right; + } + if (fromArg == null && from == 0) left = leftSide; + if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part + add(left, leftPos.top, null, leftPos.bottom); + left = leftSide; + if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); + } + if (toArg == null && to == lineLen) right = rightSide; + if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) + start = leftPos; + if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) + end = rightPos; + if (left < leftSide + 1) left = leftSide; + add(left, rightPos.top, right - left, rightPos.bottom); + }); + return {start: start, end: end}; + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + add(leftSide, leftEnd.bottom, null, rightStart.top); + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) return; + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + display.blinker = setInterval(function() { + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); + else if (cm.options.cursorBlinkRate < 0) + display.cursorDiv.style.visibility = "hidden"; + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) + cm.state.highlight.set(time, bind(highlightWorker, cm)); + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.frontier < doc.first) doc.frontier = doc.first; + if (doc.frontier >= cm.display.viewTo) return; + var end = +new Date + cm.options.workTime; + var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); + var changedLines = []; + + doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) { + if (doc.frontier >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var highlighted = highlightLine(cm, line, state, true); + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) line.styleClasses = newCls; + else if (oldCls) line.styleClasses = null; + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]; + if (ischange) changedLines.push(doc.frontier); + line.stateAfter = copyState(doc.mode, state); + } else { + processLine(cm, line.text, state); + line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; + } + ++doc.frontier; + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true; + } + }); + if (changedLines.length) runInOp(cm, function() { + for (var i = 0; i < changedLines.length; i++) + regLineChange(cm, changedLines[i], "text"); + }); + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) return doc.first; + var line = getLine(doc, search - 1); + if (line.stateAfter && (!precise || search <= doc.frontier)) return search; + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline; + } + + function getStateBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) return true; + var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter; + if (!state) state = startState(doc.mode); + else state = copyState(doc.mode, state); + doc.iter(pos, n, function(line) { + processLine(cm, line.text, state); + var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo; + line.stateAfter = save ? copyState(doc.mode, state) : null; + ++pos; + }); + if (precise) doc.frontier = pos; + return state; + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop;} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;} + function paddingH(display) { + if (display.cachedPaddingH) return display.cachedPaddingH; + var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data; + return data; + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth; + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight; + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + heights.push((cur.bottom + next.top) / 2 - rect.top); + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + return {map: lineView.measure.map, cache: lineView.measure.cache}; + for (var i = 0; i < lineView.rest.length; i++) + if (lineView.rest[i] == line) + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]}; + for (var i = 0; i < lineView.rest.length; i++) + if (lineNo(lineView.rest[i]) > lineN) + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true}; + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view; + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias); + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + return cm.display.view[findViewIndex(cm, lineN)]; + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + return ext; + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) + view = null; + else if (view && view.changes) + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + if (!view) + view = updateExternalMeasurement(cm, line); + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + }; + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) ch = -1; + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + prepared.rect = prepared.view.text.getBoundingClientRect(); + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) prepared.cache[key] = found; + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom}; + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + var mStart = map[i], mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) collapse = "right"; + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + collapse = bias; + if (bias == "left" && start == 0) + while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } + if (bias == "right" && start == mEnd - mStart) + while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } + break; + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}; + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start; + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end; + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { + rect = node.parentNode.getBoundingClientRect(); + } else if (ie && cm.options.lineWrapping) { + var rects = range(node, start, end).getClientRects(); + if (rects.length) + rect = rects[bias == "right" ? rects.length - 1 : 0]; + else + rect = nullRect; + } else { + rect = range(node, start, end).getBoundingClientRect() || nullRect; + } + if (rect.left || rect.right || start == 0) break; + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) collapse = bias = "right"; + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + rect = rects[bias == "right" ? rects.length - 1 : 0]; + else + rect = node.getBoundingClientRect(); + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; + else + rect = nullRect; + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + for (var i = 0; i < heights.length - 1; i++) + if (mid < heights[i]) break; + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) result.bogus = true; + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result; + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + return rect; + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY}; + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) + lineView.measure.caches[i] = {}; + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + clearLineMeasurementCacheFor(cm.display.view[i]); + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) cm.display.maxLineChanged = true; + cm.display.lineNumChars = null; + } + + function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } + function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"/null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context) { + if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { + var size = widgetHeight(lineObj.widgets[i]); + rect.top += size; rect.bottom += size; + } + if (context == "line") return rect; + if (!context) context = "local"; + var yOff = heightAtLine(lineObj); + if (context == "local") yOff += paddingTop(cm.display); + else yOff -= cm.display.viewOffset; + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect; + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"/null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") return coords; + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) lineObj = getLine(cm.doc, pos.line); + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context); + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj); + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) m.left = m.right; else m.right = m.left; + return intoCoordSystem(cm, lineObj, m, context); + } + function getBidi(ch, partPos) { + var part = order[partPos], right = part.level % 2; + if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { + part = order[--partPos]; + ch = bidiRight(part) - (part.level % 2 ? 0 : 1); + right = true; + } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { + part = order[++partPos]; + ch = bidiLeft(part) - part.level % 2; + right = false; + } + if (right && ch == part.to && ch > part.from) return get(ch - 1); + return get(ch, right); + } + var order = getOrder(lineObj), ch = pos.ch; + if (!order) return get(ch); + var partPos = getBidiPartAt(order, ch); + var val = getBidi(ch, partPos); + if (bidiOther != null) val.other = getBidi(ch, bidiOther); + return val; + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0, pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch; + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height}; + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, outside, xRel) { + var pos = Pos(line, ch); + pos.xRel = xRel; + if (outside) pos.outside = true; + return pos; + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) return PosWithInfo(doc.first, 0, true, -1); + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); + if (x < 0) x = 0; + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var merged = collapsedSpanAtEnd(lineObj); + var mergedPos = merged && merged.find(0, true); + if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) + lineN = lineNo(lineObj = mergedPos.to.line); + else + return found; + } + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + var innerOff = y - heightAtLine(lineObj); + var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth; + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + + function getX(ch) { + var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure); + wrongLine = true; + if (innerOff > sp.bottom) return sp.left - adjust; + else if (innerOff < sp.top) return sp.left + adjust; + else wrongLine = false; + return sp.left; + } + + var bidi = getOrder(lineObj), dist = lineObj.text.length; + var from = lineLeft(lineObj), to = lineRight(lineObj); + var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; + + if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); + // Do a binary search between these bounds. + for (;;) { + if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { + var ch = x < fromX || x - fromX <= toX - x ? from : to; + var xDiff = x - (ch == from ? fromX : toX); + while (isExtendingChar(lineObj.text.charAt(ch))) ++ch; + var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside, + xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0); + return pos; + } + var step = Math.ceil(dist / 2), middle = from + step; + if (bidi) { + middle = from; + for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); + } + var middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;} + else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;} + } + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) return display.cachedTextHeight; + if (measureText == null) { + measureText = elt("pre"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) display.cachedTextHeight = height; + removeChildren(display.measure); + return height || 1; + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) return display.cachedCharWidth; + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) display.cachedCharWidth = width; + return width || 10; + } + + // OPERATIONS + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var operationGroup = null; + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: null, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + id: ++nextOpId // Unique ID + }; + if (operationGroup) { + operationGroup.ops.push(cm.curOp); + } else { + cm.curOp.ownsGroup = operationGroup = { + ops: [cm.curOp], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + callbacks[i](); + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm); + } + } while (i < callbacks.length); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp, group = op.ownsGroup; + if (!group) return; + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + for (var i = 0; i < group.ops.length; i++) + group.ops[i].cm.curOp = null; + endOperations(group); + } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_R1(ops[i]); + for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W1(ops[i]); + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_R2(ops[i]); + for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W2(ops[i]); + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_finish(ops[i]); + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) findMaxLine(cm); + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) updateHeightsInViewport(cm); + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + op.preparedSelection = display.input.prepareSelection(); + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); + cm.display.maxLineChanged = false; + } + + if (op.preparedSelection) + cm.display.input.showSelection(op.preparedSelection); + if (op.updatedDisplay) + setDocumentHeight(cm, op.barMeasure); + if (op.updatedDisplay || op.startHeight != cm.doc.height) + updateScrollbars(cm, op.barMeasure); + + if (op.selectionChanged) restartBlink(cm); + + if (cm.state.focused && op.updateInput) + cm.display.input.reset(op.typing); + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) postUpdateDisplay(cm, op.update); + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + display.wheelStartX = display.wheelStartY = null; + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) { + doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); + display.scrollbars.setScrollTop(doc.scrollTop); + display.scroller.scrollTop = doc.scrollTop; + } + if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) { + doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft)); + display.scrollbars.setScrollLeft(doc.scrollLeft); + display.scroller.scrollLeft = doc.scrollLeft; + alignHorizontally(cm); + } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) for (var i = 0; i < hidden.length; ++i) + if (!hidden[i].lines.length) signal(hidden[i], "hide"); + if (unhidden) for (var i = 0; i < unhidden.length; ++i) + if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); + + if (display.wrapper.offsetHeight) + doc.scrollTop = cm.display.scroller.scrollTop; + + // Fire change events, and delayed event handlers + if (op.changeObjs) + signal(cm, "changes", cm, op.changeObjs); + if (op.update) + op.update.finish(); + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) return f(); + startOperation(cm); + try { return f(); } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) return f.apply(cm, arguments); + startOperation(cm); + try { return f.apply(cm, arguments); } + finally { endOperation(cm); } + }; + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) return f.apply(this, arguments); + startOperation(this); + try { return f.apply(this, arguments); } + finally { endOperation(this); } + }; + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) return f.apply(this, arguments); + startOperation(cm); + try { return f.apply(this, arguments); } + finally { endOperation(cm); } + }; + } + + // VIEW TRACKING + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array; + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) from = cm.doc.first; + if (to == null) to = cm.doc.first + cm.doc.size; + if (!lendiff) lendiff = 0; + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + display.updateLineNumbers = from; + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + resetView(cm); + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut = viewCuttingPoint(cm, from, from, -1); + if (cut) { + display.view = display.view.slice(0, cut.index); + display.viewTo = cut.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + ext.lineN += lendiff; + else if (from < ext.lineN + ext.size) + display.externalMeasured = null; + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + display.externalMeasured = null; + + if (line < display.viewFrom || line >= display.viewTo) return; + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) return; + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) arr.push(type); + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) return null; + n -= cm.display.viewFrom; + if (n < 0) return null; + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) return i; + } + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + return {index: index, lineN: newN}; + for (var i = 0, n = cm.display.viewFrom; i < index; i++) + n += view[i].size; + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) return null; + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) return null; + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN}; + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); + else if (display.viewFrom < from) + display.view = display.view.slice(findViewIndex(cm, from)); + display.viewFrom = from; + if (display.viewTo < to) + display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); + else if (display.viewTo > to) + display.view = display.view.slice(0, findViewIndex(cm, to)); + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty; + } + return dirty; + } + + // EVENT HANDLERS + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + on(d.scroller, "dblclick", operation(cm, function(e) { + if (signalDOMEvent(cm, e)) return; + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); + else + on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); }); + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + }; + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) return false; + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1; + } + function farAway(touch, other) { + if (other.left == null) return true; + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20; + } + on(d.scroller, "touchstart", function(e) { + if (!isMouseLikeTouchEvent(e)) { + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function() { + if (d.activeTouch) d.activeTouch.moved = true; + }); + on(d.scroller, "touchend", function(e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + range = new Range(pos, pos); + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + range = cm.findWordAt(pos); + else // Triple tap + range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function() { + if (d.scroller.clientHeight) { + setScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); + on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + function drag_(e) { + if (!signalDOMEvent(cm, e)) e_stop(e); + } + if (cm.options.dragDrop) { + on(d.scroller, "dragstart", function(e){onDragStart(cm, e);}); + on(d.scroller, "dragenter", drag_); + on(d.scroller, "dragover", drag_); + on(d.scroller, "drop", operation(cm, onDrop)); + } + + var inp = d.input.getField(); + on(inp, "keyup", function(e) { onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", bind(onFocus, cm)); + on(inp, "blur", bind(onBlur, cm)); + } + + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) + return; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + // MOUSE EVENTS + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + return true; + } + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null; + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e) { return null; } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords; + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return; + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function(){display.scroller.draggable = true;}, 100); + } + return; + } + if (clickInGutter(cm, e)) return; + var start = posFromMouse(cm, e); + window.focus(); + + switch (e_button(e)) { + case 1: + if (start) + leftButtonDown(cm, e, start); + else if (e_target(e) == display.scroller) + e_preventDefault(e); + break; + case 2: + if (webkit) cm.state.lastMiddleDown = +new Date; + if (start) extendSelection(cm.doc, start); + setTimeout(function() {display.input.focus();}, 20); + e_preventDefault(e); + break; + case 3: + if (captureRightClick) onContextMenu(cm, e); + else delayBlurEvent(cm); + break; + } + } + + var lastClick, lastDoubleClick; + function leftButtonDown(cm, e, start) { + if (ie) setTimeout(bind(ensureFocus, cm), 0); + else ensureFocus(cm); + + var now = +new Date, type; + if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { + type = "triple"; + } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { + type = "double"; + lastDoubleClick = {time: now, pos: start}; + } else { + type = "single"; + lastClick = {time: now, pos: start}; + } + + var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained; + if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && + type == "single" && (contained = sel.contains(start)) > -1 && + !sel.ranges[contained].empty()) + leftButtonStartDrag(cm, e, start, modifier); + else + leftButtonSelect(cm, e, start, type, modifier); + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, e, start, modifier) { + var display = cm.display; + var dragEnd = operation(cm, function(e2) { + if (webkit) display.scroller.draggable = false; + cm.state.draggingText = false; + off(document, "mouseup", dragEnd); + off(display.scroller, "drop", dragEnd); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + if (!modifier) + extendSelection(cm.doc, start); + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if (webkit || ie && ie_version == 9) + setTimeout(function() {document.body.focus(); display.input.focus();}, 20); + else + display.input.focus(); + } + }); + // Let the drag handler handle this. + if (webkit) display.scroller.draggable = true; + cm.state.draggingText = dragEnd; + // IE's approach to draggable + if (display.scroller.dragDrop) display.scroller.dragDrop(); + on(document, "mouseup", dragEnd); + on(display.scroller, "drop", dragEnd); + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, e, start, type, addNew) { + var display = cm.display, doc = cm.doc; + e_preventDefault(e); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (addNew && !e.shiftKey) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + ourRange = ranges[ourIndex]; + else + ourRange = new Range(start, start); + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (e.altKey) { + type = "rect"; + if (!addNew) ourRange = new Range(start, start); + start = posFromMouse(cm, e, true, true); + ourIndex = -1; + } else if (type == "double") { + var word = cm.findWordAt(start); + if (cm.display.shift || doc.extend) + ourRange = extendRange(doc, ourRange, word.anchor, word.head); + else + ourRange = word; + } else if (type == "triple") { + var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))); + if (cm.display.shift || doc.extend) + ourRange = extendRange(doc, ourRange, line.anchor, line.head); + else + ourRange = line; + } else { + ourRange = extendRange(doc, ourRange, start); + } + + if (!addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) { + setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0)); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) return; + lastPos = pos; + + if (type == "rect") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); + else if (text.length > leftPos) + ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); + } + if (!ranges.length) ranges.push(new Range(start, start)); + setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var anchor = oldRange.anchor, head = pos; + if (type != "single") { + if (type == "double") + var range = cm.findWordAt(pos); + else + var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + } + var ranges = startSel.ranges.slice(0); + ranges[ourIndex] = new Range(clipPos(doc, anchor), head); + setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, type == "rect"); + if (!cur) return; + if (cmp(cur, lastPos) != 0) { + ensureFocus(cm); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150); + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) setTimeout(operation(cm, function() { + if (counter != curCount) return; + display.scroller.scrollTop += outside; + extend(e); + }), 50); + } + } + + function done(e) { + counter = Infinity; + e_preventDefault(e); + display.input.focus(); + off(document, "mousemove", move); + off(document, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function(e) { + if (!e_button(e)) done(e); + else extend(e); + }); + var up = operation(cm, done); + on(document, "mousemove", move); + on(document, "mouseup", up); + } + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent, signalfn) { + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false; + if (prevent) e_preventDefault(e); + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e); + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.options.gutters[i]; + signalfn(cm, type, cm, line, gutter, e); + return e_defaultPrevented(e); + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true, signalLater); + } + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + return; + e_preventDefault(e); + if (ie) lastDrop = +new Date; + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || isReadOnly(cm)) return; + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function(file, i) { + var reader = new FileReader; + reader.onload = operation(cm, function() { + text[i] = reader.result; + if (++read == n) { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + } + }); + reader.readAsText(file); + }; + for (var i = 0; i < n; ++i) loadFile(files[i], i); + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function() {cm.display.input.focus();}, 20); + return; + } + try { + var text = e.dataTransfer.getData("Text"); + if (text) { + if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey)) + var selected = cm.listSelections(); + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) for (var i = 0; i < selected.length; ++i) + replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag"); + cm.replaceSelection(text, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; + + e.dataTransfer.setData("Text", cm.getSelection()); + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) img.parentNode.removeChild(img); + } + } + + // SCROLL EVENTS + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function setScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) return; + cm.doc.scrollTop = val; + if (!gecko) updateDisplaySimple(cm, {top: val}); + if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (gecko) updateDisplaySimple(cm); + startWorker(cm, 100); + } + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller) { + if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return; + val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; + cm.display.scrollbars.setScrollLeft(val); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) wheelPixelsPerUnit = -.53; + else if (gecko) wheelPixelsPerUnit = 15; + else if (chrome) wheelPixelsPerUnit = -.7; + else if (safari) wheelPixelsPerUnit = -1/3; + + var wheelEventDelta = function(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail; + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail; + else if (dy == null) dy = e.wheelDelta; + return {x: dx, y: dy}; + }; + CodeMirror.wheelEventPixels = function(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta; + }; + + function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + if (!(dx && scroll.scrollWidth > scroll.clientWidth || + dy && scroll.scrollHeight > scroll.clientHeight)) return; + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer; + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy) + setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))); + setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth))); + e_preventDefault(e); + display.wheelStartX = null; // Abort measurement, if in progress + return; + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) top = Math.max(0, top + pixels - 50); + else bot = Math.min(cm.doc.height, bot + pixels + 50); + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function() { + if (display.wheelStartX == null) return; + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) return; + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // KEY EVENTS + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) return false; + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (isReadOnly(cm)) cm.state.suppressEdits = true; + if (dropShift) cm.display.shift = false; + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done; + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) return result; + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm); + } + + var stopSeq = new Delayed; + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) return "handled"; + stopSeq.set(50, function() { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); + name = seq + " " + name; + } + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + cm.state.keySeq = name; + if (result == "handled") + signalLater(cm, "keyHandled", cm, name, e); + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + if (seq && !result && /\'$/.test(name)) { + e_preventDefault(e); + return true; + } + return !!result; + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) return false; + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);}) + || dispatchKey(cm, name, e, function(b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + return doHandleBinding(cm, b); + }); + } else { + return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); }); + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, + function(b) { return doHandleBinding(cm, b, true); }); + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + ensureFocus(cm); + if (signalDOMEvent(cm, e)) return; + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false; + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + cm.replaceSelection("", null, "cut"); + } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + showCrossHair(cm); + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) this.doc.sel.shift = false; + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return; + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return; + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + if (handleCharBinding(cm, e, ch)) return; + cm.display.input.onKeyPress(e); + } + + // FOCUS/BLUR EVENTS + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function() { + if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + onBlur(cm); + } + }, 100); + } + + function onFocus(cm) { + if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false; + + if (cm.options.readOnly == "nocursor") return; + if (!cm.state.focused) { + signal(cm, "focus", cm); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm) { + if (cm.state.delayingBlurEvent) return; + + if (cm.state.focused) { + signal(cm, "blur", cm); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150); + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return; + cm.display.input.onContextMenu(e); + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) return false; + return gutterEvent(cm, e, "gutterContextMenu", false, signal); + } + + // UPDATING + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + var changeEnd = CodeMirror.changeEnd = function(change) { + if (!change.text) return change.to; + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); + }; + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) return pos; + if (cmp(pos, change.to) <= 0) return changeEnd(change); + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch; + return Pos(line, ch); + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(out, doc.sel.primIndex); + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + return Pos(nw.line, pos.ch - old.ch + nw.ch); + else + return Pos(nw.line + (pos.line - old.line), pos.ch); + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex); + } + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function() { this.canceled = true; } + }; + if (update) obj.update = function(from, to, text, origin) { + if (from) this.from = clipPos(doc, from); + if (to) this.to = clipPos(doc, to); + if (text) this.text = text; + if (origin !== undefined) this.origin = origin; + }; + signal(doc, "beforeChange", doc, obj); + if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); + + if (obj.canceled) return null; + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly); + if (doc.cm.state.suppressEdits) return; + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) return; + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return; + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function(doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + if (doc.cm && doc.cm.state.suppressEdits) return; + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + for (var i = 0; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + break; + } + if (i == source.length) return; + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return; + } + selAfter = event; + } + else break; + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + for (var i = event.changes.length - 1; i >= 0; --i) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return; + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function(doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) return; + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function(range) { + return new Range(Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch)); + }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + regLineChange(doc.cm, l, "gutter"); + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans); + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return; + } + if (change.from.line > doc.lastLine()) return; + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) selAfter = computeSelAfterChange(doc, change); + if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); + else updateDoc(doc, change, spans); + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function(line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true; + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + signalCursorActivity(cm); + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function(line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) cm.curOp.updateMaxLine = true; + } + + // Adjust frontier, schedule worker + doc.frontier = Math.min(doc.frontier, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + regChange(cm); + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + regLineChange(cm, from.line, "text"); + else + regChange(cm, from.line, to.line + 1, lendiff); + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) signalLater(cm, "change", cm, obj); + if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + if (!to) to = from; + if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } + if (typeof code == "string") code = splitLines(code); + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, coords) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) return; + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " + + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " + + coords.left + "px; width: 2px;"); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) margin = 0; + for (var limit = 0; limit < 5; limit++) { + var changed = false, coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), + Math.min(coords.top, endCoords.top) - margin, + Math.max(coords.left, endCoords.left), + Math.max(coords.bottom, endCoords.bottom) + margin); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + setScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true; + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true; + } + if (!changed) break; + } + return coords; + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, x1, y1, x2, y2) { + var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2); + if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); + if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft); + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, x1, y1, x2, y2) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (y1 < 0) y1 = 0; + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (y2 - y1 > screen) y2 = y1 + screen; + var docBottom = cm.doc.height + paddingVert(display); + var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; + if (y1 < screentop) { + result.scrollTop = atTop ? 0 : y1; + } else if (y2 > screentop + screen) { + var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); + if (newTop != screentop) result.scrollTop = newTop; + } + + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; + var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); + var tooWide = x2 - x1 > screenw; + if (tooWide) x2 = x1 + screenw; + if (x1 < 10) + result.scrollLeft = 0; + else if (x1 < screenleft) + result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)); + else if (x2 > screenw + screenleft - 3) + result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw; + return result; + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollPos(cm, left, top) { + if (left != null || top != null) resolveScrollToPos(cm); + if (left != null) + cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left; + if (top != null) + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(), from = cur, to = cur; + if (!cm.options.lineWrapping) { + from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur; + to = Pos(cur.line, cur.ch + 1); + } + cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + var sPos = calculateScrollPos(cm, Math.min(from.left, to.left), + Math.min(from.top, to.top) - range.margin, + Math.max(from.right, to.right), + Math.max(from.bottom, to.bottom) + range.margin); + cm.scrollTo(sPos.scrollLeft, sPos.scrollTop); + } + } + + // API UTILITIES + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) how = "add"; + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) how = "prev"; + else state = getStateBefore(cm, n); + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) line.stateAfter = null; + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) return; + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); + else indentation = 0; + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} + if (pos < indentation) indentString += spaceStr(indentation - pos); + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i, new Range(pos, pos)); + break; + } + } + } + line.stateAfter = null; + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); + else no = lineNo(handle); + if (no == null) return null; + if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType); + return line; + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break; + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function() { + for (var i = kill.length - 1; i >= 0; i--) + replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); + ensureCursorVisible(cm); + }); + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "char", "column" (like char, but doesn't + // cross line boundaries), "word" (across next word), or "group" (to + // the start of next group of word or non-word-non-whitespace + // chars). The visually param controls whether, in right-to-left + // text, direction 1 means to move towards the next index in the + // string, or towards the character to the right of the current + // position. The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var line = pos.line, ch = pos.ch, origDir = dir; + var lineObj = getLine(doc, line); + var possible = true; + function findNextLine() { + var l = line + dir; + if (l < doc.first || l >= doc.first + doc.size) return (possible = false); + line = l; + return lineObj = getLine(doc, l); + } + function moveOnce(boundToLine) { + var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); + if (next == null) { + if (!boundToLine && findNextLine()) { + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); + else ch = dir < 0 ? lineObj.text.length : 0; + } else return (possible = false); + } else ch = next; + return true; + } + + if (unit == "char") moveOnce(); + else if (unit == "column") moveOnce(true); + else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) break; + var cur = lineObj.text.charAt(ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) type = "s"; + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce();} + break; + } + + if (type) sawType = type; + if (dir > 0 && !moveOnce(!first)) break; + } + } + var result = skipAtomic(doc, Pos(line, ch), origDir, true); + if (!possible) result.hitSide = true; + return result; + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display)); + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + for (;;) { + var target = coordsChar(cm, x, y); + if (!target.outside) break; + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; } + y += dir * 5; + } + return target; + } + + // EDITOR METHODS + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") return; + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + operation(this, optionHandlers[option])(this, value, old); + }, + + getOption: function(option) {return this.options[option];}, + getDoc: function() {return this.doc;}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true; + } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) throw new Error("Overlays may not be stateful."); + this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque}); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return; + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; + else dir = dir ? "add" : "subtract"; + } + if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + indentLine(this, j, how); + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) ensureCursorVisible(this); + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise); + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true); + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) type = styles[2]; + else for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; + else if (styles[mid * 2 + 1] < ch) before = mid + 1; + else { type = styles[mid * 2 + 2]; break; } + } + var cut = type ? type.indexOf("cm-overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1); + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) return mode; + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0]; + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) return found; + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) found.push(help[mode[type]]); + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) found.push(val); + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i = 0; i < help._global.length; i++) { + var cur = help._global[i]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + found.push(cur.val); + } + return found; + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getStateBefore(this, line + 1, precise); + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) pos = range.head; + else if (typeof start == "object") pos = clipPos(this.doc, start); + else pos = start ? range.from() : range.to(); + return cursorCoords(this, pos, mode || "page"); + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page"); + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top); + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset); + }, + heightAtLine: function(line, mode) { + var end = false, last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) line = this.doc.first; + else if (line > last) { line = last; end = true; } + var lineObj = getLine(this.doc, line); + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + + (end ? this.doc.height - heightAtLine(lineObj) : 0); + }, + + defaultTextHeight: function() { return textHeight(this.display); }, + defaultCharWidth: function() { return charWidth(this.display); }, + + setGutterMarker: methodOp(function(line, gutterID, value) { + return changeLine(this.doc, line, "gutter", function(line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) line.gutterMarkers = null; + return true; + }); + }), + + clearGutter: methodOp(function(gutterID) { + var cm = this, doc = cm.doc, i = doc.first; + doc.iter(function(line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + line.gutterMarkers[gutterID] = null; + regLineChange(cm, i, "gutter"); + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; + } + ++i; + }); + }), + + lineInfo: function(line) { + if (typeof line == "number") { + if (!isLine(this.doc, line)) return null; + var n = line; + line = getLine(this.doc, line); + if (!line) return null; + } else { + var n = lineNo(line); + if (n == null) return null; + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets}; + }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + top = pos.top - node.offsetHeight; + else if (pos.bottom + node.offsetHeight <= vspace) + top = pos.bottom; + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth; + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") left = 0; + else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2; + node.style.left = left + "px"; + } + if (scroll) + scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight); + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + return commands[cmd](this); + }, + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) break; + } + return cur; + }, + + moveH: methodOp(function(dir, unit) { + var cm = this; + cm.extendSelectionsBy(function(range) { + if (cm.display.shift || cm.doc.extend || range.empty()) + return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually); + else + return dir < 0 ? range.from() : range.to(); + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + doc.replaceSelection("", null, "+delete"); + else + deleteNearSelection(this, function(range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}; + }); + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) x = coords.left; + else coords.left = x; + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) break; + } + return cur; + }, + + moveV: methodOp(function(dir, unit) { + var cm = this, doc = this.doc, goals = []; + var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function(range) { + if (collapse) + return dir < 0 ? range.from() : range.to(); + var headPos = cursorCoords(cm, range.head, "div"); + if (range.goalColumn != null) headPos.left = range.goalColumn; + goals.push(headPos.left); + var pos = findPosV(cm, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top); + return pos; + }, sel_move); + if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) + doc.sel.ranges[i].goalColumn = goals[i]; + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function(ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} + : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + while (start > 0 && check(line.charAt(start - 1))) --start; + while (end < line.length && check(line.charAt(end))) ++end; + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)); + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) return; + if (this.state.overwrite = !this.state.overwrite) + addClass(this.display.cursorDiv, "CodeMirror-overwrite"); + else + rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt(); }, + + scrollTo: methodOp(function(x, y) { + if (x != null || y != null) resolveScrollToPos(this); + if (x != null) this.curOp.scrollLeft = x; + if (y != null) this.curOp.scrollTop = y; + }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)}; + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) margin = this.options.cursorScrollMargin; + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) range.to = range.from; + range.margin = margin || 0; + + if (range.from.line != null) { + resolveScrollToPos(this); + this.curOp.scrollToPos = range; + } else { + var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), + Math.min(range.from.top, range.to.top) - range.margin, + Math.max(range.from.right, range.to.right), + Math.max(range.from.bottom, range.to.bottom) + range.margin); + this.scrollTo(sPos.scrollLeft, sPos.scrollTop); + } + }), + + setSize: methodOp(function(width, height) { + var cm = this; + function interpret(val) { + return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; + } + if (width != null) cm.display.wrapper.style.width = interpret(width); + if (height != null) cm.display.wrapper.style.height = interpret(height); + if (cm.options.lineWrapping) clearLineMeasurementCache(this); + var lineNo = cm.display.viewFrom; + cm.doc.iter(lineNo, cm.display.viewTo, function(line) { + if (line.widgets) for (var i = 0; i < line.widgets.length; i++) + if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; } + ++lineNo; + }); + cm.curOp.forceUpdate = true; + signal(cm, "refresh", this); + }), + + operation: function(f){return runInOp(this, f);}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + estimateLineHeights(this); + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + this.scrollTo(doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old; + }), + + getInputField: function(){return this.display.input.getField();}, + getWrapperElement: function(){return this.display.wrapper;}, + getScrollerElement: function(){return this.display.scroller;}, + getGutterElement: function(){return this.display.gutters;} + }; + eventMixin(CodeMirror); + + // OPTION DEFAULTS + + // The default configuration options. + var defaults = CodeMirror.defaults = {}; + // Functions to run when options are changed. + var optionHandlers = CodeMirror.optionHandlers = {}; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) optionHandlers[name] = + notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle; + } + + // Passed to option handlers when there is no old value. + var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}}; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function(cm, val) { + cm.setValue(val); + }, true); + option("mode", null, function(cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function(cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != CodeMirror.Init) cm.refresh(); + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function() { + throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME + }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function(cm) { + themeChanged(cm); + guttersChanged(cm); + }, true); + option("keyMap", "default", function(cm, val, old) { + var next = getKeyMap(val); + var prev = old != CodeMirror.Init && getKeyMap(old); + if (prev && prev.detach) prev.detach(cm, next); + if (next.attach) next.attach(cm, prev || null); + }); + option("extraKeys", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function(cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("fixedGutter", true, function(cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true); + option("scrollbarStyle", "native", function(cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function(cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("firstLineNumber", 1, guttersChanged, true); + option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + + option("readOnly", false, function(cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + cm.display.disabled = true; + } else { + cm.display.disabled = false; + if (!val) cm.display.input.reset(); + } + }); + option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true); + option("dragDrop", true); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;}); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function(cm){cm.refresh();}, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function(cm, val) { + if (!val) cm.display.input.resetPosition(); + }); + + option("tabindex", null, function(cm, val) { + cm.display.input.getField().tabIndex = val || ""; + }); + option("autofocus", null); + + // MODE DEFINITION AND QUERYING + + // Known modes, by name and by MIME + var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name, mode) { + if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; + if (arguments.length > 2) + mode.dependencies = Array.prototype.slice.call(arguments, 2); + modes[name] = mode; + }; + + CodeMirror.defineMIME = function(mime, spec) { + mimeModes[mime] = spec; + }; + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + CodeMirror.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") found = {name: found}; + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return CodeMirror.resolveMode("application/xml"); + } + if (typeof spec == "string") return {name: spec}; + else return spec || {name: "null"}; + }; + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + CodeMirror.getMode = function(options, spec) { + var spec = CodeMirror.resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) return CodeMirror.getMode(options, "text/plain"); + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) continue; + if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop]; + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) modeObj.helperType = spec.helperType; + if (spec.modeProps) for (var prop in spec.modeProps) + modeObj[prop] = spec.modeProps[prop]; + + return modeObj; + }; + + // Minimal default mode. + CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; + }); + CodeMirror.defineMIME("text/plain", "null"); + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = CodeMirror.modeExtensions = {}; + CodeMirror.extendMode = function(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + }; + + // EXTENSIONS + + CodeMirror.defineExtension = function(name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function(name, func) { + Doc.prototype[name] = func; + }; + CodeMirror.defineOption = option; + + var initHooks = []; + CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; + + var helpers = CodeMirror.helpers = {}; + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}; + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + + // MODE STATE HANDLING + + // Utility functions for working with state. Exported because nested + // modes need to do this for their inner modes. + + var copyState = CodeMirror.copyState = function(mode, state) { + if (state === true) return state; + if (mode.copyState) return mode.copyState(state); + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) val = val.concat([]); + nstate[n] = val; + } + return nstate; + }; + + var startState = CodeMirror.startState = function(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; + }; + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + CodeMirror.innerMode = function(mode, state) { + while (mode.innerMode) { + var info = mode.innerMode(state); + if (!info || info.mode == mode) break; + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; + }; + + // STANDARD COMMANDS + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = CodeMirror.commands = { + selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);}, + singleSelection: function(cm) { + cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); + }, + killLine: function(cm) { + deleteNearSelection(cm, function(range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + return {from: range.head, to: Pos(range.head.line + 1, 0)}; + else + return {from: range.head, to: Pos(range.head.line, len)}; + } else { + return {from: range.from(), to: range.to()}; + } + }); + }, + deleteLine: function(cm) { + deleteNearSelection(cm, function(range) { + return {from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; + }); + }, + delLineLeft: function(cm) { + deleteNearSelection(cm, function(range) { + return {from: Pos(range.from().line, 0), to: range.from()}; + }); + }, + delWrappedLineLeft: function(cm) { + deleteNearSelection(cm, function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()}; + }); + }, + delWrappedLineRight: function(cm) { + deleteNearSelection(cm, function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos }; + }); + }, + undo: function(cm) {cm.undo();}, + redo: function(cm) {cm.redo();}, + undoSelection: function(cm) {cm.undoSelection();}, + redoSelection: function(cm) {cm.redoSelection();}, + goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, + goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, + goLineStart: function(cm) { + cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1}); + }, + goLineStartSmart: function(cm) { + cm.extendSelectionsBy(function(range) { + return lineStartSmart(cm, range.head); + }, {origin: "+move", bias: 1}); + }, + goLineEnd: function(cm) { + cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1}); + }, + goLineRight: function(cm) { + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + }, sel_move); + }, + goLineLeft: function(cm) { + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div"); + }, sel_move); + }, + goLineLeftSmart: function(cm) { + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head); + return pos; + }, sel_move); + }, + goLineUp: function(cm) {cm.moveV(-1, "line");}, + goLineDown: function(cm) {cm.moveV(1, "line");}, + goPageUp: function(cm) {cm.moveV(-1, "page");}, + goPageDown: function(cm) {cm.moveV(1, "page");}, + goCharLeft: function(cm) {cm.moveH(-1, "char");}, + goCharRight: function(cm) {cm.moveH(1, "char");}, + goColumnLeft: function(cm) {cm.moveH(-1, "column");}, + goColumnRight: function(cm) {cm.moveH(1, "column");}, + goWordLeft: function(cm) {cm.moveH(-1, "word");}, + goGroupRight: function(cm) {cm.moveH(1, "group");}, + goGroupLeft: function(cm) {cm.moveH(-1, "group");}, + goWordRight: function(cm) {cm.moveH(1, "word");}, + delCharBefore: function(cm) {cm.deleteH(-1, "char");}, + delCharAfter: function(cm) {cm.deleteH(1, "char");}, + delWordBefore: function(cm) {cm.deleteH(-1, "word");}, + delWordAfter: function(cm) {cm.deleteH(1, "word");}, + delGroupBefore: function(cm) {cm.deleteH(-1, "group");}, + delGroupAfter: function(cm) {cm.deleteH(1, "group");}, + indentAuto: function(cm) {cm.indentSelection("smart");}, + indentMore: function(cm) {cm.indentSelection("add");}, + indentLess: function(cm) {cm.indentSelection("subtract");}, + insertTab: function(cm) {cm.replaceSelection("\t");}, + insertSoftTab: function(cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(new Array(tabSize - col % tabSize + 1).join(" ")); + } + cm.replaceSelections(spaces); + }, + defaultTab: function(cm) { + if (cm.somethingSelected()) cm.indentSelection("add"); + else cm.execCommand("insertTab"); + }, + transposeChars: function(cm) { + runInOp(cm, function() { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) + cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose"); + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); + }, + newlineAndIndent: function(cm) { + runInOp(cm, function() { + var len = cm.listSelections().length; + for (var i = 0; i < len; i++) { + var range = cm.listSelections()[i]; + cm.replaceRange("\n", range.anchor, range.head, "+input"); + cm.indentLine(range.from().line + 1, null, true); + ensureCursorVisible(cm); + } + }); + }, + toggleOverwrite: function(cm) {cm.toggleOverwrite();} + }; + + + // STANDARD KEYMAPS + + var keyMap = CodeMirror.keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + fallthrough: "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + fallthrough: ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/), name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) cmd = true; + else if (/^a(lt)?$/i.test(mod)) alt = true; + else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true; + else if (/^s(hift)$/i.test(mod)) shift = true; + else throw new Error("Unrecognized modifier name: " + mod); + } + if (alt) name = "Alt-" + name; + if (ctrl) name = "Ctrl-" + name; + if (cmd) name = "Cmd-" + name; + if (shift) name = "Shift-" + name; + return name; + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + CodeMirror.normalizeKeyMap = function(keymap) { + var copy = {}; + for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue; + if (value == "...") { delete keymap[keyname]; continue; } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val, name; + if (i == keys.length - 1) { + name = keyname; + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) copy[name] = val; + else if (prev != val) throw new Error("Inconsistent bindings for " + name); + } + delete keymap[keyname]; + } + for (var prop in copy) keymap[prop] = copy[prop]; + return keymap; + }; + + var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) return "nothing"; + if (found === "...") return "multi"; + if (found != null && handle(found)) return "handled"; + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + return lookupKey(key, map.fallthrough, handle, context); + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) return result; + } + } + }; + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + var isModifierKey = CodeMirror.isModifierKey = function(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; + }; + + // Look up the name of a key as indicated by an event object. + var keyName = CodeMirror.keyName = function(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) return false; + var base = keyNames[event.keyCode], name = base; + if (name == null || event.altGraphKey) return false; + if (event.altKey && base != "Alt") name = "Alt-" + name; + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name; + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name; + if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name; + return name; + }; + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val; + } + + // FROMTEXTAREA + + CodeMirror.fromTextArea = function(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + options.tabindex = textarea.tabIndex; + if (!options.placeholder && textarea.placeholder) + options.placeholder = textarea.placeholder; + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form, realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function() { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function(cm) { + cm.save = save; + cm.getTextArea = function() { return textarea; }; + cm.toTextArea = function() { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (typeof textarea.form.submit == "function") + textarea.form.submit = realSubmit; + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function(node) { + textarea.parentNode.insertBefore(node, textarea.nextSibling); + }, options); + return cm; + }; + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = CodeMirror.StringStream = function(string, tabSize) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + }; + + StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == this.lineStart;}, + peek: function() {return this.string.charAt(this.pos) || undefined;}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); + }, + indentation: function() { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); + }, + match: function(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } + }; + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + var nextMarkerId = 0; + + var TextMarker = CodeMirror.TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + eventMixin(TextMarker); + + // Clear the marker. + TextMarker.prototype.clear = function() { + if (this.explicitlyCleared) return; + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) startOperation(cm); + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) signalLater(this, "clear", found.from, found.to); + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text"); + else if (cm) { + if (span.to != null) max = lineNo(line); + if (span.from != null) min = lineNo(line); + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + updateLineHeight(line, textHeight(cm.display)); + } + if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { + var visual = visualLine(this.lines[i]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } + + if (min != null && cm && this.collapsed) regChange(cm, min, max + 1); + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) reCheckSelection(cm.doc); + } + if (cm) signalLater(cm, "markerCleared", cm, this); + if (withOp) endOperation(cm); + if (this.parent) this.parent.clear(); + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function(side, lineObj) { + if (side == null && this.type == "bookmark") side = 1; + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) return from; + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) return to; + } + } + return from && {from: from, to: to}; + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function() { + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) return; + runInOp(cm, function() { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + updateLineHeight(line, line.height + dHeight); + } + }); + }; + + TextMarker.prototype.attachLine = function(line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); + } + this.lines.push(line); + }; + TextMarker.prototype.detachLine = function(line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) return markTextShared(doc, from, to, options, type); + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type); + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) copyObj(options, marker, false); + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + return marker; + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true"); + if (options.insertLeft) marker.widgetNode.insertLeft = true; + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + throw new Error("Inserting collapsed marker partially overlapping an existing one"); + sawCollapsedSpans = true; + } + + if (marker.addToHistory) + addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function(line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + updateMaxLine = true; + if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0); + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) { + if (lineIsHidden(doc, line)) updateLineHeight(line, 0); + }); + + if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); }); + + if (marker.readOnly) { + sawReadOnlySpans = true; + if (doc.history.done.length || doc.history.undone.length) + doc.clearHistory(); + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) cm.curOp.updateMaxLine = true; + if (marker.collapsed) + regChange(cm, from.line, to.line + 1); + else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) + for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text"); + if (marker.atomic) reCheckSelection(cm.doc); + signalLater(cm, "markerAdded", cm, marker); + } + return marker; + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + markers[i].parent = this; + }; + eventMixin(SharedTextMarker); + + SharedTextMarker.prototype.clear = function() { + if (this.explicitlyCleared) return; + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + this.markers[i].clear(); + signalLater(this, "clear"); + }; + SharedTextMarker.prototype.find = function(side, lineObj) { + return this.primary.find(side, lineObj); + }; + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function(doc) { + if (widget) options.widgetNode = widget.cloneNode(true); + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + if (doc.linked[i].isParent) return; + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary); + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), + function(m) { return m.parent; }); + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], linked = [marker.primary.doc];; + linkedDocs(marker.primary.doc, function(d) { linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + } + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) return span; + } + } + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + for (var r, i = 0; i < spans.length; ++i) + if (spans[i] != span) (r || (r = [])).push(spans[i]); + return r; + } + // Add a span to a line. + function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); + (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } + return nw; + } + function markedSpansAfter(old, endCh, isInsert) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); + (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } + return nw; + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) return null; + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) return null; + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) span.to = startCh; + else if (sameLine) span.to = found.to == null ? null : found.to + offset; + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i = 0; i < last.length; ++i) { + var span = last[i]; + if (span.to != null) span.to += offset; + if (span.from == null) { + var found = getMarkedSpanFor(first, span.marker); + if (!found) { + span.from = offset; + if (sameLine) (first || (first = [])).push(span); + } + } else { + span.from += offset; + if (sameLine) (first || (first = [])).push(span); + } + } + } + // Make sure we didn't create any zero-length spans + if (first) first = clearEmptySpans(first); + if (last && last != first) last = clearEmptySpans(last); + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + for (var i = 0; i < first.length; ++i) + if (first[i].to == null) + (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)); + for (var i = 0; i < gap; ++i) + newMarkers.push(gapMarkers); + newMarkers.push(last); + } + return newMarkers; + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + spans.splice(i--, 1); + } + if (!spans.length) return null; + return spans; + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) return stretched; + if (!stretched) return old; + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + if (oldCur[k].marker == span.marker) continue spans; + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old; + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function(line) { + if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + (markers || (markers = [])).push(mark); + } + }); + if (!markers) return null; + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + newParts.push({from: p.from, to: m.from}); + if (dto > 0 || !mk.inclusiveRight && !dto) + newParts.push({from: m.to, to: p.to}); + parts.splice.apply(parts, newParts); + j += newParts.length - 1; + } + } + return parts; + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + spans[i].marker.detachLine(line); + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + spans[i].marker.attachLine(line); + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) return lenDiff; + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) return -fromCmp; + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) return toCmp; + return b.id - a.id; + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) for (var sp, i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + found = sp.marker; + } + return found; + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) continue; + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; + if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) || + fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight))) + return true; + } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + line = merged.find(-1, true).line; + return line; + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line; + (lines || (lines = [])).push(line); + } + return lines; + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) return lineN; + return lineNo(vis); + } + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) return lineN; + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) return lineN; + while (merged = collapsedSpanAtEnd(line)) + line = merged.find(1, true).line; + return lineNo(line) + 1; + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) for (var sp, i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) continue; + if (sp.from == null) return true; + if (sp.marker.widgetNode) continue; + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + return true; + } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)); + } + if (span.marker.inclusiveRight && span.to == line.text.length) + return true; + for (var sp, i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) return true; + } + } + + // LINE WIDGETS + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = CodeMirror.LineWidget = function(doc, node, options) { + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) + this[opt] = options[opt]; + this.doc = doc; + this.node = node; + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + addToScrollPos(cm, null, diff); + } + + LineWidget.prototype.clear = function() { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) return; + for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); + if (!ws.length) line.widgets = null; + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) runInOp(cm, function() { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + }; + LineWidget.prototype.changed = function() { + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) return; + updateLineHeight(line, line.height + diff); + if (cm) runInOp(cm, function() { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + }); + }; + + function widgetHeight(widget) { + if (widget.height != null) return widget.height; + var cm = widget.doc.cm; + if (!cm) return 0; + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; + if (widget.noHScroll) + parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.offsetHeight; + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) cm.display.alignWidgets = true; + changeLine(doc, handle, "widget", function(line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) widgets.push(widget); + else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) addToScrollPos(cm, null, widget.height); + cm.curOp.forceUpdate = true; + } + return true; + }); + return widget; + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + eventMixin(Line); + Line.prototype.lineNo = function() { return lineNo(this); }; + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) line.stateAfter = null; + if (line.styles) line.styles = null; + if (line.order != null) line.order = null; + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) updateLineHeight(line, estHeight); + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + function extractLineClasses(type, output) { + if (type) for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) break; + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + output[prop] = lineClass[2]; + else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + output[prop] += " " + lineClass[2]; + } + return type; + } + + function callBlankLine(mode, state) { + if (mode.blankLine) return mode.blankLine(state); + if (!mode.innerMode) return; + var inner = CodeMirror.innerMode(mode, state); + if (inner.mode.blankLine) return inner.mode.blankLine(inner.state); + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode; + var style = mode.token(stream, state); + if (stream.pos > stream.start) return style; + } + throw new Error("Mode " + mode.name + " failed to advance stream."); + } + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + function getObj(copy) { + return {start: stream.start, end: stream.pos, + string: stream.current(), + type: style || null, + state: copy ? copyState(doc.mode, state) : state}; + } + + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize), tokens; + if (asArray) tokens = []; + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, state); + if (asArray) tokens.push(getObj(true)); + } + return asArray ? tokens : getObj(); + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses); + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) processLine(cm, text, state, stream.pos); + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) style = "m-" + (style ? mName + " " + style : mName); + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 50000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 characters + var pos = Math.min(stream.pos, curStart + 50000); + f(pos, curStyle); + curStart = pos; + } + } + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, state, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, state, function(end, style) { + st.push(end, style); + }, lineClasses, forceToEnd); + + // Run overlays, adjust style array. + for (var o = 0; o < cm.state.overlays.length; ++o) { + var overlay = cm.state.overlays[o], i = 1, at = 0; + runMode(cm, line.text, overlay.mode, true, function(end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + st.splice(i, 1, end, st[i+1], i_end); + i += 2; + at = Math.min(end, i_end); + } + if (!style) return; + if (overlay.opaque) { + st.splice(start, i - start, end, "cm-overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style; + } + } + }, lineClasses); + } + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); + line.styles = result.styles; + if (result.classes) line.styleClasses = result.classes; + else if (line.styleClasses) line.styleClasses = null; + if (updateFrontier === cm.doc.frontier) cm.doc.frontier++; + } + return line.styles; + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, state, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize); + stream.start = stream.pos = startAt || 0; + if (text == "") callBlankLine(mode, state); + while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) { + readToken(mode, stream, state); + stream.start = stream.pos; + } + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) return null; + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")); + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = elt("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: elt("pre", [content]), content: content, + col: 0, pos: 0, cm: cm, + splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order; + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) + builder.addToken = buildTokenBadBidi(builder.addToken, order); + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); + if (line.styleClasses.textClass) + builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map); + (lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className)) + builder.content.className = "cm-tab-wrap-hack"; + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); + + return builder; + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token; + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, title, css) { + if (!text) return; + var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + if (!special.test(text)) { + builder.col += text.length; + var content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) mustWrap = true; + builder.pos += text.length; + } else { + var content = document.createDocumentFragment(), pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); + else content.appendChild(txt); + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) break; + pos += skipped + 1; + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt.setAttribute("role", "presentation"); + txt.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else { + var txt = builder.cm.options.specialCharPlaceholder(m[0]); + txt.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); + else content.appendChild(txt); + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt); + builder.pos++; + } + } + if (style || startStyle || endStyle || mustWrap || css) { + var fullStyle = style || ""; + if (startStyle) fullStyle += startStyle; + if (endStyle) fullStyle += endStyle; + var token = elt("span", [content], fullStyle, css); + if (title) token.title = title; + return builder.content.appendChild(token); + } + builder.content.appendChild(content); + } + + function splitSpaces(old) { + var out = " "; + for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; + out += " "; + return out; + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function(builder, text, style, startStyle, endStyle, title, css) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + for (var i = 0; i < order.length; i++) { + var part = order[i]; + if (part.to > start && part.from <= start) break; + } + if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css); + inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + }; + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) builder.map.push(builder.pos, builder.pos + size, widget); + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + widget = builder.content.appendChild(document.createElement("span")); + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i = 1; i < styles.length; i+=2) + builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)); + return; + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = title = css = ""; + collapsed = null; nextChange = Infinity; + var foundBookmarks = []; + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (sp.from <= pos && (sp.to == null || sp.to > pos)) { + if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } + if (m.className) spanStyle += " " + m.className; + if (m.css) css = m.css; + if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; + if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; + if (m.title && !title) title = m.title; + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + collapsed = sp; + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) foundBookmarks.push(m); + } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) return; + } + if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j) + buildCollapsedSpan(builder, 0, foundBookmarks[j]); + } + if (pos >= len) break; + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore); + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null;} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + for (var i = start, result = []; i < end; ++i) + result.push(new Line(text[i], spansFor(i), estimateHeight)); + return result; + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) doc.remove(from.line, nlines); + if (added.length) doc.insert(from.line, added); + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added = linesFor(1, text.length - 1); + added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added = linesFor(1, text.length - 1); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1); + doc.insert(from.line + 1, added); + } + + signalLater(doc, "change", doc, change); + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + for (var i = 0, height = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length; }, + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) lines[i].parent = this; + }, + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + if (op(this.lines[at])) return true; + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size; }, + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) break; + at = 0; + } else at -= sz; + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines); + }, + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + while (child.lines.length > 50) { + var spilled = child.lines.splice(child.lines.length - 25, 25); + var newleaf = new LeafChunk(spilled); + child.height -= newleaf.height; + this.children.splice(i + 1, 0, newleaf); + newleaf.parent = this; + } + this.maybeSpill(); + } + break; + } + at -= sz; + } + }, + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) return; + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10); + me.parent.maybeSpill(); + }, + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) return true; + if ((n -= used) == 0) break; + at = 0; + } else at -= sz; + } + } + }; + + var nextDocId = 0; + var Doc = CodeMirror.Doc = function(text, mode, firstLine) { + if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); + if (firstLine == null) firstLine = 0; + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.frontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + + if (typeof text == "string") text = splitLines(text); + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) this.iterN(from - this.first, to - from, op); + else this.iterN(this.first, this.first + this.size, from); + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) height += lines[i].height; + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) return lines; + return lines.join(lineSep || "\n"); + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: splitLines(code), origin: "setValue", full: true}, true); + setSelection(this, simpleSelection(top)); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) return lines; + return lines.join(lineSep || "\n"); + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, + + getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, + getLineNumber: function(line) {return lineNo(line);}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") line = getLine(this, line); + return visualLine(line); + }, + + lineCount: function() {return this.size;}, + firstLine: function() {return this.first;}, + lastLine: function() {return this.first + this.size - 1;}, + + clipPos: function(pos) {return clipPos(this, pos);}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") pos = range.head; + else if (start == "anchor") pos = range.anchor; + else if (start == "end" || start == "to" || start === false) pos = range.to(); + else pos = range.from(); + return pos; + }, + listSelections: function() { return this.sel.ranges; }, + somethingSelected: function() {return this.sel.somethingSelected();}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads, options)); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + extendSelections(this, map(this.sel.ranges, f), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) return; + for (var i = 0, out = []; i < ranges.length; i++) + out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)); + if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex); + setSelection(this, normalizeSelection(out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) return lines; + else return lines.join(lineSep || "\n"); + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) sel = sel.join(lineSep || "\n"); + parts[i] = sel; + } + return parts; + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + dup[i] = code; + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i = changes.length - 1; i >= 0; i--) + makeChange(this, changes[i]); + if (newSel) setSelectionReplaceHistory(this, newSel); + else if (this.cm) ensureCursorVisible(this.cm); + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend;}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done; + for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone; + return {undo: done, redo: undone}; + }, + clearHistory: function() {this.history = new History(this.history.maxGeneration);}, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; + return this.history.generation; + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration); + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)}; + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history.maxGeneration); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) line[prop] = cls; + else if (classTest(cls).test(line[prop])) return false; + else line[prop] += " " + cls; + return true; + }); + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) return false; + else if (cls == null) line[prop] = null; + else { + var found = cur.match(classTest(cls)); + if (!found) return false; + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true; + }); + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options); + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, "range"); + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark"); + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker.parent || span.marker); + } + return markers; + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function(line) { + var spans = line.markedSpans; + if (spans) for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(lineNo == from.line && from.ch > span.to || + span.from == null && lineNo != from.line|| + lineNo == to.line && span.from > to.ch) && + (!filter || filter(span.marker))) + found.push(span.marker.parent || span.marker); + } + ++lineNo; + }); + return found; + }, + getAllMarks: function() { + var markers = []; + this.iter(function(line) { + var sps = line.markedSpans; + if (sps) for (var i = 0; i < sps.length; ++i) + if (sps[i].from != null) markers.push(sps[i].marker); + }); + return markers; + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first; + this.iter(function(line) { + var sz = line.text.length + 1; + if (sz > off) { ch = off; return true; } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)); + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) return 0; + this.iter(this.first, coords.line, function (line) { + index += line.text.length + 1; + }); + return index; + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc; + }, + + linkedDoc: function(options) { + if (!options) options = {}; + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) from = options.from; + if (options.to != null && options.to < to) to = options.to; + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from); + if (options.sharedHist) copy.history = this.history; + (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy; + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) other = other.doc; + if (this.linked) for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) continue; + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break; + } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode;}, + getEditor: function() {return this.cm;} + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor".split(" "); + for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments);}; + })(Doc.prototype[prop]); + + eventMixin(Doc); + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) continue; + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) continue; + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) throw new Error("This document is already in use."); + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + if (!cm.options.lineWrapping) findMaxLine(cm); + cm.options.mode = doc.modeOption; + regChange(cm); + } + + // LINE UTILITIES + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document."); + for (var chunk = doc; !chunk.lines;) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break; } + n -= sz; + } + } + return chunk.lines[n]; + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function(line) { + var text = line.text; + if (n == end.line) text = text.slice(0, end.ch); + if (n == start.line) text = text.slice(start.ch); + out.push(text); + ++n; + }); + return out; + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function(line) { out.push(line.text); }); + return out; + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) for (var n = line; n; n = n.parent) n.height += diff; + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) return null; + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) break; + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first; + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i = 0; i < chunk.children.length; ++i) { + var child = chunk.children[i], ch = child.height; + if (h < ch) { chunk = child; continue outer; } + h -= ch; + n += child.chunkSize(); + } + return n; + } while (!chunk.lines); + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) break; + h -= lh; + } + return n + i; + } + + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) break; + else h += line.height; + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i = 0; i < p.children.length; ++i) { + var cur = p.children[i]; + if (cur == chunk) break; + else h += cur.height; + } + } + return h; + } + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line) { + var order = line.order; + if (order == null) order = line.order = bidiOrdering(line.text); + return order; + } + + // HISTORY + + function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true); + return histChange; + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) array.pop(); + else break; + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done); + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done); + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done); + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, ore are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + var last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + pushSelectionToHistory(doc.sel, hist.done); + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) hist.done.shift(); + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) signal(doc, "historyAdded"); + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500); + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + hist.done[hist.done.length - 1] = sel; + else + pushSelectionToHistory(sel, hist.done); + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + clearSelectionEvents(hist.undone); + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + dest.push(sel); + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) { + if (line.markedSpans) + (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) return null; + for (var i = 0, out; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } + else if (out) out.push(spans[i]); + } + return !out ? spans : out.length ? out : null; + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) return null; + for (var i = 0, nw = []; i < change.text.length; ++i) + nw.push(removeClearedSpans(found[i])); + return nw; + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + for (var i = 0, copy = []; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue; + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m; + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } + } + } + return copy; + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue; + } + for (var j = 0; j < sub.changes.length; ++j) { + var cur = sub.changes[j]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break; + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // EVENT UTILITIES + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + var e_preventDefault = CodeMirror.e_preventDefault = function(e) { + if (e.preventDefault) e.preventDefault(); + else e.returnValue = false; + }; + var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) { + if (e.stopPropagation) e.stopPropagation(); + else e.cancelBubble = true; + }; + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false; + } + var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);}; + + function e_target(e) {return e.target || e.srcElement;} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) b = 1; + else if (e.button & 2) b = 3; + else if (e.button & 4) b = 2; + } + if (mac && e.ctrlKey && b == 1) b = 3; + return b; + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var on = CodeMirror.on = function(emitter, type, f) { + if (emitter.addEventListener) + emitter.addEventListener(type, f, false); + else if (emitter.attachEvent) + emitter.attachEvent("on" + type, f); + else { + var map = emitter._handlers || (emitter._handlers = {}); + var arr = map[type] || (map[type] = []); + arr.push(f); + } + }; + + var off = CodeMirror.off = function(emitter, type, f) { + if (emitter.removeEventListener) + emitter.removeEventListener(type, f, false); + else if (emitter.detachEvent) + emitter.detachEvent("on" + type, f); + else { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + for (var i = 0; i < arr.length; ++i) + if (arr[i] == f) { arr.splice(i, 1); break; } + } + }; + + var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); + }; + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + function bnd(f) {return function(){f.apply(null, args);};}; + for (var i = 0; i < arr.length; ++i) + list.push(bnd(arr[i])); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) delayed[i](); + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore; + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) return; + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) + set.push(arr[i]); + } + + function hasHandler(emitter, type) { + var arr = emitter._handlers && emitter._handlers[type]; + return arr && arr.length > 0; + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // MISC UTILITIES + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 30; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + function Delayed() {this.id = null;} + Delayed.prototype.set = function(ms, f) { + clearTimeout(this.id); + this.id = setTimeout(f, ms); + }; + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) end = string.length; + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + return n + (end - i); + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + }; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) nextTab = string.length; + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + return pos + Math.min(skipped, goal - col); + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) return pos; + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + spaceStrs.push(lst(spaceStrs) + " "); + return spaceStrs[n]; + } + + function lst(arr) { return arr[arr.length-1]; } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; + else if (ie) // Suppress mysterious IE10 errors + selectInput = function(node) { try { node.select(); } catch(_e) {} }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + if (array[i] == elt) return i; + return -1; + } + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); + return out; + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) copyObj(props, inst); + return inst; + }; + + function copyObj(obj, target, overwrite) { + if (!target) target = {}; + for (var prop in obj) + if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + target[prop] = obj[prop]; + return target; + } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args);}; + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + var isWordCharBasic = CodeMirror.isWordChar = function(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); + }; + function isWordChar(ch, helper) { + if (!helper) return isWordCharBasic(ch); + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true; + return helper.test(ch); + } + + function isEmpty(obj) { + for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; + return true; + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); } + + // DOM UTILITIES + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) e.className = className; + if (style) e.style.cssText = style; + if (typeof content == "string") e.appendChild(document.createTextNode(content)); + else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); + return e; + } + + var range; + if (document.createRange) range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r; + }; + else range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r; } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r; + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + e.removeChild(e.firstChild); + return e; + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e); + } + + var contains = CodeMirror.contains = function(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + child = child.parentNode; + if (parent.contains) + return parent.contains(child); + do { + if (child.nodeType == 11) child = child.host; + if (child == parent) return true; + } while (child = child.parentNode); + }; + + function activeElt() { return document.activeElement; } + // Older versions of IE throws unspecified error when touching + // document.activeElement in some cases (during loading, in iframe) + if (ie && ie_version < 11) activeElt = function() { + try { return document.activeElement; } + catch(e) { return document.body; } + }; + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); } + var rmClass = CodeMirror.rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + var addClass = CodeMirror.addClass = function(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls; + }; + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i]; + return b; + } + + // WINDOW-WIDE EVENTS + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.body.getElementsByClassName) return; + var byClass = document.body.getElementsByClassName("CodeMirror"); + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) f(cm); + } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) return; + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function() { + if (resizeTimer == null) resizeTimer = setTimeout(function() { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function() { + forEachCodeMirror(onBlur); + }); + } + + // FEATURE DETECTION + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) return false; + var div = elt('div'); + return "draggable" in div || "dragDrop" in div; + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node; + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) return badBidiRects; + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780) + var r1 = range(txt, 1, 2).getBoundingClientRect(); + return badBidiRects = (r1.right - r0.right < 3); + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) nl = string.length; + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result; + } : function(string){return string.split(/\r\n?|\n/);}; + + var hasSelection = window.getSelection ? function(te) { + try { return te.selectionStart != te.selectionEnd; } + catch(e) { return false; } + } : function(te) { + try {var range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) return false; + return range.compareEndPoints("StartToEnd", range) != 0; + }; + + var hasCopyEvent = (function() { + var e = elt("div"); + if ("oncopy" in e) return true; + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function"; + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) return badZoomedRects; + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1; + } + + // KEY NAMES + + var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"}; + CodeMirror.keyNames = keyNames; + (function() { + // Number keys + for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i); + // Alphabetic keys + for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); + // Function keys + for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; + })(); + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) return f(from, to, "ltr"); + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + found = true; + } + } + if (!found) f(from, to, "ltr"); + } + + function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } + function bidiRight(part) { return part.level % 2 ? part.from : part.to; } + + function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; } + function lineRight(line) { + var order = getOrder(line); + if (!order) return line.text.length; + return bidiRight(lst(order)); + } + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) lineN = lineNo(visual); + var order = getOrder(visual); + var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); + return Pos(lineN, ch); + } + function lineEnd(cm, lineN) { + var merged, line = getLine(cm.doc, lineN); + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line; + lineN = null; + } + var order = getOrder(line); + var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); + return Pos(lineN == null ? lineNo(line) : lineN, ch); + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS); + } + return start; + } + + function compareBidiLevel(order, a, b) { + var linedir = order[0].level; + if (a == linedir) return true; + if (b == linedir) return false; + return a < b; + } + var bidiOther; + function getBidiPartAt(order, pos) { + bidiOther = null; + for (var i = 0, found; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < pos && cur.to > pos) return i; + if ((cur.from == pos || cur.to == pos)) { + if (found == null) { + found = i; + } else if (compareBidiLevel(order, cur.level, order[found].level)) { + if (cur.from != cur.to) bidiOther = found; + return i; + } else { + if (cur.from != cur.to) bidiOther = i; + return found; + } + } + } + return found; + } + + function moveInLine(line, pos, dir, byUnit) { + if (!byUnit) return pos + dir; + do pos += dir; + while (pos > 0 && isExtendingChar(line.text.charAt(pos))); + return pos; + } + + // This is needed in order to move 'visually' through bi-directional + // text -- i.e., pressing left should make the cursor go left, even + // when in RTL text. The tricky part is the 'jumps', where RTL and + // LTR text touch each other. This often requires the cursor offset + // to move more than one unit, in order to visually move one unit. + function moveVisually(line, start, dir, byUnit) { + var bidi = getOrder(line); + if (!bidi) return moveLogically(line, start, dir, byUnit); + var pos = getBidiPartAt(bidi, start), part = bidi[pos]; + var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit); + + for (;;) { + if (target > part.from && target < part.to) return target; + if (target == part.from || target == part.to) { + if (getBidiPartAt(bidi, target) == pos) return target; + part = bidi[pos += dir]; + return (dir > 0) == part.level % 2 ? part.to : part.from; + } else { + part = bidi[pos += dir]; + if (!part) return null; + if ((dir > 0) == part.level % 2) + target = moveInLine(line, part.to, -1, byUnit); + else + target = moveInLine(line, part.from, 1, byUnit); + } + } + } + + function moveLogically(line, start, dir, byUnit) { + var target = start + dir; + if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir; + return target < 0 || target > line.text.length ? null : target; + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6ff + var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm"; + function charType(code) { + if (code <= 0xf7) return lowTypes.charAt(code); + else if (0x590 <= code && code <= 0x5f4) return "R"; + else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600); + else if (0x6ee <= code && code <= 0x8ac) return "r"; + else if (0x2000 <= code && code <= 0x200b) return "w"; + else if (code == 0x200c) return "b"; + else return "L"; + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + // Browsers seem to always treat the boundaries of block elements as being L. + var outerType = "L"; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str) { + if (!bidiRE.test(str)) return false; + var len = str.length, types = []; + for (var i = 0, type; i < len; ++i) + types.push(type = charType(str.charCodeAt(i))); + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i = 0, prev = outerType; i < len; ++i) { + var type = types[i]; + if (type == "m") types[i] = prev; + else prev = type; + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i = 0, cur = outerType; i < len; ++i) { + var type = types[i]; + if (type == "1" && cur == "r") types[i] = "n"; + else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i = 1, prev = types[0]; i < len - 1; ++i) { + var type = types[i]; + if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"; + else if (type == "," && prev == types[i+1] && + (prev == "1" || prev == "n")) types[i] = prev; + prev = type; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i = 0; i < len; ++i) { + var type = types[i]; + if (type == ",") types[i] = "N"; + else if (type == "%") { + for (var end = i + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i = 0, cur = outerType; i < len; ++i) { + var type = types[i]; + if (cur == "L" && type == "1") types[i] = "L"; + else if (isStrong.test(type)) cur = type; + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i = 0; i < len; ++i) { + if (isNeutral.test(types[i])) { + for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} + var before = (i ? types[i-1] : outerType) == "L"; + var after = (end < len ? types[end] : outerType) == "L"; + var replace = before || after ? "L" : "R"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i = 0; i < len;) { + if (countsAsLeft.test(types[i])) { + var start = i; + for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} + order.push(new BidiSpan(0, start, i)); + } else { + var pos = i, at = order.length; + for (++i; i < len && types[i] != "L"; ++i) {} + for (var j = pos; j < i;) { + if (countsAsNum.test(types[j])) { + if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j)); + var nstart = j; + for (++j; j < i && countsAsNum.test(types[j]); ++j) {} + order.splice(at, 0, new BidiSpan(2, nstart, j)); + pos = j; + } else ++j; + } + if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)); + } + } + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + if (order[0].level != lst(order).level) + order.push(new BidiSpan(order[0].level, len, len)); + + return order; + }; + })(); + + // THE END + + CodeMirror.version = "5.1.0"; + + return CodeMirror; +}); diff --git a/assets/js/codemirror-addon-continuelist-5.1.0.js b/assets/js/codemirror-addon-continuelist-5.1.0.js new file mode 100644 index 0000000..ca8d267 --- /dev/null +++ b/assets/js/codemirror-addon-continuelist-5.1.0.js @@ -0,0 +1,51 @@ +// 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, match; + var eolState = cm.getStateAfter(pos.line); + var inList = eolState.list !== false; + var inQuote = eolState.quote !== false; + + if (!ranges[i].empty() || (!inList && !inQuote) || !(match = cm.getLine(pos.line).match(listRE))) { + cm.execCommand("newlineAndIndent"); + return; + } + if (cm.getLine(pos.line).match(emptyListRE)) { + cm.replaceRange("", { + line: pos.line, ch: 0 + }, { + line: pos.line, ch: pos.ch + 1 + }); + replacements[i] = "\n"; + + } else { + var indent = match[1], after = match[4]; + var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0 + ? match[2] + : (parseInt(match[3], 10) + 1) + "."; + + replacements[i] = "\n" + indent + bullet + after; + } + } + + cm.replaceSelections(replacements); + }; +}); diff --git a/assets/js/codemirror-mode-markdown-5.1.0.js b/assets/js/codemirror-mode-markdown-5.1.0.js new file mode 100644 index 0000000..3c80311 --- /dev/null +++ b/assets/js/codemirror-mode-markdown-5.1.0.js @@ -0,0 +1,765 @@ +// 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; + + // Turn on fenced code blocks? ("```" to start/end) + if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false; + + // 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 = /^#+/ + , setextHeaderRE = /^(?:\={1,}|-{1,})$/ + , textRE = /^[^#!\[\]*_\\<>` "'(~]+/; + + 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); + } + + + // 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; + 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.thisLineHasContent = false; + return null; + } + + function blockNormal(stream, state) { + + var sol = stream.sol(); + + var prevLineIsList = (state.list !== false); + if (state.list !== false && 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.list !== false && state.indentation > 0) { + state.list = null; + state.listDepth = Math.floor(state.indentation / 4); + } else if (state.list !== false) { // No longer a list + state.list = false; + state.listDepth = 0; + } + + var match = null; + if (state.indentationDiff >= 4) { + state.indentation -= 4; + stream.skipToEnd(); + return code; + } else if (stream.eatSpace()) { + return null; + } else if (match = stream.match(atxHeaderRE)) { + state.header = match[0].length <= 6 ? match[0].length : 6; + if (modeCfg.highlightFormatting) state.formatting = "header"; + state.f = state.inline; + return getType(state); + } else if (state.prevLineHasContent && (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.indentation++; + 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)) { + return hr; + } else if ((!state.prevLineHasContent || 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 += 4; + 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 && stream.match(/^```[ \t]*([\w+#]*)/, true)) { + // try switching mode + state.localMode = getMode(RegExp.$1); + 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.md_inside && stream.current().indexOf(">") > -1)) { + state.f = inlineNormal; + state.block = blockNormal; + state.htmlState = null; + } + return style; + } + + function local(stream, state) { + if (stream.sol() && stream.match("```", 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.block = blockNormal; + state.f = inlineNormal; + 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); + return styles.length ? styles.join(' ') : null; + } + + 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)) { + if (stream.string.indexOf(">") != -1) { + var atts = stream.string.substring(1,stream.string.indexOf(">")); + 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; + } + + 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, + + prevLineHasContent: false, + thisLineHasContent: false, + + block: blockNormal, + htmlState: null, + indentation: 0, + + inline: inlineNormal, + text: handleText, + + formatting: false, + linkText: false, + linkHref: false, + linkTitle: false, + em: false, + strong: false, + header: 0, + taskList: false, + list: false, + listDepth: 0, + quote: 0, + trailingSpace: 0, + trailingSpaceNewLine: false, + strikethrough: false + }; + }, + + copyState: function(s) { + return { + f: s.f, + + prevLineHasContent: s.prevLineHasContent, + thisLineHasContent: s.thisLineHasContent, + + 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, + em: s.em, + strong: s.strong, + strikethrough: s.strikethrough, + header: s.header, + taskList: s.taskList, + list: s.list, + listDepth: s.listDepth, + quote: s.quote, + trailingSpace: s.trailingSpace, + trailingSpaceNewLine: s.trailingSpaceNewLine, + md_inside: s.md_inside + }; + }, + + token: function(stream, state) { + + // Reset state.formatting + state.formatting = false; + + if (stream.sol()) { + var forceBlankLine = !!state.header; + + // Reset state.header + state.header = 0; + + if (stream.match(/^\s*$/, true) || forceBlankLine) { + state.prevLineHasContent = false; + blankLine(state); + return forceBlankLine ? this.token(stream, state) : null; + } else { + state.prevLineHasContent = state.thisLineHasContent; + state.thisLineHasContent = true; + } + + // Reset state.taskList + state.taskList = false; + + // Reset state.code + state.code = 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"); + +}); diff --git a/assets/js/lodash.js b/assets/js/lodash.js new file mode 100644 index 0000000..5d8cb1c --- /dev/null +++ b/assets/js/lodash.js @@ -0,0 +1,89 @@ +/** + * @license + * lodash 3.6.0 (Custom Build) lodash.com/license | Underscore.js 1.8.2 underscorejs.org/LICENSE + * Build: `lodash modern -o ./lodash.js` + */ +;(function(){function n(n,t){if(n!==t){var r=n===n,e=t===t;if(n>t||!r||typeof n=="undefined"&&e)return 1;if(n=n&&9<=n&&13>=n||32==n||160==n||5760==n||6158==n||8192<=n&&(8202>=n||8232==n||8233==n||8239==n||8287==n||12288==n||65279==n)}function v(n,t){for(var r=-1,e=n.length,u=-1,o=[];++re&&(e=u)}return e}function Yt(n,t){for(var r=-1,e=n.length;++ri(t,a,0)&&u.push(a);return u}function er(n,t){var r=true;return Ao(n,function(n,e,u){return r=!!t(n,e,u)}),r}function ur(n,t){var r=[];return Ao(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function or(n,t,r,e){var u;return r(n,function(n,r,o){return t(n,r,o)?(u=e?r:n,false):void 0}),u}function ir(n,t,r){for(var e=-1,u=n.length,o=-1,i=[];++et&&(t=-t>u?0:u+t),r=typeof r=="undefined"||r>u?u:+r||0,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=wu(u);++eu(a,p,0)&&((t||f)&&a.push(p),c.push(l))}return c}function Ir(n,t){for(var r=-1,e=t.length,u=wu(e);++r>>1,i=n[o];(r?i<=t:ir||null==e)return e; +var u=t[r-2],o=t[r-1],i=t[3];for(3arguments.length;return typeof e=="function"&&typeof o=="undefined"&&si(r)?n(r,e,u,i):wr(r,le(e,o,4),u,i,t)}}function te(n,t,r,e,u,o,i,f,a,c){function l(){for(var b=arguments.length,j=b,k=wu(b);j--;)k[j]=arguments[j]; +if(e&&(k=Ur(k,e,u)),o&&(k=Fr(k,o,i)),_||y){var j=l.placeholder,E=v(k,j),b=b-E.length;if(bu)||i===e&&i===o)&&(u=i,o=n) +}),o}function le(n,t,r){var e=Wt.callback||_u,e=e===_u?Qt:e;return r?e(n,t,r):e}function pe(n,t,e){var u=Wt.indexOf||Ce,u=u===Ce?r:u;return n?u(n,t,e):u}function se(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&Nu.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function he(n){return n=n.constructor,typeof n=="function"&&n instanceof n||(n=Eu),new n}function _e(n,t,r){var e=n.constructor;switch(t){case J:return Tr(n);case D:case P:return new e(+n);case X:case H:case Q:case nt:case tt:case rt:case et:case ut:case ot:return t=n.buffer,new e(r?Tr(t):t,n.byteOffset,n.length); +case V:case G:return new e(n);case Z:var u=new e(n.source,dt.exec(n));u.lastIndex=n.lastIndex}return u}function ve(n,t){return n=+n,t=null==t?yo:t,-1t?0:t)):[]}function Oe(n,t,r){var e=n?n.length:0;return e?((r?ge(n,t,r):null==t)&&(t=1),t=e-(+t||0),br(n,0,0>t?0:t)):[]}function Re(n){return n?n[0]:w}function Ce(n,t,e){var u=n?n.length:0; +if(!u)return-1;if(typeof e=="number")e=0>e?oo(u+e,0):e;else if(e)return e=Cr(n,t),n=n[e],(t===t?t===n:n!==n)?e:-1;return r(n,t,e||0)}function Se(n){var t=n?n.length:0;return t?n[t-1]:w}function We(n){return Ie(n,1)}function Te(n,t,e,u){if(!n||!n.length)return[];null!=t&&typeof t!="boolean"&&(u=e,e=ge(n,t,u)?null:t,t=false);var o=le();if((o!==Qt||null!=e)&&(e=o(e,u,3)),t&&pe()==r){t=e;var i;e=-1,u=n.length;for(var o=-1,f=[];++e>>0,e=wu(r);++tr?oo(u+r,0):r||0,typeof n=="string"||!si(n)&&ru(n)?rt?0:+t||0,n.length),n)}function Pe(n){n=je(n);for(var t=-1,r=n.length,e=wu(r);++t=r||r>t?(f&&qu(f),r=s,f=p=s=w,r&&(h=Qo(),a=n.apply(l,i),p||f||(i=l=null))):p=Ju(e,r)}function u(){p&&qu(p),f=p=s=w,(v||_!==t)&&(h=Qo(),a=n.apply(l,i),p||f||(i=l=null)) +}function o(){if(i=arguments,c=Qo(),l=this,s=v&&(p||!g),false===_)var r=g&&!p;else{f||g||(h=c);var o=_-(c-h),y=0>=o||o>_;y?(f&&(f=qu(f)),h=c,a=n.apply(l,i)):f||(f=Ju(u,o))}return y&&p?p=qu(p):p||t===_||(p=Ju(e,t)),r&&(y=true,a=n.apply(l,i)),!y||p||f||(i=l=null),a}var i,f,a,c,l,p,s,h=0,_=false,v=true;if(typeof n!="function")throw new Ru(L);if(t=0>t?0:+t||0,true===r)var g=true,v=false;else He(r)&&(g=r.leading,_="maxWait"in r&&oo(+r.maxWait||0,t),v="trailing"in r?r.trailing:v);return o.cancel=function(){p&&qu(p),f&&qu(f),f=p=s=w +},o}function Ye(n,t){function r(){var e=arguments,u=r.cache,o=t?t.apply(this,e):e[0];return u.has(o)?u.get(o):(e=n.apply(this,e),u.set(o,e),e)}if(typeof n!="function"||t&&typeof t!="function")throw new Ru(L);return r.cache=new Ye.Cache,r}function Ze(n,t){if(typeof n!="function")throw new Ru(L);return t=oo(typeof t=="undefined"?n.length-1:+t||0,0),function(){for(var r=arguments,e=-1,u=oo(r.length-t,0),o=wu(u);++et||!n||!eo(t))return r;do t%2&&(r+=n),t=Ku(t/2),n+=n;while(t);return r}function su(n,t,r){var e=n;return(n=u(n))?(r?ge(e,t,r):null==t)?n.slice(g(n),y(n)+1):(t+="",n.slice(i(n,t),f(n,t)+1)):n}function hu(n,t,r){return r&&ge(n,t,r)&&(t=null),n=u(n),n.match(t||Et)||[] +}function _u(n,t,r){return r&&ge(n,t,r)&&(t=null),h(n)?yu(n):Qt(n,t)}function vu(n){return function(){return n}}function gu(n){return n}function yu(n){return vr(nr(n,true))}function du(n,t,r){if(null==r){var e=He(t),u=e&&ji(t);((u=u&&u.length&&lr(t,u))?u.length:e)||(u=false,r=t,t=n,n=this)}u||(u=lr(t,ji(t)));var o=true,e=-1,i=_i(n),f=u.length;false===r?o=false:He(r)&&"chain"in r&&(o=r.chain);for(;++e>>1,go=no?no.BYTES_PER_ELEMENT:0,yo=ju.pow(2,53)-1,mo=Qu&&new Qu,wo={},bo=Wt.support={};!function(n){bo.funcDecomp=/\bthis\b/.test(function(){return this}),bo.funcNames=typeof Au.name=="string";try{bo.dom=11===Tu.createDocumentFragment().nodeType +}catch(t){bo.dom=false}try{bo.nonEnumArgs=!Zu.call(arguments,1)}catch(r){bo.nonEnumArgs=true}}(0,0),Wt.templateSettings={escape:ht,evaluate:_t,interpolate:vt,variable:"",imports:{_:Wt}};var xo=function(){function n(){}return function(t){if(He(t)){n.prototype=t;var r=new n;n.prototype=null}return r||_.Object()}}(),Ao=Lr(ar),jo=Lr(cr,true),ko=Br(),Eo=Br(true),Io=mo?function(n,t){return mo.set(n,t),n}:gu;Du||(Tr=Mu&&Hu?function(n){var t=n.byteLength,r=no?Ku(t/go):0,e=r*go,u=new Mu(t);if(r){var o=new no(u,0,r); +o.set(new no(n,0,r))}return t!=e&&(o=new Hu(u,e),o.set(new Hu(n,e))),u}:vu(null));var Oo=ro&&Gu?function(n){return new $t(n)}:vu(null),Ro=mo?function(n){return mo.get(n)}:mu,Co=function(){return bo.funcNames?"constant"==vu.name?dr("name"):function(n){for(var t=n.name,r=wo[t],e=r?r.length:0;e--;){var u=r[e],o=u.func;if(null==o||o==n)return u.name}return t}:vu("")}(),So=function(){var n=0,t=0;return function(r,e){var u=Qo(),o=U-(u-t);if(t=u,0=T)return r}else n=0;return Io(r,e)}}(),Wo=Ze(function(n,t){return si(n)||Ge(n)?rr(n,ir(t,false,true)):[] +}),To=Vr(),Uo=Vr(true),Fo=Ze(function(t,r){t||(t=[]),r=ir(r);var e=r.length,u=Xt(t,r);for(r.sort(n);e--;){var o=parseFloat(r[e]);if(o!=i&&ve(o)){var i=o;Xu.call(t,o,1)}}return u}),No=ue(),$o=ue(true),Lo=Ze(function(n){return Er(ir(n,false,true))}),Bo=Ze(function(n,t){return si(n)||Ge(n)?rr(n,t):[]}),zo=Ze(Ue),Mo=Ze(function(n,t){return de(n?n.length:0)&&(n=je(n)),Xt(n,ir(t))}),Do=Nr(function(n,t,r){Nu.call(n,r)?++n[r]:n[r]=1}),Po=Kr(Ao),qo=Kr(jo,true),Ko=Gr(zt,Ao),Vo=Gr(function(n,t){for(var r=n.length;r--&&false!==t(n[r],r,n););return n +},jo),Yo=Nr(function(n,t,r){Nu.call(n,r)?n[r].push(t):n[r]=[t]}),Zo=Nr(function(n,t,r){n[r]=t}),Go=Ze(function(n,t,r){var e=-1,u=typeof t=="function",o=n?n.length:0,i=de(o)?wu(o):[];return Ao(n,function(n){var o=u?t:null!=n&&n[t];i[++e]=o?o.apply(n,r):w}),i}),Jo=Nr(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),Xo=ne(function(n,t,r,e){var u=-1,o=n.length;for(e&&o&&(r=n[++u]);++u--n?t.apply(this,arguments):void 0 +}},Wt.ary=function(n,t,r){return r&&ge(n,t,r)&&(t=null),t=n&&null==t?n.length:oo(+t||0,0),oe(n,R,null,null,null,null,t)},Wt.assign=gi,Wt.at=Mo,Wt.before=Ke,Wt.bind=ni,Wt.bindAll=ti,Wt.bindKey=ri,Wt.callback=_u,Wt.chain=Ne,Wt.chunk=function(n,t,r){t=(r?ge(n,t,r):null==t)?1:oo(+t||1,1),r=0;for(var e=n?n.length:0,u=-1,o=wu(Pu(e/t));rr&&(r=-r>u?0:u+r),e=typeof e=="undefined"||e>u?u:+e||0,0>e&&(e+=u),u=r>e?0:e>>>0,r>>>=0;r(p?Lt(p,f):o(l,f,0))){for(t=e;--t;){var s=u[t];if(0>(s?Lt(s,f):o(n[t],f,0)))continue n}p&&p.push(f),l.push(f)}return l},Wt.invert=function(n,t,r){r&&ge(n,t,r)&&(t=null),r=-1;for(var e=ji(n),u=e.length,o={};++rt?0:t)):[]},Wt.takeRight=function(n,t,r){var e=n?n.length:0;return e?((r?ge(n,t,r):null==t)&&(t=1),t=e-(+t||0),br(n,0>t?0:t)):[] +},Wt.takeRightWhile=function(n,t,r){return n&&n.length?Or(n,le(t,r,3),false,true):[]},Wt.takeWhile=function(n,t,r){return n&&n.length?Or(n,le(t,r,3)):[]},Wt.tap=function(n,t,r){return t.call(r,n),n},Wt.throttle=function(n,t,r){var e=true,u=true;if(typeof n!="function")throw new Ru(L);return false===r?e=false:He(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),St.leading=e,St.maxWait=+t,St.trailing=u,Ve(n,t,St)},Wt.thru=$e,Wt.times=function(n,t,r){if(n=+n,1>n||!eo(n))return[];var e=-1,u=wu(io(n,ho)); +for(t=Wr(t,r,1);++er?0:+r||0,e),r-=t.length,0<=r&&n.indexOf(t,r)==r},Wt.escape=function(n){return(n=u(n))&&st.test(n)?n.replace(lt,l):n},Wt.escapeRegExp=cu,Wt.every=Le,Wt.find=Po,Wt.findIndex=To,Wt.findKey=di,Wt.findLast=qo,Wt.findLastIndex=Uo,Wt.findLastKey=mi,Wt.findWhere=function(n,t){return Po(n,vr(t))},Wt.first=Re,Wt.has=function(n,t){return n?Nu.call(n,t):false +},Wt.identity=gu,Wt.includes=ze,Wt.indexOf=Ce,Wt.inRange=function(n,t,r){return t=+t||0,"undefined"===typeof r?(r=t,t=0):r=+r||0,n>=t&&nr?oo(e+r,0):io(r||0,e-1))+1;else if(r)return u=Cr(n,t,true)-1,n=n[u],(t===t?t===n:n!==n)?u:-1;if(t!==t)return s(n,u,true);for(;u--;)if(n[u]===t)return u;return-1},Wt.max=Fi,Wt.min=Ni,Wt.noConflict=function(){return _._=Bu,this},Wt.noop=mu,Wt.now=Qo,Wt.pad=function(n,t,r){n=u(n),t=+t;var e=n.length;return er?0:+r||0,n.length),n.lastIndexOf(t,r)==r},Wt.sum=function(n,t,r){r&&ge(n,t,r)&&(t=null);var e=le(),u=null==t;if(e===Qt&&u||(u=false,t=e(t,r,3)),u){for(n=si(n)?n:je(n),t=n.length,r=0;t--;)r+=+n[t]||0;n=r}else n=kr(n,t);return n},Wt.template=function(n,t,r){var e=Wt.templateSettings;r&&ge(n,t,r)&&(t=r=null),n=u(n),t=Jt(Jt({},r||t),e,Gt),r=Jt(Jt({},t.imports),e.imports,Gt); +var o,i,f=ji(r),a=Ir(r,f),c=0;r=t.interpolate||xt;var l="__p+='";r=Iu((t.escape||xt).source+"|"+r.source+"|"+(r===vt?yt:xt).source+"|"+(t.evaluate||xt).source+"|$","g");var s="sourceURL"in t?"//# sourceURL="+t.sourceURL+"\n":"";if(n.replace(r,function(t,r,e,u,f,a){return e||(e=u),l+=n.slice(c,a).replace(kt,p),r&&(o=true,l+="'+__e("+r+")+'"),f&&(i=true,l+="';"+f+";\n__p+='"),e&&(l+="'+((__t=("+e+"))==null?'':__t)+'"),c=a+t.length,t}),l+="';",(t=t.variable)||(l="with(obj){"+l+"}"),l=(i?l.replace(it,""):l).replace(ft,"$1").replace(at,"$1;"),l="function("+(t||"obj")+"){"+(t?"":"obj||(obj={});")+"var __t,__p=''"+(o?",__e=_.escape":"")+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}",t=Ui(function(){return Au(f,s+"return "+l).apply(w,a) +}),t.source=l,Xe(t))throw t;return t},Wt.trim=su,Wt.trimLeft=function(n,t,r){var e=n;return(n=u(n))?n.slice((r?ge(e,t,r):null==t)?g(n):i(n,t+"")):n},Wt.trimRight=function(n,t,r){var e=n;return(n=u(n))?(r?ge(e,t,r):null==t)?n.slice(0,y(n)+1):n.slice(0,f(n,t+"")+1):n},Wt.trunc=function(n,t,r){r&&ge(n,t,r)&&(t=null);var e=S;if(r=W,null!=t)if(He(t)){var o="separator"in t?t.separator:o,e="length"in t?+t.length||0:e;r="omission"in t?u(t.omission):r}else e=+t||0;if(n=u(n),e>=n.length)return n;if(e-=r.length,1>e)return r; +if(t=n.slice(0,e),null==o)return t+r;if(tu(o)){if(n.slice(e).search(o)){var i,f=n.slice(0,e);for(o.global||(o=Iu(o.source,(dt.exec(o)||"")+"g")),o.lastIndex=0;n=o.exec(f);)i=n.index;t=t.slice(0,null==i?e:i)}}else n.indexOf(o,e)!=e&&(o=t.lastIndexOf(o),-1u.__dir__?"Right":"")}),u},Ft.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()},Ft.prototype[n+"RightWhile"]=function(n,t){return this.reverse()[r](n,t).reverse()}}),zt(["first","last"],function(n,t){var r="take"+(t?"Right":""); +Ft.prototype[n]=function(){return this[r](1).value()[0]}}),zt(["initial","rest"],function(n,t){var r="drop"+(t?"":"Right");Ft.prototype[n]=function(){return this[r](1)}}),zt(["pluck","where"],function(n,t){var r=t?"filter":"map",e=t?vr:dr;Ft.prototype[n]=function(n){return this[r](e(n))}}),Ft.prototype.compact=function(){return this.filter(gu)},Ft.prototype.reject=function(n,t){return n=le(n,t,1),this.filter(function(t){return!n(t)})},Ft.prototype.slice=function(n,t){n=null==n?0:+n||0;var r=0>n?this.takeRight(-n):this.drop(n); +return typeof t!="undefined"&&(t=+t||0,r=0>t?r.dropRight(-t):r.take(t-n)),r},Ft.prototype.toArray=function(){return this.drop(0)},ar(Ft.prototype,function(n,t){var r=Wt[t];if(r){var e=/^(?:filter|map|reject)|While$/.test(t),u=/^(?:first|last)$/.test(t);Wt.prototype[t]=function(){function t(n){return n=[n],Yu.apply(n,o),r.apply(Wt,n)}var o=arguments,i=this.__chain__,f=this.__wrapped__,a=!!this.__actions__.length,c=f instanceof Ft,l=o[0],p=c||si(f);return p&&e&&typeof l=="function"&&1!=l.length&&(c=p=false),c=c&&!a,u&&!i?c?n.call(f):r.call(Wt,this.value()):p?(f=n.apply(c?f:new Ft(this),o),u||!a&&!f.__actions__||(f.__actions__||(f.__actions__=[])).push({func:$e,args:[t],thisArg:Wt}),new Ut(f,i)):this.thru(t) +}}}),zt("concat join pop push replace shift sort splice split unshift".split(" "),function(n){var t=(/^(?:replace|split)$/.test(n)?Wu:Cu)[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:join|pop|replace|shift)$/.test(n);Wt.prototype[n]=function(){var n=arguments;return e&&!this.__chain__?t.apply(this.value(),n):this[r](function(r){return t.apply(r,n)})}}),ar(Ft.prototype,function(n,t){var r=Wt[t];if(r){var e=r.name;(wo[e]||(wo[e]=[])).push({name:t,func:r})}}),wo[te(null,A).name]=[{name:"wrapper",func:null}],Ft.prototype.clone=function(){var n=this.__actions__,t=this.__iteratees__,r=this.__views__,e=new Ft(this.__wrapped__); +return e.__actions__=n?Bt(n):null,e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=t?Bt(t):null,e.__takeCount__=this.__takeCount__,e.__views__=r?Bt(r):null,e},Ft.prototype.reverse=function(){if(this.__filtered__){var n=new Ft(this);n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},Ft.prototype.value=function(){var n=this.__wrapped__.value();if(!si(n))return Rr(n,this.__actions__);var t,r=this.__dir__,e=0>r;t=n.length;for(var u=this.__views__,o=0,i=-1,f=u?u.length:0;++is.index:u=_:!h(p))))continue n}else if(s=h(p),_==$)p=s;else if(!s){if(_==N)continue n; +break n}}c[a++]=p}return c},Wt.prototype.chain=function(){return Ne(this)},Wt.prototype.commit=function(){return new Ut(this.value(),this.__chain__)},Wt.prototype.plant=function(n){for(var t,r=this;r instanceof Tt;){var e=Ee(r);t?u.__wrapped__=e:t=e;var u=e,r=r.__wrapped__}return u.__wrapped__=n,t},Wt.prototype.reverse=function(){var n=this.__wrapped__;return n instanceof Ft?(this.__actions__.length&&(n=new Ft(this)),new Ut(n.reverse(),this.__chain__)):this.thru(function(n){return n.reverse()})},Wt.prototype.toString=function(){return this.value()+"" +},Wt.prototype.run=Wt.prototype.toJSON=Wt.prototype.valueOf=Wt.prototype.value=function(){return Rr(this.__wrapped__,this.__actions__)},Wt.prototype.collect=Wt.prototype.map,Wt.prototype.head=Wt.prototype.first,Wt.prototype.select=Wt.prototype.filter,Wt.prototype.tail=Wt.prototype.rest,Wt}var w,b="3.6.0",x=1,A=2,j=4,k=8,E=16,I=32,O=64,R=128,C=256,S=30,W="...",T=150,U=16,F=0,N=1,$=2,L="Expected a function",B="__lodash_placeholder__",z="[object Arguments]",M="[object Array]",D="[object Boolean]",P="[object Date]",q="[object Error]",K="[object Function]",V="[object Number]",Y="[object Object]",Z="[object RegExp]",G="[object String]",J="[object ArrayBuffer]",X="[object Float32Array]",H="[object Float64Array]",Q="[object Int8Array]",nt="[object Int16Array]",tt="[object Int32Array]",rt="[object Uint8Array]",et="[object Uint8ClampedArray]",ut="[object Uint16Array]",ot="[object Uint32Array]",it=/\b__p\+='';/g,ft=/\b(__p\+=)''\+/g,at=/(__e\(.*?\)|\b__t\))\+'';/g,ct=/&(?:amp|lt|gt|quot|#39|#96);/g,lt=/[&<>"'`]/g,pt=RegExp(ct.source),st=RegExp(lt.source),ht=/<%-([\s\S]+?)%>/g,_t=/<%([\s\S]+?)%>/g,vt=/<%=([\s\S]+?)%>/g,gt=/[\u0300-\u036f\ufe20-\ufe23]/g,yt=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,dt=/\w*$/,mt=/^0[xX]/,wt=/^\[object .+?Constructor\]$/,bt=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,xt=/($^)/,At=/[.*+?^${}()|[\]\/\\]/g,jt=RegExp(At.source),kt=/['\n\r\u2028\u2029\\]/g,Et=RegExp("[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?=[A-Z\\xc0-\\xd6\\xd8-\\xde][a-z\\xdf-\\xf6\\xf8-\\xff]+)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+|[A-Z\\xc0-\\xd6\\xd8-\\xde]+|[0-9]+","g"),It=" \t\x0b\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",Ot="Array ArrayBuffer Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Math Number Object RegExp Set String _ clearTimeout document isFinite parseInt setTimeout TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap window".split(" "),Rt={}; +Rt[X]=Rt[H]=Rt[Q]=Rt[nt]=Rt[tt]=Rt[rt]=Rt[et]=Rt[ut]=Rt[ot]=true,Rt[z]=Rt[M]=Rt[J]=Rt[D]=Rt[P]=Rt[q]=Rt[K]=Rt["[object Map]"]=Rt[V]=Rt[Y]=Rt[Z]=Rt["[object Set]"]=Rt[G]=Rt["[object WeakMap]"]=false;var Ct={};Ct[z]=Ct[M]=Ct[J]=Ct[D]=Ct[P]=Ct[X]=Ct[H]=Ct[Q]=Ct[nt]=Ct[tt]=Ct[V]=Ct[Y]=Ct[Z]=Ct[G]=Ct[rt]=Ct[et]=Ct[ut]=Ct[ot]=true,Ct[q]=Ct[K]=Ct["[object Map]"]=Ct["[object Set]"]=Ct["[object WeakMap]"]=false;var St={leading:false,maxWait:0,trailing:false},Wt={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss"},Tt={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},Ut={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},Ft={"function":true,object:true},Nt={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},$t=Ft[typeof exports]&&exports&&!exports.nodeType&&exports,Lt=Ft[typeof module]&&module&&!module.nodeType&&module,Bt=Ft[typeof self]&&self&&self.Object&&self,Ft=Ft[typeof window]&&window&&window.Object&&window,zt=Lt&&Lt.exports===$t&&$t,Mt=$t&&Lt&&typeof global=="object"&&global||Ft!==(this&&this.window)&&Ft||Bt||this,Dt=m(); +typeof define=="function"&&typeof define.amd=="object"&&define.amd?(Mt._=Dt, define(function(){return Dt})):$t&&Lt?zt?(Lt.exports=Dt)._=Dt:$t._=Dt:Mt._=Dt}).call(this); diff --git a/assets/js/markdown.js b/assets/js/markdown.js deleted file mode 100644 index 884aef5..0000000 --- a/assets/js/markdown.js +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Visual Markdown Editor Field for Kirby 2 - * - * @version 1.1.0 - * @author Jonas Döbertin - * @copyright Jonas Döbertin - * @link https://github.com/JonasDoebertin/kirby-visual-markdown - * @license GNU GPL v3.0 - */ - -MarkdownField = (function($, $field) { - - var self = this; - - this.$field = $field; - this.$draggable = $('.sidebar').find('.draggable'); - this.$wrapper = $field.closest('.markdownfield-wrapper'); - this.$toolbar = null; - this.$editor = null; - - this.mirrormark = null; - this.codemirror = null; - - this.options = { - toolbar: $field.data('toolbar'), - header1: $field.data('header1'), - header2: $field.data('header2') - }; - - /** - * Initialize editor field - * - * @since 1.0.0 - */ - this.init = function() { - - /* - Initialize MirrorMark - */ - self.mirrormark = mirrorMark(self.$field.get(0), { - showToolbar: this.options.toolbar - }); - self.registerCustomTools(); - self.mirrormark.render(); - self.registerCustomKeymaps(); - - /* - Store some references to the underlying codemirror instance - our field instance and the toolbar and editor elements - */ - self.mirrormark.fieldInstance = self; - self.codemirror = self.mirrormark.cm; - self.$toolbar = self.$field.siblings('.mirrormark-toolbar'); - self.$editor = self.$field.siblings('.CodeMirror'); - - /* - Set up change event handler - */ - self.codemirror.on('change', self.updateStorage); - - /* - Set up focus and blur event handlers - */ - self.codemirror.on('focus', self.attachFocusStyles); - self.codemirror.on('blur', self.detachFocusStyles); - - /* - Set up fullscreen mode change event handler - */ - $(document).bind(screenfull.raw.fullscreenchange, self.changeFullscreenModeHandler); - - /** - * Make the editor field accept Kirby typical - * file/page drag and drop events. - * - * @since 1.0.0 - */ - self.$wrapper.droppable({ - hoverClass: 'markdownfield-wrapper-over', - accept: self.draggable, - drop: function(event, element) { - self.mirrormark.insert(element.draggable.data('text')); - } - }); - - /** - * Observe when the field element is destroyed (=the user leaves the - * current view) and deactivate MirrorMark accordingly. - * - * @since 1.0.0 - */ - self.$field.bind('destroyed', function() { - self.deactivate(); - }); - - }; - - /** - * Register custom actions and toolbar icons - * - * @since 1.0.0 - */ - this.registerCustomTools = function() { - - // Register custom (and overwritten) actions - self.mirrormark.registerActions({ - header1: function() { - var header = self.translateHeaderValue(this.fieldInstance.options.header1); - this.insertBefore(header + ' ', header.length + 1); - }, - header2: function() { - var header = self.translateHeaderValue(this.fieldInstance.options.header2); - this.insertBefore(header + ' ', header.length + 1); - }, - image: function() { - this.insertBefore('(image: filename.jpg)'); - }, - line: function() { - this.insert('****'); - }, - fullscreen: function() { - // TODO: Use this.fieldInstance instead of self - self.toggleFullscreenMode(this); - } - }); - - // Register toolbar icons - self.mirrormark.registerTools([ - { - name: self.options.header1, - action: 'header1', - className: 'markdownfield-icon-text markdownfield-icon-header1', - showName: true, - }, - { - name: self.options.header2, - action: 'header2', - className: 'markdownfield-icon-text markdownfield-icon-header1', - showName: true, - }, - { - name: "bold", - action: "bold", - className: "fa fa-bold" - }, - { - name: "italicize", - action: "italicize", - className: "fa fa-italic" - }, - { - name: "blockquote", - action: "blockquote", - className: "fa fa-quote-left" - }, - { - name: "link", - action: "link", - className: "fa fa-link" - }, - { - name: "image", - action: "image", - className: "fa fa-image" - }, - { - name: "unorderedList", - action: "unorderedList", - className: "fa fa-list" - }, - { - name: "orderedList", - action: "orderedList", - className: "fa fa-list-ol" - }, - { - name: 'line', - action: 'line', - className: 'fa fa-minus' - }, - { - name: "fullScreen", - action: "fullscreen", - className: "fa fa-expand" - } - ], true); - }; - - /** - * Register custom keymaps - * - * @since 1.1.0 - */ - this.registerCustomKeymaps = function() { - self.mirrormark.registerKeyMaps({ - "Cmd-H": 'header1', - "Cmd-Alt-H": 'header2', - "Cmd-B": 'bold', - "Cmd-I": 'italicize', - "Cmd-'": 'blockquote', - "Cmd-Alt-L": 'orderedList', - "Cmd-L": 'unorderedList', - "Cmd-Alt-I": 'image', - "Cmd-A": 'link' - }); - }; - - /** - * Update storage input element - * - * @since 1.0.0 - */ - this.updateStorage = function(instance, change) { - self.$field.text(self.codemirror.getValue()); - }; - - /** - * Deactivate plugin instance - * - * @since 1.0.0 - */ - this.deactivate = function(e) { - self.updateStorage(); - self.codemirror.toTextArea(); - }; - - /** - * Handle fullscreen mode change event - * - * @since 1.0.1 - */ - this.changeFullscreenModeHandler = function() { - // Remove indicator class if fullscreen mode was exited other then - // by clicking or custom fullscreen mode icon - if(!screenfull.isFullscreen) { - self.$wrapper.removeClass('markdownfield-wrapper-fullscreen'); - } - }; - - /** - * Handle a click on the toggle fullscreen mode icon - * - * @since 1.0.1 - */ - this.toggleFullscreenMode = function(instance) { - - var wrapper; - - // Abort if fullscreen mode isn't supported - if(!screenfull.enabled) { - return; - } - - // find related wrapper element - wrapper = jQuery(instance.cm.getWrapperElement()).closest('.markdownfield-wrapper'); - - // Enable fullscreen mode - if(!screenfull.isFullscreen) { - self.attachFullscreenStyles(wrapper); - screenfull.request(wrapper.get(0)); - - // Disable fullscreen mode - } else { - screenfull.exit(); - self.detachFullscreenStyles(wrapper); - } - }; - - /** - * Add focus style classes to the editor wrapper - * - * @since 1.0.0 - */ - this.attachFocusStyles = function(instance) { - self.$wrapper.addClass('markdownfield-wrapper-focused'); - }; - - /** - * Remove focus style classes from the editor wrapper - * - * @since 1.0.0 - */ - this.detachFocusStyles = function(instance) { - self.$wrapper.removeClass('markdownfield-wrapper-focused'); - }; - - /** - * Add fullscreen style classes to the editor wrapper - * - * @since 1.0.1 - */ - this.attachFullscreenStyles = function(wrapper) { - wrapper.addClass('markdownfield-wrapper-fullscreen'); - }; - - /** - * Remove fullscreen style classes from the editor wrapper - * - * @since 1.0.1 - */ - this.detachFullscreenStyles = function(wrapper) { - wrapper.removeClass('markdownfield-wrapper-fullscreen'); - }; - - /** - * Translate a header value string (h1 to h6) into it's - * markdown representation. - * - * @since 1.1.0 - */ - this.translateHeaderValue = function(value) { - switch(value) { - case 'h6': - return '######'; - case 'h5': - return '#####'; - case 'h4': - return '####'; - case 'h3': - return '###'; - case 'h2': - return '##'; - case 'h1': - default: - return '#'; - } - }; - - /** - * Run initialization - */ - return this.init(); - -}); - -(function($) { - - /** - * Set up special "destroyed" event. - * - * @since 1.0.0 - */ - $.event.special.destroyed = { - remove: function(event) { - if(event.handler) { - event.handler.apply(this, arguments); - } - } - }; - - /** - * Tell the Panel to run our initialization. - * - * This callback will fire for every WYSIWYG Editor - * Field on the current panel page. - * - * @see https://github.com/getkirby/panel/issues/228#issuecomment-58379016 - * @since 1.0.0 - */ - $.fn.markdownfield = function() { - return new MarkdownField($, this); - }; - -})(jQuery); diff --git a/assets/js/mirrormark.js b/assets/js/mirrormark.js new file mode 100644 index 0000000..1bd34c2 --- /dev/null +++ b/assets/js/mirrormark.js @@ -0,0 +1,324 @@ +/** + * @license MirrorMark v0.1 + * (c) 2015 Musicbed http://www.musicbed.com + * License: MIT + */ +(function(CodeMirror) { 'use strict'; + /** + * Bootstrap our module + */ + (function(fn) { + if (typeof exports == "object" && typeof module == "object") { // CommonJS + module.exports = fn; + } else if (typeof define == "function" && define.amd) { // AMD + return define([], fn); + } + + if (window) + window.mirrorMark = fn + })(mirrorMark); + + /** + * Our delegate prototype used by our factory + * @type {Object} + */ + var mirrorMarkProto = { + + /** + * Render the component + */ + render: function render() { + this.registerKeyMaps(this.keyMaps); + this.cm = CodeMirror.fromTextArea(this.element, this.options); + + if (this.options.showToolbar) { + this.setToolbar(this.tools); + } + }, + + /** + * Setup the toolbar + */ + setToolbar: function setToolbar(tools) { + + var toolbar = document.createElement('ul'); + toolbar.className = this.options.theme + '-' + 'toolbar'; + + var tools = this.generateToolList(tools); + + tools.forEach(function(tool) { + toolbar.appendChild(tool) + }); + + var cmWrapper = this.cm.getWrapperElement(); + cmWrapper.parentNode.insertBefore(toolbar, cmWrapper); + + }, + + /** + * Register Keymaps by extending the extraKeys object + * @param {Object} keyMaps + */ + registerKeyMaps: function registerKeyMaps(keyMaps) { + for (var name in keyMaps) { + if (typeof(this.actions[keyMaps[name]]) !== 'function') throw "MirrorMark - '" + keyMaps[name] + "' is not a registered action"; + + var obj = {}; + obj[name] = this.actions[keyMaps[name]].bind(this); + _.assign(this.options.extraKeys, obj); + } + }, + + + /** + * Register actions by extending the default actions + * @param {Object} actions [description] + */ + registerActions: function registerActions(actions) { + return _.assign(this.actions, actions); + }, + + + /** + * Register tools by extending and overwriting the default tools + * @param {Array} tools + * @param {Bool} replace - replace the default tools with the ones provided. Defaults to false. + */ + registerTools: function registerTools(tools, replace) { + for (var action in tools) { + if (this.actions[tools[action].action] && typeof(this.actions[tools[action].action]) !== 'function') throw "MirrorMark - '" + tools[action].action + "' is not a registered action"; + } + + if (replace) { + this.tools = tools; + return; + } + + this.tools = this.tools.concat(tools) + }, + + /** + * A recursive function to generate and return an unordered list of tools + * @param {Object} + */ + generateToolList: function generateToolList(tools) { + return tools.map(function(tool) { + var item = document.createElement("li"), + anchor = document.createElement("a"); + + item.className = tool.name; + + if (tool.className) { + anchor.className = tool.className; + } + + if (tool.showName) { + var text = document.createTextNode(tool.name); + anchor.appendChild(text); + } + + if (tool.action) { + anchor.onclick = function(e) { + this.cm.focus(); + this.actions[tool.action].call(this); + }.bind(this); + } + + item.appendChild(anchor); + + if (tool.nested) { + item.className += " has-nested"; + var ul = document.createElement('ul'); + ul.className = this.options.theme + "-toolbar-list" + var nested = generateToolList.call(this, tool.nested); + nested.forEach(function(nestedItem) { + ul.appendChild(nestedItem); + }); + + item.appendChild(ul); + } + + return item + + }.bind(this)); + }, + + /** + * Default Tools in Toolbar + * @todo - update so it's not so tightly coupled with Font Awesome. + */ + tools: [ + { name: "bold", action: "bold", className: "fa fa-bold" }, + { name: "italicize", action: "italicize", className: "fa fa-italic" }, + { name: "blockquote", action: "blockquote", className: "fa fa-quote-left" }, + { name: "link", action: "link", className: "fa fa-link" }, + { name: "image", action: "image", className: "fa fa-image" }, + { name: "unorderedList", action: "unorderedList", className: "fa fa-list" }, + { name: "orderedList", action: "orderedList", className: "fa fa-list-ol" }, + { name: "fullScreen", action: "fullScreen", className: "fa fa-expand" }, + ], + + /** + * Default Keymaps + * @type {Object} + */ + keyMaps: { + "Cmd-B": 'bold', + "Cmd-I": 'italicize', + "Cmd-'": 'blockquote', + "Cmd-Alt-L": 'orderedList', + "Cmd-L": 'unorderedList', + "Cmd-Alt-I": 'image', + "Cmd-H": 'hr', + "Cmd-K": 'link' + }, + + /** + * Default Actions + * @type {Object} + */ + actions: { + bold: function () { + this.insertAround('**', '**') + }, + italicize: function () { + this.insertAround('*', '*') + }, + "code": function () { + this.insertAround('```\r\n', '\r\n```') + }, + "blockquote": function () { + this.insertBefore('> ', 2); + }, + "orderedList": function () { + this.insertBefore('1. ', 3); + }, + "unorderedList": function () { + this.insertBefore('* ', 2); + }, + "image": function () { + this.insertBefore('![](http://)', 2); + }, + "link": function () { + this.insertAround('[', '](http://)'); + }, + "hr": function () { + this.insert('---'); + }, + "fullScreen": function () { + var el = this.cm.getWrapperElement(); + + // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode + var doc = document; + var isFull = doc.fullScreen || doc.mozFullScreen || doc.webkitFullScreen; + var request = function() { + if (el.requestFullscreen) { + el.requestFullscreen(); + } else if (el.webkitRequestFullscreen) { + el.webkitRequestFullscreen(); + } else if (el.mozRequestFullScreen) { + el.mozRequestFullScreen(); + } else if (el.msRequestFullscreen) { + el.msRequestFullscreen(); + } + } + var cancel = function() { + if (doc.cancelFullScreen) { + doc.cancelFullScreen(); + } else if (doc.mozCancelFullScreen) { + doc.mozCancelFullScreen(); + } else if (doc.webkitCancelFullScreen) { + doc.webkitCancelFullScreen(); + } + }; + if (!isFull) { + request(); + } else if (cancel) { + cancel(); + } + } + }, + + /** + * Insert a string at cursor position + * @param {String} insertion + */ + insert: function insert(insertion) { + var doc = this.cm.getDoc(); + var cursor = doc.getCursor(); + + doc.replaceRange(insertion, { line: cursor.line, ch: cursor.ch }); + }, + + /** + * Insert a string at the start and end of a selection + * @param {String} start + * @param {String} end + */ + insertAround: function insertAround(start, end) { + var doc = this.cm.getDoc(); + var cursor = doc.getCursor(); + + if (doc.somethingSelected()) { + var selection = doc.getSelection(); + doc.replaceSelection(start + selection + end); + } else { + // If no selection then insert start and end args and set cursor position between the two. + doc.replaceRange(start + end, { line: cursor.line, ch: cursor.ch }); + doc.setCursor({ line: cursor.line, ch: cursor.ch + start.length }) + } + }, + + /** + * Insert a string before a selection + * @param {String} insertion + */ + insertBefore: function insertBefore(insertion, cursorOffset) { + var doc = this.cm.getDoc(); + var cursor = doc.getCursor(); + + if (doc.somethingSelected()) { + var selections = doc.listSelections(); + selections.forEach(function(selection) { + var pos = [selection.head.line, selection.anchor.line].sort(); + + for (var i = pos[0]; i <= pos[1]; i++) { + doc.replaceRange(insertion, { line: i, ch: 0 }); + } + + doc.setCursor({ line: pos[0], ch: cursorOffset || 0 }); + }); + } else { + doc.replaceRange(insertion, { line: cursor.line, ch: 0 }); + doc.setCursor({ line: cursor.line, ch: cursorOffset || 0 }) + } + } + } + + /** + * Our Factory + * @param {Object} element + * @param {Object} options + * @return {Object} + */ + function mirrorMark(element, options) { + + // Defaults + var defaults = { + theme: 'visualmarkdown', + tabSize: '2', + indentWithTabs: true, + lineWrapping: true, + extraKeys: { + "Enter": 'newlineAndIndentContinueMarkdownList', + }, + mode: 'markdown' + } + + // Extend our defaults with the options provided + _.assign(defaults, options); + + return _.assign(Object.create(mirrorMarkProto), { element: element, options: defaults }); + } + +})(window.CodeMirror); diff --git a/assets/js/mirrormark.package.min.js b/assets/js/mirrormark.package.min.js deleted file mode 100644 index 0bc413a..0000000 --- a/assets/js/mirrormark.package.min.js +++ /dev/null @@ -1,7 +0,0 @@ -!function(t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else{if("function"==typeof define&&define.amd)return define([],t);this.CodeMirror=t()}}(function(){"use strict";function t(n,r){if(!(this instanceof t))return new t(n,r);this.options=r=r?Oo(r):{},Oo(Gl,r,!1),d(r);var i=r.value;"string"==typeof i&&(i=new pa(i,r.mode)),this.doc=i;var o=new t.inputStyles[r.inputStyle](this),l=this.display=new e(n,i,o);l.wrapper.CodeMirror=this,u(this),a(this),r.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),r.autofocus&&!wl&&l.input.focus(),m(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,focused:!1,suppressEdits:!1,pasteIncoming:!1,cutIncoming:!1,draggingText:!1,highlight:new So,keySeq:null};var s=this;cl&&11>fl&&setTimeout(function(){s.display.input.reset(!0)},20),Pn(this),Uo(),yn(this),this.curOp.forceUpdate=!0,Ui(this,i),r.autofocus&&!wl||s.hasFocus()?setTimeout(Wo(cr,this),20):fr(this);for(var c in ql)ql.hasOwnProperty(c)&&ql[c](this,r[c],Kl);C(this),r.finishInit&&r.finishInit(this);for(var f=0;ffl&&(r.gutters.style.zIndex=-1,r.scroller.style.paddingRight=0),hl||al&&wl||(r.scroller.draggable=!0),t&&(t.appendChild?t.appendChild(r.wrapper):t(r.wrapper)),r.viewFrom=r.viewTo=e.first,r.reportedViewFrom=r.reportedViewTo=e.first,r.view=[],r.renderedView=null,r.externalMeasured=null,r.viewOffset=0,r.lastWrapHeight=r.lastWrapWidth=0,r.updateLineNumbers=null,r.nativeBarWidth=r.barHeight=r.barWidth=0,r.scrollbarsClipped=!1,r.lineNumWidth=r.lineNumInnerWidth=r.lineNumChars=null,r.alignWidgets=!1,r.cachedCharWidth=r.cachedTextHeight=r.cachedPaddingH=null,r.maxLine=null,r.maxLineLength=0,r.maxLineChanged=!1,r.wheelDX=r.wheelDY=r.wheelStartX=r.wheelStartY=null,r.shift=!1,r.selForContextMenu=null,r.activeTouch=null,n.init(r)}function n(e){e.doc.mode=t.getMode(e.options,e.doc.modeOption),r(e)}function r(t){t.doc.iter(function(t){t.stateAfter&&(t.stateAfter=null),t.styles&&(t.styles=null)}),t.doc.frontier=t.doc.first,Fe(t,100),t.state.modeGen++,t.curOp&&Dn(t)}function i(t){t.options.lineWrapping?(Pa(t.display.wrapper,"CodeMirror-wrap"),t.display.sizer.style.minWidth="",t.display.sizerWidth=null):(za(t.display.wrapper,"CodeMirror-wrap"),h(t)),l(t),Dn(t),on(t),setTimeout(function(){y(t)},100)}function o(t){var e=vn(t.display),n=t.options.lineWrapping,r=n&&Math.max(5,t.display.scroller.clientWidth/mn(t.display)-3);return function(i){if(gi(t.doc,i))return 0;var o=0;if(i.widgets)for(var l=0;le.maxLineLength&&(e.maxLineLength=n,e.maxLine=t)})}function d(t){var e=To(t.gutters,"CodeMirror-linenumbers");-1==e&&t.lineNumbers?t.gutters=t.gutters.concat(["CodeMirror-linenumbers"]):e>-1&&!t.lineNumbers&&(t.gutters=t.gutters.slice(0),t.gutters.splice(e,1))}function p(t){var e=t.display,n=e.gutters.offsetWidth,r=Math.round(t.doc.height+Be(t.display));return{clientHeight:e.scroller.clientHeight,viewHeight:e.wrapper.clientHeight,scrollWidth:e.scroller.scrollWidth,clientWidth:e.scroller.clientWidth,viewWidth:e.wrapper.clientWidth,barLeft:t.options.fixedGutter?n:0,docHeight:r,scrollHeight:r+Ue(t)+e.barHeight,nativeBarWidth:e.nativeBarWidth,gutterWidth:n}}function g(t,e,n){this.cm=n;var r=this.vert=Fo("div",[Fo("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),i=this.horiz=Fo("div",[Fo("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");t(r),t(i),wa(r,"scroll",function(){r.clientHeight&&e(r.scrollTop,"vertical")}),wa(i,"scroll",function(){i.clientWidth&&e(i.scrollLeft,"horizontal")}),this.checkedOverlay=!1,cl&&8>fl&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")}function v(){}function m(e){e.display.scrollbars&&(e.display.scrollbars.clear(),e.display.scrollbars.addClass&&za(e.display.wrapper,e.display.scrollbars.addClass)),e.display.scrollbars=new t.scrollbarModel[e.options.scrollbarStyle](function(t){e.display.wrapper.insertBefore(t,e.display.scrollbarFiller),wa(t,"mousedown",function(){e.state.focused&&setTimeout(function(){e.display.input.focus()},0)}),t.setAttribute("cm-not-content","true")},function(t,n){"horizontal"==n?Qn(e,t):Jn(e,t)},e),e.display.scrollbars.addClass&&Pa(e.display.wrapper,e.display.scrollbars.addClass)}function y(t,e){e||(e=p(t));var n=t.display.barWidth,r=t.display.barHeight;b(t,e);for(var i=0;4>i&&n!=t.display.barWidth||r!=t.display.barHeight;i++)n!=t.display.barWidth&&t.options.lineWrapping&&O(t),b(t,p(t)),n=t.display.barWidth,r=t.display.barHeight}function b(t,e){var n=t.display,r=n.scrollbars.update(e);n.sizer.style.paddingRight=(n.barWidth=r.right)+"px",n.sizer.style.paddingBottom=(n.barHeight=r.bottom)+"px",r.right&&r.bottom?(n.scrollbarFiller.style.display="block",n.scrollbarFiller.style.height=r.bottom+"px",n.scrollbarFiller.style.width=r.right+"px"):n.scrollbarFiller.style.display="",r.bottom&&t.options.coverGutterNextToScrollbar&&t.options.fixedGutter?(n.gutterFiller.style.display="block",n.gutterFiller.style.height=r.bottom+"px",n.gutterFiller.style.width=e.gutterWidth+"px"):n.gutterFiller.style.display=""}function w(t,e,n){var r=n&&null!=n.top?Math.max(0,n.top):t.scroller.scrollTop;r=Math.floor(r-Pe(t));var i=n&&null!=n.bottom?n.bottom:r+t.wrapper.clientHeight,o=Xi(e,r),l=Xi(e,i);if(n&&n.ensure){var a=n.ensure.from.line,s=n.ensure.to.line;o>a?(o=a,l=Xi(e,Yi(Gi(e,a))+t.wrapper.clientHeight)):Math.min(s,e.lastLine())>=l&&(o=Xi(e,Yi(Gi(e,s))-t.wrapper.clientHeight),l=s)}return{from:o,to:Math.max(l,o+1)}}function x(t){var e=t.display,n=e.view;if(e.alignWidgets||e.gutters.firstChild&&t.options.fixedGutter){for(var r=k(e)-e.scroller.scrollLeft+t.doc.scrollLeft,i=e.gutters.offsetWidth,o=r+"px",l=0;l=n.viewFrom&&e.visible.to<=n.viewTo&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo)&&n.renderedView==n.view&&0==zn(t))return!1;C(t)&&(En(t),e.dims=D(t));var i=r.first+r.size,o=Math.max(e.visible.from-t.options.viewportMargin,r.first),l=Math.min(i,e.visible.to+t.options.viewportMargin);n.viewFroml&&n.viewTo-l<20&&(l=Math.min(i,n.viewTo)),Tl&&(o=di(t.doc,o),l=pi(t.doc,l));var a=o!=n.viewFrom||l!=n.viewTo||n.lastWrapHeight!=e.wrapperHeight||n.lastWrapWidth!=e.wrapperWidth;Rn(t,o,l),n.viewOffset=Yi(Gi(t.doc,n.viewFrom)),t.display.mover.style.top=n.viewOffset+"px";var s=zn(t);if(!a&&0==s&&!e.force&&n.renderedView==n.view&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo))return!1;var u=zo();return s>4&&(n.lineDiv.style.display="none"),H(t,n.updateLineNumbers,e.dims),s>4&&(n.lineDiv.style.display=""),n.renderedView=n.view,u&&zo()!=u&&u.offsetHeight&&u.focus(),Io(n.cursorDiv),Io(n.selectionDiv),n.gutters.style.height=0,a&&(n.lastWrapHeight=e.wrapperHeight,n.lastWrapWidth=e.wrapperWidth,Fe(t,400)),n.updateLineNumbers=null,!0}function M(t,e){for(var n=e.force,r=e.viewport,i=!0;;i=!1){if(i&&t.options.lineWrapping&&e.oldDisplayWidth!=Ge(t))n=!0;else if(n=!1,r&&null!=r.top&&(r={top:Math.min(t.doc.height+Be(t.display)-qe(t),r.top)}),e.visible=w(t.display,t.doc,r),e.visible.from>=t.display.viewFrom&&e.visible.to<=t.display.viewTo)break;if(!T(t,e))break;O(t);var o=p(t);Oe(t),N(t,o),y(t,o)}e.signal(t,"update",t),(t.display.viewFrom!=t.display.reportedViewFrom||t.display.viewTo!=t.display.reportedViewTo)&&(e.signal(t,"viewportChange",t,t.display.viewFrom,t.display.viewTo),t.display.reportedViewFrom=t.display.viewFrom,t.display.reportedViewTo=t.display.viewTo)}function A(t,e){var n=new L(t,e);if(T(t,n)){O(t),M(t,n);var r=p(t);Oe(t),N(t,r),y(t,r),n.finish()}}function N(t,e){t.display.sizer.style.minHeight=e.docHeight+"px";var n=e.docHeight+t.display.barHeight;t.display.heightForcer.style.top=n+"px",t.display.gutters.style.height=Math.max(n+Ue(t),e.clientHeight)+"px"}function O(t){for(var e=t.display,n=e.lineDiv.offsetTop,r=0;rfl){var l=o.node.offsetTop+o.node.offsetHeight;i=l-n,n=l}else{var a=o.node.getBoundingClientRect();i=a.bottom-a.top}var s=o.line.height-i;if(2>i&&(i=vn(e)),(s>.001||-.001>s)&&(Vi(o.line,i),W(o.line),o.rest))for(var u=0;u=e&&f.lineNumber;f.changes&&(To(f.changes,"gutter")>-1&&(h=!1),E(t,f,u,n)),h&&(Io(f.lineNumber),f.lineNumber.appendChild(document.createTextNode(S(t.options,u)))),a=f.node.nextSibling}else{var d=U(t,f,u,n);l.insertBefore(d,a)}u+=f.size}for(;a;)a=r(a)}function E(t,e,n,r){for(var i=0;ifl&&(t.node.style.zIndex=2)),t.node}function I(t){var e=t.bgClass?t.bgClass+" "+(t.line.bgClass||""):t.line.bgClass;if(e&&(e+=" CodeMirror-linebackground"),t.background)e?t.background.className=e:(t.background.parentNode.removeChild(t.background),t.background=null);else if(e){var n=F(t);t.background=n.insertBefore(Fo("div",null,e),n.firstChild)}}function R(t,e){var n=t.display.externalMeasured;return n&&n.line==e.line?(t.display.externalMeasured=null,e.measure=n.measure,n.built):Oi(t,e)}function z(t,e){var n=e.text.className,r=R(t,e);e.text==e.node&&(e.node=r.pre),e.text.parentNode.replaceChild(r.pre,e.text),e.text=r.pre,r.bgClass!=e.bgClass||r.textClass!=e.textClass?(e.bgClass=r.bgClass,e.textClass=r.textClass,P(e)):n&&(e.text.className=n)}function P(t){I(t),t.line.wrapClass?F(t).className=t.line.wrapClass:t.node!=t.text&&(t.node.className="");var e=t.textClass?t.textClass+" "+(t.line.textClass||""):t.line.textClass;t.text.className=e||""}function B(t,e,n,r){e.gutter&&(e.node.removeChild(e.gutter),e.gutter=null);var i=e.line.gutterMarkers;if(t.options.lineNumbers||i){var o=F(e),l=e.gutter=Fo("div",null,"CodeMirror-gutter-wrapper","left: "+(t.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px; width: "+r.gutterTotalWidth+"px");if(t.display.input.setUneditable(l),o.insertBefore(l,e.text),e.line.gutterClass&&(l.className+=" "+e.line.gutterClass),!t.options.lineNumbers||i&&i["CodeMirror-linenumbers"]||(e.lineNumber=l.appendChild(Fo("div",S(t.options,n),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+r.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+t.display.lineNumInnerWidth+"px"))),i)for(var a=0;a1&&(Nl&&Nl.join("\n")==e?l=r.ranges.length%Nl.length==0&&Mo(Nl,Ua):o.length==r.ranges.length&&(l=Mo(o,function(t){return[t]})));for(var a=r.ranges.length-1;a>=0;a--){var s=r.ranges[a],u=s.from(),c=s.to();s.empty()&&(n&&n>0?u=Ml(u.line,u.ch-n):t.state.overwrite&&!t.state.pasteIncoming&&(c=Ml(c.line,Math.min(Gi(i,c.line).text.length,c.ch+_o(o).length))));var f=t.curOp.updateInput,h={from:u,to:c,text:l?l[a%l.length]:o,origin:t.state.pasteIncoming?"paste":t.state.cutIncoming?"cut":"+input"};if(br(t.doc,h),mo(t,"inputRead",t,h),e&&!t.state.pasteIncoming&&t.options.electricChars&&t.options.smartIndent&&s.head.ch<100&&(!a||r.ranges[a-1].head.line!=s.head.line)){var d=t.getModeAt(s.head),p=Ul(h);if(d.electricChars){for(var g=0;g-1){Dr(t,p.line,"smart");break}}else d.electricInput&&d.electricInput.test(Gi(i,p.line).text.slice(0,p.ch))&&Dr(t,p.line,"smart")}}Or(t),t.curOp.updateInput=f,t.curOp.typing=!0,t.state.pasteIncoming=t.state.cutIncoming=!1}function Q(t){for(var e=[],n=[],r=0;ri?u.map:c[i],l=0;li?t.line:t.rest[i]),f=o[l]+r;return(0>r||a!=e)&&(f=o[l+(r?1:0)]),Ml(s,f)}}}var i=t.text.firstChild,o=!1;if(!e||!Fa(i,e))return oe(Ml($i(t.line),0),!0);if(e==i&&(o=!0,e=i.childNodes[n],n=0,!e)){var l=t.rest?_o(t.rest):t.line;return oe(Ml($i(l),l.text.length),o)}var a=3==e.nodeType?e:null,s=e;for(a||1!=e.childNodes.length||3!=e.firstChild.nodeType||(a=e.firstChild,n&&(n=a.nodeValue.length));s.parentNode!=i;)s=s.parentNode;var u=t.measure,c=u.maps,f=r(a,s,n);if(f)return oe(f,o);for(var h=s.nextSibling,d=a?a.nodeValue.length-n:0;h;h=h.nextSibling){if(f=r(h,h.firstChild,0))return oe(Ml(f.line,f.ch-d),o);d+=h.textContent.length}for(var p=s.previousSibling,d=n;p;p=p.previousSibling){if(f=r(p,p.firstChild,-1))return oe(Ml(f.line,f.ch+d),o);d+=h.textContent.length}}function se(t,e,n,r,i){function o(t){return function(e){return e.id==t}}function l(e){if(1==e.nodeType){var n=e.getAttribute("cm-text");if(null!=n)return""==n&&(n=e.textContent.replace(/\u200b/g,"")),void(a+=n);var u,c=e.getAttribute("cm-marker");if(c){var f=t.findMarks(Ml(r,0),Ml(i+1,0),o(+c));return void(f.length&&(u=f[0].find())&&(a+=qi(t.doc,u.from,u.to).join("\n")))}if("false"==e.getAttribute("contenteditable"))return;for(var h=0;h=0){var l=X(o.from(),i.from()),a=$(o.to(),i.to()),s=o.empty()?i.from()==i.head:o.from()==o.head;e>=r&&--e,t.splice(--r,2,new ce(s?a:l,s?l:a))}}return new ue(t,e)}function he(t,e){return new ue([new ce(t,e||t)],0)}function de(t,e){return Math.max(t.first,Math.min(e,t.first+t.size-1))}function pe(t,e){if(e.linen?Ml(n,Gi(t,n).text.length):ge(e,Gi(t,e.line).text.length)}function ge(t,e){var n=t.ch;return null==n||n>e?Ml(t.line,e):0>n?Ml(t.line,0):t}function ve(t,e){return e>=t.first&&e=o.ch:u.to>o.ch))){if(r&&(Ca(c,"beforeCursorEnter"),c.explicitlyCleared)){if(a.markedSpans){--s;continue}break}if(!c.atomic)continue;var f=c.find(0>l?-1:1);if(0==Al(f,o)&&(f.ch+=l,f.ch<0?f=f.line>t.first?pe(t,Ml(f.line-1)):null:f.ch>a.text.length&&(f=f.linee&&(e=0),e=Math.round(e),r=Math.round(r),a.appendChild(Fo("div",null,"CodeMirror-selected","position: absolute; left: "+t+"px; top: "+e+"px; width: "+(null==n?c-t:n)+"px; height: "+(r-e)+"px"))}function i(e,n,i){function o(n,r){return cn(t,Ml(e,n),"div",f,r)}var a,s,f=Gi(l,e),h=f.text.length;return $o(Zi(f),n||0,null==i?h:i,function(t,e,l){var f,d,p,g=o(t,"left");if(t==e)f=g,d=p=g.left;else{if(f=o(e-1,"right"),"rtl"==l){var v=g;g=f,f=v}d=g.left,p=f.right}null==n&&0==t&&(d=u),f.top-g.top>3&&(r(d,g.top,null,g.bottom),d=u,g.bottoms.bottom||f.bottom==s.bottom&&f.right>s.right)&&(s=f),u+1>d&&(d=u),r(d,f.top,p-d,f.bottom)}),{start:a,end:s}}var o=t.display,l=t.doc,a=document.createDocumentFragment(),s=je(t.display),u=s.left,c=Math.max(o.sizerWidth,Ge(t)-o.sizer.offsetLeft)-s.right,f=e.from(),h=e.to();if(f.line==h.line)i(f.line,f.ch,h.ch);else{var d=Gi(l,f.line),p=Gi(l,h.line),g=fi(d)==fi(p),v=i(f.line,f.ch,g?d.text.length+1:null).end,m=i(h.line,g?0:null,h.ch).start;g&&(v.top0?e.blinker=setInterval(function(){e.cursorDiv.style.visibility=(n=!n)?"":"hidden"},t.options.cursorBlinkRate):t.options.cursorBlinkRate<0&&(e.cursorDiv.style.visibility="hidden")}}function Fe(t,e){t.doc.mode.startState&&t.doc.frontier=t.display.viewTo)){var n=+new Date+t.options.workTime,r=Jl(e.mode,ze(t,e.frontier)),i=[];e.iter(e.frontier,Math.min(e.first+e.size,t.display.viewTo+500),function(o){if(e.frontier>=t.display.viewFrom){var l=o.styles,a=Ti(t,o,r,!0);o.styles=a.styles;var s=o.styleClasses,u=a.classes;u?o.styleClasses=u:s&&(o.styleClasses=null);for(var c=!l||l.length!=o.styles.length||s!=u&&(!s||!u||s.bgClass!=u.bgClass||s.textClass!=u.textClass),f=0;!c&&fn?(Fe(t,t.options.workDelay),!0):void 0}),i.length&&Tn(t,function(){for(var e=0;el;--a){if(a<=o.first)return o.first;var s=Gi(o,a-1);if(s.stateAfter&&(!n||a<=o.frontier))return a;var u=Aa(s.text,null,t.options.tabSize);(null==i||r>u)&&(i=a-1,r=u)}return i}function ze(t,e,n){var r=t.doc,i=t.display;if(!r.mode.startState)return!0;var o=Re(t,e,n),l=o>r.first&&Gi(r,o-1).stateAfter;return l=l?Jl(r.mode,l):Ql(r.mode),r.iter(o,e,function(n){Ai(t,n.text,l);var a=o==e-1||o%5==0||o>=i.viewFrom&&o2&&o.push((s.bottom+u.top)/2-n.top)}}o.push(n.bottom-n.top)}}function Ve(t,e,n){if(t.line==e)return{map:t.measure.map,cache:t.measure.cache};for(var r=0;rn)return{map:t.measure.maps[r],cache:t.measure.caches[r],before:!0}}function $e(t,e){e=fi(e);var n=$i(e),r=t.display.externalMeasured=new On(t.doc,e,n);r.lineN=n;var i=r.built=Oi(t,r);return r.text=i.pre,Ro(t.display.lineMeasure,i.pre),r}function Xe(t,e,n,r){return Je(t,Ze(t,e),n,r)}function Ye(t,e){if(e>=t.display.viewFrom&&e=n.lineN&&ee?(i=0,o=1,l="left"):u>e?(i=e-s,o=i+1):(a==t.length-3||e==u&&t[a+3]>e)&&(o=u-s,i=o-1,e>=u&&(l="right")),null!=i){if(r=t[a+2],s==u&&n==(r.insertLeft?"left":"right")&&(l=n),"left"==n&&0==i)for(;a&&t[a-2]==t[a-3]&&t[a-1].insertLeft;)r=t[(a-=3)+2],l="left";if("right"==n&&i==u-s)for(;ac;c++){for(;a&&Eo(e.line.text.charAt(o.coverStart+a));)--a;for(;o.coverStart+sfl&&0==a&&s==o.coverEnd-o.coverStart)i=l.parentNode.getBoundingClientRect();else if(cl&&t.options.lineWrapping){var f=Wa(l,a,s).getClientRects();i=f.length?f["right"==r?f.length-1:0]:Hl}else i=Wa(l,a,s).getBoundingClientRect()||Hl;if(i.left||i.right||0==a)break;s=a,a-=1,u="right"}cl&&11>fl&&(i=en(t.display.measure,i))}else{a>0&&(u=r="right");var f;i=t.options.lineWrapping&&(f=l.getClientRects()).length>1?f["right"==r?f.length-1:0]:l.getBoundingClientRect()}if(cl&&9>fl&&!a&&(!i||!i.left&&!i.right)){var h=l.parentNode.getClientRects()[0];i=h?{left:h.left,right:h.left+mn(t.display),top:h.top,bottom:h.bottom}:Hl}for(var d=i.top-e.rect.top,p=i.bottom-e.rect.top,g=(d+p)/2,v=e.view.measure.heights,c=0;cn.from?l(t-1):l(t,r)}r=r||Gi(t.doc,e.line),i||(i=Ze(t,r));var s=Zi(r),u=e.ch;if(!s)return l(u);var c=rl(s,u),f=a(u,c);return null!=$a&&(f.other=a(u,$a)),f}function hn(t,e){var n=0,e=pe(t.doc,e);t.options.lineWrapping||(n=mn(t.display)*e.ch);var r=Gi(t.doc,e.line),i=Yi(r)+Pe(t.display);return{left:n,right:n,top:i,bottom:i+r.height}}function dn(t,e,n,r){var i=Ml(t,e);return i.xRel=r,n&&(i.outside=!0),i}function pn(t,e,n){var r=t.doc;if(n+=t.display.viewOffset,0>n)return dn(r.first,0,!0,-1);var i=Xi(r,n),o=r.first+r.size-1;if(i>o)return dn(r.first+r.size-1,Gi(r,o).text.length,!0,1);0>e&&(e=0);for(var l=Gi(r,i);;){var a=gn(t,l,i,e,n),s=ui(l),u=s&&s.find(0,!0);if(!s||!(a.ch>u.from.ch||a.ch==u.from.ch&&a.xRel>0))return a;i=$i(l=u.to.line)}}function gn(t,e,n,r,i){function o(r){var i=fn(t,Ml(n,r),"line",e,u);return a=!0,l>i.bottom?i.left-s:lv)return dn(n,d,m,1);for(;;){if(c?d==h||d==ol(e,h,1):1>=d-h){for(var y=p>r||v-r>=r-p?h:d,b=r-(y==h?p:v);Eo(e.text.charAt(y));)++y;var w=dn(n,y,y==h?g:m,-1>b?-1:b>1?1:0);return w}var x=Math.ceil(f/2),C=h+x;if(c){C=h;for(var S=0;x>S;++S)C=ol(e,C,1)}var k=o(C);k>r?(d=C,v=k,(m=a)&&(v+=1e3),f=x):(h=C,p=k,g=a,f-=x)}}function vn(t){if(null!=t.cachedTextHeight)return t.cachedTextHeight;if(null==Ol){Ol=Fo("pre");for(var e=0;49>e;++e)Ol.appendChild(document.createTextNode("x")),Ol.appendChild(Fo("br"));Ol.appendChild(document.createTextNode("x"))}Ro(t.measure,Ol);var n=Ol.offsetHeight/50;return n>3&&(t.cachedTextHeight=n),Io(t.measure),n||1}function mn(t){if(null!=t.cachedCharWidth)return t.cachedCharWidth;var e=Fo("span","xxxxxxxxxx"),n=Fo("pre",[e]);Ro(t.measure,n);var r=e.getBoundingClientRect(),i=(r.right-r.left)/10;return i>2&&(t.cachedCharWidth=i),i||10}function yn(t){t.curOp={cm:t,viewChanged:!1,startHeight:t.doc.height,forceUpdate:!1,updateInput:null,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,id:++Fl},El?El.ops.push(t.curOp):t.curOp.ownsGroup=El={ops:[t.curOp],delayedCallbacks:[]}}function bn(t){var e=t.delayedCallbacks,n=0;do{for(;n=n.viewTo)||n.maxLineChanged&&e.options.lineWrapping,t.update=t.mustUpdate&&new L(e,t.mustUpdate&&{top:t.scrollTop,ensure:t.scrollToPos},t.forceUpdate)}function Sn(t){t.updatedDisplay=t.mustUpdate&&T(t.cm,t.update)}function kn(t){var e=t.cm,n=e.display;t.updatedDisplay&&O(e),t.barMeasure=p(e),n.maxLineChanged&&!e.options.lineWrapping&&(t.adjustWidthTo=Xe(e,n.maxLine,n.maxLine.text.length).left+3,e.display.sizerWidth=t.adjustWidthTo,t.barMeasure.scrollWidth=Math.max(n.scroller.clientWidth,n.sizer.offsetLeft+t.adjustWidthTo+Ue(e)+e.display.barWidth),t.maxScrollLeft=Math.max(0,n.sizer.offsetLeft+t.adjustWidthTo-Ge(e))),(t.updatedDisplay||t.selectionChanged)&&(t.preparedSelection=n.input.prepareSelection())}function Ln(t){var e=t.cm;null!=t.adjustWidthTo&&(e.display.sizer.style.minWidth=t.adjustWidthTo+"px",t.maxScrollLefto;o=r){var l=new On(t.doc,Gi(t.doc,o),o);r=o+l.size,i.push(l)}return i}function Dn(t,e,n,r){null==e&&(e=t.doc.first),null==n&&(n=t.doc.first+t.doc.size),r||(r=0);var i=t.display;if(r&&ne)&&(i.updateLineNumbers=e),t.curOp.viewChanged=!0,e>=i.viewTo)Tl&&di(t.doc,e)i.viewFrom?En(t):(i.viewFrom+=r,i.viewTo+=r);else if(e<=i.viewFrom&&n>=i.viewTo)En(t);else if(e<=i.viewFrom){var o=In(t,n,n+r,1);o?(i.view=i.view.slice(o.index),i.viewFrom=o.lineN,i.viewTo+=r):En(t)}else if(n>=i.viewTo){var o=In(t,e,e,-1);o?(i.view=i.view.slice(0,o.index),i.viewTo=o.lineN):En(t)}else{var l=In(t,e,e,-1),a=In(t,n,n+r,1);l&&a?(i.view=i.view.slice(0,l.index).concat(Wn(t,l.lineN,a.lineN)).concat(i.view.slice(a.index)),i.viewTo+=r):En(t)}var s=i.externalMeasured;s&&(n=i.lineN&&e=r.viewTo)){var o=r.view[Fn(t,e)];if(null!=o.node){var l=o.changes||(o.changes=[]);-1==To(l,n)&&l.push(n)}}}function En(t){t.display.viewFrom=t.display.viewTo=t.doc.first,t.display.view=[],t.display.viewOffset=0}function Fn(t,e){if(e>=t.display.viewTo)return null;if(e-=t.display.viewFrom,0>e)return null;for(var n=t.display.view,r=0;re)return r}function In(t,e,n,r){var i,o=Fn(t,e),l=t.display.view;if(!Tl||n==t.doc.first+t.doc.size)return{index:o,lineN:n};for(var a=0,s=t.display.viewFrom;o>a;a++)s+=l[a].size;if(s!=e){if(r>0){if(o==l.length-1)return null;i=s+l[o].size-e,o++}else i=s-e;e+=i,n+=i}for(;di(t.doc,n)!=n;){if(o==(0>r?0:l.length-1))return null;n+=r*l[o-(0>r?1:0)].size,o+=r}return{index:o,lineN:n}}function Rn(t,e,n){var r=t.display,i=r.view;0==i.length||e>=r.viewTo||n<=r.viewFrom?(r.view=Wn(t,e,n),r.viewFrom=e):(r.viewFrom>e?r.view=Wn(t,e,r.viewFrom).concat(r.view):r.viewFromn&&(r.view=r.view.slice(0,Fn(t,n)))),r.viewTo=n}function zn(t){for(var e=t.display.view,n=0,r=0;r400}function i(e){bo(t,e)||ba(e)}var o=t.display;wa(o.scroller,"mousedown",Mn(t,Gn)),cl&&11>fl?wa(o.scroller,"dblclick",Mn(t,function(e){if(!bo(t,e)){var n=Un(t,e);if(n&&!Xn(t,e)&&!jn(t.display,e)){ma(e);var r=t.findWordAt(n);be(t.doc,r.anchor,r.head)}}})):wa(o.scroller,"dblclick",function(e){bo(t,e)||ma(e)}),Ll||wa(o.scroller,"contextmenu",function(e){hr(t,e)});var l,a={end:0};wa(o.scroller,"touchstart",function(t){if(!n(t)){clearTimeout(l);var e=+new Date;o.activeTouch={start:e,moved:!1,prev:e-a.end<=300?a:null},1==t.touches.length&&(o.activeTouch.left=t.touches[0].pageX,o.activeTouch.top=t.touches[0].pageY)}}),wa(o.scroller,"touchmove",function(){o.activeTouch&&(o.activeTouch.moved=!0)}),wa(o.scroller,"touchend",function(n){var i=o.activeTouch;if(i&&!jn(o,n)&&null!=i.left&&!i.moved&&new Date-i.start<300){var l,a=t.coordsChar(o.activeTouch,"page");l=!i.prev||r(i,i.prev)?new ce(a,a):!i.prev.prev||r(i,i.prev.prev)?t.findWordAt(a):new ce(Ml(a.line,0),pe(t.doc,Ml(a.line+1,0))),t.setSelection(l.anchor,l.head),t.focus(),ma(n)}e()}),wa(o.scroller,"touchcancel",e),wa(o.scroller,"scroll",function(){o.scroller.clientHeight&&(Jn(t,o.scroller.scrollTop),Qn(t,o.scroller.scrollLeft,!0),Ca(t,"scroll",t))}),wa(o.scroller,"mousewheel",function(e){tr(t,e)}),wa(o.scroller,"DOMMouseScroll",function(e){tr(t,e)}),wa(o.wrapper,"scroll",function(){o.wrapper.scrollTop=o.wrapper.scrollLeft=0}),t.options.dragDrop&&(wa(o.scroller,"dragstart",function(e){Zn(t,e)}),wa(o.scroller,"dragenter",i),wa(o.scroller,"dragover",i),wa(o.scroller,"drop",Mn(t,Yn)));var s=o.input.getField();wa(s,"keyup",function(e){sr.call(t,e)}),wa(s,"keydown",Mn(t,lr)),wa(s,"keypress",Mn(t,ur)),wa(s,"focus",Wo(cr,t)),wa(s,"blur",Wo(fr,t))}function Bn(t){var e=t.display;(e.lastWrapHeight!=e.wrapper.clientHeight||e.lastWrapWidth!=e.wrapper.clientWidth)&&(e.cachedCharWidth=e.cachedTextHeight=e.cachedPaddingH=null,e.scrollbarsClipped=!1,t.setSize())}function jn(t,e){for(var n=go(e);n!=t.wrapper;n=n.parentNode)if(!n||1==n.nodeType&&"true"==n.getAttribute("cm-ignore-events")||n.parentNode==t.sizer&&n!=t.mover)return!0}function Un(t,e,n,r){var i=t.display;if(!n&&"true"==go(e).getAttribute("cm-not-content"))return null;var o,l,a=i.lineSpace.getBoundingClientRect();try{o=e.clientX-a.left,l=e.clientY-a.top}catch(e){return null}var s,u=pn(t,o,l);if(r&&1==u.xRel&&(s=Gi(t.doc,u.line).text).length==u.ch){var c=Aa(s,s.length,t.options.tabSize)-s.length;u=Ml(u.line,Math.max(0,Math.round((o-je(t.display).left)/mn(t.display))-c))}return u}function Gn(t){var e=this,n=e.display;if(!(n.activeTouch&&n.input.supportsTouch()||bo(e,t))){if(n.shift=t.shiftKey,jn(n,t))return void(hl||(n.scroller.draggable=!1,setTimeout(function(){n.scroller.draggable=!0},100)));if(!Xn(e,t)){var r=Un(e,t);switch(window.focus(),vo(t)){case 1:r?qn(e,t,r):go(t)==n.scroller&&ma(t);break;case 2:hl&&(e.state.lastMiddleDown=+new Date),r&&be(e.doc,r),setTimeout(function(){n.input.focus()},20),ma(t);break;case 3:Ll&&hr(e,t)}}}}function qn(t,e,n){cl?setTimeout(Wo(Y,t),0):Y(t);var r,i=+new Date;Dl&&Dl.time>i-400&&0==Al(Dl.pos,n)?r="triple":Wl&&Wl.time>i-400&&0==Al(Wl.pos,n)?(r="double",Dl={time:i,pos:n}):(r="single",Wl={time:i,pos:n});var o,l=t.doc.sel,a=xl?e.metaKey:e.ctrlKey;t.options.dragDrop&&ja&&!Z(t)&&"single"==r&&(o=l.contains(n))>-1&&!l.ranges[o].empty()?Kn(t,e,n,a):Vn(t,e,n,r,a)}function Kn(t,e,n,r){var i=t.display,o=Mn(t,function(l){hl&&(i.scroller.draggable=!1),t.state.draggingText=!1,xa(document,"mouseup",o),xa(i.scroller,"drop",o),Math.abs(e.clientX-l.clientX)+Math.abs(e.clientY-l.clientY)<10&&(ma(l),r||be(t.doc,n),i.input.focus(),cl&&9==fl&&setTimeout(function(){document.body.focus(),i.input.focus()},20))});hl&&(i.scroller.draggable=!0),t.state.draggingText=o,i.scroller.dragDrop&&i.scroller.dragDrop(),wa(document,"mouseup",o),wa(i.scroller,"drop",o)}function Vn(t,e,n,r,i){function o(e){if(0!=Al(v,e))if(v=e,"rect"==r){for(var i=[],o=t.options.tabSize,l=Aa(Gi(u,n.line).text,n.ch,o),a=Aa(Gi(u,e.line).text,e.ch,o),s=Math.min(l,a),d=Math.max(l,a),p=Math.min(n.line,e.line),g=Math.min(t.lastLine(),Math.max(n.line,e.line));g>=p;p++){var m=Gi(u,p).text,y=ko(m,s,o);s==d?i.push(new ce(Ml(p,y),Ml(p,y))):m.length>y&&i.push(new ce(Ml(p,y),Ml(p,ko(m,d,o))))}i.length||i.push(new ce(n,n)),Le(u,fe(h.ranges.slice(0,f).concat(i),f),{origin:"*mouse",scroll:!1}),t.scrollIntoView(e)}else{var b=c,w=b.anchor,x=e;if("single"!=r){if("double"==r)var C=t.findWordAt(e);else var C=new ce(Ml(e.line,0),pe(u,Ml(e.line+1,0)));Al(C.anchor,w)>0?(x=C.head,w=X(b.from(),C.anchor)):(x=C.anchor,w=$(b.to(),C.head))}var i=h.ranges.slice(0);i[f]=new ce(pe(u,w),x),Le(u,fe(i,f),Ta)}}function l(e){var n=++y,i=Un(t,e,!0,"rect"==r);if(i)if(0!=Al(i,v)){Y(t),o(i);var a=w(s,u);(i.line>=a.to||i.linem.bottom?20:0;c&&setTimeout(Mn(t,function(){y==n&&(s.scroller.scrollTop+=c,l(e))}),50)}}function a(t){y=1/0,ma(t),s.input.focus(),xa(document,"mousemove",b),xa(document,"mouseup",x),u.history.lastSelOrigin=null}var s=t.display,u=t.doc;ma(e);var c,f,h=u.sel,d=h.ranges;if(i&&!e.shiftKey?(f=u.sel.contains(n),c=f>-1?d[f]:new ce(n,n)):c=u.sel.primary(),e.altKey)r="rect",i||(c=new ce(n,n)),n=Un(t,e,!0,!0),f=-1;else if("double"==r){var p=t.findWordAt(n);c=t.display.shift||u.extend?ye(u,c,p.anchor,p.head):p}else if("triple"==r){var g=new ce(Ml(n.line,0),pe(u,Ml(n.line+1,0)));c=t.display.shift||u.extend?ye(u,c,g.anchor,g.head):g}else c=ye(u,c,n);i?-1==f?(f=d.length,Le(u,fe(d.concat([c]),f),{scroll:!1,origin:"*mouse"})):d.length>1&&d[f].empty()&&"single"==r?(Le(u,fe(d.slice(0,f).concat(d.slice(f+1)),0)),h=u.sel):xe(u,f,c,Ta):(f=0,Le(u,new ue([c],0),Ta),h=u.sel);var v=n,m=s.wrapper.getBoundingClientRect(),y=0,b=Mn(t,function(t){vo(t)?l(t):a(t)}),x=Mn(t,a);wa(document,"mousemove",b),wa(document,"mouseup",x)}function $n(t,e,n,r,i){try{var o=e.clientX,l=e.clientY}catch(e){return!1}if(o>=Math.floor(t.display.gutters.getBoundingClientRect().right))return!1;r&&ma(e);var a=t.display,s=a.lineDiv.getBoundingClientRect();if(l>s.bottom||!xo(t,n))return po(e);l-=s.top-a.viewOffset;for(var u=0;u=o){var f=Xi(t.doc,l),h=t.options.gutters[u];return i(t,n,t,f,h,e),po(e)}}}function Xn(t,e){return $n(t,e,"gutterClick",!0,mo)}function Yn(t){var e=this;if(!bo(e,t)&&!jn(e.display,t)){ma(t),cl&&(Il=+new Date);var n=Un(e,t,!0),r=t.dataTransfer.files;if(n&&!Z(e))if(r&&r.length&&window.FileReader&&window.File)for(var i=r.length,o=Array(i),l=0,a=function(t,r){var a=new FileReader;a.onload=Mn(e,function(){if(o[r]=a.result,++l==i){n=pe(e.doc,n);var t={from:n,to:n,text:Ua(o.join("\n")),origin:"paste"};br(e.doc,t),ke(e.doc,he(n,Ul(t)))}}),a.readAsText(t)},s=0;i>s;++s)a(r[s],s);else{if(e.state.draggingText&&e.doc.sel.contains(n)>-1)return e.state.draggingText(t),void setTimeout(function(){e.display.input.focus()},20);try{var o=t.dataTransfer.getData("Text");if(o){if(e.state.draggingText&&!(xl?t.metaKey:t.ctrlKey))var u=e.listSelections();if(_e(e.doc,he(n,n)),u)for(var s=0;sl.clientWidth||i&&l.scrollHeight>l.clientHeight){if(i&&xl&&hl)t:for(var a=e.target,s=o.view;a!=l;a=a.parentNode)for(var u=0;uc?f=Math.max(0,f+c-50):h=Math.min(t.doc.height,h+c+50),A(t,{top:f,bottom:h})}20>Rl&&(null==o.wheelStartX?(o.wheelStartX=l.scrollLeft,o.wheelStartY=l.scrollTop,o.wheelDX=r,o.wheelDY=i,setTimeout(function(){if(null!=o.wheelStartX){var t=l.scrollLeft-o.wheelStartX,e=l.scrollTop-o.wheelStartY,n=e&&o.wheelDY&&e/o.wheelDY||t&&o.wheelDX&&t/o.wheelDX;o.wheelStartX=o.wheelStartY=null,n&&(zl=(zl*Rl+n)/(Rl+1),++Rl)}},200)):(o.wheelDX+=r,o.wheelDY+=i))}}function er(t,e,n){if("string"==typeof e&&(e=ta[e],!e))return!1;t.display.input.ensurePolled();var r=t.display.shift,i=!1;try{Z(t)&&(t.state.suppressEdits=!0),n&&(t.display.shift=!1),i=e(t)!=La}finally{t.display.shift=r,t.state.suppressEdits=!1}return i}function nr(t,e,n){for(var r=0;rfl&&27==t.keyCode&&(t.returnValue=!1);var n=t.keyCode;e.display.shift=16==n||t.shiftKey;var r=ir(e,t);gl&&(jl=r?n:null,!r&&88==n&&!qa&&(xl?t.metaKey:t.ctrlKey)&&e.replaceSelection("",null,"cut")),18!=n||/\bCodeMirror-crosshair\b/.test(e.display.lineDiv.className)||ar(e)}}function ar(t){function e(t){18!=t.keyCode&&t.altKey||(za(n,"CodeMirror-crosshair"),xa(document,"keyup",e),xa(document,"mouseover",e))}var n=t.display.lineDiv;Pa(n,"CodeMirror-crosshair"),wa(document,"keyup",e),wa(document,"mouseover",e)}function sr(t){16==t.keyCode&&(this.doc.sel.shift=!1),bo(this,t)}function ur(t){var e=this;if(!(jn(e.display,t)||bo(e,t)||t.ctrlKey&&!t.altKey||xl&&t.metaKey)){var n=t.keyCode,r=t.charCode;if(gl&&n==jl)return jl=null,void ma(t);if(!gl||t.which&&!(t.which<10)||!ir(e,t)){var i=String.fromCharCode(null==r?n:r);or(e,t,i)||e.display.input.onKeyPress(t)}}}function cr(t){"nocursor"!=t.options.readOnly&&(t.state.focused||(Ca(t,"focus",t),t.state.focused=!0,Pa(t.display.wrapper,"CodeMirror-focused"),t.curOp||t.display.selForContextMenu==t.doc.sel||(t.display.input.reset(),hl&&setTimeout(function(){t.display.input.reset(!0)},20)),t.display.input.receivedFocus()),Ee(t))}function fr(t){t.state.focused&&(Ca(t,"blur",t),t.state.focused=!1,za(t.display.wrapper,"CodeMirror-focused")),clearInterval(t.display.blinker),setTimeout(function(){t.state.focused||(t.display.shift=!1)},150)}function hr(t,e){jn(t.display,e)||dr(t,e)||t.display.input.onContextMenu(e)}function dr(t,e){return xo(t,"gutterContextMenu")?$n(t,e,"gutterContextMenu",!1,Ca):!1}function pr(t,e){if(Al(t,e.from)<0)return t;if(Al(t,e.to)<=0)return Ul(e);var n=t.line+e.text.length-(e.to.line-e.from.line)-1,r=t.ch;return t.line==e.to.line&&(r+=Ul(e).ch-e.to.ch),Ml(n,r)}function gr(t,e){for(var n=[],r=0;r=0;--i)wr(t,{from:r[i].from,to:r[i].to,text:i?[""]:e.text});else wr(t,e)}}function wr(t,e){if(1!=e.text.length||""!=e.text[0]||0!=Al(e.from,e.to)){var n=gr(t,e);no(t,e,n,t.cm?t.cm.curOp.id:0/0),Sr(t,e,n,Jr(t,e));var r=[];ji(t,function(t,n){n||-1!=To(r,t.history)||(ho(t.history,e),r.push(t.history)),Sr(t,e,null,Jr(t,e))})}}function xr(t,e,n){if(!t.cm||!t.cm.state.suppressEdits){for(var r,i=t.history,o=t.sel,l="undo"==e?i.done:i.undone,a="undo"==e?i.undone:i.done,s=0;s=0;--s){var f=r.changes[s];if(f.origin=e,c&&!yr(t,f,!1))return void(l.length=0);u.push(Qi(t,f));var h=s?gr(t,f):_o(l);Sr(t,f,h,ti(t,f)),!s&&t.cm&&t.cm.scrollIntoView({from:f.from,to:Ul(f)});var d=[];ji(t,function(t,e){e||-1!=To(d,t.history)||(ho(t.history,f),d.push(t.history)),Sr(t,f,null,ti(t,f))})}}}}function Cr(t,e){if(0!=e&&(t.first+=e,t.sel=new ue(Mo(t.sel.ranges,function(t){return new ce(Ml(t.anchor.line+e,t.anchor.ch),Ml(t.head.line+e,t.head.ch))}),t.sel.primIndex),t.cm)){Dn(t.cm,t.first,t.first-e,e);for(var n=t.cm.display,r=n.viewFrom;rt.lastLine())){if(e.from.lineo&&(e={from:e.from,to:Ml(o,Gi(t,o).text.length),text:[e.text[0]],origin:e.origin}),e.removed=qi(t,e.from,e.to),n||(n=gr(t,e)),t.cm?kr(t.cm,e,r):zi(t,e,r),_e(t,n,_a)}}function kr(t,e,n){var r=t.doc,i=t.display,l=e.from,a=e.to,s=!1,u=l.line;t.options.lineWrapping||(u=$i(fi(Gi(r,l.line))),r.iter(u,a.line+1,function(t){return t==i.maxLine?(s=!0,!0):void 0})),r.sel.contains(e.from,e.to)>-1&&wo(t),zi(r,e,n,o(t)),t.options.lineWrapping||(r.iter(u,l.line+e.text.length,function(t){var e=f(t);e>i.maxLineLength&&(i.maxLine=t,i.maxLineLength=e,i.maxLineChanged=!0,s=!1)}),s&&(t.curOp.updateMaxLine=!0)),r.frontier=Math.min(r.frontier,l.line),Fe(t,400);var c=e.text.length-(a.line-l.line)-1;e.full?Dn(t):l.line!=a.line||1!=e.text.length||Ri(t.doc,e)?Dn(t,l.line,a.line+1,c):Hn(t,l.line,"text");var h=xo(t,"changes"),d=xo(t,"change");if(d||h){var p={from:l,to:a,text:e.text,removed:e.removed,origin:e.origin};d&&mo(t,"change",t,p),h&&(t.curOp.changeObjs||(t.curOp.changeObjs=[])).push(p)}t.display.selForContextMenu=null}function Lr(t,e,n,r,i){if(r||(r=n),Al(r,n)<0){var o=r;r=n,n=o}"string"==typeof e&&(e=Ua(e)),br(t,{from:n,to:r,text:e,origin:i})}function _r(t,e){if(!bo(t,"scrollCursorIntoView")){var n=t.display,r=n.sizer.getBoundingClientRect(),i=null;if(e.top+r.top<0?i=!0:e.bottom+r.top>(window.innerHeight||document.documentElement.clientHeight)&&(i=!1),null!=i&&!yl){var o=Fo("div","​",null,"position: absolute; top: "+(e.top-n.viewOffset-Pe(t.display))+"px; height: "+(e.bottom-e.top+Ue(t)+n.barHeight)+"px; left: "+e.left+"px; width: 2px;");t.display.lineSpace.appendChild(o),o.scrollIntoView(i),t.display.lineSpace.removeChild(o)}}}function Tr(t,e,n,r){null==r&&(r=0);for(var i=0;5>i;i++){var o=!1,l=fn(t,e),a=n&&n!=e?fn(t,n):l,s=Ar(t,Math.min(l.left,a.left),Math.min(l.top,a.top)-r,Math.max(l.left,a.left),Math.max(l.bottom,a.bottom)+r),u=t.doc.scrollTop,c=t.doc.scrollLeft;if(null!=s.scrollTop&&(Jn(t,s.scrollTop),Math.abs(t.doc.scrollTop-u)>1&&(o=!0)),null!=s.scrollLeft&&(Qn(t,s.scrollLeft),Math.abs(t.doc.scrollLeft-c)>1&&(o=!0)),!o)break}return l}function Mr(t,e,n,r,i){var o=Ar(t,e,n,r,i);null!=o.scrollTop&&Jn(t,o.scrollTop),null!=o.scrollLeft&&Qn(t,o.scrollLeft)}function Ar(t,e,n,r,i){var o=t.display,l=vn(t.display);0>n&&(n=0);var a=t.curOp&&null!=t.curOp.scrollTop?t.curOp.scrollTop:o.scroller.scrollTop,s=qe(t),u={};i-n>s&&(i=n+s);var c=t.doc.height+Be(o),f=l>n,h=i>c-l;if(a>n)u.scrollTop=f?0:n;else if(i>a+s){var d=Math.min(n,(h?c:i)-s);d!=a&&(u.scrollTop=d)}var p=t.curOp&&null!=t.curOp.scrollLeft?t.curOp.scrollLeft:o.scroller.scrollLeft,g=Ge(t)-(t.options.fixedGutter?o.gutters.offsetWidth:0),v=r-e>g;return v&&(r=e+g),10>e?u.scrollLeft=0:p>e?u.scrollLeft=Math.max(0,e-(v?0:10)):r>g+p-3&&(u.scrollLeft=r+(v?0:10)-g),u}function Nr(t,e,n){(null!=e||null!=n)&&Wr(t),null!=e&&(t.curOp.scrollLeft=(null==t.curOp.scrollLeft?t.doc.scrollLeft:t.curOp.scrollLeft)+e),null!=n&&(t.curOp.scrollTop=(null==t.curOp.scrollTop?t.doc.scrollTop:t.curOp.scrollTop)+n)}function Or(t){Wr(t);var e=t.getCursor(),n=e,r=e;t.options.lineWrapping||(n=e.ch?Ml(e.line,e.ch-1):e,r=Ml(e.line,e.ch+1)),t.curOp.scrollToPos={from:n,to:r,margin:t.options.cursorScrollMargin,isCursor:!0}}function Wr(t){var e=t.curOp.scrollToPos;if(e){t.curOp.scrollToPos=null;var n=hn(t,e.from),r=hn(t,e.to),i=Ar(t,Math.min(n.left,r.left),Math.min(n.top,r.top)-e.margin,Math.max(n.right,r.right),Math.max(n.bottom,r.bottom)+e.margin);t.scrollTo(i.scrollLeft,i.scrollTop)}}function Dr(t,e,n,r){var i,o=t.doc;null==n&&(n="add"),"smart"==n&&(o.mode.indent?i=ze(t,e):n="prev");var l=t.options.tabSize,a=Gi(o,e),s=Aa(a.text,null,l);a.stateAfter&&(a.stateAfter=null);var u,c=a.text.match(/^\s*/)[0];if(r||/\S/.test(a.text)){if("smart"==n&&(u=o.mode.indent(i,a.text.slice(c.length),a.text),u==La||u>150)){if(!r)return;n="prev"}}else u=0,n="not";"prev"==n?u=e>o.first?Aa(Gi(o,e-1).text,null,l):0:"add"==n?u=s+t.options.indentUnit:"subtract"==n?u=s-t.options.indentUnit:"number"==typeof n&&(u=s+n),u=Math.max(0,u);var f="",h=0;if(t.options.indentWithTabs)for(var d=Math.floor(u/l);d;--d)h+=l,f+=" ";if(u>h&&(f+=Lo(u-h)),f!=c)Lr(o,f,Ml(e,0),Ml(e,c.length),"+input");else for(var d=0;d=0;e--)Lr(t.doc,"",r[e].from,r[e].to,"+delete");Or(t)})}function Fr(t,e,n,r,i){function o(){var e=a+n;return e=t.first+t.size?f=!1:(a=e,c=Gi(t,e))}function l(t){var e=(i?ol:ll)(c,s,n,!0);if(null==e){if(t||!o())return f=!1;s=i?(0>n?Jo:Zo)(c):0>n?c.text.length:0}else s=e;return!0}var a=e.line,s=e.ch,u=n,c=Gi(t,a),f=!0;if("char"==r)l();else if("column"==r)l(!0);else if("word"==r||"group"==r)for(var h=null,d="group"==r,p=t.cm&&t.cm.getHelper(e,"wordChars"),g=!0;!(0>n)||l(!g);g=!1){var v=c.text.charAt(s)||"\n",m=Do(v,p)?"w":d&&"\n"==v?"n":!d||/\s/.test(v)?null:"p";if(!d||g||m||(m="s"),h&&h!=m){0>n&&(n=1,l());break}if(m&&(h=m),n>0&&!l(!g))break}var y=Ne(t,Ml(a,s),u,!0);return f||(y.hitSide=!0),y}function Ir(t,e,n,r){var i,o=t.doc,l=e.left;if("page"==r){var a=Math.min(t.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);i=e.top+n*(a-(0>n?1.5:.5)*vn(t.display))}else"line"==r&&(i=n>0?e.bottom+3:e.top-3);for(;;){var s=pn(t,l,i);if(!s.outside)break;if(0>n?0>=i:i>=o.height){s.hitSide=!0;break}i+=5*n}return s}function Rr(e,n,r,i){t.defaults[e]=n,r&&(ql[e]=i?function(t,e,n){n!=Kl&&r(t,e,n)}:r)}function zr(t){for(var e,n,r,i,o=t.split(/-(?!$)/),t=o[o.length-1],l=0;l0||0==l&&o.clearWhenEmpty!==!1)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=Fo("span",[o.replacedWith],"CodeMirror-widget"),r.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),r.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(ci(t,e.line,e,n,o)||e.line!=n.line&&ci(t,n.line,e,n,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");Tl=!0}o.addToHistory&&no(t,{from:e,to:n,origin:"markText"},t.sel,0/0);var a,s=e.line,u=t.cm;if(t.iter(s,n.line+1,function(t){u&&o.collapsed&&!u.options.lineWrapping&&fi(t)==u.display.maxLine&&(a=!0),o.collapsed&&s!=e.line&&Vi(t,0),Xr(t,new Kr(o,s==e.line?e.ch:null,s==n.line?n.ch:null)),++s}),o.collapsed&&t.iter(e.line,n.line+1,function(e){gi(t,e)&&Vi(e,0)}),o.clearOnEnter&&wa(o,"beforeCursorEnter",function(){o.clear()}),o.readOnly&&(_l=!0,(t.history.done.length||t.history.undone.length)&&t.clearHistory()),o.collapsed&&(o.id=++la,o.atomic=!0),u){if(a&&(u.curOp.updateMaxLine=!0),o.collapsed)Dn(u,e.line,n.line+1);else if(o.className||o.title||o.startStyle||o.endStyle||o.css)for(var c=e.line;c<=n.line;c++)Hn(u,c,"text");o.atomic&&Me(u.doc),mo(u,"markerAdded",u,o)}return o}function jr(t,e,n,r,i){r=Oo(r),r.shared=!1;var o=[Br(t,e,n,r,i)],l=o[0],a=r.widgetNode;return ji(t,function(t){a&&(r.widgetNode=a.cloneNode(!0)),o.push(Br(t,pe(t,e),pe(t,n),r,i));for(var s=0;s=e:o.to>e);(r||(r=[])).push(new Kr(l,o.from,s?null:o.to))}}return r}function Zr(t,e,n){if(t)for(var r,i=0;i=e:o.to>e);if(a||o.from==e&&"bookmark"==l.type&&(!n||o.marker.insertLeft)){var s=null==o.from||(l.inclusiveLeft?o.from<=e:o.from0&&a)for(var f=0;ff;++f)p.push(g);p.push(s)}return p}function Qr(t){for(var e=0;e0)){var c=[s,1],f=Al(u.from,a.from),h=Al(u.to,a.to);(0>f||!l.inclusiveLeft&&!f)&&c.push({from:u.from,to:a.from}),(h>0||!l.inclusiveRight&&!h)&&c.push({from:a.to,to:u.to}),i.splice.apply(i,c),s+=c.length-1}}return i}function ni(t){var e=t.markedSpans;if(e){for(var n=0;n=0&&0>=f||0>=c&&f>=0)&&(0>=c&&(Al(u.to,n)>0||s.marker.inclusiveRight&&i.inclusiveLeft)||c>=0&&(Al(u.from,r)<0||s.marker.inclusiveLeft&&i.inclusiveRight)))return!0}}}function fi(t){for(var e;e=si(t);)t=e.find(-1,!0).line;return t}function hi(t){for(var e,n;e=ui(t);)t=e.find(1,!0).line,(n||(n=[])).push(t);return n}function di(t,e){var n=Gi(t,e),r=fi(n);return n==r?e:$i(r)}function pi(t,e){if(e>t.lastLine())return e;var n,r=Gi(t,e);if(!gi(t,r))return e;for(;n=ui(r);)r=n.find(1,!0).line;return $i(r)+1}function gi(t,e){var n=Tl&&e.markedSpans;if(n)for(var r,i=0;io;o++){i&&(i[0]=t.innerMode(e,r).mode);var l=e.token(n,r);if(n.pos>n.start)return l}throw new Error("Mode "+e.name+" failed to advance stream.")}function Li(t,e,n,r){function i(t){return{start:f.start,end:f.pos,string:f.current(),type:o||null,state:t?Jl(l.mode,c):c}}var o,l=t.doc,a=l.mode;e=pe(l,e);var s,u=Gi(l,e.line),c=ze(t,e.line,n),f=new oa(u.text,t.options.tabSize);for(r&&(s=[]);(r||f.post.options.maxHighlightLength?(a=!1,l&&Ai(t,e,r,f.pos),f.pos=e.length,s=null):s=Ci(ki(n,f,r,h),o),h){var d=h[0].name;d&&(s="m-"+(s?d+" "+s:d))}if(!a||c!=s){for(;uu;){var r=i[s];r>t&&i.splice(s,1,t,i[s+1],r),s+=2,u=Math.min(t,r)}if(e)if(a.opaque)i.splice(n,s-n,t,"cm-overlay "+e),s=n+2;else for(;s>n;n+=2){var o=i[n+1];i[n+1]=(o?o+" ":"")+"cm-overlay "+e}},o)}return{styles:i,classes:o.bgClass||o.textClass?o:null}}function Mi(t,e,n){if(!e.styles||e.styles[0]!=t.state.modeGen){var r=Ti(t,e,e.stateAfter=ze(t,$i(e)));e.styles=r.styles,r.classes?e.styleClasses=r.classes:e.styleClasses&&(e.styleClasses=null),n===t.doc.frontier&&t.doc.frontier++}return e.styles}function Ai(t,e,n,r){var i=t.doc.mode,o=new oa(e,t.options.tabSize);for(o.start=o.pos=r||0,""==e&&Si(i,n);!o.eol()&&o.pos<=t.options.maxHighlightLength;)ki(i,o,n),o.start=o.pos}function Ni(t,e){if(!t||/^\s*$/.test(t))return null;var n=e.addModeClass?ha:fa;return n[t]||(n[t]=t.replace(/\S+/g,"cm-$&"))}function Oi(t,e){var n=Fo("span",null,null,hl?"padding-right: .1px":null),r={pre:Fo("pre",[n]),content:n,col:0,pos:0,cm:t};e.measure={};for(var i=0;i<=(e.rest?e.rest.length:0);i++){var o,l=i?e.rest[i-1]:e.line;r.pos=0,r.addToken=Di,(cl||hl)&&t.getOption("lineWrapping")&&(r.addToken=Hi(r.addToken)),Ko(t.display.measure)&&(o=Zi(l))&&(r.addToken=Ei(r.addToken,o)),r.map=[];var a=e!=t.display.externalMeasured&&$i(l);Ii(l,r,Mi(t,l,a)),l.styleClasses&&(l.styleClasses.bgClass&&(r.bgClass=Bo(l.styleClasses.bgClass,r.bgClass||"")),l.styleClasses.textClass&&(r.textClass=Bo(l.styleClasses.textClass,r.textClass||""))),0==r.map.length&&r.map.push(0,0,r.content.appendChild(qo(t.display.measure))),0==i?(e.measure.map=r.map,e.measure.cache={}):((e.measure.maps||(e.measure.maps=[])).push(r.map),(e.measure.caches||(e.measure.caches=[])).push({}))}return hl&&/\bcm-tab\b/.test(r.content.lastChild.className)&&(r.content.className="cm-tab-wrap-hack"),Ca(t,"renderLine",t,e.line,r.pre),r.pre.className&&(r.textClass=Bo(r.pre.className,r.textClass||"")),r}function Wi(t){var e=Fo("span","•","cm-invalidchar");return e.title="\\u"+t.charCodeAt(0).toString(16),e.setAttribute("aria-label",e.title),e}function Di(t,e,n,r,i,o,l){if(e){var a=t.cm.options.specialChars,s=!1;if(a.test(e))for(var u=document.createDocumentFragment(),c=0;;){a.lastIndex=c;var f=a.exec(e),h=f?f.index-c:e.length-c;if(h){var d=document.createTextNode(e.slice(c,c+h));u.appendChild(cl&&9>fl?Fo("span",[d]):d),t.map.push(t.pos,t.pos+h,d),t.col+=h,t.pos+=h}if(!f)break;if(c+=h+1," "==f[0]){var p=t.cm.options.tabSize,g=p-t.col%p,d=u.appendChild(Fo("span",Lo(g),"cm-tab"));d.setAttribute("role","presentation"),d.setAttribute("cm-text"," "),t.col+=g}else{var d=t.cm.options.specialCharPlaceholder(f[0]);d.setAttribute("cm-text",f[0]),u.appendChild(cl&&9>fl?Fo("span",[d]):d),t.col+=1}t.map.push(t.pos,t.pos+1,d),t.pos++}else{t.col+=e.length;var u=document.createTextNode(e);t.map.push(t.pos,t.pos+e.length,u),cl&&9>fl&&(s=!0),t.pos+=e.length}if(n||r||i||s||l){var v=n||"";r&&(v+=r),i&&(v+=i);var m=Fo("span",[u],v,l);return o&&(m.title=o),t.content.appendChild(m)}t.content.appendChild(u)}}function Hi(t){function e(t){for(var e=" ",n=0;ns&&f.from<=s)break}if(f.to>=u)return t(n,r,i,o,l,a);t(n,r.slice(0,f.to-s),i,o,null,a),o=null,r=r.slice(f.to-s),s=f.to}}}function Fi(t,e,n,r){var i=!r&&n.widgetNode;i&&t.map.push(t.pos,t.pos+e,i),!r&&t.cm.display.input.needsContentAttribute&&(i||(i=t.content.appendChild(document.createElement("span"))),i.setAttribute("cm-marker",n.id)),i&&(t.cm.display.input.setUneditable(i),t.content.appendChild(i)),t.pos+=e}function Ii(t,e,n){var r=t.markedSpans,i=t.text,o=0;if(r)for(var l,a,s,u,c,f,h,d=i.length,p=0,g=1,v="",m=0;;){if(m==p){s=u=c=f=a="",h=null,m=1/0;for(var y=[],b=0;bp)?(null!=w.to&&m>w.to&&(m=w.to,u=""),x.className&&(s+=" "+x.className),x.css&&(a=x.css),x.startStyle&&w.from==p&&(c+=" "+x.startStyle),x.endStyle&&w.to==m&&(u+=" "+x.endStyle),x.title&&!f&&(f=x.title),x.collapsed&&(!h||li(h.marker,x)<0)&&(h=w)):w.from>p&&m>w.from&&(m=w.from),"bookmark"==x.type&&w.from==p&&x.widgetNode&&y.push(x)}if(h&&(h.from||0)==p&&(Fi(e,(null==h.to?d+1:h.to)-p,h.marker,null==h.from),null==h.to))return;if(!h&&y.length)for(var b=0;b=d)break;for(var C=Math.min(d,m);;){if(v){var S=p+v.length;if(!h){var k=S>C?v.slice(0,C-p):v;e.addToken(e,k,l?l+s:s,c,p+k.length==m?u:"",f,a)}if(S>=C){v=v.slice(C-p),p=C;break}p=S,c=""}v=i.slice(o,o=n[g++]),l=Ni(n[g++],e.cm.options)}}else for(var g=1;gn;++n)o.push(new ca(u[n],i(n),r));return o}var a=e.from,s=e.to,u=e.text,c=Gi(t,a.line),f=Gi(t,s.line),h=_o(u),d=i(u.length-1),p=s.line-a.line;if(e.full)t.insert(0,l(0,u.length)),t.remove(u.length,t.size-u.length);else if(Ri(t,e)){var g=l(0,u.length-1);o(f,f.text,d),p&&t.remove(a.line,p),g.length&&t.insert(a.line,g)}else if(c==f)if(1==u.length)o(c,c.text.slice(0,a.ch)+h+c.text.slice(s.ch),d);else{var g=l(1,u.length-1);g.push(new ca(h+c.text.slice(s.ch),d,r)),o(c,c.text.slice(0,a.ch)+u[0],i(0)),t.insert(a.line+1,g)}else if(1==u.length)o(c,c.text.slice(0,a.ch)+u[0]+f.text.slice(s.ch),i(0)),t.remove(a.line+1,p);else{o(c,c.text.slice(0,a.ch)+u[0],i(0)),o(f,h+f.text.slice(s.ch),d);var g=l(1,u.length-1);p>1&&t.remove(a.line+1,p-1),t.insert(a.line+1,g)}mo(t,"change",t,e)}function Pi(t){this.lines=t,this.parent=null;for(var e=0,n=0;ee||e>=t.size)throw new Error("There is no line "+(e+t.first)+" in the document.");for(var n=t;!n.lines;)for(var r=0;;++r){var i=n.children[r],o=i.chunkSize();if(o>e){n=i;break}e-=o}return n.lines[e]}function qi(t,e,n){var r=[],i=e.line;return t.iter(e.line,n.line+1,function(t){var o=t.text;i==n.line&&(o=o.slice(0,n.ch)),i==e.line&&(o=o.slice(e.ch)),r.push(o),++i}),r}function Ki(t,e,n){var r=[];return t.iter(e,n,function(t){r.push(t.text)}),r}function Vi(t,e){var n=e-t.height;if(n)for(var r=t;r;r=r.parent)r.height+=n}function $i(t){if(null==t.parent)return null;for(var e=t.parent,n=To(e.lines,t),r=e.parent;r;e=r,r=r.parent)for(var i=0;r.children[i]!=e;++i)n+=r.children[i].chunkSize();return n+e.first}function Xi(t,e){var n=t.first;t:do{for(var r=0;re){t=i;continue t}e-=o,n+=i.chunkSize()}return n}while(!t.lines);for(var r=0;re)break;e-=a}return n+r}function Yi(t){t=fi(t);for(var e=0,n=t.parent,r=0;r1&&!t.done[t.done.length-2].ranges?(t.done.pop(),_o(t.done)):void 0}function no(t,e,n,r){var i=t.history;i.undone.length=0;var o,l=+new Date;if((i.lastOp==r||i.lastOrigin==e.origin&&e.origin&&("+"==e.origin.charAt(0)&&t.cm&&i.lastModTime>l-t.cm.options.historyEventDelay||"*"==e.origin.charAt(0)))&&(o=eo(i,i.lastOp==r))){var a=_o(o.changes);0==Al(e.from,e.to)&&0==Al(e.from,a.to)?a.to=Ul(e):o.changes.push(Qi(t,e))}else{var s=_o(i.done);for(s&&s.ranges||oo(t.sel,i.done),o={changes:[Qi(t,e)],generation:i.generation},i.done.push(o);i.done.length>i.undoDepth;)i.done.shift(),i.done[0].ranges||i.done.shift()}i.done.push(n),i.generation=++i.maxGeneration,i.lastModTime=i.lastSelTime=l,i.lastOp=i.lastSelOp=r,i.lastOrigin=i.lastSelOrigin=e.origin,a||Ca(t,"historyAdded")}function ro(t,e,n,r){var i=e.charAt(0);return"*"==i||"+"==i&&n.ranges.length==r.ranges.length&&n.somethingSelected()==r.somethingSelected()&&new Date-t.history.lastSelTime<=(t.cm?t.cm.options.historyEventDelay:500)}function io(t,e,n,r){var i=t.history,o=r&&r.origin;n==i.lastSelOp||o&&i.lastSelOrigin==o&&(i.lastModTime==i.lastSelTime&&i.lastOrigin==o||ro(t,o,_o(i.done),e))?i.done[i.done.length-1]=e:oo(e,i.done),i.lastSelTime=+new Date,i.lastSelOrigin=o,i.lastSelOp=n,r&&r.clearRedo!==!1&&to(i.undone)}function oo(t,e){var n=_o(e);n&&n.ranges&&n.equals(t)||e.push(t)}function lo(t,e,n,r){var i=e["spans_"+t.id],o=0;t.iter(Math.max(t.first,n),Math.min(t.first+t.size,r),function(n){n.markedSpans&&((i||(i=e["spans_"+t.id]={}))[o]=n.markedSpans),++o})}function ao(t){if(!t)return null;for(var e,n=0;n-1&&(_o(a)[f]=c[f],delete c[f])}}}return i}function co(t,e,n,r){n0}function Co(t){t.prototype.on=function(t,e){wa(this,t,e)},t.prototype.off=function(t,e){xa(this,t,e)}}function So(){this.id=null}function ko(t,e,n){for(var r=0,i=0;;){var o=t.indexOf(" ",r);-1==o&&(o=t.length);var l=o-r;if(o==t.length||i+l>=e)return r+Math.min(l,e-i);if(i+=o-r,i+=n-i%n,r=o+1,i>=e)return r}}function Lo(t){for(;Na.length<=t;)Na.push(_o(Na)+" ");return Na[t]}function _o(t){return t[t.length-1]}function To(t,e){for(var n=0;n-1&&Ha(t)?!0:e.test(t):Ha(t)}function Ho(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e])return!1;return!0}function Eo(t){return t.charCodeAt(0)>=768&&Ea.test(t)}function Fo(t,e,n,r){var i=document.createElement(t);if(n&&(i.className=n),r&&(i.style.cssText=r),"string"==typeof e)i.appendChild(document.createTextNode(e));else if(e)for(var o=0;o0;--e)t.removeChild(t.firstChild);return t}function Ro(t,e){return Io(t).appendChild(e)}function zo(){return document.activeElement}function Po(t){return new RegExp("(^|\\s)"+t+"(?:$|\\s)\\s*")}function Bo(t,e){for(var n=t.split(" "),r=0;r2&&!(cl&&8>fl))}var n=Ia?Fo("span","​"):Fo("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return n.setAttribute("cm-text",""),n}function Ko(t){if(null!=Ra)return Ra;var e=Ro(t,document.createTextNode("AخA")),n=Wa(e,0,1).getBoundingClientRect();if(!n||n.left==n.right)return!1;var r=Wa(e,1,2).getBoundingClientRect();return Ra=r.right-n.right<3}function Vo(t){if(null!=Ka)return Ka;var e=Ro(t,Fo("span","x")),n=e.getBoundingClientRect(),r=Wa(e,0,1).getBoundingClientRect();return Ka=Math.abs(n.left-r.left)>1}function $o(t,e,n,r){if(!t)return r(e,n,"ltr");for(var i=!1,o=0;oe||e==n&&l.to==e)&&(r(Math.max(l.from,e),Math.min(l.to,n),1==l.level?"rtl":"ltr"),i=!0)}i||r(e,n,"ltr")}function Xo(t){return t.level%2?t.to:t.from}function Yo(t){return t.level%2?t.from:t.to}function Zo(t){var e=Zi(t);return e?Xo(e[0]):0}function Jo(t){var e=Zi(t);return e?Yo(_o(e)):t.text.length}function Qo(t,e){var n=Gi(t.doc,e),r=fi(n);r!=n&&(e=$i(r));var i=Zi(r),o=i?i[0].level%2?Jo(r):Zo(r):0;return Ml(e,o)}function tl(t,e){for(var n,r=Gi(t.doc,e);n=ui(r);)r=n.find(1,!0).line,e=null;var i=Zi(r),o=i?i[0].level%2?Zo(r):Jo(r):r.text.length;return Ml(null==e?$i(r):e,o)}function el(t,e){var n=Qo(t,e.line),r=Gi(t.doc,n.line),i=Zi(r);if(!i||0==i[0].level){var o=Math.max(0,r.text.search(/\S/)),l=e.line==n.line&&e.ch<=o&&e.ch;return Ml(n.line,l?0:o)}return n}function nl(t,e,n){var r=t[0].level;return e==r?!0:n==r?!1:n>e}function rl(t,e){$a=null;for(var n,r=0;re)return r;if(i.from==e||i.to==e){if(null!=n)return nl(t,i.level,t[n].level)?(i.from!=i.to&&($a=n),r):(i.from!=i.to&&($a=r),n);n=r}}return n}function il(t,e,n,r){if(!r)return e+n;do e+=n;while(e>0&&Eo(t.text.charAt(e)));return e}function ol(t,e,n,r){var i=Zi(t);if(!i)return ll(t,e,n,r);for(var o=rl(i,e),l=i[o],a=il(t,e,l.level%2?-n:n,r);;){if(a>l.from&&a0==l.level%2?l.to:l.from);if(l=i[o+=n],!l)return null;a=n>0==l.level%2?il(t,l.to,-1,r):il(t,l.from,1,r)}}function ll(t,e,n,r){var i=e+n;if(r)for(;i>0&&Eo(t.text.charAt(i));)i+=n;return 0>i||i>t.text.length?null:i}var al=/gecko\/\d/i.test(navigator.userAgent),sl=/MSIE \d/.test(navigator.userAgent),ul=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),cl=sl||ul,fl=cl&&(sl?document.documentMode||6:ul[1]),hl=/WebKit\//.test(navigator.userAgent),dl=hl&&/Qt\/\d+\.\d+/.test(navigator.userAgent),pl=/Chrome\//.test(navigator.userAgent),gl=/Opera\//.test(navigator.userAgent),vl=/Apple Computer/.test(navigator.vendor),ml=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent),yl=/PhantomJS/.test(navigator.userAgent),bl=/AppleWebKit/.test(navigator.userAgent)&&/Mobile\/\w+/.test(navigator.userAgent),wl=bl||/Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent),xl=bl||/Mac/.test(navigator.platform),Cl=/win/i.test(navigator.platform),Sl=gl&&navigator.userAgent.match(/Version\/(\d*\.\d*)/);Sl&&(Sl=Number(Sl[1])),Sl&&Sl>=15&&(gl=!1,hl=!0);var kl=xl&&(dl||gl&&(null==Sl||12.11>Sl)),Ll=al||cl&&fl>=9,_l=!1,Tl=!1;g.prototype=Oo({update:function(t){var e=t.scrollWidth>t.clientWidth+1,n=t.scrollHeight>t.clientHeight+1,r=t.nativeBarWidth;if(n){this.vert.style.display="block",this.vert.style.bottom=e?r+"px":"0";var i=t.viewHeight-(e?r:0);this.vert.firstChild.style.height=Math.max(0,t.scrollHeight-t.clientHeight+i)+"px"}else this.vert.style.display="",this.vert.firstChild.style.height="0";if(e){this.horiz.style.display="block",this.horiz.style.right=n?r+"px":"0",this.horiz.style.left=t.barLeft+"px";var o=t.viewWidth-t.barLeft-(n?r:0);this.horiz.firstChild.style.width=t.scrollWidth-t.clientWidth+o+"px"}else this.horiz.style.display="",this.horiz.firstChild.style.width="0";return!this.checkedOverlay&&t.clientHeight>0&&(0==r&&this.overlayHack(),this.checkedOverlay=!0),{right:n?r:0,bottom:e?r:0}},setScrollLeft:function(t){this.horiz.scrollLeft!=t&&(this.horiz.scrollLeft=t)},setScrollTop:function(t){this.vert.scrollTop!=t&&(this.vert.scrollTop=t)},overlayHack:function(){var t=xl&&!ml?"12px":"18px";this.horiz.style.minHeight=this.vert.style.minWidth=t;var e=this,n=function(t){go(t)!=e.vert&&go(t)!=e.horiz&&Mn(e.cm,Gn)(t)};wa(this.vert,"mousedown",n),wa(this.horiz,"mousedown",n)},clear:function(){var t=this.horiz.parentNode;t.removeChild(this.horiz),t.removeChild(this.vert)}},g.prototype),v.prototype=Oo({update:function(){return{bottom:0,right:0}},setScrollLeft:function(){},setScrollTop:function(){},clear:function(){}},v.prototype),t.scrollbarModel={"native":g,"null":v},L.prototype.signal=function(t,e){xo(t,e)&&this.events.push(arguments)},L.prototype.finish=function(){for(var t=0;t=9&&n.hasSelection&&(n.hasSelection=null),n.poll()}),wa(o,"paste",function(){if(hl&&!r.state.fakedLastChar&&!(new Date-r.state.lastMiddleDown<200)){var t=o.selectionStart,e=o.selectionEnd;o.value+="$",o.selectionEnd=e,o.selectionStart=t,r.state.fakedLastChar=!0}r.state.pasteIncoming=!0,n.fastPoll()}),wa(o,"cut",e),wa(o,"copy",e),wa(t.scroller,"paste",function(e){jn(t,e)||(r.state.pasteIncoming=!0,n.focus())}),wa(t.lineSpace,"selectstart",function(e){jn(t,e)||ma(e)})},prepareSelection:function(){var t=this.cm,e=t.display,n=t.doc,r=We(t);if(t.options.moveInputWithCursor){var i=fn(t,n.sel.primary().head,"div"),o=e.wrapper.getBoundingClientRect(),l=e.lineDiv.getBoundingClientRect();r.teTop=Math.max(0,Math.min(e.wrapper.clientHeight-10,i.top+l.top-o.top)),r.teLeft=Math.max(0,Math.min(e.wrapper.clientWidth-10,i.left+l.left-o.left))}return r},showSelection:function(t){var e=this.cm,n=e.display;Ro(n.cursorDiv,t.cursors),Ro(n.selectionDiv,t.selection),null!=t.teTop&&(this.wrapper.style.top=t.teTop+"px",this.wrapper.style.left=t.teLeft+"px")},reset:function(t){if(!this.contextMenuPending){var e,n,r=this.cm,i=r.doc;if(r.somethingSelected()){this.prevInput="";var o=i.sel.primary();e=qa&&(o.to().line-o.from().line>100||(n=r.getSelection()).length>1e3);var l=e?"-":n||r.getSelection();this.textarea.value=l,r.state.focused&&Oa(this.textarea),cl&&fl>=9&&(this.hasSelection=l)}else t||(this.prevInput=this.textarea.value="",cl&&fl>=9&&(this.hasSelection=null));this.inaccurateSelection=e}},getField:function(){return this.textarea},supportsTouch:function(){return!1},focus:function(){if("nocursor"!=this.cm.options.readOnly&&(!wl||zo()!=this.textarea))try{this.textarea.focus()}catch(t){}},blur:function(){this.textarea.blur()},resetPosition:function(){this.wrapper.style.top=this.wrapper.style.left=0},receivedFocus:function(){this.slowPoll()},slowPoll:function(){var t=this;t.pollingFast||t.polling.set(this.cm.options.pollInterval,function(){t.poll(),t.cm.state.focused&&t.slowPoll()})},fastPoll:function(){function t(){var r=n.poll();r||e?(n.pollingFast=!1,n.slowPoll()):(e=!0,n.polling.set(60,t))}var e=!1,n=this;n.pollingFast=!0,n.polling.set(20,t)},poll:function(){var t=this.cm,e=this.textarea,n=this.prevInput;if(!t.state.focused||Ga(e)&&!n||Z(t)||t.options.disableInput||t.state.keySeq)return!1;t.state.pasteIncoming&&t.state.fakedLastChar&&(e.value=e.value.substring(0,e.value.length-1),t.state.fakedLastChar=!1);var r=e.value;if(r==n&&!t.somethingSelected())return!1;if(cl&&fl>=9&&this.hasSelection===r||xl&&/[\uf700-\uf7ff]/.test(r))return t.display.input.reset(),!1;8203!=r.charCodeAt(0)||t.doc.sel!=t.display.selForContextMenu||n||(n="​");for(var i=0,o=Math.min(n.length,r.length);o>i&&n.charCodeAt(i)==r.charCodeAt(i);)++i;var l=this;return Tn(t,function(){J(t,r.slice(i),n.length-i),r.length>1e3||r.indexOf("\n")>-1?e.value=l.prevInput="":l.prevInput=r -}),!0},ensurePolled:function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},onKeyPress:function(){cl&&fl>=9&&(this.hasSelection=null),this.fastPoll()},onContextMenu:function(t){function e(){if(null!=l.selectionStart){var t=i.somethingSelected(),e=l.value="​"+(t?l.value:"");r.prevInput=t?"":"​",l.selectionStart=1,l.selectionEnd=e.length,o.selForContextMenu=i.doc.sel}}function n(){if(r.contextMenuPending=!1,r.wrapper.style.position="relative",l.style.cssText=c,cl&&9>fl&&o.scrollbars.setScrollTop(o.scroller.scrollTop=s),null!=l.selectionStart){(!cl||cl&&9>fl)&&e();var t=0,n=function(){o.selForContextMenu==i.doc.sel&&0==l.selectionStart?Mn(i,ta.selectAll)(i):t++<10?o.detectingSelectAll=setTimeout(n,500):o.input.reset()};o.detectingSelectAll=setTimeout(n,200)}}var r=this,i=r.cm,o=i.display,l=r.textarea,a=Un(i,t),s=o.scroller.scrollTop;if(a&&!gl){var u=i.options.resetSelectionOnContextMenu;u&&-1==i.doc.sel.contains(a)&&Mn(i,Le)(i.doc,he(a),_a);var c=l.style.cssText;if(r.wrapper.style.position="absolute",l.style.cssText="position: fixed; width: 30px; height: 30px; top: "+(t.clientY-5)+"px; left: "+(t.clientX-5)+"px; z-index: 1000; background: "+(cl?"rgba(255, 255, 255, .05)":"transparent")+"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",hl)var f=window.scrollY;if(o.input.focus(),hl&&window.scrollTo(null,f),o.input.reset(),i.somethingSelected()||(l.value=r.prevInput=" "),r.contextMenuPending=!0,o.selForContextMenu=i.doc.sel,clearTimeout(o.detectingSelectAll),cl&&fl>=9&&e(),Ll){ba(t);var h=function(){xa(window,"mouseup",h),setTimeout(n,20)};wa(window,"mouseup",h)}else setTimeout(n,50)}},setUneditable:Ao,needsContentAttribute:!1},ee.prototype),re.prototype=Oo({init:function(t){function e(t){if(r.somethingSelected())Nl=r.getSelections(),"cut"==t.type&&r.replaceSelection("",null,"cut");else{var e=Q(r);Nl=e.text,"cut"==t.type&&r.operation(function(){r.setSelections(e.ranges,0,_a),r.replaceSelection("",null,"cut")})}if(t.clipboardData&&!bl)t.preventDefault(),t.clipboardData.clearData(),t.clipboardData.setData("text/plain",Nl.join("\n"));else{var n=ne(),i=n.firstChild;r.display.lineSpace.insertBefore(n,r.display.lineSpace.firstChild),i.value=Nl.join("\n");var o=document.activeElement;Oa(i),setTimeout(function(){r.display.lineSpace.removeChild(n),o.focus()},50)}}var n=this,r=n.cm,i=n.div=t.lineDiv;i.contentEditable="true",te(i),wa(i,"paste",function(t){var e=t.clipboardData&&t.clipboardData.getData("text/plain");e&&(t.preventDefault(),r.replaceSelection(e,null,"paste"))}),wa(i,"compositionstart",function(t){var e=t.data;if(n.composing={sel:r.doc.sel,data:e,startData:e},e){var i=r.doc.sel.primary(),o=r.getLine(i.head.line),l=o.indexOf(e,Math.max(0,i.head.ch-e.length));l>-1&&l<=i.head.ch&&(n.composing.sel=he(Ml(i.head.line,l),Ml(i.head.line,l+e.length)))}}),wa(i,"compositionupdate",function(t){n.composing.data=t.data}),wa(i,"compositionend",function(t){var e=n.composing;e&&(t.data==e.startData||/\u200b/.test(t.data)||(e.data=t.data),setTimeout(function(){e.handled||n.applyComposition(e),n.composing==e&&(n.composing=null)},50))}),wa(i,"touchstart",function(){n.forceCompositionEnd()}),wa(i,"input",function(){n.composing||n.pollContent()||Tn(n.cm,function(){Dn(r)})}),wa(i,"copy",e),wa(i,"cut",e)},prepareSelection:function(){var t=We(this.cm,!1);return t.focus=this.cm.state.focused,t},showSelection:function(t){t&&this.cm.display.view.length&&(t.focus&&this.showPrimarySelection(),this.showMultipleSelections(t))},showPrimarySelection:function(){var t=window.getSelection(),e=this.cm.doc.sel.primary(),n=le(this.cm,t.anchorNode,t.anchorOffset),r=le(this.cm,t.focusNode,t.focusOffset);if(!n||n.bad||!r||r.bad||0!=Al(X(n,r),e.from())||0!=Al($(n,r),e.to())){var i=ie(this.cm,e.from()),o=ie(this.cm,e.to());if(i||o){var l=this.cm.display.view,a=t.rangeCount&&t.getRangeAt(0);if(i){if(!o){var s=l[l.length-1].measure,u=s.maps?s.maps[s.maps.length-1]:s.map;o={node:u[u.length-1],offset:u[u.length-2]-u[u.length-3]}}}else i={node:l[0].measure.map[2],offset:0};try{var c=Wa(i.node,i.offset,o.offset,o.node)}catch(f){}c&&(t.removeAllRanges(),t.addRange(c),a&&null==t.anchorNode&&t.addRange(a)),this.rememberSelection()}}},showMultipleSelections:function(t){Ro(this.cm.display.cursorDiv,t.cursors),Ro(this.cm.display.selectionDiv,t.selection)},rememberSelection:function(){var t=window.getSelection();this.lastAnchorNode=t.anchorNode,this.lastAnchorOffset=t.anchorOffset,this.lastFocusNode=t.focusNode,this.lastFocusOffset=t.focusOffset},selectionInEditor:function(){var t=window.getSelection();if(!t.rangeCount)return!1;var e=t.getRangeAt(0).commonAncestorContainer;return Fa(this.div,e)},focus:function(){"nocursor"!=this.cm.options.readOnly&&this.div.focus()},blur:function(){this.div.blur()},getField:function(){return this.div},supportsTouch:function(){return!0},receivedFocus:function(){function t(){e.cm.state.focused&&(e.pollSelection(),e.polling.set(e.cm.options.pollInterval,t))}var e=this;this.selectionInEditor()?this.pollSelection():Tn(this.cm,function(){e.cm.curOp.selectionChanged=!0}),this.polling.set(this.cm.options.pollInterval,t)},pollSelection:function(){if(!this.composing){var t=window.getSelection(),e=this.cm;if(t.anchorNode!=this.lastAnchorNode||t.anchorOffset!=this.lastAnchorOffset||t.focusNode!=this.lastFocusNode||t.focusOffset!=this.lastFocusOffset){this.rememberSelection();var n=le(e,t.anchorNode,t.anchorOffset),r=le(e,t.focusNode,t.focusOffset);n&&r&&Tn(e,function(){Le(e.doc,he(n,r),_a),(n.bad||r.bad)&&(e.curOp.selectionChanged=!0)})}}},pollContent:function(){var t=this.cm,e=t.display,n=t.doc.sel.primary(),r=n.from(),i=n.to();if(r.linee.viewTo-1)return!1;var o;if(r.line==e.viewFrom||0==(o=Fn(t,r.line)))var l=$i(e.view[0].line),a=e.view[0].node;else var l=$i(e.view[o].line),a=e.view[o-1].node.nextSibling;var s=Fn(t,i.line);if(s==e.view.length-1)var u=e.viewTo-1,c=e.view[s].node;else var u=$i(e.view[s+1].line)-1,c=e.view[s+1].node.previousSibling;for(var f=Ua(se(t,a,c,l,u)),h=qi(t.doc,Ml(l,0),Ml(u,Gi(t.doc,u).text.length));f.length>1&&h.length>1;)if(_o(f)==_o(h))f.pop(),h.pop(),u--;else{if(f[0]!=h[0])break;f.shift(),h.shift(),l++}for(var d=0,p=0,g=f[0],v=h[0],m=Math.min(g.length,v.length);m>d&&g.charCodeAt(d)==v.charCodeAt(d);)++d;for(var y=_o(f),b=_o(h),w=Math.min(y.length-(1==f.length?d:0),b.length-(1==h.length?d:0));w>p&&y.charCodeAt(y.length-p-1)==b.charCodeAt(b.length-p-1);)++p;f[f.length-1]=y.slice(0,y.length-p),f[0]=f[0].slice(d);var x=Ml(l,d),C=Ml(u,h.length?_o(h).length-p:0);return f.length>1||f[0]||Al(x,C)?(Lr(t.doc,f,x,C,"+input"),!0):void 0},ensurePolled:function(){this.forceCompositionEnd()},reset:function(){this.forceCompositionEnd()},forceCompositionEnd:function(){this.composing&&!this.composing.handled&&(this.applyComposition(this.composing),this.composing.handled=!0,this.div.blur(),this.div.focus())},applyComposition:function(t){t.data&&t.data!=t.startData&&Mn(this.cm,J)(this.cm,t.data,0,t.sel)},setUneditable:function(t){t.setAttribute("contenteditable","false")},onKeyPress:function(t){t.preventDefault(),Mn(this.cm,J)(this.cm,String.fromCharCode(null==t.charCode?t.keyCode:t.charCode),0)},onContextMenu:Ao,resetPosition:Ao,needsContentAttribute:!0},re.prototype),t.inputStyles={textarea:ee,contenteditable:re},ue.prototype={primary:function(){return this.ranges[this.primIndex]},equals:function(t){if(t==this)return!0;if(t.primIndex!=this.primIndex||t.ranges.length!=this.ranges.length)return!1;for(var e=0;e=0&&Al(t,r.to())<=0)return n}return-1}},ce.prototype={from:function(){return X(this.anchor,this.head)},to:function(){return $(this.anchor,this.head)},empty:function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch}};var Ol,Wl,Dl,Hl={left:0,right:0,top:0,bottom:0},El=null,Fl=0,Il=0,Rl=0,zl=null;cl?zl=-.53:al?zl=15:pl?zl=-.7:vl&&(zl=-1/3);var Pl=function(t){var e=t.wheelDeltaX,n=t.wheelDeltaY;return null==e&&t.detail&&t.axis==t.HORIZONTAL_AXIS&&(e=t.detail),null==n&&t.detail&&t.axis==t.VERTICAL_AXIS?n=t.detail:null==n&&(n=t.wheelDelta),{x:e,y:n}};t.wheelEventPixels=function(t){var e=Pl(t);return e.x*=zl,e.y*=zl,e};var Bl=new So,jl=null,Ul=t.changeEnd=function(t){return t.text?Ml(t.from.line+t.text.length-1,_o(t.text).length+(1==t.text.length?t.from.ch:0)):t.to};t.prototype={constructor:t,focus:function(){window.focus(),this.display.input.focus()},setOption:function(t,e){var n=this.options,r=n[t];(n[t]!=e||"mode"==t)&&(n[t]=e,ql.hasOwnProperty(t)&&Mn(this,ql[t])(this,e,r))},getOption:function(t){return this.options[t]},getDoc:function(){return this.doc},addKeyMap:function(t,e){this.state.keyMaps[e?"push":"unshift"](Pr(t))},removeKeyMap:function(t){for(var e=this.state.keyMaps,n=0;nn&&(Dr(this,i.head.line,t,!0),n=i.head.line,r==this.doc.sel.primIndex&&Or(this));else{var o=i.from(),l=i.to(),a=Math.max(n,o.line);n=Math.min(this.lastLine(),l.line-(l.ch?0:1))+1;for(var s=a;n>s;++s)Dr(this,s,t);var u=this.doc.sel.ranges;0==o.ch&&e.length==u.length&&u[r].from().ch>0&&xe(this.doc,r,new ce(o,u[r].to()),_a)}}}),getTokenAt:function(t,e){return Li(this,t,e)},getLineTokens:function(t,e){return Li(this,Ml(t),e,!0)},getTokenTypeAt:function(t){t=pe(this.doc,t);var e,n=Mi(this,Gi(this.doc,t.line)),r=0,i=(n.length-1)/2,o=t.ch;if(0==o)e=n[2];else for(;;){var l=r+i>>1;if((l?n[2*l-1]:0)>=o)i=l;else{if(!(n[2*l+1]a?e:0==a?null:e.slice(0,a-1)},getModeAt:function(e){var n=this.doc.mode;return n.innerMode?t.innerMode(n,this.getTokenAt(e).state).mode:n},getHelper:function(t,e){return this.getHelpers(t,e)[0]},getHelpers:function(t,e){var n=[];if(!Zl.hasOwnProperty(e))return Zl;var r=Zl[e],i=this.getModeAt(t);if("string"==typeof i[e])r[i[e]]&&n.push(r[i[e]]);else if(i[e])for(var o=0;or&&(t=r,n=!0);var i=Gi(this.doc,t);return sn(this,i,{top:0,left:0},e||"page").top+(n?this.doc.height-Yi(i):0)},defaultTextHeight:function(){return vn(this.display)},defaultCharWidth:function(){return mn(this.display)},setGutterMarker:An(function(t,e,n){return Hr(this.doc,t,"gutter",function(t){var r=t.gutterMarkers||(t.gutterMarkers={});return r[e]=n,!n&&Ho(r)&&(t.gutterMarkers=null),!0})}),clearGutter:An(function(t){var e=this,n=e.doc,r=n.first;n.iter(function(n){n.gutterMarkers&&n.gutterMarkers[t]&&(n.gutterMarkers[t]=null,Hn(e,r,"gutter"),Ho(n.gutterMarkers)&&(n.gutterMarkers=null)),++r})}),addLineWidget:An(function(t,e,n){return bi(this,t,e,n)}),removeLineWidget:function(t){t.clear()},lineInfo:function(t){if("number"==typeof t){if(!ve(this.doc,t))return null;var e=t;if(t=Gi(this.doc,t),!t)return null}else{var e=$i(t);if(null==e)return null}return{line:e,handle:t,text:t.text,gutterMarkers:t.gutterMarkers,textClass:t.textClass,bgClass:t.bgClass,wrapClass:t.wrapClass,widgets:t.widgets}},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(t,e,n,r,i){var o=this.display;t=fn(this,pe(this.doc,t));var l=t.bottom,a=t.left;if(e.style.position="absolute",e.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(e),o.sizer.appendChild(e),"over"==r)l=t.top;else if("above"==r||"near"==r){var s=Math.max(o.wrapper.clientHeight,this.doc.height),u=Math.max(o.sizer.clientWidth,o.lineSpace.clientWidth);("above"==r||t.bottom+e.offsetHeight>s)&&t.top>e.offsetHeight?l=t.top-e.offsetHeight:t.bottom+e.offsetHeight<=s&&(l=t.bottom),a+e.offsetWidth>u&&(a=u-e.offsetWidth)}e.style.top=l+"px",e.style.left=e.style.right="","right"==i?(a=o.sizer.clientWidth-e.offsetWidth,e.style.right="0px"):("left"==i?a=0:"middle"==i&&(a=(o.sizer.clientWidth-e.offsetWidth)/2),e.style.left=a+"px"),n&&Mr(this,a,l,a+e.offsetWidth,l+e.offsetHeight)},triggerOnKeyDown:An(lr),triggerOnKeyPress:An(ur),triggerOnKeyUp:sr,execCommand:function(t){return ta.hasOwnProperty(t)?ta[t](this):void 0},findPosH:function(t,e,n,r){var i=1;0>e&&(i=-1,e=-e);for(var o=0,l=pe(this.doc,t);e>o&&(l=Fr(this.doc,l,i,n,r),!l.hitSide);++o);return l},moveH:An(function(t,e){var n=this;n.extendSelectionsBy(function(r){return n.display.shift||n.doc.extend||r.empty()?Fr(n.doc,r.head,t,e,n.options.rtlMoveVisually):0>t?r.from():r.to()},Ma)}),deleteH:An(function(t,e){var n=this.doc.sel,r=this.doc;n.somethingSelected()?r.replaceSelection("",null,"+delete"):Er(this,function(n){var i=Fr(r,n.head,t,e,!1);return 0>t?{from:i,to:n.head}:{from:n.head,to:i}})}),findPosV:function(t,e,n,r){var i=1,o=r;0>e&&(i=-1,e=-e);for(var l=0,a=pe(this.doc,t);e>l;++l){var s=fn(this,a,"div");if(null==o?o=s.left:s.left=o,a=Ir(this,s,i,n),a.hitSide)break}return a},moveV:An(function(t,e){var n=this,r=this.doc,i=[],o=!n.display.shift&&!r.extend&&r.sel.somethingSelected();if(r.extendSelectionsBy(function(l){if(o)return 0>t?l.from():l.to();var a=fn(n,l.head,"div");null!=l.goalColumn&&(a.left=l.goalColumn),i.push(a.left);var s=Ir(n,a,t,e);return"page"==e&&l==r.sel.primary()&&Nr(n,null,cn(n,s,"div").top-a.top),s},Ma),i.length)for(var l=0;l0&&a(n.charAt(r-1));)--r;for(;i.5)&&l(this),Ca(this,"refresh",this)}),swapDoc:An(function(t){var e=this.doc;return e.cm=null,Ui(this,t),on(this),this.display.input.reset(),this.scrollTo(t.scrollLeft,t.scrollTop),this.curOp.forceScroll=!0,mo(this,"swapDoc",this,e),e}),getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},Co(t);var Gl=t.defaults={},ql=t.optionHandlers={},Kl=t.Init={toString:function(){return"CodeMirror.Init"}};Rr("value","",function(t,e){t.setValue(e)},!0),Rr("mode",null,function(t,e){t.doc.modeOption=e,n(t)},!0),Rr("indentUnit",2,n,!0),Rr("indentWithTabs",!1),Rr("smartIndent",!0),Rr("tabSize",4,function(t){r(t),on(t),Dn(t)},!0),Rr("specialChars",/[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g,function(t,e){t.options.specialChars=new RegExp(e.source+(e.test(" ")?"":"| "),"g"),t.refresh()},!0),Rr("specialCharPlaceholder",Wi,function(t){t.refresh()},!0),Rr("electricChars",!0),Rr("inputStyle",wl?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},!0),Rr("rtlMoveVisually",!Cl),Rr("wholeLineUpdateBefore",!0),Rr("theme","default",function(t){a(t),s(t)},!0),Rr("keyMap","default",function(e,n,r){var i=Pr(n),o=r!=t.Init&&Pr(r);o&&o.detach&&o.detach(e,i),i.attach&&i.attach(e,o||null)}),Rr("extraKeys",null),Rr("lineWrapping",!1,i,!0),Rr("gutters",[],function(t){d(t.options),s(t)},!0),Rr("fixedGutter",!0,function(t,e){t.display.gutters.style.left=e?k(t.display)+"px":"0",t.refresh()},!0),Rr("coverGutterNextToScrollbar",!1,function(t){y(t)},!0),Rr("scrollbarStyle","native",function(t){m(t),y(t),t.display.scrollbars.setScrollTop(t.doc.scrollTop),t.display.scrollbars.setScrollLeft(t.doc.scrollLeft)},!0),Rr("lineNumbers",!1,function(t){d(t.options),s(t)},!0),Rr("firstLineNumber",1,s,!0),Rr("lineNumberFormatter",function(t){return t},s,!0),Rr("showCursorWhenSelecting",!1,Oe,!0),Rr("resetSelectionOnContextMenu",!0),Rr("readOnly",!1,function(t,e){"nocursor"==e?(fr(t),t.display.input.blur(),t.display.disabled=!0):(t.display.disabled=!1,e||t.display.input.reset())}),Rr("disableInput",!1,function(t,e){e||t.display.input.reset()},!0),Rr("dragDrop",!0),Rr("cursorBlinkRate",530),Rr("cursorScrollMargin",0),Rr("cursorHeight",1,Oe,!0),Rr("singleCursorHeightPerLine",!0,Oe,!0),Rr("workTime",100),Rr("workDelay",100),Rr("flattenSpans",!0,r,!0),Rr("addModeClass",!1,r,!0),Rr("pollInterval",100),Rr("undoDepth",200,function(t,e){t.doc.history.undoDepth=e}),Rr("historyEventDelay",1250),Rr("viewportMargin",10,function(t){t.refresh()},!0),Rr("maxHighlightLength",1e4,r,!0),Rr("moveInputWithCursor",!0,function(t,e){e||t.display.input.resetPosition()}),Rr("tabindex",null,function(t,e){t.display.input.getField().tabIndex=e||""}),Rr("autofocus",null);var Vl=t.modes={},$l=t.mimeModes={};t.defineMode=function(e,n){t.defaults.mode||"null"==e||(t.defaults.mode=e),arguments.length>2&&(n.dependencies=Array.prototype.slice.call(arguments,2)),Vl[e]=n},t.defineMIME=function(t,e){$l[t]=e},t.resolveMode=function(e){if("string"==typeof e&&$l.hasOwnProperty(e))e=$l[e];else if(e&&"string"==typeof e.name&&$l.hasOwnProperty(e.name)){var n=$l[e.name];"string"==typeof n&&(n={name:n}),e=No(n,e),e.name=n.name}else if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+xml$/.test(e))return t.resolveMode("application/xml");return"string"==typeof e?{name:e}:e||{name:"null"}},t.getMode=function(e,n){var n=t.resolveMode(n),r=Vl[n.name];if(!r)return t.getMode(e,"text/plain");var i=r(e,n);if(Xl.hasOwnProperty(n.name)){var o=Xl[n.name];for(var l in o)o.hasOwnProperty(l)&&(i.hasOwnProperty(l)&&(i["_"+l]=i[l]),i[l]=o[l])}if(i.name=n.name,n.helperType&&(i.helperType=n.helperType),n.modeProps)for(var l in n.modeProps)i[l]=n.modeProps[l];return i},t.defineMode("null",function(){return{token:function(t){t.skipToEnd()}}}),t.defineMIME("text/plain","null");var Xl=t.modeExtensions={};t.extendMode=function(t,e){var n=Xl.hasOwnProperty(t)?Xl[t]:Xl[t]={};Oo(e,n)},t.defineExtension=function(e,n){t.prototype[e]=n},t.defineDocExtension=function(t,e){pa.prototype[t]=e},t.defineOption=Rr;var Yl=[];t.defineInitHook=function(t){Yl.push(t)};var Zl=t.helpers={};t.registerHelper=function(e,n,r){Zl.hasOwnProperty(e)||(Zl[e]=t[e]={_global:[]}),Zl[e][n]=r},t.registerGlobalHelper=function(e,n,r,i){t.registerHelper(e,n,i),Zl[e]._global.push({pred:r,val:i})};var Jl=t.copyState=function(t,e){if(e===!0)return e;if(t.copyState)return t.copyState(e);var n={};for(var r in e){var i=e[r];i instanceof Array&&(i=i.concat([])),n[r]=i}return n},Ql=t.startState=function(t,e,n){return t.startState?t.startState(e,n):!0};t.innerMode=function(t,e){for(;t.innerMode;){var n=t.innerMode(e);if(!n||n.mode==t)break;e=n.state,t=n.mode}return n||{mode:t,state:e}};var ta=t.commands={selectAll:function(t){t.setSelection(Ml(t.firstLine(),0),Ml(t.lastLine()),_a)},singleSelection:function(t){t.setSelection(t.getCursor("anchor"),t.getCursor("head"),_a)},killLine:function(t){Er(t,function(e){if(e.empty()){var n=Gi(t.doc,e.head.line).text.length;return e.head.ch==n&&e.head.line0)i=new Ml(i.line,i.ch+1),t.replaceRange(o.charAt(i.ch-1)+o.charAt(i.ch-2),Ml(i.line,i.ch-2),i,"+transpose");else if(i.line>t.doc.first){var l=Gi(t.doc,i.line-1).text;l&&t.replaceRange(o.charAt(0)+"\n"+l.charAt(l.length-1),Ml(i.line-1,l.length-1),Ml(i.line,1),"+transpose")}n.push(new ce(i,i))}t.setSelections(n)})},newlineAndIndent:function(t){Tn(t,function(){for(var e=t.listSelections().length,n=0;e>n;n++){var r=t.listSelections()[n];t.replaceRange("\n",r.anchor,r.head,"+input"),t.indentLine(r.from().line+1,null,!0),Or(t)}})},toggleOverwrite:function(t){t.toggleOverwrite()}},ea=t.keyMap={};ea.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},ea.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection","Alt-U":"redoSelection",fallthrough:"basic"},ea.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-D":"delWordAfter","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars"},ea.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]},ea["default"]=xl?ea.macDefault:ea.pcDefault,t.normalizeKeyMap=function(t){var e={};for(var n in t)if(t.hasOwnProperty(n)){var r=t[n];if(/^(name|fallthrough|(de|at)tach)$/.test(n))continue;if("..."==r){delete t[n];continue}for(var i=Mo(n.split(" "),zr),o=0;o=this.string.length},sol:function(){return this.pos==this.lineStart},peek:function(){return this.string.charAt(this.pos)||void 0},next:function(){return this.pose},eatSpace:function(){for(var t=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>t},skipToEnd:function(){this.pos=this.string.length},skipTo:function(t){var e=this.string.indexOf(t,this.pos);return e>-1?(this.pos=e,!0):void 0},backUp:function(t){this.pos-=t},column:function(){return this.lastColumnPos0?null:(r&&e!==!1&&(this.pos+=r[0].length),r)}var i=function(t){return n?t.toLowerCase():t},o=this.string.substr(this.pos,t.length);return i(o)==i(t)?(e!==!1&&(this.pos+=t.length),!0):void 0},current:function(){return this.string.slice(this.start,this.pos)},hideFirstChars:function(t,e){this.lineStart+=t;try{return e()}finally{this.lineStart-=t}}};var la=0,aa=t.TextMarker=function(t,e){this.lines=[],this.type=e,this.doc=t,this.id=++la};Co(aa),aa.prototype.clear=function(){if(!this.explicitlyCleared){var t=this.doc.cm,e=t&&!t.curOp;if(e&&yn(t),xo(this,"clear")){var n=this.find();n&&mo(this,"clear",n.from,n.to)}for(var r=null,i=null,o=0;ot.display.maxLineLength&&(t.display.maxLine=s,t.display.maxLineLength=u,t.display.maxLineChanged=!0)}null!=r&&t&&this.collapsed&&Dn(t,r,i+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,t&&Me(t.doc)),t&&mo(t,"markerCleared",t,this),e&&wn(t),this.parent&&this.parent.clear()}},aa.prototype.find=function(t,e){null==t&&"bookmark"==this.type&&(t=1);for(var n,r,i=0;in;++n){var i=this.lines[n];this.height-=i.height,xi(i),mo(i,"delete")}this.lines.splice(t,e)},collapse:function(t){t.push.apply(t,this.lines)},insertInner:function(t,e,n){this.height+=n,this.lines=this.lines.slice(0,t).concat(e).concat(this.lines.slice(t));for(var r=0;rt;++t)if(n(this.lines[t]))return!0}},Bi.prototype={chunkSize:function(){return this.size},removeInner:function(t,e){this.size-=e;for(var n=0;nt){var o=Math.min(e,i-t),l=r.height;if(r.removeInner(t,o),this.height-=l-r.height,i==o&&(this.children.splice(n--,1),r.parent=null),0==(e-=o))break;t=0}else t-=i}if(this.size-e<25&&(this.children.length>1||!(this.children[0]instanceof Pi))){var a=[];this.collapse(a),this.children=[new Pi(a)],this.children[0].parent=this}},collapse:function(t){for(var e=0;e=t){if(i.insertInner(t,e,n),i.lines&&i.lines.length>50){for(;i.lines.length>50;){var l=i.lines.splice(i.lines.length-25,25),a=new Pi(l);i.height-=a.height,this.children.splice(r+1,0,a),a.parent=this}this.maybeSpill()}break}t-=o}},maybeSpill:function(){if(!(this.children.length<=10)){var t=this;do{var e=t.children.splice(t.children.length-5,5),n=new Bi(e);if(t.parent){t.size-=n.size,t.height-=n.height;var r=To(t.parent.children,t);t.parent.children.splice(r+1,0,n)}else{var i=new Bi(t.children);i.parent=t,t.children=[i,n],t=i}n.parent=t.parent}while(t.children.length>10);t.parent.maybeSpill()}},iterN:function(t,e,n){for(var r=0;rt){var l=Math.min(e,o-t);if(i.iterN(t,l,n))return!0;if(0==(e-=l))break;t=0}else t-=o}}};var da=0,pa=t.Doc=function(t,e,n){if(!(this instanceof pa))return new pa(t,e,n);null==n&&(n=0),Bi.call(this,[new Pi([new ca("",null)])]),this.first=n,this.scrollTop=this.scrollLeft=0,this.cantEdit=!1,this.cleanGeneration=1,this.frontier=n;var r=Ml(n,0);this.sel=he(r),this.history=new Ji(null),this.id=++da,this.modeOption=e,"string"==typeof t&&(t=Ua(t)),zi(this,{from:r,to:r,text:t}),Le(this,he(r),_a)};pa.prototype=No(Bi.prototype,{constructor:pa,iter:function(t,e,n){n?this.iterN(t-this.first,e-t,n):this.iterN(this.first,this.first+this.size,t)},insert:function(t,e){for(var n=0,r=0;r=0;o--)br(this,r[o]);a?ke(this,a):this.cm&&Or(this.cm)}),undo:Nn(function(){xr(this,"undo")}),redo:Nn(function(){xr(this,"redo")}),undoSelection:Nn(function(){xr(this,"undo",!0)}),redoSelection:Nn(function(){xr(this,"redo",!0)}),setExtending:function(t){this.extend=t},getExtending:function(){return this.extend},historySize:function(){for(var t=this.history,e=0,n=0,r=0;r=t.ch)&&e.push(i.marker.parent||i.marker)}return e},findMarks:function(t,e,n){t=pe(this,t),e=pe(this,e);var r=[],i=t.line;return this.iter(t.line,e.line+1,function(o){var l=o.markedSpans;if(l)for(var a=0;as.to||null==s.from&&i!=t.line||i==e.line&&s.from>e.ch||n&&!n(s.marker)||r.push(s.marker.parent||s.marker)}++i}),r},getAllMarks:function(){var t=[];return this.iter(function(e){var n=e.markedSpans;if(n)for(var r=0;rt?(e=t,!0):(t-=i,void++n)}),pe(this,Ml(n,e))},indexFromPos:function(t){t=pe(this,t);var e=t.ch;return t.linee&&(e=t.from),null!=t.to&&t.toa||a>=e)return l+(e-o);l+=a-o,l+=n-l%n,o=a+1}},Na=[""],Oa=function(t){t.select()};bl?Oa=function(t){t.selectionStart=0,t.selectionEnd=t.value.length}:cl&&(Oa=function(t){try{t.select()}catch(e){}});var Wa,Da=/[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/,Ha=t.isWordChar=function(t){return/\w/.test(t)||t>"€"&&(t.toUpperCase()!=t.toLowerCase()||Da.test(t))},Ea=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;Wa=document.createRange?function(t,e,n,r){var i=document.createRange();return i.setEnd(r||t,n),i.setStart(t,e),i}:function(t,e,n){var r=document.body.createTextRange();try{r.moveToElementText(t.parentNode)}catch(i){return r}return r.collapse(!0),r.moveEnd("character",n),r.moveStart("character",e),r};var Fa=t.contains=function(t,e){if(3==e.nodeType&&(e=e.parentNode),t.contains)return t.contains(e);do if(11==e.nodeType&&(e=e.host),e==t)return!0;while(e=e.parentNode)};cl&&11>fl&&(zo=function(){try{return document.activeElement}catch(t){return document.body}});var Ia,Ra,za=t.rmClass=function(t,e){var n=t.className,r=Po(e).exec(n);if(r){var i=n.slice(r.index+r[0].length);t.className=n.slice(0,r.index)+(i?r[1]+i:"")}},Pa=t.addClass=function(t,e){var n=t.className;Po(e).test(n)||(t.className+=(n?" ":"")+e)},Ba=!1,ja=function(){if(cl&&9>fl)return!1;var t=Fo("div");return"draggable"in t||"dragDrop"in t}(),Ua=t.splitLines=3!="\n\nb".split(/\n/).length?function(t){for(var e=0,n=[],r=t.length;r>=e;){var i=t.indexOf("\n",e);-1==i&&(i=t.length);var o=t.slice(e,"\r"==t.charAt(i-1)?i-1:i),l=o.indexOf("\r");-1!=l?(n.push(o.slice(0,l)),e+=l+1):(n.push(o),e=i+1)}return n}:function(t){return t.split(/\r\n?|\n/)},Ga=window.getSelection?function(t){try{return t.selectionStart!=t.selectionEnd}catch(e){return!1}}:function(t){try{var e=t.ownerDocument.selection.createRange()}catch(n){}return e&&e.parentElement()==t?0!=e.compareEndPoints("StartToEnd",e):!1},qa=function(){var t=Fo("div");return"oncopy"in t?!0:(t.setAttribute("oncopy","return;"),"function"==typeof t.oncopy)}(),Ka=null,Va={3:"Enter",8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Ctrl",18:"Alt",19:"Pause",20:"CapsLock",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"PrintScrn",45:"Insert",46:"Delete",59:";",61:"=",91:"Mod",92:"Mod",93:"Mod",107:"=",109:"-",127:"Delete",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",63232:"Up",63233:"Down",63234:"Left",63235:"Right",63272:"Delete",63273:"Home",63275:"End",63276:"PageUp",63277:"PageDown",63302:"Insert"};t.keyNames=Va,function(){for(var t=0;10>t;t++)Va[t+48]=Va[t+96]=String(t);for(var t=65;90>=t;t++)Va[t]=String.fromCharCode(t);for(var t=1;12>=t;t++)Va[t+111]=Va[t+63235]="F"+t}();var $a,Xa=function(){function t(t){return 247>=t?n.charAt(t):t>=1424&&1524>=t?"R":t>=1536&&1773>=t?r.charAt(t-1536):t>=1774&&2220>=t?"r":t>=8192&&8203>=t?"w":8204==t?"b":"L"}function e(t,e,n){this.level=t,this.from=e,this.to=n}var n="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN",r="rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm",i=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,o=/[stwN]/,l=/[LRr]/,a=/[Lb1n]/,s=/[1n]/,u="L";return function(n){if(!i.test(n))return!1;for(var r,c=n.length,f=[],h=0;c>h;++h)f.push(r=t(n.charCodeAt(h)));for(var h=0,d=u;c>h;++h){var r=f[h];"m"==r?f[h]=d:d=r}for(var h=0,p=u;c>h;++h){var r=f[h];"1"==r&&"r"==p?f[h]="n":l.test(r)&&(p=r,"r"==r&&(f[h]="R"))}for(var h=1,d=f[0];c-1>h;++h){var r=f[h];"+"==r&&"1"==d&&"1"==f[h+1]?f[h]="1":","!=r||d!=f[h+1]||"1"!=d&&"n"!=d||(f[h]=d),d=r}for(var h=0;c>h;++h){var r=f[h];if(","==r)f[h]="N";else if("%"==r){for(var g=h+1;c>g&&"%"==f[g];++g);for(var v=h&&"!"==f[h-1]||c>g&&"1"==f[g]?"1":"N",m=h;g>m;++m)f[m]=v;h=g-1}}for(var h=0,p=u;c>h;++h){var r=f[h];"L"==p&&"1"==r?f[h]="L":l.test(r)&&(p=r)}for(var h=0;c>h;++h)if(o.test(f[h])){for(var g=h+1;c>g&&o.test(f[g]);++g);for(var y="L"==(h?f[h-1]:u),b="L"==(c>g?f[g]:u),v=y||b?"L":"R",m=h;g>m;++m)f[m]=v;h=g-1}for(var w,x=[],h=0;c>h;)if(a.test(f[h])){var C=h;for(++h;c>h&&a.test(f[h]);++h);x.push(new e(0,C,h))}else{var S=h,k=x.length;for(++h;c>h&&"L"!=f[h];++h);for(var m=S;h>m;)if(s.test(f[m])){m>S&&x.splice(k,0,new e(1,S,m));var L=m;for(++m;h>m&&s.test(f[m]);++m);x.splice(k,0,new e(2,L,m)),S=m}else++m;h>S&&x.splice(k,0,new e(1,S,h))}return 1==x[0].level&&(w=n.match(/^\s+/))&&(x[0].from=w[0].length,x.unshift(new e(0,0,w[0].length))),1==_o(x).level&&(w=n.match(/\s+$/))&&(_o(x).to-=w[0].length,x.push(new e(0,c-w[0].length,c))),x[0].level!=_o(x).level&&x.push(new e(x[0].level,c,c)),x}}();return t.version="5.0.0",t}),function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror"),require("../xml/xml"),require("../meta")):"function"==typeof define&&define.amd?define(["../../lib/codemirror","../xml/xml","../meta"],t):t(CodeMirror)}(function(t){"use strict";t.defineMode("markdown",function(e,n){function r(n){if(t.findModeByName){var r=t.findModeByName(n);r&&(n=r.mime||r.mimes[0])}var i=t.getMode(e,n);return"null"==i.name?null:i}function i(t,e,n){return e.f=e.inline=n,n(t,e)}function o(t,e,n){return e.f=e.block=n,n(t,e)}function l(t){return t.linkTitle=!1,t.em=!1,t.strong=!1,t.strikethrough=!1,t.quote=0,x||t.f!=s||(t.f=d,t.block=a),t.trailingSpace=0,t.trailingSpaceNewLine=!1,t.thisLineHasContent=!1,null}function a(t,e){var o=t.sol(),l=e.list!==!1;e.list!==!1&&e.indentationDiff>=0?(e.indentationDiff<4&&(e.indentation-=e.indentationDiff),e.list=null):e.list!==!1&&e.indentation>0?(e.list=null,e.listDepth=Math.floor(e.indentation/4)):e.list!==!1&&(e.list=!1,e.listDepth=0);var a=null;if(e.indentationDiff>=4)return e.indentation-=4,t.skipToEnd(),L;if(t.eatSpace())return null;if(a=t.match(G))return e.header=a[0].length<=6?a[0].length:6,n.highlightFormatting&&(e.formatting="header"),e.f=e.inline,f(e);if(e.prevLineHasContent&&(a=t.match(q)))return e.header="="==a[0].charAt(0)?1:2,n.highlightFormatting&&(e.formatting="header"),e.f=e.inline,f(e);if(t.eat(">"))return e.indentation++,e.quote=o?1:e.quote+1,n.highlightFormatting&&(e.formatting="quote"),t.eatSpace(),f(e);if("["===t.peek())return i(t,e,m);if(t.match(P,!0))return N;if((!e.prevLineHasContent||l)&&(t.match(B,!1)||t.match(j,!1))){var s=null;return t.match(B,!0)?s="ul":(t.match(j,!0),s="ol"),e.indentation+=4,e.list=!0,e.listDepth++,n.taskLists&&t.match(U,!1)&&(e.taskList=!0),e.f=e.inline,n.highlightFormatting&&(e.formatting=["list","list-"+s]),f(e)}return n.fencedCodeBlocks&&t.match(/^```[ \t]*([\w+#]*)/,!0)?(e.localMode=r(RegExp.$1),e.localMode&&(e.localState=e.localMode.startState()),e.f=e.block=u,n.highlightFormatting&&(e.formatting="code-block"),e.code=!0,f(e)):i(t,e,e.inline)}function s(t,e){var n=C.token(t,e.htmlState);return(x&&null===e.htmlState.tagStart&&!e.htmlState.context||e.md_inside&&t.current().indexOf(">")>-1)&&(e.f=d,e.block=a,e.htmlState=null),n}function u(t,e){return t.sol()&&t.match("```",!1)?(e.localMode=e.localState=null,e.f=e.block=c,null):e.localMode?e.localMode.token(t,e.localState):(t.skipToEnd(),L)}function c(t,e){t.match("```"),e.block=a,e.f=d,n.highlightFormatting&&(e.formatting="code-block"),e.code=!0;var r=f(e);return e.code=!1,r}function f(t){var e=[];if(t.formatting){e.push(W),"string"==typeof t.formatting&&(t.formatting=[t.formatting]);for(var r=0;r=t.quote?W+"-"+t.formatting[r]+"-"+t.quote:"error")}if(t.taskOpen)return e.push("meta"),e.length?e.join(" "):null;if(t.taskClosed)return e.push("property"),e.length?e.join(" "):null;if(t.linkHref)return e.push(F),e.length?e.join(" "):null;if(t.strong&&e.push(R),t.em&&e.push(I),t.strikethrough&&e.push(z),t.linkText&&e.push(E),t.code&&e.push(L),t.header&&(e.push(k),e.push(k+"-"+t.header)),t.quote&&(e.push(_),e.push(!n.maxBlockquoteDepth||n.maxBlockquoteDepth>=t.quote?_+"-"+t.quote:_+"-"+n.maxBlockquoteDepth)),t.list!==!1){var i=(t.listDepth-1)%3;e.push(i?1===i?M:A:T)}return t.trailingSpaceNewLine?e.push("trailing-space-new-line"):t.trailingSpace&&e.push("trailing-space-"+(t.trailingSpace%2?"a":"b")),e.length?e.join(" "):null}function h(t,e){return t.match(K,!0)?f(e):void 0}function d(e,r){var i=r.text(e,r);if("undefined"!=typeof i)return i;if(r.list)return r.list=null,f(r);if(r.taskList){var l="x"!==e.match(U,!0)[1];return l?r.taskOpen=!0:r.taskClosed=!0,n.highlightFormatting&&(r.formatting="task"),r.taskList=!1,f(r)}if(r.taskOpen=!1,r.taskClosed=!1,r.header&&e.match(/^#+$/,!0))return n.highlightFormatting&&(r.formatting="header"),f(r);var a=e.sol(),u=e.next();if("\\"===u&&(e.next(),n.highlightFormatting)){var c=f(r);return c?c+" formatting-escape":"formatting-escape"}if(r.linkTitle){r.linkTitle=!1;var h=u;"("===u&&(h=")"),h=(h+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1");var d="^\\s*(?:[^"+h+"\\\\]+|\\\\\\\\|\\\\.)"+h;if(e.match(new RegExp(d),!0))return F}if("`"===u){var v=r.formatting;n.highlightFormatting&&(r.formatting="code");var m=f(r),y=e.pos;e.eatWhile("`");var b=1+e.pos-y;return r.code?b===S?(r.code=!1,m):(r.formatting=v,f(r)):(S=b,r.code=!0,f(r))}if(r.code)return f(r);if("!"===u&&e.match(/\[[^\]]*\] ?(?:\(|\[)/,!1))return e.match(/\[[^\]]*\]/),r.inline=r.f=g,O;if("["===u&&e.match(/.*\](\(.*\)| ?\[.*\])/,!1))return r.linkText=!0,n.highlightFormatting&&(r.formatting="link"),f(r);if("]"===u&&r.linkText&&e.match(/\(.*\)| ?\[.*\]/,!1)){n.highlightFormatting&&(r.formatting="link");var c=f(r);return r.linkText=!1,r.inline=r.f=g,c}if("<"===u&&e.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/,!1)){r.f=r.inline=p,n.highlightFormatting&&(r.formatting="link");var c=f(r);return c?c+=" ":c="",c+D}if("<"===u&&e.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/,!1)){r.f=r.inline=p,n.highlightFormatting&&(r.formatting="link");var c=f(r);return c?c+=" ":c="",c+H}if("<"===u&&e.match(/^\w/,!1)){if(-1!=e.string.indexOf(">")){var w=e.string.substring(1,e.string.indexOf(">"));/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(w)&&(r.md_inside=!0)}return e.backUp(1),r.htmlState=t.startState(C),o(e,r,s)}if("<"===u&&e.match(/^\/\w*?>/))return r.md_inside=!1,"tag";var x=!1;if(!n.underscoresBreakWords&&"_"===u&&"_"!==e.peek()&&e.match(/(\w)/,!1)){var k=e.pos-2;if(k>=0){var L=e.string.charAt(k);"_"!==L&&L.match(/(\w)/,!1)&&(x=!0)}}if("*"===u||"_"===u&&!x)if(a&&" "===e.peek());else{if(r.strong===u&&e.eat(u)){n.highlightFormatting&&(r.formatting="strong");var m=f(r);return r.strong=!1,m}if(!r.strong&&e.eat(u))return r.strong=u,n.highlightFormatting&&(r.formatting="strong"),f(r);if(r.em===u){n.highlightFormatting&&(r.formatting="em");var m=f(r);return r.em=!1,m}if(!r.em)return r.em=u,n.highlightFormatting&&(r.formatting="em"),f(r)}else if(" "===u&&(e.eat("*")||e.eat("_"))){if(" "===e.peek())return f(r);e.backUp(1)}if(n.strikethrough)if("~"===u&&e.eatWhile(u)){if(r.strikethrough){n.highlightFormatting&&(r.formatting="strikethrough");var m=f(r);return r.strikethrough=!1,m}if(e.match(/^[^\s]/,!1))return r.strikethrough=!0,n.highlightFormatting&&(r.formatting="strikethrough"),f(r)}else if(" "===u&&e.match(/^~~/,!0)){if(" "===e.peek())return f(r);e.backUp(2)}return" "===u&&(e.match(/ +$/,!1)?r.trailingSpace++:r.trailingSpace&&(r.trailingSpaceNewLine=!0)),f(r)}function p(t,e){var r=t.next();if(">"===r){e.f=e.inline=d,n.highlightFormatting&&(e.formatting="link");var i=f(e);return i?i+=" ":i="",i+D}return t.match(/^[^>]+/,!0),D}function g(t,e){if(t.eatSpace())return null;var r=t.next();return"("===r||"["===r?(e.f=e.inline=v("("===r?")":"]"),n.highlightFormatting&&(e.formatting="link-string"),e.linkHref=!0,f(e)):"error"}function v(t){return function(e,r){var i=e.next();if(i===t){r.f=r.inline=d,n.highlightFormatting&&(r.formatting="link-string");var o=f(r);return r.linkHref=!1,o}return e.match(w(t),!0)&&e.backUp(1),r.linkHref=!0,f(r)}}function m(t,e){return t.match(/^[^\]]*\]:/,!1)?(e.f=y,t.next(),n.highlightFormatting&&(e.formatting="link"),e.linkText=!0,f(e)):i(t,e,d)}function y(t,e){if(t.match(/^\]:/,!0)){e.f=e.inline=b,n.highlightFormatting&&(e.formatting="link");var r=f(e);return e.linkText=!1,r}return t.match(/^[^\]]+/,!0),E}function b(t,e){return t.eatSpace()?null:(t.match(/^[^\s]+/,!0),void 0===t.peek()?e.linkTitle=!0:t.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/,!0),e.f=e.inline=d,F)}function w(t){return V[t]||(t=(t+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1"),V[t]=new RegExp("^(?:[^\\\\]|\\\\.)*?("+t+")")),V[t]}var x=t.modes.hasOwnProperty("xml"),C=t.getMode(e,x?{name:"xml",htmlMode:!0}:"text/plain");void 0===n.highlightFormatting&&(n.highlightFormatting=!1),void 0===n.maxBlockquoteDepth&&(n.maxBlockquoteDepth=0),void 0===n.underscoresBreakWords&&(n.underscoresBreakWords=!0),void 0===n.fencedCodeBlocks&&(n.fencedCodeBlocks=!1),void 0===n.taskLists&&(n.taskLists=!1),void 0===n.strikethrough&&(n.strikethrough=!1);var S=0,k="header",L="comment",_="quote",T="variable-2",M="variable-3",A="keyword",N="hr",O="tag",W="formatting",D="link",H="link",E="link",F="string",I="em",R="strong",z="strikethrough",P=/^([*\-=_])(?:\s*\1){2,}\s*$/,B=/^[*\-+]\s+/,j=/^[0-9]+\.\s+/,U=/^\[(x| )\](?=\s)/,G=/^#+/,q=/^(?:\={1,}|-{1,})$/,K=/^[^#!\[\]*_\\<>` "'(~]+/,V=[],$={startState:function(){return{f:a,prevLineHasContent:!1,thisLineHasContent:!1,block:a,htmlState:null,indentation:0,inline:d,text:h,formatting:!1,linkText:!1,linkHref:!1,linkTitle:!1,em:!1,strong:!1,header:0,taskList:!1,list:!1,listDepth:0,quote:0,trailingSpace:0,trailingSpaceNewLine:!1,strikethrough:!1}},copyState:function(e){return{f:e.f,prevLineHasContent:e.prevLineHasContent,thisLineHasContent:e.thisLineHasContent,block:e.block,htmlState:e.htmlState&&t.copyState(C,e.htmlState),indentation:e.indentation,localMode:e.localMode,localState:e.localMode?t.copyState(e.localMode,e.localState):null,inline:e.inline,text:e.text,formatting:!1,linkTitle:e.linkTitle,em:e.em,strong:e.strong,strikethrough:e.strikethrough,header:e.header,taskList:e.taskList,list:e.list,listDepth:e.listDepth,quote:e.quote,trailingSpace:e.trailingSpace,trailingSpaceNewLine:e.trailingSpaceNewLine,md_inside:e.md_inside}},token:function(t,e){if(e.formatting=!1,t.sol()){var n=!!e.header;if(e.header=0,t.match(/^\s*$/,!0)||n)return e.prevLineHasContent=!1,l(e),n?this.token(t,e):null;e.prevLineHasContent=e.thisLineHasContent,e.thisLineHasContent=!0,e.taskList=!1,e.code=!1,e.trailingSpace=0,e.trailingSpaceNewLine=!1,e.f=e.block;var r=t.match(/^\s*/,!0)[0].replace(/\t/g," ").length,i=4*Math.floor((r-e.indentation)/4);i>4&&(i=4);var o=e.indentation+i;if(e.indentationDiff=o-e.indentation,e.indentation=o,r>0)return null}return e.f(t,e)},innerMode:function(t){return t.block==s?{state:t.htmlState,mode:C}:t.localState?{state:t.localState,mode:t.localMode}:{state:t,mode:$}},blankLine:l,getType:f,fold:"markdown"};return $},"xml"),t.defineMIME("text/x-markdown","markdown")}),function(){function t(t,e){if(t!==e){var n=t===t,r=e===e;if(t>e||!n||"undefined"==typeof t&&r)return 1;if(e>t||!r||"undefined"==typeof e&&n)return-1}return 0}function e(t,e,n){if(e!==e)return h(t,n);for(var r=n-1,i=t.length;++r-1;);return n -}function l(t,e){for(var n=t.length;n--&&e.indexOf(t.charAt(n))>-1;);return n}function a(e,n){return t(e.criteria,n.criteria)||e.index-n.index}function s(e,n,r){for(var i=-1,o=e.criteria,l=n.criteria,a=o.length,s=r.length;++i=s?u:u*(r[i]?1:-1)}return e.index-n.index}function u(t){return Re[t]}function c(t){return ze[t]}function f(t){return"\\"+je[t]}function h(t,e,n){for(var r=t.length,i=e+(n?0:-1);n?i--:++i=t&&t>=9&&13>=t||32==t||160==t||5760==t||6158==t||t>=8192&&(8202>=t||8232==t||8233==t||8239==t||8287==t||12288==t||65279==t)}function g(t,e){for(var n=-1,r=t.length,i=-1,o=[];++ne,r=Cr(0,t.length,this.__views__),i=r.start,o=r.end,l=o-i,a=n?o:i-1,s=Na(l,this.__takeCount__),u=this.__iteratees__,c=u?u.length:0,f=0,h=[];t:for(;l--&&s>f;){a+=e;for(var d=-1,p=t[a];++dg.index:a-1?g.count++>=y:!v(p)))continue t}}else{var b=v(p);if(m==R)p=b;else if(!b){if(m==I)continue t;break t}}}h[f++]=p}return h}function je(){this.__data__={}}function Ue(t){return this.has(t)&&delete this.__data__[t]}function Ge(t){return"__proto__"==t?x:this.__data__[t]}function qe(t){return"__proto__"!=t&&la.call(this.__data__,t)}function Ke(t,e){return"__proto__"!=t&&(this.__data__[t]=e),this}function Ve(t){var e=t?t.length:0;for(this.data={hash:_a(null),set:new ba};e--;)this.push(t[e])}function Ye(t,e){var n=t.data,r="string"==typeof e||No(e)?n.set.has(e):n.hash[e];return r?0:-1}function Ze(t){var e=this.data;"string"==typeof t||No(t)?e.set.add(t):e.hash[t]=!0}function Je(t,e){var n=-1,r=t.length;for(e||(e=Gl(r));++nr&&(r=i)}return r}function ln(t){for(var e=-1,n=t.length,r=Fa;++ei&&(r=i)}return r}function an(t,e,n,r){var i=-1,o=t.length;for(r&&o&&(n=t[++i]);++i=200?Ka(n):null,u=n.length;s&&(l=Ye,a=!1,n=s);t:for(;++on&&(n=-n>i?0:i+n),r="undefined"==typeof r||r>i?i:+r||0,0>r&&(r+=i),i=n>r?0:r>>>0,n>>>=0;i>n;)t[n++]=e;return t}function kn(t,e){var n=[];return wn(t,function(t,r,i){e(t,r,i)&&n.push(t)}),n}function Ln(t,e,n,r){var i;return n(t,function(t,n,o){return e(t,n,o)?(i=r?n:t,!1):void 0}),i}function _n(t,e,n,r){for(var i=r-1,o=t.length,l=-1,a=[];++ie&&(e=-e>i?0:i+e),n="undefined"==typeof n||n>i?i:+n||0,0>n&&(n+=i),i=e>n?0:n-e>>>0,e>>>=0;for(var o=Gl(i);++r=200,s=a?Ka():null,u=[];s?(i=Ye,l=!1):(a=!1,s=n?[]:u);t:for(;++r=i){for(;i>r;){var o=r+i>>>1,l=t[o];(n?e>=l:e>l)?r=o+1:i=o}return i}return tr(t,e,Ol,n)}function tr(t,e,n,r){e=n(e);for(var i=0,o=t?t.length:0,l=e!==e,a="undefined"==typeof e;o>i;){var s=ga((i+o)/2),u=n(t[s]),c=u===u;if(l)var f=c||r;else f=a?c&&(r||"undefined"!=typeof u):r?e>=u:e>u;f?i=s+1:o=s}return Na(o,Ra)}function er(t,e,n){if("function"!=typeof t)return Ol;if("undefined"==typeof e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 3:return function(n,r,i){return t.call(e,n,r,i)};case 4:return function(n,r,i,o){return t.call(e,n,r,i,o)};case 5:return function(n,r,i,o,l){return t.call(e,n,r,i,o,l)}}return function(){return t.apply(e,arguments)}}function nr(t){return ha.call(t,0)}function rr(t,e,n){for(var r=n.length,i=-1,o=Aa(t.length-r,0),l=-1,a=e.length,s=Gl(o+a);++ln||null==r)return r;var i=e[n-2],o=e[n-1],l=e[3];n>3&&"function"==typeof i?(i=er(i,o,5),n-=2):(i=n>2&&"function"==typeof o?o:null,n-=i?1:0),l&&Mr(e[1],e[2],l)&&(i=3==n?null:i,n=2);for(var a=0;++aw){var T=a?Je(a):null,N=Aa(u-w,0),O=p?_:null,W=p?null:_,D=p?C:null,H=p?null:C;e|=p?M:A,e&=~(p?A:M),v||(e&=~(S|k));var E=hr(t,e,n,D,O,H,W,T,s,N);return E.placeholder=L,E}}var F=h?n:this;d&&(t=F[b]),a&&(C=Hr(C,a)),f&&s=e||!Ta(e))return"";var i=e-r;return n=null==n?" ":n+"",bl(n,da(i/n.length)).slice(0,i)}function pr(t,e,n,r){function i(){for(var e=-1,a=arguments.length,s=-1,u=r.length,c=Gl(a+u);++ss))return!1;for(;c&&++as:s>i)||s===r&&s===o)&&(i=s,o=t)}),o}function wr(t,e,n){var r=V.callback||Al;return r=r===Al?vn:r,n?r(t,e,n):r}function xr(t,n,r){var i=V.indexOf||Qr;return i=i===Qr?e:i,t?i(t,n,r):i}function Cr(t,e,n){for(var r=-1,i=n?n.length:0;++r-1&&t%1==0&&e>t}function Mr(t,e,n){if(!No(n))return!1;var r=typeof e;if("number"==r)var i=n.length,o=Ar(i)&&Tr(e,i);else o="string"==r&&e in n;if(o){var l=n[e];return t===t?t===l:l!==l}return!1}function Ar(t){return"number"==typeof t&&t>-1&&t%1==0&&Ba>=t}function Nr(t){return t===t&&(0===t?1/t>0:!No(t))}function Or(t,e){var n=t[1],r=e[1],i=n|r,o=O|N,l=S|k,a=o|l|L|T,s=n&O&&!(r&O),u=n&N&&!(r&N),c=(u?t:e)[7],f=(s?t:e)[8],h=!(n>=N&&r>l||n>l&&r>=N),d=i>=o&&a>=i&&(N>n||(u||s)&&c.length<=f);if(!h&&!d)return t;r&S&&(t[2]=e[2],i|=n&S?0:L);var p=e[3];if(p){var v=t[3];t[3]=v?rr(v,p,e[4]):Je(p),t[4]=v?g(t[3],P):Je(e[4])}return p=e[5],p&&(v=t[5],t[5]=v?ir(v,p,e[6]):Je(p),t[6]=v?g(t[5],P):Je(e[6])),p=e[7],p&&(t[7]=Je(p)),r&O&&(t[8]=null==t[8]?e[8]:Na(t[8],e[8])),null==t[9]&&(t[9]=e[9]),t[0]=e[0],t[1]=i,t}function Wr(t,e){t=Rr(t);for(var n=-1,r=e.length,i={};++nr;)l[++o]=Kn(t,r,r+=e);return l}function Br(t){for(var e=-1,n=t?t.length:0,r=-1,i=[];++ee?0:e)):[]}function Gr(t,e,n){var r=t?t.length:0;return r?((n?Mr(t,e,n):null==e)&&(e=1),e=r-(+e||0),Kn(t,0,0>e?0:e)):[]}function qr(t,e,n){var r=t?t.length:0;if(!r)return[];for(e=wr(e,n,3);r--&&e(t[r],r,t););return Kn(t,0,r+1)}function Kr(t,e,n){var r=t?t.length:0;if(!r)return[];var i=-1;for(e=wr(e,n,3);++ir?Aa(i+r,0):r;else if(r){var o=Qn(t,n),l=t[o];return(n===n?n===l:l!==l)?o:-1}return e(t,n,r||0)}function ti(t){return Gr(t,1)}function ei(){for(var t=[],n=-1,r=arguments.length,i=[],o=xr(),l=o==e;++n=120?Ka(n&&a):null))}r=t.length;var s=t[0],u=-1,c=s?s.length:0,f=[],h=i[0];t:for(;++un?Aa(r+n,0):Na(n||0,r-1))+1;else if(n){i=Qn(t,e,!0)-1;var o=t[i];return(e===e?e===o:o!==o)?i:-1}if(e!==e)return h(t,i,!0);for(;i--;)if(t[i]===e)return i;return-1}function ii(){var t=arguments,e=t[0];if(!e||!e.length)return e;for(var n=0,r=xr(),i=t.length;++n-1;)xa.call(e,o,1);return e}function oi(t){return Un(t||[],_n(arguments,!1,!1,1))}function li(t,e,n){var r=-1,i=t?t.length:0,o=[];for(e=wr(e,n,3);++re?0:e)):[]}function hi(t,e,n){var r=t?t.length:0;return r?((n?Mr(t,e,n):null==e)&&(e=1),e=r-(+e||0),Kn(t,0>e?0:e)):[]}function di(t,e,n){var r=t?t.length:0;if(!r)return[];for(e=wr(e,n,3);r--&&e(t[r],r,t););return Kn(t,r+1)}function pi(t,e,n){var r=t?t.length:0;if(!r)return[];var i=-1;for(e=wr(e,n,3);++i>>0,r=Gl(n);++e-1?t[r]:x}return e=wr(e,n,3),Ln(t,e,wn)}function Ei(t,e,n){return e=wr(e,n,3),Ln(t,e,xn)}function Fi(t,e){return Hi(t,Rn(e))}function Ii(t,e,n){return"function"==typeof e&&"undefined"==typeof n&&ns(t)?Qe(t,e):wn(t,er(e,n,3))}function Ri(t,e,n){return"function"==typeof e&&"undefined"==typeof n&&ns(t)?tn(t,e):xn(t,er(e,n,3))}function zi(t,e,n){var r=t?t.length:0;return Ar(r)||(t=ll(t),r=t.length),r?(n="number"==typeof n?0>n?Aa(r+n,0):n||0:0,"string"==typeof t||!ns(t)&&Io(t)?r>n&&t.indexOf(e,n)>-1:xr(t,e,n)>-1):!1}function Pi(t,e){return Dn(t,e,Kn(arguments,2))}function Bi(t,e,n){var r=ns(t)?rn:In;return e=wr(e,n,3),r(t,e)}function ji(t,e){return Bi(t,jn(e))}function Ui(t,e,n,r){var i=ns(t)?an:qn;return i(t,wr(e,r,4),n,arguments.length<3,wn)}function Gi(t,e,n,r){var i=ns(t)?sn:qn;return i(t,wr(e,r,4),n,arguments.length<3,xn)}function qi(t,e,n){var r=ns(t)?nn:kn;return e=wr(e,n,3),r(t,function(t,n,r){return!e(t,n,r)})}function Ki(t,e,n){if(n?Mr(t,e,n):null==e){t=Ir(t);var r=t.length;return r>0?t[Gn(0,r-1)]:x}var i=Vi(t);return i.length=Na(0>e?0:+e||0,i.length),i}function Vi(t){t=Ir(t);for(var e=-1,n=t.length,r=Gl(n);++e0?n=e.apply(this,arguments):e=null,n}}function ro(t,e){var n=S;if(arguments.length>2){var r=Kn(arguments,2),i=g(r,ro.placeholder);n|=M}return gr(t,n,e,r,i)}function io(t){return gn(t,arguments.length>1?_n(arguments,!1,!1,1):Yo(t))}function oo(t,e){var n=S|k;if(arguments.length>2){var r=Kn(arguments,2),i=g(r,oo.placeholder);n|=M}return gr(e,n,t,r,i)}function lo(t,e,n){n&&Mr(t,e,n)&&(e=null);var r=gr(t,_,null,null,null,null,null,e);return r.placeholder=lo.placeholder,r}function ao(t,e,n){n&&Mr(t,e,n)&&(e=null);var r=gr(t,T,null,null,null,null,null,e);return r.placeholder=ao.placeholder,r}function so(t,e,n){function r(){h&&pa(h),s&&pa(s),s=h=d=x}function i(){var n=e-(Qa()-c);if(0>=n||n>e){s&&pa(s);var r=d;s=h=d=x,r&&(p=Qa(),u=t.apply(f,a),h||s||(a=f=null))}else h=wa(i,n)}function o(){h&&pa(h),s=h=d=x,(v||g!==e)&&(p=Qa(),u=t.apply(f,a),h||s||(a=f=null))}function l(){if(a=arguments,c=Qa(),f=this,d=v&&(h||!m),g===!1)var n=m&&!h;else{s||m||(p=c);var r=g-(c-p),l=0>=r||r>g;l?(s&&(s=pa(s)),p=c,u=t.apply(f,a)):s||(s=wa(o,r))}return l&&h?h=pa(h):h||e===g||(h=wa(i,e)),n&&(l=!0,u=t.apply(f,a)),!l||h||s||(a=f=null),u}var a,s,u,c,f,h,d,p=0,g=!1,v=!0;if("function"!=typeof t)throw new Ql(z);if(e=0>e?0:+e||0,n===!0){var m=!0;v=!1}else No(n)&&(m=n.leading,g="maxWait"in n&&Aa(+n.maxWait||0,e),v="trailing"in n?n.trailing:v);return l.cancel=r,l}function uo(t){return yn(t,1,arguments,1)}function co(t,e){return yn(t,e,arguments,2)}function fo(t,e){if("function"!=typeof t||e&&"function"!=typeof e)throw new Ql(z);var n=function(){var r=arguments,i=n.cache,o=e?e.apply(this,r):r[0];if(i.has(o))return i.get(o);var l=t.apply(this,r);return i.set(o,l),l};return n.cache=new fo.Cache,n}function ho(t){if("function"!=typeof t)throw new Ql(z);return function(){return!t.apply(this,arguments)}}function po(t){return no(t,2)}function go(t){var e=Kn(arguments,1),n=g(e,go.placeholder);return gr(t,M,null,e,n)}function vo(t){var e=Kn(arguments,1),n=g(e,vo.placeholder);return gr(t,A,null,e,n)}function mo(t){var e=_n(arguments,!1,!1,1);return gr(t,N,null,null,null,e)}function yo(t){if("function"!=typeof t)throw new Ql(z);return function(e){return t.apply(this,e)}}function bo(t,e,n){var r=!0,i=!0;if("function"!=typeof t)throw new Ql(z);return n===!1?r=!1:No(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),Ie.leading=r,Ie.maxWait=+e,Ie.trailing=i,so(t,e,Ie)}function wo(t,e){return e=null==e?Ol:e,gr(e,M,null,[t],[])}function xo(t,e,n,r){return e&&"boolean"!=typeof e&&Mr(t,e,n)?e=!1:"function"==typeof e&&(r=n,n=e,e=!1),n="function"==typeof n&&er(n,r,1),mn(t,e,n)}function Co(t,e,n){return e="function"==typeof e&&er(e,n,1),mn(t,!0,e)}function So(t){var e=d(t)?t.length:x;return Ar(e)&&sa.call(t)==B||!1}function ko(t){return t===!0||t===!1||d(t)&&sa.call(t)==U||!1}function Lo(t){return d(t)&&sa.call(t)==G||!1}function _o(t){return t&&1===t.nodeType&&d(t)&&sa.call(t).indexOf("Element")>-1||!1}function To(t){if(null==t)return!0;var e=t.length;return Ar(e)&&(ns(t)||Io(t)||So(t)||d(t)&&is(t.splice))?!e:!as(t).length}function Mo(t,e,n,r){if(n="function"==typeof n&&er(n,r,3),!n&&Nr(t)&&Nr(e))return t===e;var i=n?n(t,e):x;return"undefined"==typeof i?Hn(t,e,n):!!i}function Ao(t){return d(t)&&"string"==typeof t.message&&sa.call(t)==q||!1}function No(t){var e=typeof t;return"function"==e||t&&"object"==e||!1}function Oo(t,e,n,r){var i=as(e),o=i.length;if(n="function"==typeof n&&er(n,r,3),!n&&1==o){var l=i[0],a=e[l];if(Nr(a))return null!=t&&a===t[l]&&la.call(t,l)}for(var s=Gl(o),u=Gl(o);o--;)a=s[o]=e[i[o]],u[o]=Nr(a);return Fn(t,i,s,u,n)}function Wo(t){return Eo(t)&&t!=+t}function Do(t){return null==t?!1:sa.call(t)==K?ca.test(ia.call(t)):d(t)&&ke.test(t)||!1}function Ho(t){return null===t}function Eo(t){return"number"==typeof t||d(t)&&sa.call(t)==$||!1}function Fo(t){return d(t)&&sa.call(t)==Y||!1}function Io(t){return"string"==typeof t||d(t)&&sa.call(t)==J||!1}function Ro(t){return d(t)&&Ar(t.length)&&Ee[sa.call(t)]||!1}function zo(t){return"undefined"==typeof t}function Po(t){var e=t?t.length:0;return Ar(e)?e?Je(t):[]:ll(t)}function Bo(t){return pn(t,Qo(t))}function jo(t,e,n){var r=Ga(t);return n&&Mr(t,e,n)&&(e=null),e?pn(e,r,as(e)):r}function Uo(t){if(null==t)return t;var e=Je(arguments);return e.push(cn),ls.apply(x,e)}function Go(t,e,n){return e=wr(e,n,3),Ln(t,e,Nn,!0)}function qo(t,e,n){return e=wr(e,n,3),Ln(t,e,On,!0)}function Ko(t,e,n){return("function"!=typeof e||"undefined"!=typeof n)&&(e=er(e,n,3)),Tn(t,e,Qo)}function Vo(t,e,n){return e=er(e,n,3),Mn(t,e,Qo)}function $o(t,e,n){return("function"!=typeof e||"undefined"!=typeof n)&&(e=er(e,n,3)),Nn(t,e)}function Xo(t,e,n){return e=er(e,n,3),Mn(t,e,as)}function Yo(t){return Wn(t,Qo(t))}function Zo(t,e){return t?la.call(t,e):!1}function Jo(t,e,n){n&&Mr(t,e,n)&&(e=null);for(var r=-1,i=as(t),o=i.length,l={};++r0;++r=e&&n>t}function ul(t,e,n){n&&Mr(t,e,n)&&(e=n=null);var r=null==t,i=null==e;if(null==n&&(i&&"boolean"==typeof t?(n=t,t=1):"boolean"==typeof e&&(n=e,i=!0)),r&&i&&(e=1,i=!1),t=+t||0,i?(e=t,t=0):e=+e||0,n||t%1||e%1){var o=Ha();return Na(t+o*(e-t+parseFloat("1e-"+((o+"").length-1))),e)}return Gn(t,e)}function cl(t){return t=r(t),t&&t.charAt(0).toUpperCase()+t.slice(1)}function fl(t){return t=r(t),t&&t.replace(Le,u)}function hl(t,e,n){t=r(t),e+="";var i=t.length;return n="undefined"==typeof n?i:Na(0>n?0:+n||0,i),n-=e.length,n>=0&&t.indexOf(e,n)==n}function dl(t){return t=r(t),t&&ve.test(t)?t.replace(pe,c):t}function pl(t){return t=r(t),t&&Me.test(t)?t.replace(Te,"\\$&"):t -}function gl(t,e,n){t=r(t),e=+e;var i=t.length;if(i>=e||!Ta(e))return t;var o=(e-i)/2,l=ga(o),a=da(o);return n=dr("",a,n),n.slice(0,l)+t+n}function vl(t,e,n){return t=r(t),t&&dr(t,e,n)+t}function ml(t,e,n){return t=r(t),t&&t+dr(t,e,n)}function yl(t,e,n){return n&&Mr(t,e,n)&&(e=0),Da(t,e)}function bl(t,e){var n="";if(t=r(t),e=+e,1>e||!t||!Ta(e))return n;do e%2&&(n+=t),e=ga(e/2),t+=t;while(e);return n}function wl(t,e,n){return t=r(t),n=null==n?0:Na(0>n?0:+n||0,t.length),t.lastIndexOf(e,n)==n}function xl(t,e,n){var i=V.templateSettings;n&&Mr(t,e,n)&&(e=n=null),t=r(t),e=hn(hn({},n||e),i,fn);var o,l,a=hn(hn({},e.imports),i.imports,fn),s=as(a),u=Zn(a,s),c=0,h=e.interpolate||_e,d="__p += '",p=Zl((e.escape||_e).source+"|"+h.source+"|"+(h===be?we:_e).source+"|"+(e.evaluate||_e).source+"|$","g"),g="//# sourceURL="+("sourceURL"in e?e.sourceURL:"lodash.templateSources["+ ++He+"]")+"\n";t.replace(p,function(e,n,r,i,a,s){return r||(r=i),d+=t.slice(c,s).replace(Ne,f),n&&(o=!0,d+="' +\n__e("+n+") +\n'"),a&&(l=!0,d+="';\n"+a+";\n__p += '"),r&&(d+="' +\n((__t = ("+r+")) == null ? '' : __t) +\n'"),c=s+e.length,e}),d+="';\n";var v=e.variable;v||(d="with (obj) {\n"+d+"\n}\n"),d=(l?d.replace(ce,""):d).replace(fe,"$1").replace(he,"$1;"),d="function("+(v||"obj")+") {\n"+(v?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(o?", __e = _.escape":"")+(l?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+d+"return __p\n}";var m=Ml(function(){return Vl(s,g+"return "+d).apply(x,u)});if(m.source=d,Ao(m))throw m;return m}function Cl(t,e,n){var i=t;return(t=r(t))?(n?Mr(i,e,n):null==e)?t.slice(m(t),y(t)+1):(e+="",t.slice(o(t,e),l(t,e)+1)):t}function Sl(t,e,n){var i=t;return t=r(t),t?t.slice((n?Mr(i,e,n):null==e)?m(t):o(t,e+"")):t}function kl(t,e,n){var i=t;return t=r(t),t?(n?Mr(i,e,n):null==e)?t.slice(0,y(t)+1):t.slice(0,l(t,e+"")+1):t}function Ll(t,e,n){n&&Mr(t,e,n)&&(e=null);var i=W,o=D;if(null!=e)if(No(e)){var l="separator"in e?e.separator:l;i="length"in e?+e.length||0:i,o="omission"in e?r(e.omission):o}else i=+e||0;if(t=r(t),i>=t.length)return t;var a=i-o.length;if(1>a)return o;var s=t.slice(0,a);if(null==l)return s+o;if(Fo(l)){if(t.slice(a).search(l)){var u,c,f=t.slice(0,a);for(l.global||(l=Zl(l.source,(xe.exec(l)||"")+"g")),l.lastIndex=0;u=l.exec(f);)c=u.index;s=s.slice(0,null==c?a:c)}}else if(t.indexOf(l,a)!=a){var h=s.lastIndexOf(l);h>-1&&(s=s.slice(0,h))}return s+o}function _l(t){return t=r(t),t&&ge.test(t)?t.replace(de,b):t}function Tl(t,e,n){return n&&Mr(t,e,n)&&(e=null),t=r(t),t.match(e||Oe)||[]}function Ml(){for(var t=arguments[0],e=arguments.length,n=Gl(e?e-1:0);--e>0;)n[e-1]=arguments[e];try{return t.apply(x,n)}catch(r){return Ao(r)?r:new Kl(r)}}function Al(t,e,n){return n&&Mr(t,e,n)&&(e=null),d(t)?Wl(t):vn(t,e)}function Nl(t){return function(){return t}}function Ol(t){return t}function Wl(t){return Rn(mn(t,!0))}function Dl(t,e){return zn(t+"",mn(e,!0))}function Hl(t,e,n){if(null==n){var r=No(e),i=r&&as(e),o=i&&i.length&&Wn(e,i);(o?o.length:r)||(o=!1,n=e,e=t,t=this)}o||(o=Wn(e,as(e)));var l=!0,a=-1,s=is(t),u=o.length;n===!1?l=!1:No(n)&&"chain"in n&&(l=n.chain);for(;++at||!Ta(t))return[];var r=-1,i=Gl(Na(t,Ia));for(e=er(e,n,1);++rr?i[r]=e(r):e(r);return i}function Bl(t){var e=++aa;return r(t)+e}function jl(t,e){return t+e}function Ul(t){ns(t)||(t=Ir(t));for(var e=t.length,n=0;e--;)n+=+t[e]||0;return n}p=p?Xe.defaults($e.Object(),p,Xe.pick($e,De)):$e;var Gl=p.Array,ql=p.Date,Kl=p.Error,Vl=p.Function,$l=p.Math,Xl=p.Number,Yl=p.Object,Zl=p.RegExp,Jl=p.String,Ql=p.TypeError,ta=Gl.prototype,ea=Yl.prototype,na=Jl.prototype,ra=(ra=p.window)&&ra.document,ia=Vl.prototype.toString,oa=jn("length"),la=ea.hasOwnProperty,aa=0,sa=ea.toString,ua=p._,ca=Zl("^"+pl(sa).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),fa=Do(fa=p.ArrayBuffer)&&fa,ha=Do(ha=fa&&new fa(0).slice)&&ha,da=$l.ceil,pa=p.clearTimeout,ga=$l.floor,va=Do(va=Yl.getPrototypeOf)&&va,ma=ta.push,ya=ea.propertyIsEnumerable,ba=Do(ba=p.Set)&&ba,wa=p.setTimeout,xa=ta.splice,Ca=Do(Ca=p.Uint8Array)&&Ca,Sa=Do(Sa=p.WeakMap)&&Sa,ka=function(){try{var t=Do(t=p.Float64Array)&&t,e=new t(new fa(10),0,1)&&t}catch(n){}return e}(),La=Do(La=Gl.isArray)&&La,_a=Do(_a=Yl.create)&&_a,Ta=p.isFinite,Ma=Do(Ma=Yl.keys)&&Ma,Aa=$l.max,Na=$l.min,Oa=Do(Oa=ql.now)&&Oa,Wa=Do(Wa=Xl.isFinite)&&Wa,Da=p.parseInt,Ha=$l.random,Ea=Xl.NEGATIVE_INFINITY,Fa=Xl.POSITIVE_INFINITY,Ia=$l.pow(2,32)-1,Ra=Ia-1,za=Ia>>>1,Pa=ka?ka.BYTES_PER_ELEMENT:0,Ba=$l.pow(2,53)-1,ja=Sa&&new Sa,Ua=V.support={};!function(){Ua.funcDecomp=!Do(p.WinRTError)&&Ae.test(w),Ua.funcNames="string"==typeof Vl.name;try{Ua.dom=11===ra.createDocumentFragment().nodeType}catch(t){Ua.dom=!1}try{Ua.nonEnumArgs=!ya.call(arguments,1)}catch(t){Ua.nonEnumArgs=!0}}(0,0),V.templateSettings={escape:me,evaluate:ye,interpolate:be,variable:"",imports:{_:V}};var Ga=function(){function t(){}return function(e){if(No(e)){t.prototype=e;var n=new t;t.prototype=null}return n||p.Object()}}(),qa=ja?function(t,e){return ja.set(t,e),t}:Ol;ha||(nr=fa&&Ca?function(t){var e=t.byteLength,n=ka?ga(e/Pa):0,r=n*Pa,i=new fa(e);if(n){var o=new ka(i,0,n);o.set(new ka(t,0,n))}return e!=r&&(o=new Ca(i,r),o.set(new Ca(t,r))),i}:Nl(null));var Ka=_a&&ba?function(t){return new Ve(t)}:Nl(null),Va=ja?function(t){return ja.get(t)}:Fl,$a=function(){var t=0,e=0;return function(n,r){var i=Qa(),o=E-(i-e);if(e=i,o>0){if(++t>=H)return n}else t=0;return qa(n,r)}}(),Xa=or(function(t,e,n){la.call(t,n)?++t[n]:t[n]=1}),Ya=or(function(t,e,n){la.call(t,n)?t[n].push(e):t[n]=[e]}),Za=or(function(t,e,n){t[n]=e}),Ja=or(function(t,e,n){t[n?0:1].push(e)},function(){return[[],[]]}),Qa=Oa||function(){return(new ql).getTime()},ts=sr(),es=sr(!0),ns=La||function(t){return d(t)&&Ar(t.length)&&sa.call(t)==j||!1};Ua.dom||(_o=function(t){return t&&1===t.nodeType&&d(t)&&!os(t)||!1});var rs=Wa||function(t){return"number"==typeof t&&Ta(t)},is=n(/x/)||Ca&&!n(Ca)?function(t){return sa.call(t)==K}:n,os=va?function(t){if(!t||sa.call(t)!=X)return!1;var e=t.valueOf,n=Do(e)&&(n=va(e))&&va(n);return n?t==n||va(t)==n:Er(t)}:Er,ls=lr(hn),as=Ma?function(t){if(t)var e=t.constructor,n=t.length;return"function"==typeof e&&e.prototype===t||"function"!=typeof t&&n&&Ar(n)?Fr(t):No(t)?Ma(t):[]}:Fr,ss=lr(Pn),us=ur(function(t,e,n){return e=e.toLowerCase(),t+(n?e.charAt(0).toUpperCase()+e.slice(1):e)}),cs=ur(function(t,e,n){return t+(n?"-":"")+e.toLowerCase()});8!=Da(We+"08")&&(yl=function(t,e,n){return(n?Mr(t,e,n):null==e)?e=0:e&&(e=+e),t=Cl(t),Da(t,e||(Se.test(t)?16:10))});var fs=ur(function(t,e,n){return t+(n?"_":"")+e.toLowerCase()}),hs=ur(function(t,e,n){return t+(n?" ":"")+(e.charAt(0).toUpperCase()+e.slice(1))}),ds=fr(on),ps=fr(ln,!0);return V.prototype=Z.prototype,Q.prototype=Ga(Z.prototype),Q.prototype.constructor=Q,Re.prototype=Ga(Z.prototype),Re.prototype.constructor=Re,je.prototype["delete"]=Ue,je.prototype.get=Ge,je.prototype.has=qe,je.prototype.set=Ke,Ve.prototype.push=Ze,fo.Cache=je,V.after=to,V.ary=eo,V.assign=ls,V.at=Oi,V.before=no,V.bind=ro,V.bindAll=io,V.bindKey=oo,V.callback=Al,V.chain=Ci,V.chunk=Pr,V.compact=Br,V.constant=Nl,V.countBy=Xa,V.create=jo,V.curry=lo,V.curryRight=ao,V.debounce=so,V.defaults=Uo,V.defer=uo,V.delay=co,V.difference=jr,V.drop=Ur,V.dropRight=Gr,V.dropRightWhile=qr,V.dropWhile=Kr,V.fill=Vr,V.filter=Di,V.flatten=Zr,V.flattenDeep=Jr,V.flow=ts,V.flowRight=es,V.forEach=Ii,V.forEachRight=Ri,V.forIn=Ko,V.forInRight=Vo,V.forOwn=$o,V.forOwnRight=Xo,V.functions=Yo,V.groupBy=Ya,V.indexBy=Za,V.initial=ti,V.intersection=ei,V.invert=Jo,V.invoke=Pi,V.keys=as,V.keysIn=Qo,V.map=Bi,V.mapValues=tl,V.matches=Wl,V.matchesProperty=Dl,V.memoize=fo,V.merge=ss,V.mixin=Hl,V.negate=ho,V.omit=el,V.once=po,V.pairs=nl,V.partial=go,V.partialRight=vo,V.partition=Ja,V.pick=rl,V.pluck=ji,V.property=Il,V.propertyOf=Rl,V.pull=ii,V.pullAt=oi,V.range=zl,V.rearg=mo,V.reject=qi,V.remove=li,V.rest=ai,V.shuffle=Vi,V.slice=si,V.sortBy=Yi,V.sortByAll=Zi,V.sortByOrder=Ji,V.spread=yo,V.take=fi,V.takeRight=hi,V.takeRightWhile=di,V.takeWhile=pi,V.tap=Si,V.throttle=bo,V.thru=ki,V.times=Pl,V.toArray=Po,V.toPlainObject=Bo,V.transform=ol,V.union=gi,V.uniq=vi,V.unzip=mi,V.values=ll,V.valuesIn=al,V.where=Qi,V.without=yi,V.wrap=wo,V.xor=bi,V.zip=wi,V.zipObject=xi,V.backflow=es,V.collect=Bi,V.compose=es,V.each=Ii,V.eachRight=Ri,V.extend=ls,V.iteratee=Al,V.methods=Yo,V.object=xi,V.select=Di,V.tail=ai,V.unique=vi,Hl(V,V),V.add=jl,V.attempt=Ml,V.camelCase=us,V.capitalize=cl,V.clone=xo,V.cloneDeep=Co,V.deburr=fl,V.endsWith=hl,V.escape=dl,V.escapeRegExp=pl,V.every=Wi,V.find=Hi,V.findIndex=$r,V.findKey=Go,V.findLast=Ei,V.findLastIndex=Xr,V.findLastKey=qo,V.findWhere=Fi,V.first=Yr,V.has=Zo,V.identity=Ol,V.includes=zi,V.indexOf=Qr,V.inRange=sl,V.isArguments=So,V.isArray=ns,V.isBoolean=ko,V.isDate=Lo,V.isElement=_o,V.isEmpty=To,V.isEqual=Mo,V.isError=Ao,V.isFinite=rs,V.isFunction=is,V.isMatch=Oo,V.isNaN=Wo,V.isNative=Do,V.isNull=Ho,V.isNumber=Eo,V.isObject=No,V.isPlainObject=os,V.isRegExp=Fo,V.isString=Io,V.isTypedArray=Ro,V.isUndefined=zo,V.kebabCase=cs,V.last=ni,V.lastIndexOf=ri,V.max=ds,V.min=ps,V.noConflict=El,V.noop=Fl,V.now=Qa,V.pad=gl,V.padLeft=vl,V.padRight=ml,V.parseInt=yl,V.random=ul,V.reduce=Ui,V.reduceRight=Gi,V.repeat=bl,V.result=il,V.runInContext=w,V.size=$i,V.snakeCase=fs,V.some=Xi,V.sortedIndex=ui,V.sortedLastIndex=ci,V.startCase=hs,V.startsWith=wl,V.sum=Ul,V.template=xl,V.trim=Cl,V.trimLeft=Sl,V.trimRight=kl,V.trunc=Ll,V.unescape=_l,V.uniqueId=Bl,V.words=Tl,V.all=Wi,V.any=Xi,V.contains=zi,V.detect=Hi,V.foldl=Ui,V.foldr=Gi,V.head=Yr,V.include=zi,V.inject=Ui,Hl(V,function(){var t={};return Nn(V,function(e,n){V.prototype[n]||(t[n]=e)}),t}(),!1),V.sample=Ki,V.prototype.sample=function(t){return this.__chain__||null!=t?this.thru(function(e){return Ki(e,t)}):Ki(this.value())},V.VERSION=C,Qe(["bind","bindKey","curry","curryRight","partial","partialRight"],function(t){V[t].placeholder=V}),Qe(["dropWhile","filter","map","takeWhile"],function(t,e){var n=e!=R,r=e==F;Re.prototype[t]=function(t,i){var o=this.__filtered__,l=o&&r?new Re(this):this.clone(),a=l.__iteratees__||(l.__iteratees__=[]);return a.push({done:!1,count:0,index:0,iteratee:wr(t,i,1),limit:-1,type:e}),l.__filtered__=o||n,l}}),Qe(["drop","take"],function(t,e){var n=t+"While";Re.prototype[t]=function(n){var r=this.__filtered__,i=r&&!e?this.dropWhile():this.clone();if(n=null==n?1:Aa(ga(n)||0,0),r)e?i.__takeCount__=Na(i.__takeCount__,n):ni(i.__iteratees__).limit=n;else{var o=i.__views__||(i.__views__=[]);o.push({size:n,type:t+(i.__dir__<0?"Right":"")})}return i},Re.prototype[t+"Right"]=function(e){return this.reverse()[t](e).reverse()},Re.prototype[t+"RightWhile"]=function(t,e){return this.reverse()[n](t,e).reverse()}}),Qe(["first","last"],function(t,e){var n="take"+(e?"Right":"");Re.prototype[t]=function(){return this[n](1).value()[0]}}),Qe(["initial","rest"],function(t,e){var n="drop"+(e?"":"Right");Re.prototype[t]=function(){return this[n](1)}}),Qe(["pluck","where"],function(t,e){var n=e?"filter":"map",r=e?Rn:jn;Re.prototype[t]=function(t){return this[n](r(t))}}),Re.prototype.compact=function(){return this.filter(Ol)},Re.prototype.reject=function(t,e){return t=wr(t,e,1),this.filter(function(e){return!t(e)})},Re.prototype.slice=function(t,e){t=null==t?0:+t||0;var n=0>t?this.takeRight(-t):this.drop(t);return"undefined"!=typeof e&&(e=+e||0,n=0>e?n.dropRight(-e):n.take(e-t)),n},Re.prototype.toArray=function(){return this.drop(0)},Nn(Re.prototype,function(t,e){var n=V[e],r=/^(?:filter|map|reject)|While$/.test(e),i=/^(?:first|last)$/.test(e);V.prototype[e]=function(){var e=arguments,o=(e.length,this.__chain__),l=this.__wrapped__,a=!!this.__actions__.length,s=l instanceof Re,u=e[0],c=s||ns(l);c&&r&&"function"==typeof u&&1!=u.length&&(s=c=!1);var f=s&&!a;if(i&&!o)return f?t.call(l):n.call(V,this.value());var h=function(t){var r=[t];return ma.apply(r,e),n.apply(V,r)};if(c){var d=f?l:new Re(this),p=t.apply(d,e);if(!i&&(a||p.__actions__)){var g=p.__actions__||(p.__actions__=[]);g.push({func:ki,args:[h],thisArg:V})}return new Q(p,o)}return this.thru(h)}}),Qe(["concat","join","pop","push","replace","shift","sort","splice","split","unshift"],function(t){var e=(/^(?:replace|split)$/.test(t)?na:ta)[t],n=/^(?:push|sort|unshift)$/.test(t)?"tap":"thru",r=/^(?:join|pop|replace|shift)$/.test(t);V.prototype[t]=function(){var t=arguments;return r&&!this.__chain__?e.apply(this.value(),t):this[n](function(n){return e.apply(n,t)})}}),Re.prototype.clone=ze,Re.prototype.reverse=Pe,Re.prototype.value=Be,V.prototype.chain=Li,V.prototype.commit=_i,V.prototype.plant=Ti,V.prototype.reverse=Mi,V.prototype.toString=Ai,V.prototype.run=V.prototype.toJSON=V.prototype.valueOf=V.prototype.value=Ni,V.prototype.collect=V.prototype.map,V.prototype.head=V.prototype.first,V.prototype.select=V.prototype.filter,V.prototype.tail=V.prototype.rest,V}var x,C="3.5.0",S=1,k=2,L=4,_=8,T=16,M=32,A=64,N=128,O=256,W=30,D="...",H=150,E=16,F=0,I=1,R=2,z="Expected a function",P="__lodash_placeholder__",B="[object Arguments]",j="[object Array]",U="[object Boolean]",G="[object Date]",q="[object Error]",K="[object Function]",V="[object Map]",$="[object Number]",X="[object Object]",Y="[object RegExp]",Z="[object Set]",J="[object String]",Q="[object WeakMap]",te="[object ArrayBuffer]",ee="[object Float32Array]",ne="[object Float64Array]",re="[object Int8Array]",ie="[object Int16Array]",oe="[object Int32Array]",le="[object Uint8Array]",ae="[object Uint8ClampedArray]",se="[object Uint16Array]",ue="[object Uint32Array]",ce=/\b__p \+= '';/g,fe=/\b(__p \+=) '' \+/g,he=/(__e\(.*?\)|\b__t\)) \+\n'';/g,de=/&(?:amp|lt|gt|quot|#39|#96);/g,pe=/[&<>"'`]/g,ge=RegExp(de.source),ve=RegExp(pe.source),me=/<%-([\s\S]+?)%>/g,ye=/<%([\s\S]+?)%>/g,be=/<%=([\s\S]+?)%>/g,we=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,xe=/\w*$/,Ce=/^\s*function[ \n\r\t]+\w/,Se=/^0[xX]/,ke=/^\[object .+?Constructor\]$/,Le=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,_e=/($^)/,Te=/[.*+?^${}()|[\]\/\\]/g,Me=RegExp(Te.source),Ae=/\bthis\b/,Ne=/['\n\r\u2028\u2029\\]/g,Oe=function(){var t="[A-Z\\xc0-\\xd6\\xd8-\\xde]",e="[a-z\\xdf-\\xf6\\xf8-\\xff]+";return RegExp(t+"+(?="+t+e+")|"+t+"?"+e+"|"+t+"+|[0-9]+","g")}(),We=" \f \n\r\u2028\u2029 ᠎              ",De=["Array","ArrayBuffer","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Math","Number","Object","RegExp","Set","String","_","clearTimeout","document","isFinite","parseInt","setTimeout","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","window","WinRTError"],He=-1,Ee={};Ee[ee]=Ee[ne]=Ee[re]=Ee[ie]=Ee[oe]=Ee[le]=Ee[ae]=Ee[se]=Ee[ue]=!0,Ee[B]=Ee[j]=Ee[te]=Ee[U]=Ee[G]=Ee[q]=Ee[K]=Ee[V]=Ee[$]=Ee[X]=Ee[Y]=Ee[Z]=Ee[J]=Ee[Q]=!1;var Fe={};Fe[B]=Fe[j]=Fe[te]=Fe[U]=Fe[G]=Fe[ee]=Fe[ne]=Fe[re]=Fe[ie]=Fe[oe]=Fe[$]=Fe[X]=Fe[Y]=Fe[J]=Fe[le]=Fe[ae]=Fe[se]=Fe[ue]=!0,Fe[q]=Fe[K]=Fe[V]=Fe[Z]=Fe[Q]=!1;var Ie={leading:!1,maxWait:0,trailing:!1},Re={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"},ze={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},Pe={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},Be={"function":!0,object:!0},je={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Ue=Be[typeof exports]&&exports&&!exports.nodeType&&exports,Ge=Be[typeof module]&&module&&!module.nodeType&&module,qe=Ue&&Ge&&"object"==typeof global&&global,Ke=Be[typeof window]&&window,Ve=Ge&&Ge.exports===Ue&&Ue,$e=qe||Ke!==(this&&this.window)&&Ke||this,Xe=w();"function"==typeof define&&"object"==typeof define.amd&&define.amd?($e._=Xe,define(function(){return Xe})):Ue&&Ge?Ve?(Ge.exports=Xe)._=Xe:Ue._=Xe:$e._=Xe}.call(this),function(t){"use strict";function e(t,e){var r={theme:"mirrormark",tabSize:"2",indentWithTabs:!0,lineWrapping:!0,extraKeys:{Enter:"newlineAndIndentContinueMarkdownList"},mode:"markdown"};return _.assign(r,e),_.assign(Object.create(n),{element:t,options:r})}!function(t){if("object"==typeof exports&&"object"==typeof module)module.exports=t;else if("function"==typeof define&&define.amd)return define([],t);window&&(window.mirrorMark=t)}(e);var n={render:function(){this.registerKeyMaps(this.keyMaps),this.cm=t.fromTextArea(this.element,this.options),this.options.showToolbar&&this.setToolbar(this.tools)},setToolbar:function(t){var e=document.createElement("ul");e.className=this.options.theme+"-toolbar";var t=this.generateToolList(t);t.forEach(function(t){e.appendChild(t)});var n=this.cm.getWrapperElement();n.parentNode.insertBefore(e,n)},registerKeyMaps:function(t){for(var e in t){if("function"!=typeof this.actions[t[e]])throw"MirrorMark - '"+t[e]+"' is not a registered action";var n={};n[e]=this.actions[t[e]].bind(this),_.assign(this.options.extraKeys,n)}},registerActions:function(t){return _.assign(this.actions,t)},registerTools:function(t,e){for(var n in t)if(this.actions[t[n].action]&&"function"!=typeof this.actions[t[n].action])throw"MirrorMark - '"+t[n].action+"' is not a registered action";return e?void(this.tools=t):void(this.tools=this.tools.concat(t))},generateToolList:function r(t){return t.map(function(t){var e=document.createElement("li"),n=document.createElement("a");if(e.className=t.name,t.className&&(n.className=t.className),t.showName){var i=document.createTextNode(t.name);n.appendChild(i)}if(t.action&&(n.onclick=function(){this.cm.focus(),this.actions[t.action].call(this)}.bind(this)),e.appendChild(n),t.nested){e.className+=" has-nested";var o=document.createElement("ul");o.className=this.options.theme+"-toolbar-list";var l=r.call(this,t.nested);l.forEach(function(t){o.appendChild(t)}),e.appendChild(o)}return e}.bind(this))},tools:[{name:"bold",action:"bold",className:"fa fa-bold"},{name:"italicize",action:"italicize",className:"fa fa-italic"},{name:"blockquote",action:"blockquote",className:"fa fa-quote-left"},{name:"link",action:"link",className:"fa fa-link"},{name:"image",action:"image",className:"fa fa-image"},{name:"unorderedList",action:"unorderedList",className:"fa fa-list"},{name:"orderedList",action:"orderedList",className:"fa fa-list-ol"},{name:"fullScreen",action:"fullScreen",className:"fa fa-expand"}],keyMaps:{"Cmd-B":"bold","Cmd-I":"italicize","Cmd-'":"blockquote","Cmd-Alt-L":"orderedList","Cmd-L":"unorderedList","Cmd-Alt-I":"image","Cmd-H":"hr","Cmd-K":"link"},actions:{bold:function(){this.insertAround("**","**")},italicize:function(){this.insertAround("*","*")},code:function(){this.insertAround("```\r\n","\r\n```")},blockquote:function(){this.insertBefore("> ",2)},orderedList:function(){this.insertBefore("1. ",3)},unorderedList:function(){this.insertBefore("* ",2)},image:function(){this.insertBefore("![](http://)",2)},link:function(){this.insertAround("[","](http://)")},hr:function(){this.insert("---")},fullScreen:function(){var t=this.cm.getWrapperElement(),e=document,n=e.fullScreen||e.mozFullScreen||e.webkitFullScreen,r=function(){t.requestFullscreen?t.requestFullscreen():t.webkitRequestFullscreen?t.webkitRequestFullscreen():t.mozRequestFullScreen?t.mozRequestFullScreen():t.msRequestFullscreen&&t.msRequestFullscreen()},i=function(){e.cancelFullScreen?e.cancelFullScreen():e.mozCancelFullScreen?e.mozCancelFullScreen():e.webkitCancelFullScreen&&e.webkitCancelFullScreen()};n?i&&i():r()}},insert:function(t){var e=this.cm.getDoc(),n=e.getCursor();e.replaceRange(t,{line:n.line,ch:n.ch})},insertAround:function(t,e){var n=this.cm.getDoc(),r=n.getCursor();if(n.somethingSelected()){var i=n.getSelection();n.replaceSelection(t+i+e)}else n.replaceRange(t+e,{line:r.line,ch:r.ch}),n.setCursor({line:r.line,ch:r.ch+t.length})},insertBefore:function(t,e){var n=this.cm.getDoc(),r=n.getCursor();if(n.somethingSelected()){var i=n.listSelections();i.forEach(function(r){for(var i=[r.head.line,r.anchor.line].sort(),o=i[0];o<=i[1];o++)n.replaceRange(t,{line:o,ch:0});n.setCursor({line:i[0],ch:e||0})})}else n.replaceRange(t,{line:r.line,ch:0}),n.setCursor({line:r.line,ch:e||0})}}}(window.CodeMirror); diff --git a/assets/js/screenfull.min.js b/assets/js/screenfull-2.0.0.min.js similarity index 100% rename from assets/js/screenfull.min.js rename to assets/js/screenfull-2.0.0.min.js diff --git a/assets/js/visualmarkdowneditor.js b/assets/js/visualmarkdowneditor.js new file mode 100644 index 0000000..d337d5f --- /dev/null +++ b/assets/js/visualmarkdowneditor.js @@ -0,0 +1,440 @@ +/** + * Visual Markdown Editor Field for Kirby 2 + * + * @version 1.1.0 + * @author Jonas Döbertin + * @copyright Jonas Döbertin + * @link https://github.com/JonasDoebertin/kirby-visual-markdown + * @license GNU GPL v3.0 + */ + +var VisualMarkdownEditor = function($, $element, options) { + + var self = this; + + this.$element = $element; + this.codemirror = null; + + this.options = {}; + this.defaults = { + toolbar: true, + header1: 'h1', + header2: 'h2', + codemirror: { + theme: 'visualmarkdown', + tabSize: 4, + indentWithTabs: false, + lineWrapping: true, + extraKeys: { + "Enter": 'newlineAndIndentContinueMarkdownList', + }, + mode: { + name: 'markdown', + highlightFormatting: true, + underscoresBreakWords: false, + maxBlockquoteDepth: 0, + fencedCodeBlocks: false, + taskLists: false, + strikethrough: false + }, + } + }; + + /** + * Actions + */ + this.actions = { + header1: function() { + var header = self.translateHeaderValue(self.options.header1); + self.toggleHeader(header); + }, + header2: function() { + var header = self.translateHeaderValue(self.options.header2); + self.toggleHeader(header); + }, + bold: function () { + self.insertAround('**', '**') + }, + italicize: function () { + self.insertAround('*', '*') + }, + blockquote: function () { + self.insertBefore('> ', 2); + }, + orderedList: function () { + self.insertBefore('1. ', 3); + }, + unorderedList: function () { + self.insertBefore('* ', 2); + }, + link: function () { + self.insertAround('[', '](http://)'); + }, + image: function () { + self.insertBefore('(image: filename.jpg)'); + }, + line: function() { + self.insert('****'); + }, + code: function () { + self.insertAround('```\r\n', '\r\n```') + }, + fullscreen: function() { + self.toggleFullscreenMode(); + } + }; + + /** + * Toolbar Icons + */ + this.tools = [ + { + name: 'h1', //self.options.header1, + action: 'header1', + className: 'markdownfield-icon-text markdownfield-icon-header1', + showName: true, + }, + { + name: 'h2', //self.options.header2, + action: 'header2', + className: 'markdownfield-icon-text markdownfield-icon-header1', + showName: true, + }, + { + name: 'divider', + }, + { + name: "bold", + action: "bold", + className: "fa fa-bold" + }, + { + name: "italicize", + action: "italicize", + className: "fa fa-italic" + }, + { + name: "blockquote", + action: "blockquote", + className: "fa fa-quote-left" + }, + { + name: "unorderedList", + action: "unorderedList", + className: "fa fa-list" + }, + { + name: "orderedList", + action: "orderedList", + className: "fa fa-list-ol" + }, + { + name: 'divider', + }, + { + name: "link", + action: "link", + className: "fa fa-link" + }, + { + name: "image", + action: "image", + className: "fa fa-image" + }, + { + name: 'line', + action: 'line', + className: 'fa fa-minus' + }, + { + name: "fullScreen", + action: "fullscreen", + className: "fa fa-expand" + } + ]; + + /** + * Keymaps + */ + this.keyMaps = { + "Cmd-H": 'header1', + "Cmd-Alt-H": 'header2', + "Cmd-B": 'bold', + "Cmd-I": 'italicize', + "Cmd-'": 'blockquote', + "Cmd-Alt-L": 'orderedList', + "Cmd-L": 'unorderedList', + "Cmd-Alt-I": 'image', + "Cmd-A": 'link' + }; + + /** + * Initialization + * + * @since 1.2.0 + */ + this.init = function(options) { + + // Merge defaults with options + self.options = $.extend({}, self.defaults, options); + + // Register key bindings + self.registerKeyMaps(self.keyMaps); + + // Initialize CodeMirror + self.codemirror = CodeMirror.fromTextArea(self.$element.get(0), self.options.codemirror); + + // Initialize toolbar + if(self.options.toolbar) { + self.initToolbar(); + } + + }; + + /** + * Initialize the toolbar + */ + this.initToolbar = function() { + + var toolbar = $('