From 05a76e265f04ece7a4377a0ea6377ea76ee77874 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=9C=E6=81=92?= <2323333339@qq.com>
Date: Thu, 25 Mar 2021 17:05:46 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 1 +
core/factory.php | 54 +-
core/function.php | 2 +-
package.json | 2 +-
typecho/editor/css/joe.editor.min.css | 1 -
typecho/editor/css/joe.editor.min.scss | 343 -
typecho/editor/js/joe.constructor.js | 380 -
typecho/editor/js/joe.constructor.min.js | 1 -
typecho/editor/js/joe.editor.js | 134 -
typecho/editor/js/joe.editor.min.js | 1 -
typecho/editor/js/joe.instance.js | 199 -
typecho/editor/js/joe.instance.min.js | 1 -
typecho/write/css/joe.write.min.css | 1 +
typecho/write/css/joe.write.min.scss | 526 +
typecho/write/js/joe.write.chunk.js | 14126 +++++++++++++++++++++
typecho/write/js/joe.write.js | 659 +
typecho/write/package.json | 47 +
typecho/write/rollup.config.js | 10 +
18 files changed, 15377 insertions(+), 1111 deletions(-)
create mode 100644 .gitignore
delete mode 100644 typecho/editor/css/joe.editor.min.css
delete mode 100644 typecho/editor/css/joe.editor.min.scss
delete mode 100644 typecho/editor/js/joe.constructor.js
delete mode 100644 typecho/editor/js/joe.constructor.min.js
delete mode 100644 typecho/editor/js/joe.editor.js
delete mode 100644 typecho/editor/js/joe.editor.min.js
delete mode 100644 typecho/editor/js/joe.instance.js
delete mode 100644 typecho/editor/js/joe.instance.min.js
create mode 100644 typecho/write/css/joe.write.min.css
create mode 100644 typecho/write/css/joe.write.min.scss
create mode 100644 typecho/write/js/joe.write.chunk.js
create mode 100644 typecho/write/js/joe.write.js
create mode 100644 typecho/write/package.json
create mode 100644 typecho/write/rollup.config.js
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..40b878d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules/
\ No newline at end of file
diff --git a/core/factory.php b/core/factory.php
index 974abc3..f694368 100644
--- a/core/factory.php
+++ b/core/factory.php
@@ -39,55 +39,11 @@ if (Helper::options()->JEditor !== 'off') {
class Editor
{
public static function Edit()
- { ?>
-
+ {
+?>
+
+
+
';
- echo '';
- /* 编辑器语法高亮 */
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- /* 编辑器附加功能 */
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- /* 代码折行 */
- echo '';
- echo '';
- echo '';
- echo '';
- echo '';
- /* 引入本地文件 */
- echo '';
- echo '';
- echo '';
- echo '';
}
}
diff --git a/core/function.php b/core/function.php
index 45c3d44..e3f91b4 100644
--- a/core/function.php
+++ b/core/function.php
@@ -2,7 +2,7 @@
/* 获取主题当前版本号 */
function _getVersion()
{
- return "6.1.0";
+ return "6.1.1";
};
/* 判断是否是手机 */
diff --git a/package.json b/package.json
index 3c7c61f..678b4c2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "typecho-joe-next",
- "version": "6.1.0",
+ "version": "6.1.1",
"description": "A Theme Of Typecho",
"main": "index.php",
"keywords": [
diff --git a/typecho/editor/css/joe.editor.min.css b/typecho/editor/css/joe.editor.min.css
deleted file mode 100644
index 91e071a..0000000
--- a/typecho/editor/css/joe.editor.min.css
+++ /dev/null
@@ -1 +0,0 @@
-input[name="fields[keywords]"]{width:100%}textarea[name="fields[description]"],textarea[name="fields[abstract]"],textarea[name="fields[thumb]"],textarea[name="fields[video]"]{width:100%;height:80px}textarea[id="text"],span[class="resize"]{display:none}.CodeMirror-progress{display:none;position:absolute;z-index:6688;right:100%;top:0;width:100%;height:2px;border-radius:1px;background:linear-gradient(to right, #4cd964, #5ac8fa, #007aff);transition:right 1s linear}.CodeMirror-fullscreen{position:fixed;top:35px;left:0;right:0;bottom:0;height:auto;z-index:999}.CodeMirror-foldmarker{cursor:pointer;padding-left:3px;font-family:"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;transition:color 0.25s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.CodeMirror-foldgutter{width:12px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.CodeMirror-foldgutter-open,.CodeMirror-foldgutter-folded{display:flex;align-items:center;justify-content:center;width:100%;height:22px;cursor:pointer}.CodeMirror-foldgutter-open::before,.CodeMirror-foldgutter-folded::before{content:"";display:block;width:11px;height:11px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAA1ElEQVRIS+3UwQnCMBQG4Bc6gzePnvTiNF1CWnhZQJ0gEHEJXcIRvOgYTpBQSWlCqEleGumthd7Kx5/3/pTBTA+byYUFdpPtRyGlXGumN9jg45+Zi6vY4wGfxuhhcRFHADiZFxs8l+CecccGa5t4q5m+AcCuBPfQV9VVddu2b9cKKWURHkLdKOzRp+Ix9AceFpmVPIUG4RycQqNwCs9Bk3AIH3Zhaum2H6sm+a8YLdQ4JEomDrQFbE+pS0Qm9vCVUkpzzj8Ump04Bxp/k514Kr7AbmJf6nCLF7D+r3IAAAAASUVORK5CYII=);background-size:100% 100%}.CodeMirror-foldgutter-folded::before{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.CodeMirror.cm-s-joe{font-size:15px;line-height:1.5;font-family:Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;background:#2d2d2d;color:#ccc}.CodeMirror.cm-s-joe .CodeMirror-placeholder{color:#666}.CodeMirror.cm-s-joe .CodeMirror-selected{background:rgba(221,240,255,0.2)}.CodeMirror.cm-s-joe .CodeMirror-gutters{background:#2d2d2d;border-right:none;padding:0 3px}.CodeMirror.cm-s-joe .CodeMirror-linenumber{color:#8f938f}.CodeMirror.cm-s-joe .CodeMirror-cursor{border-left:1px solid #a7a7a7}.CodeMirror.cm-s-joe .CodeMirror-activeline-background{background:rgba(255,255,255,0.031)}.CodeMirror.cm-s-joe .CodeMirror-matchingbracket{border:1px solid rgba(255,255,255,0.25);color:#8f938f;margin:-1px -1px 0 -1px}.CodeMirror.cm-s-joe .cm-header-1,.CodeMirror.cm-s-joe .cm-header-2,.CodeMirror.cm-s-joe .cm-header-3,.CodeMirror.cm-s-joe .cm-header-4,.CodeMirror.cm-s-joe .cm-header-5,.CodeMirror.cm-s-joe .cm-header-6{font-size:18px;color:#569cd6 !important}.CodeMirror.cm-s-joe .cm-strong,.CodeMirror.cm-s-joe .cm-em{color:#569cd6}.CodeMirror.cm-s-joe .cm-keyword{color:#cc99cd}.CodeMirror.cm-s-joe .cm-string{color:#7ec699}.CodeMirror.cm-s-joe .cm-operator{color:#67cdcc}.CodeMirror.cm-s-joe .cm-number{color:#f08d49}.CodeMirror.cm-s-joe .cm-def{color:#f08d49}.CodeMirror.cm-s-joe .cm-tag{color:#e2777a}.CodeMirror.cm-s-joe .cm-attribute{color:#e2777a}.CodeMirror.cm-s-joe .cm-bracket{color:#ccc}.CodeMirror.cm-s-joe .cm-qualifier{color:#cc99cd}.CodeMirror.cm-s-joe .cm-property{color:#f8c555}.CodeMirror.cm-s-joe .cm-comment{color:#666}.CodeMirror.cm-s-joe .cm-quote{color:#ae81ff}.CodeMirror.cm-s-joe .cm-variable-2{color:#de8e30}.CodeMirror.cm-s-joe .cm-image,.CodeMirror.cm-s-joe .cm-link{color:#f56c6c;text-decoration:none}.CodeMirror-means{display:flex;flex-wrap:wrap;background:#222;margin:0;padding:0}.CodeMirror-means.fullscreen{position:fixed;top:0;left:0;right:0;z-index:1000}.CodeMirror-means-item{position:relative;display:flex;align-items:center;justify-content:center;list-style:none;width:40px;height:35px;cursor:pointer;border-right:1px solid #2d2d2d;border-bottom:1px solid #2d2d2d;box-sizing:border-box}.CodeMirror-means-item:hover{background:#2d2d2d}.CodeMirror-means-item.active{background:#2d2d2d}.CodeMirror-means-item.active .CodeMirror-means__dropdown{visibility:visible;opacity:1;-webkit-transform:translateX(-50%) rotateX(0);transform:translateX(-50%) rotateX(0)}.CodeMirror-means__dropdown{position:absolute;top:42px;left:50%;z-index:1000;color:#bfbfbf;background:#222;padding:5px 0;border-radius:4px;visibility:hidden;-webkit-transform-origin:top;transform-origin:top;opacity:0;-webkit-transform:translateX(-50%) rotateX(-90deg);transform:translateX(-50%) rotateX(-90deg);transition:visibility 0.25s, opacity 0.25s, -webkit-transform 0.25s;transition:visibility 0.25s, transform 0.25s, opacity 0.25s;transition:visibility 0.25s, transform 0.25s, opacity 0.25s, -webkit-transform 0.25s}.CodeMirror-means__dropdown::before{content:"";position:absolute;top:-7px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #222}.CodeMirror-means__dropdown-item{padding:0 20px;line-height:34px;transition:background 0.25s}.CodeMirror-means__dropdown-item:hover{color:#f2f2f2;background:#2d2d2d}.CodeMirror-dialog{display:flex;align-items:center;justify-content:center;position:fixed;top:0;left:0;right:0;bottom:0;z-index:6666;background:rgba(0,0,0,0);visibility:hidden;transition:background 0.25s, visibility 0.25s}.CodeMirror-dialog__wrapper{width:380px;background:#fff;color:#606266;border-radius:4px;opacity:0;-webkit-transform:translateY(-30%);transform:translateY(-30%);transition:opacity 0.25s, -webkit-transform 0.25s;transition:opacity 0.25s, transform 0.25s;transition:opacity 0.25s, transform 0.25s, -webkit-transform 0.25s}.CodeMirror-dialog__wrapper-header{padding:12px 20px;border-bottom:1px solid #e4e7ed}.CodeMirror-dialog__wrapper-bodyer{padding:20px}.CodeMirror-dialog__wrapper-bodyer .fitem{display:flex;align-items:center;margin-bottom:15px}.CodeMirror-dialog__wrapper-bodyer .fitem:last-child{margin-bottom:0}.CodeMirror-dialog__wrapper-bodyer .fitem label{margin-right:10px}.CodeMirror-dialog__wrapper-bodyer .fitem input{width:auto;flex:1;-webkit-appearance:none;outline:none;border:1px solid #dcdfe6;height:34px;box-sizing:border-box;padding:0 10px;border-radius:3px;color:#606266}.CodeMirror-dialog__wrapper-footer{display:flex;align-items:center;justify-content:flex-end;padding:0 20px 10px}.CodeMirror-dialog__wrapper-footer--cancle,.CodeMirror-dialog__wrapper-footer--confirm{cursor:pointer;border:none;outline:none;margin-left:5px;background:#fff;color:#606266;-webkit-appearance:none;padding:7px 15px;border-radius:3px}.CodeMirror-dialog__wrapper-footer--confirm{color:#fff;background:#409eff}.CodeMirror-dialog.active{visibility:visible;background:rgba(0,0,0,0.75)}.CodeMirror-dialog.active .CodeMirror-dialog__wrapper{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}@media (max-width: 768px){.CodeMirror-dialog__wrapper{width:90%}}
diff --git a/typecho/editor/css/joe.editor.min.scss b/typecho/editor/css/joe.editor.min.scss
deleted file mode 100644
index de6569f..0000000
--- a/typecho/editor/css/joe.editor.min.scss
+++ /dev/null
@@ -1,343 +0,0 @@
-input[name="fields[keywords]"] {
- width: 100%;
-}
-
-textarea[name="fields[description]"],
-textarea[name="fields[abstract]"],
-textarea[name="fields[thumb]"],
-textarea[name="fields[video]"] {
- width: 100%;
- height: 80px;
-}
-
-textarea[id="text"],
-span[class="resize"] {
- display: none;
-}
-
-.CodeMirror {
- &-progress {
- display: none;
- position: absolute;
- z-index: 6688;
- right: 100%;
- top: 0;
- width: 100%;
- height: 2px;
- border-radius: 1px;
- background: linear-gradient(to right, #4cd964, #5ac8fa, #007aff);
- transition: right 1s linear;
- }
- &-fullscreen {
- position: fixed;
- top: 35px;
- left: 0;
- right: 0;
- bottom: 0;
- height: auto;
- z-index: 999;
- }
- &-foldmarker {
- cursor: pointer;
- padding-left: 3px;
- font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
- "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
- transition: color 0.25s;
- user-select: none;
- }
- &-foldgutter {
- width: 12px;
- user-select: none;
- &-open,
- &-folded {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 22px;
- cursor: pointer;
- &::before {
- content: "";
- display: block;
- width: 11px;
- height: 11px;
- background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAA1ElEQVRIS+3UwQnCMBQG4Bc6gzePnvTiNF1CWnhZQJ0gEHEJXcIRvOgYTpBQSWlCqEleGumthd7Kx5/3/pTBTA+byYUFdpPtRyGlXGumN9jg45+Zi6vY4wGfxuhhcRFHADiZFxs8l+CecccGa5t4q5m+AcCuBPfQV9VVddu2b9cKKWURHkLdKOzRp+Ix9AceFpmVPIUG4RycQqNwCs9Bk3AIH3Zhaum2H6sm+a8YLdQ4JEomDrQFbE+pS0Qm9vCVUkpzzj8Ump04Bxp/k514Kr7AbmJf6nCLF7D+r3IAAAAASUVORK5CYII=);
- background-size: 100% 100%;
- }
- }
- &-folded {
- &::before {
- transform: rotate(-90deg);
- }
- }
- }
- &.cm-s-joe {
- font-size: 15px;
- line-height: 1.5;
- font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
- background: #2d2d2d;
- color: #ccc;
- /* 占位符 */
- .CodeMirror-placeholder {
- color: #666;
- }
- /* 选择文字时候的颜色 */
- .CodeMirror-selected {
- background: rgba(221, 240, 255, 0.2);
- }
- /* 行号 */
- .CodeMirror-gutters {
- background: #2d2d2d;
- border-right: none;
- padding: 0 3px;
- }
- /* 行号字体色 */
- .CodeMirror-linenumber {
- color: #8f938f;
- }
- /* 光标颜色 */
- .CodeMirror-cursor {
- border-left: 1px solid #a7a7a7;
- }
- /* 当前行高亮 */
- .CodeMirror-activeline-background {
- background: rgba(255, 255, 255, 0.031);
- }
- /* 括号匹配 */
- .CodeMirror-matchingbracket {
- border: 1px solid rgba(255, 255, 255, 0.25);
- color: #8f938f;
- margin: -1px -1px 0 -1px;
- }
- /* 1 - 6级标题样式 */
- .cm-header-1,
- .cm-header-2,
- .cm-header-3,
- .cm-header-4,
- .cm-header-5,
- .cm-header-6 {
- font-size: 18px;
- color: #569cd6 !important;
- }
- /* 加粗/倾斜 */
- .cm-strong,
- .cm-em {
- color: #569cd6;
- }
- /* 代码高亮样式 */
- .cm-keyword {
- color: #cc99cd;
- }
- .cm-string {
- color: #7ec699;
- }
- .cm-operator {
- color: #67cdcc;
- }
- .cm-number {
- color: #f08d49;
- }
- .cm-def {
- color: #f08d49;
- }
- .cm-tag {
- color: #e2777a;
- }
- .cm-attribute {
- color: #e2777a;
- }
- .cm-bracket {
- color: #ccc;
- }
- .cm-qualifier {
- color: #cc99cd;
- }
- .cm-property {
- color: #f8c555;
- }
- .cm-comment {
- color: #666;
- }
- /* 文本引用 */
- .cm-quote {
- color: #ae81ff;
- }
- /* 列表 */
- .cm-variable-2 {
- color: #de8e30;
- }
- /* 超链接 */
- .cm-image,
- .cm-link {
- color: #f56c6c;
- text-decoration: none;
- }
- }
- &-means {
- display: flex;
- flex-wrap: wrap;
- background: #222;
- margin: 0;
- padding: 0;
- &.fullscreen {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 1000;
- }
- &-item {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- list-style: none;
- width: 40px;
- height: 35px;
- cursor: pointer;
- border-right: 1px solid #2d2d2d;
- border-bottom: 1px solid #2d2d2d;
- box-sizing: border-box;
- &:hover {
- background: #2d2d2d;
- }
- &.active {
- background: #2d2d2d;
- .CodeMirror-means__dropdown {
- visibility: visible;
- opacity: 1;
- transform: translateX(-50%) rotateX(0);
- }
- }
- }
- &__dropdown {
- position: absolute;
- top: 42px;
- left: 50%;
- z-index: 1000;
- color: #bfbfbf;
- background: #222;
- padding: 5px 0;
- border-radius: 4px;
- visibility: hidden;
- transform-origin: top;
- opacity: 0;
- transform: translateX(-50%) rotateX(-90deg);
- transition: visibility 0.25s, transform 0.25s, opacity 0.25s;
- &::before {
- content: "";
- position: absolute;
- top: -7px;
- left: 50%;
- transform: translateX(-50%);
- width: 0;
- height: 0;
- border-left: 7px solid transparent;
- border-right: 7px solid transparent;
- border-bottom: 7px solid #222;
- }
- &-item {
- padding: 0 20px;
- line-height: 34px;
- transition: background 0.25s;
- &:hover {
- color: #f2f2f2;
- background: #2d2d2d;
- }
- }
- }
- }
- &-dialog {
- display: flex;
- align-items: center;
- justify-content: center;
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 6666;
- background: rgba(0, 0, 0, 0);
- visibility: hidden;
- transition: background 0.25s, visibility 0.25s;
- &__wrapper {
- width: 380px;
- background: #fff;
- color: #606266;
- border-radius: 4px;
- opacity: 0;
- transform: translateY(-30%);
- transition: opacity 0.25s, transform 0.25s;
- &-header {
- padding: 12px 20px;
- border-bottom: 1px solid #e4e7ed;
- }
- &-bodyer {
- padding: 20px;
- .fitem {
- display: flex;
- align-items: center;
- margin-bottom: 15px;
- &:last-child {
- margin-bottom: 0;
- }
- label {
- margin-right: 10px;
- }
- input {
- width: auto;
- flex: 1;
- -webkit-appearance: none;
- outline: none;
- border: 1px solid #dcdfe6;
- height: 34px;
- box-sizing: border-box;
- padding: 0 10px;
- border-radius: 3px;
- color: #606266;
- }
- }
- }
- &-footer {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- padding: 0 20px 10px;
- &--cancle,
- &--confirm {
- cursor: pointer;
- border: none;
- outline: none;
- margin-left: 5px;
- background: #fff;
- color: #606266;
- -webkit-appearance: none;
- padding: 7px 15px;
- border-radius: 3px;
- }
- &--confirm {
- color: #fff;
- background: #409eff;
- }
- }
- }
- &.active {
- visibility: visible;
- background: rgba(0, 0, 0, 0.75);
- .CodeMirror-dialog__wrapper {
- transform: translateY(0);
- opacity: 1;
- }
- }
- }
-}
-
-@media (max-width: 768px) {
- .CodeMirror {
- &-dialog {
- &__wrapper {
- width: 90%;
- }
- }
- }
-}
diff --git a/typecho/editor/js/joe.constructor.js b/typecho/editor/js/joe.constructor.js
deleted file mode 100644
index e5dbaab..0000000
--- a/typecho/editor/js/joe.constructor.js
+++ /dev/null
@@ -1,380 +0,0 @@
-class JoeInstance {
- constructor() {
- this.init();
- }
-
- init() {
- $(".CodeMirror-wrap").before('
');
- $(".CodeMirror-wrap").append('');
- $("body").append(`
-
- `);
- $(".CodeMirror-dialog__wrapper-footer--cancle").on("click", () => {
- this.options.cancel();
- $(".CodeMirror-dialog").removeClass("active");
- $("body").css("overflow", "");
- });
- $(".CodeMirror-dialog__wrapper-footer--confirm").on("click", () => {
- this.options.confirm();
- $(".CodeMirror-dialog").removeClass("active");
- $("body").css("overflow", "");
- });
- }
-
- /* 打开弹窗 */
- __OpenTheDialog(options = {}) {
- const _options = {
- title: "提示",
- innerHtml: "内容",
- cancel: () => {},
- confirm: () => {},
- };
- this.options = Object.assign(_options, options);
- $(".CodeMirror-dialog__wrapper-header").html(this.options.title);
- $(".CodeMirror-dialog__wrapper-bodyer").html(this.options.innerHtml);
- $(".CodeMirror-dialog").addClass("active");
- $("body").css("overflow", "hidden");
- }
-
- /* 初始化插入标题功能 - 已测试 √ */
- initTitleText(tool) {
- const item = $(`
-
- ${tool.icon}
-
-
H1
-
H2
-
H3
-
H4
-
H5
-
H6
-
-
- `);
- item.on("click", function (e) {
- e.stopPropagation();
- $(this).toggleClass("active");
- });
- item.on("click", ".CodeMirror-means__dropdown-item", function (e) {
- e.stopPropagation();
- const text = $(this).attr("data-text");
- const cursor = JoeEditor.getCursor();
- if (cursor.ch === 0) JoeEditor.replaceSelection(text);
- else JoeEditor.replaceSelection("\n\n" + text);
- item.removeClass("active");
- JoeEditor.focus();
- });
- $(document).on("click", () => item.removeClass("active"));
- $(".CodeMirror-means").append(item);
- }
-
- /* 全屏/取消全屏 - 已测试 √ */
- handleFullscreen(tool) {
- const toolsHeight = $(".CodeMirror-means").height();
- const item = $(
- `${tool.icon}`
- );
- item.on("click", function () {
- const bool = JoeEditor.getOption("fullScreen");
- if (bool) {
- JoeEditor.setOption("fullScreen", false);
- $(".CodeMirror-wrap").css("top", "");
- $(".CodeMirror-means").removeClass("fullscreen");
- $(item).removeClass("active");
- } else {
- JoeEditor.setOption("fullScreen", true);
- $(".CodeMirror-wrap").css("top", toolsHeight);
- $(".CodeMirror-means").addClass("fullscreen");
- $(item).addClass("active");
- }
- });
- $(".CodeMirror-means").append(item);
- }
-
- /* 加粗 - 已测试 √ */
- insertBoldText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- JoeEditor.replaceSelection("**" + selection + "**");
- if (selection === "") JoeEditor.setCursor(cursor.line, cursor.ch + 2);
- JoeEditor.focus();
- }
-
- /* 倾斜 - 已测试 √ */
- insertItalicText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- JoeEditor.replaceSelection("*" + selection + "*");
- if (selection === "") JoeEditor.setCursor(cursor.line, cursor.ch + 1);
- JoeEditor.focus();
- }
-
- /* 删除线 - 已测试 √ */
- insertDeleteText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- JoeEditor.replaceSelection("~~" + selection + "~~");
- if (selection === "") JoeEditor.setCursor(cursor.line, cursor.ch + 2);
- JoeEditor.focus();
- }
-
- /* 引用 - 已测试 √ */
- insertQuoteText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- if (cursor.ch === 0) {
- JoeEditor.replaceSelection("> " + selection);
- } else {
- JoeEditor.setCursor(cursor.line, 0);
- JoeEditor.replaceSelection("> " + selection);
- JoeEditor.setCursor(cursor.line, cursor.ch + 2);
- }
- JoeEditor.focus();
- }
-
- /* 空格 - 已测试 √ */
- insertSpaceText() {
- JoeEditor.replaceSelection(" ");
- JoeEditor.focus();
- }
-
- /* 行内代码 - 已测试 √ */
- insertCodeInlineText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- JoeEditor.replaceSelection("`" + selection + "`");
- if (selection === "") JoeEditor.setCursor(cursor.line, cursor.ch + 1);
- JoeEditor.focus();
- }
-
- /* 横线 - 已测试 √ */
- insertHrText() {
- const cursor = JoeEditor.getCursor();
- if (cursor.ch === 0) JoeEditor.replaceSelection("\n------------\n\n");
- else JoeEditor.replaceSelection("\n\n------------\n\n");
- JoeEditor.focus();
- }
-
- /* 无序列表 - 已测试 √ */
- insertUnorderedListText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- if (cursor.ch === 0) {
- if (selection === "") {
- JoeEditor.replaceSelection("- ");
- } else {
- const selectionText = selection.split("\n");
- for (let i = 0, len = selectionText.length; i < len; i++) {
- selectionText[i] =
- selectionText[i] === "" ? "" : "- " + selectionText[i];
- }
- JoeEditor.replaceSelection(selectionText.join("\n"));
- }
- } else {
- if (selection === "") {
- JoeEditor.replaceSelection("\n- ");
- } else {
- const selectionText = selection.split("\n");
- for (let i = 0, len = selectionText.length; i < len; i++) {
- selectionText[i] =
- selectionText[i] === "" ? "" : "- " + selectionText[i];
- }
- JoeEditor.replaceSelection("\n" + selectionText.join("\n"));
- }
- }
- JoeEditor.focus();
- }
-
- /* 有序列表 - 已测试 √ */
- insertOrderedListText() {
- const cursor = JoeEditor.getCursor();
- const selection = JoeEditor.getSelection();
- if (cursor.ch === 0) {
- if (selection === "") {
- JoeEditor.replaceSelection("1. ");
- } else {
- const selectionText = selection.split("\n");
- for (let i = 0, len = selectionText.length; i < len; i++) {
- selectionText[i] =
- selectionText[i] === ""
- ? ""
- : i + 1 + ". " + selectionText[i];
- }
- JoeEditor.replaceSelection(selectionText.join("\n"));
- }
- } else {
- if (selection === "") {
- JoeEditor.replaceSelection("\n\n1. ");
- } else {
- const selectionText = selection.split("\n");
- for (let i = 0, len = selectionText.length; i < len; i++) {
- selectionText[i] =
- selectionText[i] === ""
- ? ""
- : i + 1 + ". " + selectionText[i];
- }
- JoeEditor.replaceSelection("\n" + selectionText.join("\n"));
- }
- }
- JoeEditor.focus();
- }
-
- /* 插入时间 - 已测试 √ */
- insertTimeText() {
- const time = new Date();
- const _Year = time.getFullYear();
- const _Month = String(time.getMonth() + 1).padStart(2, 0);
- const _Date = String(time.getDate()).padStart(2, 0);
- const _Hours = String(time.getHours()).padStart(2, 0);
- const _Minutes = String(time.getMinutes()).padStart(2, 0);
- const _Seconds = String(time.getSeconds()).padStart(2, 0);
- const _Day = [
- "星期日",
- "星期一",
- "星期二",
- "星期三",
- "星期四",
- "星期五",
- "星期六",
- ][time.getDay()];
- const _time = `${_Year}-${_Month}-${_Date} ${_Hours}:${_Minutes}:${_Seconds} ${_Day}`;
- const cursor = JoeEditor.getCursor();
- if (cursor.ch === 0) JoeEditor.replaceSelection(_time);
- else JoeEditor.replaceSelection("\n" + _time);
- JoeEditor.focus();
- }
-
- /* 插入URL链接 - 已测试 √ */
- insertLinkText() {
- this.__OpenTheDialog({
- title: "插入链接",
- innerHtml: `
-
-
-
-
-
-
-
-
- `,
- confirm() {
- const title = $(".CodeMirror-dialog input[name='title']").val();
- const url = $(".CodeMirror-dialog input[name='url']").val();
- JoeEditor.replaceSelection(
- `[${title || "默认标题"}](${url || "默认地址"})`
- );
- JoeEditor.focus();
- },
- });
- }
-
- /* 插入图片 - 已测试 √ */
- insertImageText() {
- this.__OpenTheDialog({
- title: "插入图片",
- innerHtml: `
-
-
-
-
-
-
-
-
- `,
- confirm() {
- const title = $(".CodeMirror-dialog input[name='title']").val();
- const url = $(".CodeMirror-dialog input[name='url']").val();
- JoeEditor.replaceSelection(
- `![${title || "默认图片"}](${url || "默认地址"})`
- );
- JoeEditor.focus();
- },
- });
- }
-
- /* 插入表格 - 已测试 √ */
- insertTableText() {
- this.__OpenTheDialog({
- title: "插入表格",
- innerHtml: `
-
-
-
-
-
-
- `,
- confirm() {
- let row = $(".CodeMirror-dialog input[name='row']").val();
- let column = $(".CodeMirror-dialog input[name='column']").val();
- if (isNaN(row)) row = 3;
- if (isNaN(column)) column = 3;
- let rowStr = "";
- let rangeStr = "";
- let columnlStr = "";
- for (let i = 0; i < column; i++) {
- rowStr += "| 表头 ";
- rangeStr += "| :--: ";
- }
- for (let i = 0; i < row; i++) {
- for (let j = 0; j < column; j++) columnlStr += "| 表格 ";
- columnlStr += "|\n";
- }
- const htmlStr = `${rowStr}|\n${rangeStr}|\n${columnlStr}\n`;
- const cursor = JoeEditor.getCursor();
- if (cursor.ch === 0) JoeEditor.replaceSelection(htmlStr);
- else JoeEditor.replaceSelection("\n\n" + htmlStr);
- JoeEditor.focus();
- },
- });
- }
-
- /* 插入代码块 - 已测试 √ */
- insertCodeBlockText() {
- this.__OpenTheDialog({
- title: "插入代码块",
- innerHtml: `
-
-
-
-
- `,
- confirm() {
- const type = $(".CodeMirror-dialog input[name='type']").val();
- const cursor = JoeEditor.getCursor();
- const htmlStr = `\`\`\`${
- type || "html"
- }\ncode here...\n\`\`\`\n\n`;
- if (cursor.ch === 0) JoeEditor.replaceSelection(htmlStr);
- else JoeEditor.replaceSelection("\n\n" + htmlStr);
- JoeEditor.focus();
- },
- });
- }
-
- /* 关于 */
- handleAbout() {
- this.__OpenTheDialog({
- title: "关于",
- innerHtml: `
-
- - 文件读取教程(将文件推拽至编辑器即可)
- - 图片粘贴上传教程(不支持本地文件复制粘贴,支持网络图片复制、截图等)
- - 本编辑器仅为Joe主题使用,未经允许不得移植至其他主题!
-
- `,
- });
- }
-}
-window.JoeInstance = JoeInstance;
diff --git a/typecho/editor/js/joe.constructor.min.js b/typecho/editor/js/joe.constructor.min.js
deleted file mode 100644
index a3cfa75..0000000
--- a/typecho/editor/js/joe.constructor.min.js
+++ /dev/null
@@ -1 +0,0 @@
-class JoeInstance{constructor(){this.init()}init(){$(".CodeMirror-wrap").before(''),$(".CodeMirror-wrap").append(''),$("body").append('\n \n '),$(".CodeMirror-dialog__wrapper-footer--cancle").on("click",()=>{this.options.cancel(),$(".CodeMirror-dialog").removeClass("active"),$("body").css("overflow","")}),$(".CodeMirror-dialog__wrapper-footer--confirm").on("click",()=>{this.options.confirm(),$(".CodeMirror-dialog").removeClass("active"),$("body").css("overflow","")})}__OpenTheDialog(e={}){const o={title:"提示",innerHtml:"内容",cancel:()=>{},confirm:()=>{}};this.options=Object.assign(o,e),$(".CodeMirror-dialog__wrapper-header").html(this.options.title),$(".CodeMirror-dialog__wrapper-bodyer").html(this.options.innerHtml),$(".CodeMirror-dialog").addClass("active"),$("body").css("overflow","hidden")}initTitleText(e){const o=$(`\n \n ${e.icon}\n \n
H1
\n
H2
\n
H3
\n
H4
\n
H5
\n
H6
\n
\n \n `);o.on("click",function(e){e.stopPropagation(),$(this).toggleClass("active")}),o.on("click",".CodeMirror-means__dropdown-item",function(e){e.stopPropagation();const t=$(this).attr("data-text"),r=JoeEditor.getCursor();0===r.ch?JoeEditor.replaceSelection(t):JoeEditor.replaceSelection("\n\n"+t),o.removeClass("active"),JoeEditor.focus()}),$(document).on("click",()=>o.removeClass("active")),$(".CodeMirror-means").append(o)}handleFullscreen(e){const o=$(".CodeMirror-means").height(),t=$(`${e.icon}`);t.on("click",function(){const e=JoeEditor.getOption("fullScreen");e?(JoeEditor.setOption("fullScreen",!1),$(".CodeMirror-wrap").css("top",""),$(".CodeMirror-means").removeClass("fullscreen"),$(t).removeClass("active")):(JoeEditor.setOption("fullScreen",!0),$(".CodeMirror-wrap").css("top",o),$(".CodeMirror-means").addClass("fullscreen"),$(t).addClass("active"))}),$(".CodeMirror-means").append(t)}insertBoldText(){const e=JoeEditor.getCursor(),o=JoeEditor.getSelection();JoeEditor.replaceSelection("**"+o+"**"),""===o&&JoeEditor.setCursor(e.line,e.ch+2),JoeEditor.focus()}insertItalicText(){const e=JoeEditor.getCursor(),o=JoeEditor.getSelection();JoeEditor.replaceSelection("*"+o+"*"),""===o&&JoeEditor.setCursor(e.line,e.ch+1),JoeEditor.focus()}insertDeleteText(){const e=JoeEditor.getCursor(),o=JoeEditor.getSelection();JoeEditor.replaceSelection("~~"+o+"~~"),""===o&&JoeEditor.setCursor(e.line,e.ch+2),JoeEditor.focus()}insertQuoteText(){const e=JoeEditor.getCursor(),o=JoeEditor.getSelection();0===e.ch?JoeEditor.replaceSelection("> "+o):(JoeEditor.setCursor(e.line,0),JoeEditor.replaceSelection("> "+o),JoeEditor.setCursor(e.line,e.ch+2)),JoeEditor.focus()}insertSpaceText(){JoeEditor.replaceSelection(" "),JoeEditor.focus()}insertCodeInlineText(){const e=JoeEditor.getCursor(),o=JoeEditor.getSelection();JoeEditor.replaceSelection("`"+o+"`"),""===o&&JoeEditor.setCursor(e.line,e.ch+1),JoeEditor.focus()}insertHrText(){const e=JoeEditor.getCursor();0===e.ch?JoeEditor.replaceSelection("\n------------\n\n"):JoeEditor.replaceSelection("\n\n------------\n\n"),JoeEditor.focus()}insertUnorderedListText(){const e=JoeEditor.getCursor(),o=JoeEditor.getSelection();if(0===e.ch)if(""===o)JoeEditor.replaceSelection("- ");else{const e=o.split("\n");for(let o=0,t=e.length;o\n \n \n \n \n \n \n
\n ',confirm(){const e=$(".CodeMirror-dialog input[name='title']").val(),o=$(".CodeMirror-dialog input[name='url']").val();JoeEditor.replaceSelection(`[${e||"默认标题"}](${o||"默认地址"})`),JoeEditor.focus()}})}insertImageText(){this.__OpenTheDialog({title:"插入图片",innerHtml:'\n \n \n \n
\n \n \n \n
\n ',confirm(){const e=$(".CodeMirror-dialog input[name='title']").val(),o=$(".CodeMirror-dialog input[name='url']").val();JoeEditor.replaceSelection(`![${e||"默认图片"}](${o||"默认地址"})`),JoeEditor.focus()}})}insertTableText(){this.__OpenTheDialog({title:"插入表格",innerHtml:'\n \n \n \n \n \n
\n ',confirm(){let e=$(".CodeMirror-dialog input[name='row']").val(),o=$(".CodeMirror-dialog input[name='column']").val();isNaN(e)&&(e=3),isNaN(o)&&(o=3);let t="",r="",i="";for(let e=0;e\n \n \n \n ',confirm(){const e=$(".CodeMirror-dialog input[name='type']").val(),o=JoeEditor.getCursor(),t=`\`\`\`${e||"html"}\ncode here...\n\`\`\`\n\n`;0===o.ch?JoeEditor.replaceSelection(t):JoeEditor.replaceSelection("\n\n"+t),JoeEditor.focus()}})}handleAbout(){this.__OpenTheDialog({title:"关于",innerHtml:"\n \n - 文件读取教程(将文件推拽至编辑器即可)
\n - 图片粘贴上传教程(不支持本地文件复制粘贴,支持网络图片复制、截图等)
\n - 本编辑器仅为Joe主题使用,未经允许不得移植至其他主题!
\n
\n "})}}window.JoeInstance=JoeInstance;
\ No newline at end of file
diff --git a/typecho/editor/js/joe.editor.js b/typecho/editor/js/joe.editor.js
deleted file mode 100644
index 4c31eda..0000000
--- a/typecho/editor/js/joe.editor.js
+++ /dev/null
@@ -1,134 +0,0 @@
-$(function () {
- $("head").append(
- ``
- );
-
- window.JoeEditor = CodeMirror.fromTextArea(
- document.querySelector("textarea#text"),
- {
- theme: "joe",
- mode: "gfm",
- /* Tab大小 */
- indentUnit: 4,
- /* 显示行号 */
- lineNumbers: true,
- /* 超出宽度自动换行 */
- lineWrapping: true,
- /* 当前行背景高亮 */
- styleActiveLine: true,
- /* 匹配括号 */
- matchBrackets: true,
- /* 自动闭合括号 */
- autoCloseBrackets: true,
- /* 自动闭合标签 */
- autoCloseTags: true,
- /* 折行 */
- foldGutter: true,
- gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
- /* 匹配标签 */
- matchTags: { bothTags: true },
- /* 按键 */
- extraKeys: {
- /* 换行时自动联想输入的类型 */
- Enter: "newlineAndIndentContinueMarkdownList",
- /* 匹配标签 */
- "Ctrl-J": "toMatchingTag",
- },
- /* 双击鼠标选中后,可以拖拽被选择的文本 */
- selectionPointer: true,
- }
- );
-
- /* 设置高度 */
- JoeEditor.setSize("100%", "550");
-
- /* 实时预览 */
- JoeEditor.on("change", function () {
- // console.log(JoeEditor.getValue());
- });
-
- /* 节流,防止傻逼疯狂ctrl + v上传 */
- let _flag = false;
- /* 实现粘贴上传图片 */
- JoeEditor.on("paste", function (editor, event) {
- let clipboardData =
- event.clipboardData ||
- window.clipboardData ||
- event.originalEvent.clipboardData;
- if (!clipboardData || !clipboardData.items) return;
- let items = clipboardData.items;
- let file = null;
- if (items.length === 0) return;
- for (let i = 0; i < items.length; i++) {
- if (items[i].kind === "file" && items[i].type.match(/^image/)) {
- event.preventDefault(), (file = items[i].getAsFile());
- }
- }
- if (!file) return;
- /* 节流,防止傻逼疯狂ctrl + v上传 */
- if (_flag) return;
- _flag = true;
- let uploadUrl = JoeUploadURL;
- let cid = $('input[name="cid"]').val();
- cid && (uploadUrl = uploadUrl + "&cid=" + cid);
- let random = Date.now().toString(36);
- let fileName = random + ".png";
- let formData = new FormData();
- formData.append("name", fileName);
- formData.append("file", file, fileName);
- $.ajax({
- url: uploadUrl,
- method: "post",
- data: formData,
- contentType: false,
- processData: false,
- xhr: () => {
- let xhr = $.ajaxSettings.xhr();
- if (!xhr.upload) return;
- $(".CodeMirror-progress").show();
- xhr.upload.addEventListener(
- "progress",
- (e) => {
- let percent = (e.loaded / e.total) * 100;
- $(".CodeMirror-progress").css("right", -percent + "%");
- },
- false
- );
- return xhr;
- },
- success(res) {
- const text = `![${res[1].title}](${res[0]})`;
- let timer = setTimeout(function () {
- $(".CodeMirror-progress").hide();
- $(".CodeMirror-progress").css("right", "100%");
- /* 节流,防止傻逼疯狂ctrl + v上传 */
- _flag = false;
- const cursor = JoeEditor.getCursor();
- if (cursor.ch === 0) JoeEditor.replaceSelection(text);
- else JoeEditor.replaceSelection("\n\n" + text);
- JoeEditor.focus();
- clearTimeout(timer);
- }, 1000);
- },
- error() {
- let timer = setTimeout(function () {
- $(".CodeMirror-progress").hide();
- $(".CodeMirror-progress").css("right", "100%");
- /* 节流,防止傻逼疯狂ctrl + v上传 */
- _flag = false;
- clearTimeout(timer);
- }, 1000);
- },
- });
- });
-
- Typecho.insertFileToEditor = function (file, url, isImage) {
- const htmlStr = isImage
- ? "![" + file + "](" + url + ")"
- : "[" + file + "](" + url + ")";
- const cursor = JoeEditor.getCursor();
- const n = cursor.ch === 0 ? "" : "\n";
- JoeEditor.replaceSelection(n + htmlStr);
- JoeEditor.focus();
- };
-});
diff --git a/typecho/editor/js/joe.editor.min.js b/typecho/editor/js/joe.editor.min.js
deleted file mode 100644
index ae3ed65..0000000
--- a/typecho/editor/js/joe.editor.min.js
+++ /dev/null
@@ -1 +0,0 @@
-$(function(){$("head").append(''),window.JoeEditor=CodeMirror.fromTextArea(document.querySelector("textarea#text"),{theme:"joe",mode:"gfm",indentUnit:4,lineNumbers:!0,lineWrapping:!0,styleActiveLine:!0,matchBrackets:!0,autoCloseBrackets:!0,autoCloseTags:!0,foldGutter:!0,gutters:["CodeMirror-linenumbers","CodeMirror-foldgutter"],matchTags:{bothTags:!0},extraKeys:{Enter:"newlineAndIndentContinueMarkdownList","Ctrl-J":"toMatchingTag"},selectionPointer:!0}),JoeEditor.setSize("100%","550"),JoeEditor.on("change",function(){});let e=!1;JoeEditor.on("paste",function(t,o){let r=o.clipboardData||window.clipboardData||o.originalEvent.clipboardData;if(!r||!r.items)return;let i=r.items,n=null;if(0===i.length)return;for(let e=0;e{let e=$.ajaxSettings.xhr();if(e.upload)return $(".CodeMirror-progress").show(),e.upload.addEventListener("progress",e=>{let t=e.loaded/e.total*100;$(".CodeMirror-progress").css("right",-t+"%")},!1),e},success(t){const o=`![${t[1].title}](${t[0]})`;let r=setTimeout(function(){$(".CodeMirror-progress").hide(),$(".CodeMirror-progress").css("right","100%"),e=!1;const t=JoeEditor.getCursor();0===t.ch?JoeEditor.replaceSelection(o):JoeEditor.replaceSelection("\n\n"+o),JoeEditor.focus(),clearTimeout(r)},1e3)},error(){let t=setTimeout(function(){$(".CodeMirror-progress").hide(),$(".CodeMirror-progress").css("right","100%"),e=!1,clearTimeout(t)},1e3)}})}),Typecho.insertFileToEditor=function(e,t,o){const r=o?"!["+e+"]("+t+")":"["+e+"]("+t+")",i=JoeEditor.getCursor(),n=0===i.ch?"":"\n";JoeEditor.replaceSelection(n+r),JoeEditor.focus()}});
\ No newline at end of file
diff --git a/typecho/editor/js/joe.instance.js b/typecho/editor/js/joe.instance.js
deleted file mode 100644
index 2d3ceed..0000000
--- a/typecho/editor/js/joe.instance.js
+++ /dev/null
@@ -1,199 +0,0 @@
-$(function () {
- const Joe = new JoeInstance();
- [
- {
- type: "bold",
- title: "文本加粗",
- icon:
- '',
- },
- {
- type: "italic",
- title: "文本倾斜",
- icon:
- '',
- },
- {
- type: "delete",
- title: "文本删除",
- icon:
- '',
- },
- {
- type: "space",
- title: "空格",
- icon:
- '',
- },
- {
- type: "code-inline",
- title: "行内代码",
- icon:
- '',
- },
- {
- type: "hr",
- title: "横线",
- icon:
- '',
- },
- {
- type: "quote",
- title: "文本引用",
- icon:
- '',
- },
- {
- type: "link",
- title: "超链接",
- icon:
- '',
- },
- {
- type: "image",
- title: "插入图片",
- icon:
- '',
- },
- {
- type: "ordered-list",
- title: "有序列表",
- icon:
- '',
- },
- {
- type: "title",
- title: "标题",
- icon:
- '',
- },
- {
- type: "unordered-list",
- title: "无序列表",
- icon:
- '',
- },
-
- {
- type: "undo",
- title: "撤销",
- icon:
- '',
- },
- {
- type: "redo",
- title: "回退",
- icon:
- '',
- },
-
- {
- type: "table",
- title: "表格",
- icon:
- '',
- },
- {
- type: "code-block",
- title: "代码块",
- icon:
- '',
- },
- {
- type: "time",
- title: "当前时间",
- icon:
- '',
- },
- {
- type: "clean",
- title: "清屏",
- icon:
- '',
- },
- {
- type: "fullscreen",
- title: "全屏/取消全屏",
- icon:
- '',
- },
- {
- type: "about",
- title: "关于",
- icon:
- '',
- },
- ].forEach((tool) => {
- if (tool.type === "title") {
- Joe.initTitleText(tool);
- } else if (tool.type === "fullscreen") {
- Joe.handleFullscreen(tool);
- } else {
- const item = $(
- `${tool.icon}`
- );
- item.on("click", () => {
- switch (tool.type) {
- case "bold":
- Joe.insertBoldText();
- break;
- case "italic":
- Joe.insertItalicText();
- break;
- case "delete":
- Joe.insertDeleteText();
- break;
- case "quote":
- Joe.insertQuoteText();
- break;
- case "space":
- Joe.insertSpaceText();
- break;
- case "code-inline":
- Joe.insertCodeInlineText();
- break;
- case "hr":
- Joe.insertHrText();
- break;
- case "unordered-list":
- Joe.insertUnorderedListText();
- break;
- case "ordered-list":
- Joe.insertOrderedListText();
- break;
- case "undo":
- JoeEditor.undo();
- JoeEditor.focus();
- break;
- case "redo":
- JoeEditor.redo();
- JoeEditor.focus();
- break;
- case "clean":
- JoeEditor.setValue("");
- JoeEditor.focus();
- break;
- case "time":
- Joe.insertTimeText();
- break;
- case "link":
- Joe.insertLinkText();
- break;
- case "image":
- Joe.insertImageText();
- break;
- case "table":
- Joe.insertTableText();
- break;
- case "code-block":
- Joe.insertCodeBlockText();
- break;
- case "about":
- Joe.handleAbout();
- break;
- }
- });
- $(".CodeMirror-means").append(item);
- }
- });
-});
diff --git a/typecho/editor/js/joe.instance.min.js b/typecho/editor/js/joe.instance.min.js
deleted file mode 100644
index 2f0a44a..0000000
--- a/typecho/editor/js/joe.instance.min.js
+++ /dev/null
@@ -1 +0,0 @@
-$(function(){const t=new JoeInstance;[{type:"bold",title:"文本加粗",icon:''},{type:"italic",title:"文本倾斜",icon:''},{type:"delete",title:"文本删除",icon:''},{type:"space",title:"空格",icon:''},{type:"code-inline",title:"行内代码",icon:''},{type:"hr",title:"横线",icon:''},{type:"quote",title:"文本引用",icon:''},{type:"link",title:"超链接",icon:''},{type:"image",title:"插入图片",icon:''},{type:"ordered-list",title:"有序列表",icon:''},{type:"title",title:"标题",icon:''},{type:"unordered-list",title:"无序列表",icon:''},{type:"undo",title:"撤销",icon:''},{type:"redo",title:"回退",icon:''},{type:"table",title:"表格",icon:''},{type:"code-block",title:"代码块",icon:''},{type:"time",title:"当前时间",icon:''},{type:"clean",title:"清屏",icon:''},{type:"fullscreen",title:"全屏/取消全屏",icon:''},{type:"about",title:"关于",icon:''}].forEach(e=>{if("title"===e.type)t.initTitleText(e);else if("fullscreen"===e.type)t.handleFullscreen(e);else{const i=$(`${e.icon}`);i.on("click",()=>{switch(e.type){case"bold":t.insertBoldText();break;case"italic":t.insertItalicText();break;case"delete":t.insertDeleteText();break;case"quote":t.insertQuoteText();break;case"space":t.insertSpaceText();break;case"code-inline":t.insertCodeInlineText();break;case"hr":t.insertHrText();break;case"unordered-list":t.insertUnorderedListText();break;case"ordered-list":t.insertOrderedListText();break;case"undo":JoeEditor.undo(),JoeEditor.focus();break;case"redo":JoeEditor.redo(),JoeEditor.focus();break;case"clean":JoeEditor.setValue(""),JoeEditor.focus();break;case"time":t.insertTimeText();break;case"link":t.insertLinkText();break;case"image":t.insertImageText();break;case"table":t.insertTableText();break;case"code-block":t.insertCodeBlockText();break;case"about":t.handleAbout()}}),$(".CodeMirror-means").append(i)}})});
\ No newline at end of file
diff --git a/typecho/write/css/joe.write.min.css b/typecho/write/css/joe.write.min.css
new file mode 100644
index 0000000..3209e71
--- /dev/null
+++ b/typecho/write/css/joe.write.min.css
@@ -0,0 +1 @@
+input[name='fields[keywords]']{width:100%}textarea[name='fields[description]'],textarea[name='fields[abstract]'],textarea[name='fields[thumb]'],textarea[name='fields[video]']{width:100%;height:80px}#text,#text ~ .resize{display:none}body.fullscreen{overflow:hidden}.cm-container{display:flex;flex-direction:column;position:relative;width:100%;height:550px;box-sizing:border-box;z-index:6666;content-visibility:auto;background:#222}.cm-container.fullscreen{position:fixed;top:0;left:0;width:100%;height:100%}.cm-container *{outline:none !important}.cm-container .cm-tools{display:flex;flex-wrap:wrap;background-color:#303133;padding:5px 0 0 5px}.cm-container .cm-tools .cm-tools-item{position:relative;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:2px;padding:5px;margin:0 5px 5px 0;transition:background 0.25s}.cm-container .cm-tools .cm-tools-item:hover{background:#393939}.cm-container .cm-tools .cm-tools-item:hover svg{fill:#efefef}.cm-container .cm-tools .cm-tools-item.active{background:#393939}.cm-container .cm-tools .cm-tools-item.active svg{fill:#efefef}.cm-container .cm-tools .cm-tools-item.active .cm-tools__dropdown{visibility:visible;opacity:1;-webkit-transform:translateX(-50%) rotateX(0);transform:translateX(-50%) rotateX(0)}.cm-container .cm-tools .cm-tools-item svg{vertical-align:middle;fill:#bfbfbf}.cm-container .cm-tools__dropdown{position:absolute;top:42px;left:50%;z-index:1000;background:#313335;padding:5px 0;border-radius:4px;visibility:hidden;-webkit-transform-origin:top;transform-origin:top;opacity:0;-webkit-transform:translateX(-50%) rotateX(-90deg);transform:translateX(-50%) rotateX(-90deg);transition:visibility 0.25s, opacity 0.25s, -webkit-transform 0.25s;transition:visibility 0.25s, transform 0.25s, opacity 0.25s;transition:visibility 0.25s, transform 0.25s, opacity 0.25s, -webkit-transform 0.25s}.cm-container .cm-tools__dropdown-item{padding:0 20px;line-height:32px;transition:background 0.25s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:15px;color:#bfbfbf;font-weight:600}.cm-container .cm-tools__dropdown-item:hover{background:#393939}.cm-container .cm-tools__dropdown::before{content:'';position:absolute;top:-7px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:0;height:0;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #313335}.cm-container .cm-mainer{flex:1;min-height:0;display:flex;width:100%}.cm-container .cm-mainer .cm-wrap{flex-shrink:0;height:100%;flex:1;min-width:0;font-size:14px}.cm-container .cm-mainer .cm-wrap .cm-scroller{line-height:1.5;font-family:Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;-ms-scroll-chaining:none;overscroll-behavior:none}.cm-container .cm-mainer .cm-wrap .cm-scroller::-webkit-scrollbar{display:none}.cm-container .cm-mainer .cm-wrap .cm-scroller ::-moz-selection{background-color:rgba(221,240,255,0.2)}.cm-container .cm-mainer .cm-wrap .cm-scroller ::selection{background-color:rgba(221,240,255,0.2)}.cm-container .cm-mainer .cm-wrap .cm-scroller .cm-content{white-space:pre-wrap;word-break:break-all;word-wrap:break-word;color:#ccc;caret-color:#ccc;padding:15px 13px 15px 11px}.cm-container .cm-mainer .cm-wrap .cm-scroller .cm-matchingBracket{color:#8f938f;border:1px solid rgba(255,255,255,0.25);margin:-1px}.cm-container .cm-mainer .cm-wrap .cm-scroller .cm-nonmatchingBracket{color:#ff5627}.cm-container .cm-mainer .cm-resize{flex-shrink:0;height:100%;position:relative;width:16px;background:#303133;cursor:col-resize}.cm-container .cm-mainer .cm-resize::before{content:'';position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);width:4px;height:40px;border-radius:2px;background:#fff}.cm-container .cm-mainer .cm-preview{position:relative;flex-shrink:0;width:0;height:100%;background:#fff;overflow:auto;box-sizing:border-box;-ms-scroll-chaining:none;overscroll-behavior:none}.cm-container .cm-mainer .cm-preview::-webkit-scrollbar-track{background:#fff}.cm-container .cm-mainer .cm-preview::-webkit-scrollbar{width:6px;height:6px}.cm-container .cm-mainer .cm-preview::-webkit-scrollbar-thumb{border-radius:3px;background:#c0c4cc}.cm-container .cm-mainer .cm-preview .cm-preview-content{padding:20px;font-size:14px;font-family:'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;color:#606266;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.cm-container .cm-mainer .cm-preview .cm-preview-content *{margin:0;padding:0;box-sizing:border-box}.cm-container .cm-mainer .cm-preview .cm-preview-content h1,.cm-container .cm-mainer .cm-preview .cm-preview-content h2,.cm-container .cm-mainer .cm-preview .cm-preview-content h3,.cm-container .cm-mainer .cm-preview .cm-preview-content h4,.cm-container .cm-mainer .cm-preview .cm-preview-content h5,.cm-container .cm-mainer .cm-preview .cm-preview-content h6{color:#303133;font-size:18px;line-height:24px;margin-bottom:15px;position:relative}.cm-container .cm-mainer .cm-preview .cm-preview-content h1{padding:0 15px}.cm-container .cm-mainer .cm-preview .cm-preview-content h1::before{content:'';position:absolute;top:8.5px;left:0;height:7px;width:7px;border-radius:50%;background:#409eff}.cm-container .cm-mainer .cm-preview .cm-preview-content h2{padding:0 15px}.cm-container .cm-mainer .cm-preview .cm-preview-content h2::before{content:'';position:absolute;top:10%;bottom:10%;left:0;width:4px;border-radius:2px;background:#409eff}.cm-container .cm-mainer .cm-preview .cm-preview-content h3{padding:0 15px 0 20px}.cm-container .cm-mainer .cm-preview .cm-preview-content h3::before{content:'#';color:#409eff;font-weight:700;position:absolute;top:0;left:0;line-height:24px}.cm-container .cm-mainer .cm-preview .cm-preview-content h4::before{content:'「';color:#409eff;font-weight:600;margin-right:5px}.cm-container .cm-mainer .cm-preview .cm-preview-content h4::after{content:'」';color:#409eff;font-weight:600;margin-left:5px}.cm-container .cm-mainer .cm-preview .cm-preview-content h5{padding:0 15px 0 28px}.cm-container .cm-mainer .cm-preview .cm-preview-content h5::before{content:'';position:absolute;top:2px;left:0;width:20px;height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAC8klEQVRYR+3WP2gTURwH8O/vKnVRRHKXP52cBO3g4p/BqYNIhy4muajUQRBFKjQV1En6ZxJBcmlRCoJDRe3FDiJVwamLS3FwqbgIgss1l2ZQF5XeT3I1Z3NJ7u5dLlAwN9699/t97vfe7/EIO/yhHe5DD9jpCv3fFVTu856+Xzi62Y/35hj9CFPNrlUwWeRJAJfBGADwBcBNI0/PRZFdAdo4xlQLjCqKjBzogat7hZCRAgPghJGBgbFnxglAOvS7b/fLb+q+qnv5BHBCyEBAWTdnANy2IxOtWSC1qsY+1jOFwAVG+gIVvXyHQbcaKuZCxmf5iMQogXFQtEsBeO5JT6BcMu+Bcb1lUhcyqfExACUAB6JEtgUqi+U5JrrmmcyFHCjwSYtQO+tSUSFbAmXdnAdwJVASdyWLPAS2kbFA8xsHNS13EzCmlx8R6KJQcBcypfFpho3cKxRna3ADsgEo6+ZjAKMhgjZ1d2KWR2gTSyD0h4jnIB2gXDIXwciFCPZvSnMl0wwshYrJGDImaMUGKovlLBPVOrDjh8APzFx8zDkjNa7FzYoGZsKT9XEarQOHmei1aJA246cqOWW6/i2l8VMGzoWIPW/k6eq2Ja6UwCz8pw2JGZ8sS8pUz8fWau/jGp+SgLchcGBgZD1Py41NUuoI+ZloV8ZU93+ogZKzfBiWfXAPCgMJU8Y42avQdMzI4ZBfmSizocqrUeJaAmsvBZGGBCtTziXeRY1rCxRAViEhXckqK93AeQJ9kYTvlmVlqmcTdhNEtefc+9X3utVmuX+CkK6oyqtu4nwrWP8bF5IZdGYjJ79wDuMCz4D+XmhFWnZbt7ab5ltBB6mbkyAaBPFCJassuwMmNb4L4EZgXwBc4AoGTZrUeA6A9x1yK6tzzvnFDlxBv0D176kCP2TCpbbjBXCRV9DZk0VeAONCE1IQ1zWg3dlF1sFQHWQIXFeBNrLAw5BwHBZWjQl6E3SbbB8X+R4Mg/Ca0wN2WtFeBTut4B84mFI4VpekyAAAAABJRU5ErkJggg==");background-size:100% 100%}.cm-container .cm-mainer .cm-preview .cm-preview-content h6{padding:0 15px 0 28px}.cm-container .cm-mainer .cm-preview .cm-preview-content h6::before{content:'';position:absolute;top:2px;left:0;width:20px;height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEI0lEQVRYR+3Xb2wTZRwH8G/vL22RPy5GW8fK6rJpGeFFY9RO3TRRE1HfmEAammEyjZmOSEg1RrPZaTD6xjhU/Ndlf0CZgwmD+qcgZBRIETeqY0Vcnc7pGonhRY2l3m2t5upqjq693l2vcy+8N81zz/NcPvf75fd7rjos8ku3yH34HyjK0PqlDLPzD56vMNL0VHxmpgXAoUIZXKgI3sMSpK+cNTAbylaj/9IkprnLM3+mkg8XQi4EsJ4lSL+ZNbAfVd+JG/XLcSERg3M8IAtZauBtDEkeNdN6fX9NfRqXueQiSwm00wQRMDMGw94snBJkqYBrSYIIXs/ojftqGq6IXHZRFIpkKYC2JSw9SJE663W0nthlqcMawwrJYpVCag1M48pNy1YNvLWRffGNIYSCv6Cnog7rjCtVIbUEXoG7qeqaNMj51F58dWoKPZY62JeWyUJG+ctcIplcIizWCpgTl9EIyODJn9BtceDWq/6B57tO/H4R688fFaZ7AWzWAiiJEyMDx39Ed6UDdyy7Ni/w1ekxbP95VJi/F8CRYoGycGLksaEJdFU6cPdy0zxkR/RbtE6FhPvHATQUm2JFODHSfyyCLmsd7lth/hf53q/jcE8OC+NTAG7PTKiNoCqcGPnpF+PotDrwwNXl+OC3H9A8cVqYPgPgFnFo1QCLwomRg4cvoPMGBzZHTgq3zwKwZ+ddKVATXAZhf+gdnPvuojAUqmJdrspRAtQU93pXEM+8clgwhQHU5itruUBNcTt6TsP9sl8weQC0S/VFOUBNcW/2folt2z+XhZPTZh6kSKJ79aqVRuFszRxfUm8sNbdz9xlsfekz2bhCQBvDMMM8z+ubN92Mjrb71brS+97dM4wtnk8U4SSBBEGErVarzeVywePxoHVLA1pb6lUh3+8bwZMv+BTjpIAVACa9Xq+uqakJ7e3tqpGd/WfR3Jr+81awIJS0mY0A+sLhMGw2W3qfGmTXvhAef/6gapxUBDuqqqoejUQiBvFbKUH2DHyNx54bLAqXF0jT9HBjY6Pd6/XOi7oc5K7936Dp2QNF4/IBrTqdLuLxeIi2tracRSGF/PDgKB55er8muHzATQB2m81m+P1+1NbmPoVyIfsOnUOj+2PNcPmAO1iWbeE4TmexWODz+WQhayrL4No2oCkuJ5Bl2VGO49ZmcisXObdeVSuRaq7ZZ3G10KBTqRQl3pQPGY1GEQgE4HQ6heVDAO5S1cklNmUDXQRB9KZSqXkfESaTCW63G7FYDKFQKDEyMoJoNKqfe/bbAJ7QGpcrxa8xDLOV5/k0kGXZv2ZnZ5FMJtNjmqYvURR1IpFInAcwAeD7ud/pUuDmAY1G41g8Hl9DkmScoqggx3FHAIyJMMlSQfI9V5zKagDC93dsoRFKimQx2dIWOV/U/yn6bx0WyDj8vgLOAAAAAElFTkSuQmCC");background-size:100% 100%}.cm-container .cm-mainer .cm-preview .cm-preview-content hr{border:none;height:1px;background-color:#e4e7ed;margin-bottom:15px}.cm-container .cm-mainer .cm-preview .cm-preview-content p{line-height:26px;margin-bottom:15px}.cm-container .cm-mainer .cm-preview .cm-preview-content blockquote{line-height:26px;margin-bottom:15px;background:#ecf8ff;border-left:5px solid #50bfff;color:#50bfff;padding:8px 15px;border-radius:0 4px 4px 0}.cm-container .cm-mainer .cm-preview .cm-preview-content blockquote p{margin:0}.cm-container .cm-mainer .cm-preview .cm-preview-content code{display:inline-block;min-height:26px;line-height:26px;border-radius:4px;font-size:12px;background:#fdf6ec;padding:0 8px;color:#e6a23c;vertical-align:top}.cm-container .cm-mainer .cm-preview .cm-preview-content pre{background:#f0f0ec;padding:15px;margin-bottom:15px}.cm-container .cm-mainer .cm-preview .cm-preview-content pre code{display:unset;vertical-align:unset;min-height:unset;line-height:unset;border-radius:unset;font-size:unset;background:unset;padding:unset;color:unset}.cm-container .cm-mainer .cm-preview .cm-preview-content ol,.cm-container .cm-mainer .cm-preview .cm-preview-content ul{margin-bottom:15px;padding-left:36px}.cm-container .cm-mainer .cm-preview .cm-preview-content ol li,.cm-container .cm-mainer .cm-preview .cm-preview-content ul li{line-height:26px}.cm-container .cm-mainer .cm-preview .cm-preview-content ol li{list-style:decimal}.cm-container .cm-mainer .cm-preview .cm-preview-content ul li{list-style:disc}.cm-container .cm-mainer .cm-preview .cm-preview-content table{width:100%;max-width:100%;table-layout:fixed;color:#909399;margin-bottom:15px;font-size:13px;border-top:1px solid #ebeef5;border-left:1px solid #ebeef5}.cm-container .cm-mainer .cm-preview .cm-preview-content table td,.cm-container .cm-mainer .cm-preview .cm-preview-content table th{padding:8px;border-bottom:1px solid #ebeef5;border-right:1px solid #ebeef5}.cm-container .cm-mainer .cm-preview .cm-preview-content table thead th{font-weight:500;background:#ebeef5}.cm-container .cm-mainer .cm-preview .cm-preview-content table tbody tr{transition:background 0.35s}.cm-container .cm-mainer .cm-preview .cm-preview-content table tbody tr:hover{background:#ebeef5}.cm-container .cm-mainer .cm-preview .cm-preview-content img:not(.owo_image){display:block;max-width:100%;border-radius:4px;transition:box-shadow 0.35s, -webkit-transform 0.35s;transition:transform 0.35s, box-shadow 0.35s;transition:transform 0.35s, box-shadow 0.35s, -webkit-transform 0.35s;margin:0 auto}.cm-container .cm-mainer .cm-preview .cm-preview-content .owo_image{max-height:26px;vertical-align:top}.cm-container .cm-mainer .cm-preview .cm-preview-content a{display:inline-block;line-height:26px;color:#409eff;position:relative;text-decoration:none}.cm-container .cm-progress-left{position:absolute;top:0;right:50%;width:0;height:3px;background-color:#ff5627;transition:width 0.5s;z-index:1}.cm-container .cm-progress-right{position:absolute;top:0;left:50%;width:0;height:3px;background-color:#ff5627;transition:width 0.5s;z-index:1}.cm-modal{display:flex;align-items:center;justify-content:center;position:fixed;top:0;left:0;right:0;bottom:0;z-index:9999;background:rgba(0,0,0,0);visibility:hidden;transition:background 0.25s, visibility 0.25s;will-change:background, visibility}.cm-modal__wrapper{width:380px;background:#fff;color:#606266;border-radius:4px;opacity:0;-webkit-transform:translateY(-30%);transform:translateY(-30%);transition:opacity 0.25s, -webkit-transform 0.25s;transition:opacity 0.25s, transform 0.25s;transition:opacity 0.25s, transform 0.25s, -webkit-transform 0.25s}.cm-modal__wrapper-header{padding:12px 20px;border-bottom:1px solid #e4e7ed}.cm-modal__wrapper-bodyer{padding:20px}.cm-modal__wrapper-bodyer .fitem{display:flex;align-items:center;margin-bottom:15px}.cm-modal__wrapper-bodyer .fitem:last-child{margin-bottom:0}.cm-modal__wrapper-bodyer .fitem label{margin-right:10px}.cm-modal__wrapper-bodyer .fitem input{width:auto;flex:1;-webkit-appearance:none;outline:none;border:1px solid #dcdfe6;height:34px;box-sizing:border-box;padding:0 10px;border-radius:3px;color:#606266}.cm-modal__wrapper-footer{display:flex;align-items:center;justify-content:flex-end;padding:0 20px 10px}.cm-modal__wrapper-footer--cancle,.cm-modal__wrapper-footer--confirm{cursor:pointer;border:none;outline:none;margin-left:5px;background:#fff;color:#606266;-webkit-appearance:none;padding:7px 15px;border-radius:3px}.cm-modal__wrapper-footer--confirm{color:#fff;background:#409eff}.cm-modal.active{visibility:visible;background:rgba(0,0,0,0.75)}.cm-modal.active .cm-modal__wrapper{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}@media (max-width: 768px){.cm-modal__wrapper{width:90%}}
diff --git a/typecho/write/css/joe.write.min.scss b/typecho/write/css/joe.write.min.scss
new file mode 100644
index 0000000..0f27676
--- /dev/null
+++ b/typecho/write/css/joe.write.min.scss
@@ -0,0 +1,526 @@
+input[name='fields[keywords]'] {
+ width: 100%;
+}
+
+textarea[name='fields[description]'],
+textarea[name='fields[abstract]'],
+textarea[name='fields[thumb]'],
+textarea[name='fields[video]'] {
+ width: 100%;
+ height: 80px;
+}
+
+#text,
+#text ~ .resize {
+ display: none;
+}
+
+body.fullscreen {
+ overflow: hidden;
+}
+
+.cm-container {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ width: 100%;
+ height: 550px;
+ box-sizing: border-box;
+ z-index: 6666;
+ content-visibility: auto;
+ background: #222;
+ &.fullscreen {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ }
+ * {
+ outline: none !important;
+ }
+ .cm-tools {
+ display: flex;
+ flex-wrap: wrap;
+ background-color: #303133;
+ padding: 5px 0 0 5px;
+ .cm-tools-item {
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 2px;
+ padding: 5px;
+ margin: 0 5px 5px 0;
+ transition: background 0.25s;
+ &:hover {
+ background: #393939;
+ svg {
+ fill: #efefef;
+ }
+ }
+ &.active {
+ background: #393939;
+ svg {
+ fill: #efefef;
+ }
+ .cm-tools__dropdown {
+ visibility: visible;
+ opacity: 1;
+ transform: translateX(-50%) rotateX(0);
+ }
+ }
+ svg {
+ vertical-align: middle;
+ fill: #bfbfbf;
+ }
+ }
+ &__dropdown {
+ position: absolute;
+ top: 42px;
+ left: 50%;
+ z-index: 1000;
+ background: #313335;
+ padding: 5px 0;
+ border-radius: 4px;
+ visibility: hidden;
+ transform-origin: top;
+ opacity: 0;
+ transform: translateX(-50%) rotateX(-90deg);
+ transition: visibility 0.25s, transform 0.25s, opacity 0.25s;
+ &-item {
+ padding: 0 20px;
+ line-height: 32px;
+ transition: background 0.25s;
+ user-select: none;
+ font-size: 15px;
+ color: #bfbfbf;
+ font-weight: 600;
+ &:hover {
+ background: #393939;
+ }
+ }
+ &::before {
+ content: '';
+ position: absolute;
+ top: -7px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 0;
+ height: 0;
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #313335;
+ }
+ }
+ }
+ .cm-mainer {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ width: 100%;
+ .cm-wrap {
+ flex-shrink: 0;
+ height: 100%;
+ flex: 1;
+ min-width: 0;
+ font-size: 14px;
+ .cm-scroller {
+ line-height: 1.5;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ overscroll-behavior: none;
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ ::selection {
+ background-color: rgba(221, 240, 255, 0.2);
+ }
+ .cm-content {
+ white-space: pre-wrap;
+ word-break: break-all;
+ word-wrap: break-word;
+ color: #ccc;
+ caret-color: #ccc;
+ padding: 15px 13px 15px 11px;
+ }
+ .cm-matchingBracket {
+ color: #8f938f;
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ margin: -1px;
+ }
+ .cm-nonmatchingBracket {
+ color: #ff5627;
+ }
+ }
+ }
+ .cm-resize {
+ flex-shrink: 0;
+ height: 100%;
+ position: relative;
+ width: 16px;
+ background: #303133;
+ cursor: col-resize;
+ &::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 4px;
+ height: 40px;
+ border-radius: 2px;
+ background: #fff;
+ }
+ }
+ .cm-preview {
+ position: relative;
+ flex-shrink: 0;
+ width: 0;
+ height: 100%;
+ background: #fff;
+ overflow: auto;
+ box-sizing: border-box;
+ overscroll-behavior: none;
+ &::-webkit-scrollbar-track {
+ background: #fff;
+ }
+ &::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+ &::-webkit-scrollbar-thumb {
+ border-radius: 3px;
+ background: #c0c4cc;
+ }
+ .cm-preview-content {
+ padding: 20px;
+ font-size: 14px;
+ font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
+ white-space: pre-wrap;
+ word-break: break-all;
+ word-wrap: break-word;
+ color: #606266;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ color: #303133;
+ font-size: 18px;
+ line-height: 24px;
+ margin-bottom: 15px;
+ position: relative;
+ }
+ h1 {
+ padding: 0 15px;
+ &::before {
+ content: '';
+ position: absolute;
+ top: 8.5px;
+ left: 0;
+ height: 7px;
+ width: 7px;
+ border-radius: 50%;
+ background: #409eff;
+ }
+ }
+ h2 {
+ padding: 0 15px;
+ &::before {
+ content: '';
+ position: absolute;
+ top: 10%;
+ bottom: 10%;
+ left: 0;
+ width: 4px;
+ border-radius: 2px;
+ background: #409eff;
+ }
+ }
+ h3 {
+ padding: 0 15px 0 20px;
+ &::before {
+ content: '#';
+ color: #409eff;
+ font-weight: 700;
+ position: absolute;
+ top: 0;
+ left: 0;
+ line-height: 24px;
+ }
+ }
+ h4 {
+ &::before {
+ content: '「';
+ color: #409eff;
+ font-weight: 600;
+ margin-right: 5px;
+ }
+ &::after {
+ content: '」';
+ color: #409eff;
+ font-weight: 600;
+ margin-left: 5px;
+ }
+ }
+ h5 {
+ padding: 0 15px 0 28px;
+ &::before {
+ content: '';
+ position: absolute;
+ top: 2px;
+ left: 0;
+ width: 20px;
+ height: 20px;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAC8klEQVRYR+3WP2gTURwH8O/vKnVRRHKXP52cBO3g4p/BqYNIhy4muajUQRBFKjQV1En6ZxJBcmlRCoJDRe3FDiJVwamLS3FwqbgIgss1l2ZQF5XeT3I1Z3NJ7u5dLlAwN9699/t97vfe7/EIO/yhHe5DD9jpCv3fFVTu856+Xzi62Y/35hj9CFPNrlUwWeRJAJfBGADwBcBNI0/PRZFdAdo4xlQLjCqKjBzogat7hZCRAgPghJGBgbFnxglAOvS7b/fLb+q+qnv5BHBCyEBAWTdnANy2IxOtWSC1qsY+1jOFwAVG+gIVvXyHQbcaKuZCxmf5iMQogXFQtEsBeO5JT6BcMu+Bcb1lUhcyqfExACUAB6JEtgUqi+U5JrrmmcyFHCjwSYtQO+tSUSFbAmXdnAdwJVASdyWLPAS2kbFA8xsHNS13EzCmlx8R6KJQcBcypfFpho3cKxRna3ADsgEo6+ZjAKMhgjZ1d2KWR2gTSyD0h4jnIB2gXDIXwciFCPZvSnMl0wwshYrJGDImaMUGKovlLBPVOrDjh8APzFx8zDkjNa7FzYoGZsKT9XEarQOHmei1aJA246cqOWW6/i2l8VMGzoWIPW/k6eq2Ja6UwCz8pw2JGZ8sS8pUz8fWau/jGp+SgLchcGBgZD1Py41NUuoI+ZloV8ZU93+ogZKzfBiWfXAPCgMJU8Y42avQdMzI4ZBfmSizocqrUeJaAmsvBZGGBCtTziXeRY1rCxRAViEhXckqK93AeQJ9kYTvlmVlqmcTdhNEtefc+9X3utVmuX+CkK6oyqtu4nwrWP8bF5IZdGYjJ79wDuMCz4D+XmhFWnZbt7ab5ltBB6mbkyAaBPFCJassuwMmNb4L4EZgXwBc4AoGTZrUeA6A9x1yK6tzzvnFDlxBv0D176kCP2TCpbbjBXCRV9DZk0VeAONCE1IQ1zWg3dlF1sFQHWQIXFeBNrLAw5BwHBZWjQl6E3SbbB8X+R4Mg/Ca0wN2WtFeBTut4B84mFI4VpekyAAAAABJRU5ErkJggg==');
+ background-size: 100% 100%;
+ }
+ }
+ h6 {
+ padding: 0 15px 0 28px;
+ &::before {
+ content: '';
+ position: absolute;
+ top: 2px;
+ left: 0;
+ width: 20px;
+ height: 20px;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEI0lEQVRYR+3Xb2wTZRwH8G/vL22RPy5GW8fK6rJpGeFFY9RO3TRRE1HfmEAammEyjZmOSEg1RrPZaTD6xjhU/Ndlf0CZgwmD+qcgZBRIETeqY0Vcnc7pGonhRY2l3m2t5upqjq693l2vcy+8N81zz/NcPvf75fd7rjos8ku3yH34HyjK0PqlDLPzD56vMNL0VHxmpgXAoUIZXKgI3sMSpK+cNTAbylaj/9IkprnLM3+mkg8XQi4EsJ4lSL+ZNbAfVd+JG/XLcSERg3M8IAtZauBtDEkeNdN6fX9NfRqXueQiSwm00wQRMDMGw94snBJkqYBrSYIIXs/ojftqGq6IXHZRFIpkKYC2JSw9SJE663W0nthlqcMawwrJYpVCag1M48pNy1YNvLWRffGNIYSCv6Cnog7rjCtVIbUEXoG7qeqaNMj51F58dWoKPZY62JeWyUJG+ctcIplcIizWCpgTl9EIyODJn9BtceDWq/6B57tO/H4R688fFaZ7AWzWAiiJEyMDx39Ed6UDdyy7Ni/w1ekxbP95VJi/F8CRYoGycGLksaEJdFU6cPdy0zxkR/RbtE6FhPvHATQUm2JFODHSfyyCLmsd7lth/hf53q/jcE8OC+NTAG7PTKiNoCqcGPnpF+PotDrwwNXl+OC3H9A8cVqYPgPgFnFo1QCLwomRg4cvoPMGBzZHTgq3zwKwZ+ddKVATXAZhf+gdnPvuojAUqmJdrspRAtQU93pXEM+8clgwhQHU5itruUBNcTt6TsP9sl8weQC0S/VFOUBNcW/2folt2z+XhZPTZh6kSKJ79aqVRuFszRxfUm8sNbdz9xlsfekz2bhCQBvDMMM8z+ubN92Mjrb71brS+97dM4wtnk8U4SSBBEGErVarzeVywePxoHVLA1pb6lUh3+8bwZMv+BTjpIAVACa9Xq+uqakJ7e3tqpGd/WfR3Jr+81awIJS0mY0A+sLhMGw2W3qfGmTXvhAef/6gapxUBDuqqqoejUQiBvFbKUH2DHyNx54bLAqXF0jT9HBjY6Pd6/XOi7oc5K7936Dp2QNF4/IBrTqdLuLxeIi2tracRSGF/PDgKB55er8muHzATQB2m81m+P1+1NbmPoVyIfsOnUOj+2PNcPmAO1iWbeE4TmexWODz+WQhayrL4No2oCkuJ5Bl2VGO49ZmcisXObdeVSuRaq7ZZ3G10KBTqRQl3pQPGY1GEQgE4HQ6heVDAO5S1cklNmUDXQRB9KZSqXkfESaTCW63G7FYDKFQKDEyMoJoNKqfe/bbAJ7QGpcrxa8xDLOV5/k0kGXZv2ZnZ5FMJtNjmqYvURR1IpFInAcwAeD7ud/pUuDmAY1G41g8Hl9DkmScoqggx3FHAIyJMMlSQfI9V5zKagDC93dsoRFKimQx2dIWOV/U/yn6bx0WyDj8vgLOAAAAAElFTkSuQmCC');
+ background-size: 100% 100%;
+ }
+ }
+ hr {
+ border: none;
+ height: 1px;
+ background-color: #e4e7ed;
+ margin-bottom: 15px;
+ }
+ p {
+ line-height: 26px;
+ margin-bottom: 15px;
+ }
+ blockquote {
+ line-height: 26px;
+ margin-bottom: 15px;
+ background: #ecf8ff;
+ border-left: 5px solid #50bfff;
+ color: #50bfff;
+ padding: 8px 15px;
+ border-radius: 0 4px 4px 0;
+ p {
+ margin: 0;
+ }
+ }
+ code {
+ display: inline-block;
+ min-height: 26px;
+ line-height: 26px;
+ border-radius: 4px;
+ font-size: 12px;
+ background: #fdf6ec;
+ padding: 0 8px;
+ color: #e6a23c;
+ vertical-align: top;
+ }
+ pre {
+ background: #f0f0ec;
+ padding: 15px;
+ margin-bottom: 15px;
+ }
+ pre code {
+ display: unset;
+ vertical-align: unset;
+ min-height: unset;
+ line-height: unset;
+ border-radius: unset;
+ font-size: unset;
+ background: unset;
+ padding: unset;
+ color: unset;
+ }
+ ol,
+ ul {
+ margin-bottom: 15px;
+ padding-left: 36px;
+ li {
+ line-height: 26px;
+ }
+ }
+ ol li {
+ list-style: decimal;
+ }
+ ul li {
+ list-style: disc;
+ }
+ table {
+ width: 100%;
+ max-width: 100%;
+ table-layout: fixed;
+ color: #909399;
+ margin-bottom: 15px;
+ font-size: 13px;
+ border-top: 1px solid #ebeef5;
+ border-left: 1px solid #ebeef5;
+ td,
+ th {
+ padding: 8px;
+ border-bottom: 1px solid #ebeef5;
+ border-right: 1px solid #ebeef5;
+ }
+ thead {
+ th {
+ font-weight: 500;
+ background: #ebeef5;
+ }
+ }
+ tbody {
+ tr {
+ transition: background 0.35s;
+ &:hover {
+ background: #ebeef5;
+ }
+ }
+ }
+ }
+ img:not(.owo_image) {
+ display: block;
+ max-width: 100%;
+ border-radius: 4px;
+ transition: transform 0.35s, box-shadow 0.35s;
+ margin: 0 auto;
+ }
+ .owo_image {
+ max-height: 26px;
+ vertical-align: top;
+ }
+ a {
+ display: inline-block;
+ line-height: 26px;
+ color: #409eff;
+ position: relative;
+ text-decoration: none;
+ }
+ }
+ }
+ }
+ .cm-progress-left {
+ position: absolute;
+ top: 0;
+ right: 50%;
+ width: 0;
+ height: 3px;
+ background-color: #ff5627;
+ transition: width 0.5s;
+ z-index: 1;
+ }
+ .cm-progress-right {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ width: 0;
+ height: 3px;
+ background-color: #ff5627;
+ transition: width 0.5s;
+ z-index: 1;
+ }
+}
+
+.cm-modal {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 9999;
+ background: rgba(0, 0, 0, 0);
+ visibility: hidden;
+ transition: background 0.25s, visibility 0.25s;
+ will-change: background, visibility;
+ &__wrapper {
+ width: 380px;
+ background: #fff;
+ color: #606266;
+ border-radius: 4px;
+ opacity: 0;
+ transform: translateY(-30%);
+ transition: opacity 0.25s, transform 0.25s;
+ &-header {
+ padding: 12px 20px;
+ border-bottom: 1px solid #e4e7ed;
+ }
+ &-bodyer {
+ padding: 20px;
+ .fitem {
+ display: flex;
+ align-items: center;
+ margin-bottom: 15px;
+ &:last-child {
+ margin-bottom: 0;
+ }
+ label {
+ margin-right: 10px;
+ }
+ input {
+ width: auto;
+ flex: 1;
+ -webkit-appearance: none;
+ outline: none;
+ border: 1px solid #dcdfe6;
+ height: 34px;
+ box-sizing: border-box;
+ padding: 0 10px;
+ border-radius: 3px;
+ color: #606266;
+ }
+ }
+ }
+ &-footer {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ padding: 0 20px 10px;
+ &--cancle,
+ &--confirm {
+ cursor: pointer;
+ border: none;
+ outline: none;
+ margin-left: 5px;
+ background: #fff;
+ color: #606266;
+ -webkit-appearance: none;
+ padding: 7px 15px;
+ border-radius: 3px;
+ }
+ &--confirm {
+ color: #fff;
+ background: #409eff;
+ }
+ }
+ }
+ &.active {
+ visibility: visible;
+ background: rgba(0, 0, 0, 0.75);
+ .cm-modal__wrapper {
+ transform: translateY(0);
+ opacity: 1;
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .cm-modal__wrapper {
+ width: 90%;
+ }
+}
diff --git a/typecho/write/js/joe.write.chunk.js b/typecho/write/js/joe.write.chunk.js
new file mode 100644
index 0000000..eb6ab74
--- /dev/null
+++ b/typecho/write/js/joe.write.chunk.js
@@ -0,0 +1,14126 @@
+(function () {
+ 'use strict';
+
+ // Compressed representation of the Grapheme_Cluster_Break=Extend
+ // information from
+ // http://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt.
+ // Each pair of elements represents a range, as an offet from the
+ // previous range and a length. Numbers are in base-36, with the empty
+ // string being a shorthand for 1.
+ let extend = "lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map(s => s ? parseInt(s, 36) : 1);
+ // Convert offsets into absolute values
+ for (let i = 1; i < extend.length; i++)
+ extend[i] += extend[i - 1];
+ function isExtendingChar(code) {
+ for (let i = 1; i < extend.length; i += 2)
+ if (extend[i] > code)
+ return extend[i - 1] <= code;
+ return false;
+ }
+ function isRegionalIndicator(code) {
+ return code >= 0x1F1E6 && code <= 0x1F1FF;
+ }
+ const ZWJ = 0x200d;
+ /// Returns a next grapheme cluster break _after_ (not equal to)
+ /// `pos`, if `forward` is true, or before otherwise. Returns `pos`
+ /// itself if no further cluster break is available in the string.
+ /// Moves across surrogate pairs, extending characters, characters
+ /// joined with zero-width joiners, and flag emoji.
+ function findClusterBreak(str, pos, forward = true) {
+ return (forward ? nextClusterBreak : prevClusterBreak)(str, pos);
+ }
+ function nextClusterBreak(str, pos) {
+ if (pos == str.length)
+ return pos;
+ // If pos is in the middle of a surrogate pair, move to its start
+ if (pos && surrogateLow(str.charCodeAt(pos)) && surrogateHigh(str.charCodeAt(pos - 1)))
+ pos--;
+ let prev = codePointAt(str, pos);
+ pos += codePointSize(prev);
+ while (pos < str.length) {
+ let next = codePointAt(str, pos);
+ if (prev == ZWJ || next == ZWJ || isExtendingChar(next)) {
+ pos += codePointSize(next);
+ prev = next;
+ }
+ else if (isRegionalIndicator(next)) {
+ let countBefore = 0, i = pos - 2;
+ while (i >= 0 && isRegionalIndicator(codePointAt(str, i))) {
+ countBefore++;
+ i -= 2;
+ }
+ if (countBefore % 2 == 0)
+ break;
+ else
+ pos += 2;
+ }
+ else {
+ break;
+ }
+ }
+ return pos;
+ }
+ function prevClusterBreak(str, pos) {
+ while (pos > 0) {
+ let found = nextClusterBreak(str, pos - 2);
+ if (found < pos)
+ return found;
+ pos--;
+ }
+ return 0;
+ }
+ function surrogateLow(ch) { return ch >= 0xDC00 && ch < 0xE000; }
+ function surrogateHigh(ch) { return ch >= 0xD800 && ch < 0xDC00; }
+ /// Find the code point at the given position in a string (like the
+ /// [`codePointAt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt)
+ /// string method).
+ function codePointAt(str, pos) {
+ let code0 = str.charCodeAt(pos);
+ if (!surrogateHigh(code0) || pos + 1 == str.length)
+ return code0;
+ let code1 = str.charCodeAt(pos + 1);
+ if (!surrogateLow(code1))
+ return code0;
+ return ((code0 - 0xd800) << 10) + (code1 - 0xdc00) + 0x10000;
+ }
+ /// Given a Unicode codepoint, return the JavaScript string that
+ /// respresents it (like
+ /// [`String.fromCodePoint`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint)).
+ function fromCodePoint(code) {
+ if (code <= 0xffff)
+ return String.fromCharCode(code);
+ code -= 0x10000;
+ return String.fromCharCode((code >> 10) + 0xd800, (code & 1023) + 0xdc00);
+ }
+ /// The first character that takes up two positions in a JavaScript
+ /// string. It is often useful to compare with this after calling
+ /// `codePointAt`, to figure out whether your character takes up 1 or
+ /// 2 index positions.
+ function codePointSize(code) { return code < 0x10000 ? 1 : 2; }
+
+ /// Count the column position at the given offset into the string,
+ /// taking extending characters and tab size into account.
+ function countColumn(string, n, tabSize) {
+ for (let i = 0; i < string.length;) {
+ if (string.charCodeAt(i) == 9) {
+ n += tabSize - (n % tabSize);
+ i++;
+ }
+ else {
+ n++;
+ i = findClusterBreak(string, i);
+ }
+ }
+ return n;
+ }
+ /// Find the offset that corresponds to the given column position in a
+ /// string, taking extending characters and tab size into account.
+ function findColumn(string, n, col, tabSize) {
+ for (let i = 0; i < string.length;) {
+ if (n >= col)
+ return { offset: i, leftOver: 0 };
+ n += string.charCodeAt(i) == 9 ? tabSize - (n % tabSize) : 1;
+ i = findClusterBreak(string, i);
+ }
+ return { offset: string.length, leftOver: col - n };
+ }
+
+ /// The data structure for documents.
+ class Text {
+ /// @internal
+ constructor() { }
+ /// Get the line description around the given position.
+ lineAt(pos) {
+ if (pos < 0 || pos > this.length)
+ throw new RangeError(`Invalid position ${pos} in document of length ${this.length}`);
+ return this.lineInner(pos, false, 1, 0);
+ }
+ /// Get the description for the given (1-based) line number.
+ line(n) {
+ if (n < 1 || n > this.lines)
+ throw new RangeError(`Invalid line number ${n} in ${this.lines}-line document`);
+ return this.lineInner(n, true, 1, 0);
+ }
+ /// Replace a range of the text with the given content.
+ replace(from, to, text) {
+ let parts = [];
+ this.decompose(0, from, parts, 2 /* To */);
+ if (text.length)
+ text.decompose(0, text.length, parts, 1 /* From */ | 2 /* To */);
+ this.decompose(to, this.length, parts, 1 /* From */);
+ return TextNode.from(parts, this.length - (to - from) + text.length);
+ }
+ /// Append another document to this one.
+ append(other) {
+ return this.replace(this.length, this.length, other);
+ }
+ /// Retrieve the text between the given points.
+ slice(from, to = this.length) {
+ let parts = [];
+ this.decompose(from, to, parts, 0);
+ return TextNode.from(parts, to - from);
+ }
+ /// Test whether this text is equal to another instance.
+ eq(other) {
+ if (other == this)
+ return true;
+ if (other.length != this.length || other.lines != this.lines)
+ return false;
+ let a = new RawTextCursor(this), b = new RawTextCursor(other);
+ for (;;) {
+ a.next();
+ b.next();
+ if (a.lineBreak != b.lineBreak || a.done != b.done || a.value != b.value)
+ return false;
+ if (a.done)
+ return true;
+ }
+ }
+ /// Iterate over the text. When `dir` is `-1`, iteration happens
+ /// from end to start. This will return lines and the breaks between
+ /// them as separate strings, and for long lines, might split lines
+ /// themselves into multiple chunks as well.
+ iter(dir = 1) { return new RawTextCursor(this, dir); }
+ /// Iterate over a range of the text. When `from` > `to`, the
+ /// iterator will run in reverse.
+ iterRange(from, to = this.length) { return new PartialTextCursor(this, from, to); }
+ /// @internal
+ toString() { return this.sliceString(0); }
+ /// Convert the document to an array of lines (which can be
+ /// deserialized again via [`Text.of`](#text.Text^of)).
+ toJSON() {
+ let lines = [];
+ this.flatten(lines);
+ return lines;
+ }
+ /// Create a `Text` instance for the given array of lines.
+ static of(text) {
+ if (text.length == 0)
+ throw new RangeError("A document must have at least one line");
+ if (text.length == 1 && !text[0])
+ return Text.empty;
+ return text.length <= 32 /* Branch */ ? new TextLeaf(text) : TextNode.from(TextLeaf.split(text, []));
+ }
+ }
+ if (typeof Symbol != "undefined")
+ Text.prototype[Symbol.iterator] = function () { return this.iter(); };
+ // Leaves store an array of line strings. There are always line breaks
+ // between these strings. Leaves are limited in size and have to be
+ // contained in TextNode instances for bigger documents.
+ class TextLeaf extends Text {
+ constructor(text, length = textLength(text)) {
+ super();
+ this.text = text;
+ this.length = length;
+ }
+ get lines() { return this.text.length; }
+ get children() { return null; }
+ lineInner(target, isLine, line, offset) {
+ for (let i = 0;; i++) {
+ let string = this.text[i], end = offset + string.length;
+ if ((isLine ? line : end) >= target)
+ return new Line(offset, end, line, string);
+ offset = end + 1;
+ line++;
+ }
+ }
+ decompose(from, to, target, open) {
+ let text = from <= 0 && to >= this.length ? this
+ : new TextLeaf(sliceText(this.text, from, to), Math.min(to, this.length) - Math.max(0, from));
+ if (open & 1 /* From */) {
+ let prev = target.pop();
+ let joined = appendText(text.text, prev.text.slice(), 0, text.length);
+ if (joined.length <= 32 /* Branch */) {
+ target.push(new TextLeaf(joined, prev.length + text.length));
+ }
+ else {
+ let mid = joined.length >> 1;
+ target.push(new TextLeaf(joined.slice(0, mid)), new TextLeaf(joined.slice(mid)));
+ }
+ }
+ else {
+ target.push(text);
+ }
+ }
+ replace(from, to, text) {
+ if (!(text instanceof TextLeaf))
+ return super.replace(from, to, text);
+ let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to);
+ let newLen = this.length + text.length - (to - from);
+ if (lines.length <= 32 /* Branch */)
+ return new TextLeaf(lines, newLen);
+ return TextNode.from(TextLeaf.split(lines, []), newLen);
+ }
+ sliceString(from, to = this.length, lineSep = "\n") {
+ let result = "";
+ for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) {
+ let line = this.text[i], end = pos + line.length;
+ if (pos > from && i)
+ result += lineSep;
+ if (from < end && to > pos)
+ result += line.slice(Math.max(0, from - pos), to - pos);
+ pos = end + 1;
+ }
+ return result;
+ }
+ flatten(target) {
+ for (let line of this.text)
+ target.push(line);
+ }
+ static split(text, target) {
+ let part = [], len = -1;
+ for (let line of text) {
+ part.push(line);
+ len += line.length + 1;
+ if (part.length == 32 /* Branch */) {
+ target.push(new TextLeaf(part, len));
+ part = [];
+ len = -1;
+ }
+ }
+ if (len > -1)
+ target.push(new TextLeaf(part, len));
+ return target;
+ }
+ }
+ // Nodes provide the tree structure of the `Text` type. They store a
+ // number of other nodes or leaves, taking care to balance themselves
+ // on changes. There are implied line breaks _between_ the children of
+ // a node (but not before the first or after the last child).
+ class TextNode extends Text {
+ constructor(children, length) {
+ super();
+ this.children = children;
+ this.length = length;
+ this.lines = 0;
+ for (let child of children)
+ this.lines += child.lines;
+ }
+ lineInner(target, isLine, line, offset) {
+ for (let i = 0;; i++) {
+ let child = this.children[i], end = offset + child.length, endLine = line + child.lines - 1;
+ if ((isLine ? endLine : end) >= target)
+ return child.lineInner(target, isLine, line, offset);
+ offset = end + 1;
+ line = endLine + 1;
+ }
+ }
+ decompose(from, to, target, open) {
+ for (let i = 0, pos = 0; pos <= to && i < this.children.length; i++) {
+ let child = this.children[i], end = pos + child.length;
+ if (from <= end && to >= pos) {
+ let childOpen = open & ((pos <= from ? 1 /* From */ : 0) | (end >= to ? 2 /* To */ : 0));
+ if (pos >= from && end <= to && !childOpen)
+ target.push(child);
+ else
+ child.decompose(from - pos, to - pos, target, childOpen);
+ }
+ pos = end + 1;
+ }
+ }
+ replace(from, to, text) {
+ if (text.lines < this.lines)
+ for (let i = 0, pos = 0; i < this.children.length; i++) {
+ let child = this.children[i], end = pos + child.length;
+ // Fast path: if the change only affects one child and the
+ // child's size remains in the acceptable range, only update
+ // that child
+ if (from >= pos && to <= end) {
+ let updated = child.replace(from - pos, to - pos, text);
+ let totalLines = this.lines - child.lines + updated.lines;
+ if (updated.lines < (totalLines >> (5 /* BranchShift */ - 1)) &&
+ updated.lines > (totalLines >> (5 /* BranchShift */ + 1))) {
+ let copy = this.children.slice();
+ copy[i] = updated;
+ return new TextNode(copy, this.length - (to - from) + text.length);
+ }
+ return super.replace(pos, end, updated);
+ }
+ pos = end + 1;
+ }
+ return super.replace(from, to, text);
+ }
+ sliceString(from, to = this.length, lineSep = "\n") {
+ let result = "";
+ for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) {
+ let child = this.children[i], end = pos + child.length;
+ if (pos > from && i)
+ result += lineSep;
+ if (from < end && to > pos)
+ result += child.sliceString(from - pos, to - pos, lineSep);
+ pos = end + 1;
+ }
+ return result;
+ }
+ flatten(target) {
+ for (let child of this.children)
+ child.flatten(target);
+ }
+ static from(children, length = children.reduce((l, ch) => l + ch.length + 1, -1)) {
+ let lines = 0;
+ for (let ch of children)
+ lines += ch.lines;
+ if (lines < 32 /* Branch */) {
+ let flat = [];
+ for (let ch of children)
+ ch.flatten(flat);
+ return new TextLeaf(flat, length);
+ }
+ let chunk = Math.max(32 /* Branch */, lines >> 5 /* BranchShift */), maxChunk = chunk << 1, minChunk = chunk >> 1;
+ let chunked = [], currentLines = 0, currentLen = -1, currentChunk = [];
+ function add(child) {
+ let last;
+ if (child.lines > maxChunk && child instanceof TextNode) {
+ for (let node of child.children)
+ add(node);
+ }
+ else if (child.lines > minChunk && (currentLines > minChunk || !currentLines)) {
+ flush();
+ chunked.push(child);
+ }
+ else if (child instanceof TextLeaf && currentLines &&
+ (last = currentChunk[currentChunk.length - 1]) instanceof TextLeaf &&
+ child.lines + last.lines <= 32 /* Branch */) {
+ currentLines += child.lines;
+ currentLen += child.length + 1;
+ currentChunk[currentChunk.length - 1] = new TextLeaf(last.text.concat(child.text), last.length + 1 + child.length);
+ }
+ else {
+ if (currentLines + child.lines > chunk)
+ flush();
+ currentLines += child.lines;
+ currentLen += child.length + 1;
+ currentChunk.push(child);
+ }
+ }
+ function flush() {
+ if (currentLines == 0)
+ return;
+ chunked.push(currentChunk.length == 1 ? currentChunk[0] : TextNode.from(currentChunk, currentLen));
+ currentLen = -1;
+ currentLines = currentChunk.length = 0;
+ }
+ for (let child of children)
+ add(child);
+ flush();
+ return chunked.length == 1 ? chunked[0] : new TextNode(chunked, length);
+ }
+ }
+ Text.empty = new TextLeaf([""], 0);
+ function textLength(text) {
+ let length = -1;
+ for (let line of text)
+ length += line.length + 1;
+ return length;
+ }
+ function appendText(text, target, from = 0, to = 1e9) {
+ for (let pos = 0, i = 0, first = true; i < text.length && pos <= to; i++) {
+ let line = text[i], end = pos + line.length;
+ if (end >= from) {
+ if (end > to)
+ line = line.slice(0, to - pos);
+ if (pos < from)
+ line = line.slice(from - pos);
+ if (first) {
+ target[target.length - 1] += line;
+ first = false;
+ }
+ else
+ target.push(line);
+ }
+ pos = end + 1;
+ }
+ return target;
+ }
+ function sliceText(text, from, to) {
+ return appendText(text, [""], from, to);
+ }
+ class RawTextCursor {
+ constructor(text, dir = 1) {
+ this.dir = dir;
+ this.done = false;
+ this.lineBreak = false;
+ this.value = "";
+ this.nodes = [text];
+ this.offsets = [dir > 0 ? 0 : text instanceof TextLeaf ? text.text.length : text.children.length];
+ }
+ next(skip = 0) {
+ for (;;) {
+ let last = this.nodes.length - 1;
+ if (last < 0) {
+ this.done = true;
+ this.value = "";
+ this.lineBreak = false;
+ return this;
+ }
+ let top = this.nodes[last], offset = this.offsets[last];
+ let size = top instanceof TextLeaf ? top.text.length : top.children.length;
+ if (offset == (this.dir > 0 ? size : 0)) {
+ this.nodes.pop();
+ this.offsets.pop();
+ }
+ else if (!this.lineBreak && offset != (this.dir > 0 ? 0 : size)) {
+ // Internal offset with lineBreak == false means we have to
+ // count the line break at this position
+ this.lineBreak = true;
+ if (skip == 0) {
+ this.value = "\n";
+ return this;
+ }
+ skip--;
+ }
+ else if (top instanceof TextLeaf) {
+ // Move to the next string
+ let next = top.text[offset - (this.dir < 0 ? 1 : 0)];
+ this.offsets[last] = (offset += this.dir);
+ this.lineBreak = false;
+ if (next.length > Math.max(0, skip)) {
+ this.value = skip == 0 ? next : this.dir > 0 ? next.slice(skip) : next.slice(0, next.length - skip);
+ return this;
+ }
+ skip -= next.length;
+ }
+ else {
+ let next = top.children[this.dir > 0 ? offset : offset - 1];
+ this.offsets[last] = offset + this.dir;
+ this.lineBreak = false;
+ if (skip > next.length) {
+ skip -= next.length;
+ }
+ else {
+ this.nodes.push(next);
+ this.offsets.push(this.dir > 0 ? 0 : next instanceof TextLeaf ? next.text.length : next.children.length);
+ }
+ }
+ }
+ }
+ }
+ class PartialTextCursor {
+ constructor(text, start, end) {
+ this.value = "";
+ this.cursor = new RawTextCursor(text, start > end ? -1 : 1);
+ if (start > end) {
+ this.skip = text.length - start;
+ this.limit = start - end;
+ }
+ else {
+ this.skip = start;
+ this.limit = end - start;
+ }
+ }
+ next(skip = 0) {
+ if (this.limit <= 0) {
+ this.limit = -1;
+ }
+ else {
+ let { value, lineBreak, done } = this.cursor.next(this.skip + skip);
+ this.skip = 0;
+ this.value = value;
+ let len = lineBreak ? 1 : value.length;
+ if (len > this.limit)
+ this.value = this.cursor.dir > 0 ? value.slice(0, this.limit) : value.slice(len - this.limit);
+ if (done || this.value.length == 0)
+ this.limit = -1;
+ else
+ this.limit -= this.value.length;
+ }
+ return this;
+ }
+ get lineBreak() { return this.cursor.lineBreak; }
+ get done() { return this.limit < 0; }
+ }
+ /// This type describes a line in the document. It is created
+ /// on-demand when lines are [queried](#text.Text.lineAt).
+ class Line {
+ /// @internal
+ constructor(
+ /// The position of the start of the line.
+ from,
+ /// The position at the end of the line (_before_ the line break,
+ /// or at the end of document for the last line).
+ to,
+ /// This line's line number (1-based).
+ number,
+ /// The line's content.
+ text) {
+ this.from = from;
+ this.to = to;
+ this.number = number;
+ this.text = text;
+ }
+ /// The length of the line (not including any line break after it).
+ get length() { return this.to - this.from; }
+ }
+
+ const DefaultSplit = /\r\n?|\n/;
+ /**
+ Distinguishes different ways in which positions can be mapped.
+ */
+ var MapMode;
+ (function (MapMode) {
+ /**
+ Map a position to a valid new position, even when its context
+ was deleted.
+ */
+ MapMode[MapMode["Simple"] = 0] = "Simple";
+ /**
+ Return null if deletion happens across the position.
+ */
+ MapMode[MapMode["TrackDel"] = 1] = "TrackDel";
+ /**
+ Return null if the character _before_ the position is deleted.
+ */
+ MapMode[MapMode["TrackBefore"] = 2] = "TrackBefore";
+ /**
+ Return null if the character _after_ the position is deleted.
+ */
+ MapMode[MapMode["TrackAfter"] = 3] = "TrackAfter";
+ })(MapMode || (MapMode = {}));
+ /**
+ A change description is a variant of [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet)
+ that doesn't store the inserted text. As such, it can't be
+ applied, but is cheaper to store and manipulate.
+ */
+ class ChangeDesc {
+ // Sections are encoded as pairs of integers. The first is the
+ // length in the current document, and the second is -1 for
+ // unaffected sections, and the length of the replacement content
+ // otherwise. So an insertion would be (0, n>0), a deletion (n>0,
+ // 0), and a replacement two positive numbers.
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ @internal
+ */
+ sections) {
+ this.sections = sections;
+ }
+ /**
+ The length of the document before the change.
+ */
+ get length() {
+ let result = 0;
+ for (let i = 0; i < this.sections.length; i += 2)
+ result += this.sections[i];
+ return result;
+ }
+ /**
+ The length of the document after the change.
+ */
+ get newLength() {
+ let result = 0;
+ for (let i = 0; i < this.sections.length; i += 2) {
+ let ins = this.sections[i + 1];
+ result += ins < 0 ? this.sections[i] : ins;
+ }
+ return result;
+ }
+ /**
+ False when there are actual changes in this set.
+ */
+ get empty() { return this.sections.length == 0 || this.sections.length == 2 && this.sections[1] < 0; }
+ /**
+ Iterate over the unchanged parts left by these changes.
+ */
+ iterGaps(f) {
+ for (let i = 0, posA = 0, posB = 0; i < this.sections.length;) {
+ let len = this.sections[i++], ins = this.sections[i++];
+ if (ins < 0) {
+ f(posA, posB, len);
+ posB += len;
+ }
+ else {
+ posB += ins;
+ }
+ posA += len;
+ }
+ }
+ /**
+ Iterate over the ranges changed by these changes. (See
+ [`ChangeSet.iterChanges`](https://codemirror.net/6/docs/ref/#state.ChangeSet.iterChanges) for a
+ variant that also provides you with the inserted text.)
+
+ When `individual` is true, adjacent changes (which are kept
+ separate for [position mapping](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) are
+ reported separately.
+ */
+ iterChangedRanges(f, individual = false) {
+ iterChanges(this, f, individual);
+ }
+ /**
+ Get a description of the inverted form of these changes.
+ */
+ get invertedDesc() {
+ let sections = [];
+ for (let i = 0; i < this.sections.length;) {
+ let len = this.sections[i++], ins = this.sections[i++];
+ if (ins < 0)
+ sections.push(len, ins);
+ else
+ sections.push(ins, len);
+ }
+ return new ChangeDesc(sections);
+ }
+ /**
+ Compute the combined effect of applying another set of changes
+ after this one. The length of the document after this set should
+ match the length before `other`.
+ */
+ composeDesc(other) { return this.empty ? other : other.empty ? this : composeSets(this, other); }
+ /**
+ Map this description, which should start with the same document
+ as `other`, over another set of changes, so that it can be
+ applied after it. When `before` is true, map as if the changes
+ in `other` happened before the ones in `this`.
+ */
+ mapDesc(other, before = false) { return other.empty ? this : mapSet(this, other, before); }
+ mapPos(pos, assoc = -1, mode = MapMode.Simple) {
+ let posA = 0, posB = 0;
+ for (let i = 0; i < this.sections.length;) {
+ let len = this.sections[i++], ins = this.sections[i++], endA = posA + len;
+ if (ins < 0) {
+ if (endA > pos)
+ return posB + (pos - posA);
+ posB += len;
+ }
+ else {
+ if (mode != MapMode.Simple && endA >= pos &&
+ (mode == MapMode.TrackDel && posA < pos && endA > pos ||
+ mode == MapMode.TrackBefore && posA < pos ||
+ mode == MapMode.TrackAfter && endA > pos))
+ return null;
+ if (endA > pos || endA == pos && assoc < 0 && !len)
+ return pos == posA || assoc < 0 ? posB : posB + ins;
+ posB += ins;
+ }
+ posA = endA;
+ }
+ if (pos > posA)
+ throw new RangeError(`Position ${pos} is out of range for changeset of length ${posA}`);
+ return posB;
+ }
+ /**
+ Check whether these changes touch a given range. When one of the
+ changes entirely covers the range, the string `"cover"` is
+ returned.
+ */
+ touchesRange(from, to = from) {
+ for (let i = 0, pos = 0; i < this.sections.length && pos <= to;) {
+ let len = this.sections[i++], ins = this.sections[i++], end = pos + len;
+ if (ins >= 0 && pos <= to && end >= from)
+ return pos < from && end > to ? "cover" : true;
+ pos = end;
+ }
+ return false;
+ }
+ /**
+ @internal
+ */
+ toString() {
+ let result = "";
+ for (let i = 0; i < this.sections.length;) {
+ let len = this.sections[i++], ins = this.sections[i++];
+ result += (result ? " " : "") + len + (ins >= 0 ? ":" + ins : "");
+ }
+ return result;
+ }
+ /**
+ Serialize this change desc to a JSON-representable value.
+ */
+ toJSON() { return this.sections; }
+ /**
+ Create a change desc from its JSON representation (as produced
+ by [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeDesc.toJSON).
+ */
+ static fromJSON(json) {
+ if (!Array.isArray(json) || json.length % 2 || json.some(a => typeof a != "number"))
+ throw new RangeError("Invalid JSON representation of ChangeDesc");
+ return new ChangeDesc(json);
+ }
+ }
+ /**
+ A change set represents a group of modifications to a document. It
+ stores the document length, and can only be applied to documents
+ with exactly that length.
+ */
+ class ChangeSet extends ChangeDesc {
+ /**
+ @internal
+ */
+ constructor(sections,
+ /**
+ @internal
+ */
+ inserted) {
+ super(sections);
+ this.inserted = inserted;
+ }
+ /**
+ Apply the changes to a document, returning the modified
+ document.
+ */
+ apply(doc) {
+ if (this.length != doc.length)
+ throw new RangeError("Applying change set to a document with the wrong length");
+ iterChanges(this, (fromA, toA, fromB, _toB, text) => doc = doc.replace(fromB, fromB + (toA - fromA), text), false);
+ return doc;
+ }
+ mapDesc(other, before = false) { return mapSet(this, other, before, true); }
+ /**
+ Given the document as it existed _before_ the changes, return a
+ change set that represents the inverse of this set, which could
+ be used to go from the document created by the changes back to
+ the document as it existed before the changes.
+ */
+ invert(doc) {
+ let sections = this.sections.slice(), inserted = [];
+ for (let i = 0, pos = 0; i < sections.length; i += 2) {
+ let len = sections[i], ins = sections[i + 1];
+ if (ins >= 0) {
+ sections[i] = ins;
+ sections[i + 1] = len;
+ let index = i >> 1;
+ while (inserted.length < index)
+ inserted.push(Text.empty);
+ inserted.push(len ? doc.slice(pos, pos + len) : Text.empty);
+ }
+ pos += len;
+ }
+ return new ChangeSet(sections, inserted);
+ }
+ /**
+ Combine two subsequent change sets into a single set. `other`
+ must start in the document produced by `this`. If `this` goes
+ `docA` → `docB` and `other` represents `docB` → `docC`, the
+ returned value will represent the change `docA` → `docC`.
+ */
+ compose(other) { return this.empty ? other : other.empty ? this : composeSets(this, other, true); }
+ /**
+ Given another change set starting in the same document, maps this
+ change set over the other, producing a new change set that can be
+ applied to the document produced by applying `other`. When
+ `before` is `true`, order changes as if `this` comes before
+ `other`, otherwise (the default) treat `other` as coming first.
+
+ Given two changes `A` and `B`, `A.compose(B.map(A))` and
+ `B.compose(A.map(B, true))` will produce the same document. This
+ provides a basic form of [operational
+ transformation](https://en.wikipedia.org/wiki/Operational_transformation),
+ and can be used for collaborative editing.
+ */
+ map(other, before = false) { return other.empty ? this : mapSet(this, other, before, true); }
+ /**
+ Iterate over the changed ranges in the document, calling `f` for
+ each.
+
+ When `individual` is true, adjacent changes are reported
+ separately.
+ */
+ iterChanges(f, individual = false) {
+ iterChanges(this, f, individual);
+ }
+ /**
+ Get a [change description](https://codemirror.net/6/docs/ref/#state.ChangeDesc) for this change
+ set.
+ */
+ get desc() { return new ChangeDesc(this.sections); }
+ /**
+ @internal
+ */
+ filter(ranges) {
+ let resultSections = [], resultInserted = [], filteredSections = [];
+ let iter = new SectionIter(this);
+ done: for (let i = 0, pos = 0;;) {
+ let next = i == ranges.length ? 1e9 : ranges[i++];
+ while (pos < next || pos == next && iter.len == 0) {
+ if (iter.done)
+ break done;
+ let len = Math.min(iter.len, next - pos);
+ addSection(filteredSections, len, -1);
+ let ins = iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0;
+ addSection(resultSections, len, ins);
+ if (ins > 0)
+ addInsert(resultInserted, resultSections, iter.text);
+ iter.forward(len);
+ pos += len;
+ }
+ let end = ranges[i++];
+ while (pos < end) {
+ if (iter.done)
+ break done;
+ let len = Math.min(iter.len, end - pos);
+ addSection(resultSections, len, -1);
+ addSection(filteredSections, len, iter.ins == -1 ? -1 : iter.off == 0 ? iter.ins : 0);
+ iter.forward(len);
+ pos += len;
+ }
+ }
+ return { changes: new ChangeSet(resultSections, resultInserted),
+ filtered: new ChangeDesc(filteredSections) };
+ }
+ /**
+ Serialize this change set to a JSON-representable value.
+ */
+ toJSON() {
+ let parts = [];
+ for (let i = 0; i < this.sections.length; i += 2) {
+ let len = this.sections[i], ins = this.sections[i + 1];
+ if (ins < 0)
+ parts.push(len);
+ else if (ins == 0)
+ parts.push([len]);
+ else
+ parts.push([len].concat(this.inserted[i >> 1].toJSON()));
+ }
+ return parts;
+ }
+ /**
+ Create a change set for the given changes, for a document of the
+ given length, using `lineSep` as line separator.
+ */
+ static of(changes, length, lineSep) {
+ let sections = [], inserted = [], pos = 0;
+ let total = null;
+ function flush(force = false) {
+ if (!force && !sections.length)
+ return;
+ if (pos < length)
+ addSection(sections, length - pos, -1);
+ let set = new ChangeSet(sections, inserted);
+ total = total ? total.compose(set.map(total)) : set;
+ sections = [];
+ inserted = [];
+ pos = 0;
+ }
+ function process(spec) {
+ if (Array.isArray(spec)) {
+ for (let sub of spec)
+ process(sub);
+ }
+ else if (spec instanceof ChangeSet) {
+ if (spec.length != length)
+ throw new RangeError(`Mismatched change set length (got ${spec.length}, expected ${length})`);
+ flush();
+ total = total ? total.compose(spec.map(total)) : spec;
+ }
+ else {
+ let { from, to = from, insert } = spec;
+ if (from > to || from < 0 || to > length)
+ throw new RangeError(`Invalid change range ${from} to ${to} (in doc of length ${length})`);
+ let insText = !insert ? Text.empty : typeof insert == "string" ? Text.of(insert.split(lineSep || DefaultSplit)) : insert;
+ let insLen = insText.length;
+ if (from == to && insLen == 0)
+ return;
+ if (from < pos)
+ flush();
+ if (from > pos)
+ addSection(sections, from - pos, -1);
+ addSection(sections, to - from, insLen);
+ addInsert(inserted, sections, insText);
+ pos = to;
+ }
+ }
+ process(changes);
+ flush(!total);
+ return total;
+ }
+ /**
+ Create an empty changeset of the given length.
+ */
+ static empty(length) {
+ return new ChangeSet(length ? [length, -1] : [], []);
+ }
+ /**
+ Create a changeset from its JSON representation (as produced by
+ [`toJSON`](https://codemirror.net/6/docs/ref/#state.ChangeSet.toJSON).
+ */
+ static fromJSON(json) {
+ if (!Array.isArray(json))
+ throw new RangeError("Invalid JSON representation of ChangeSet");
+ let sections = [], inserted = [];
+ for (let i = 0; i < json.length; i++) {
+ let part = json[i];
+ if (typeof part == "number") {
+ sections.push(part, -1);
+ }
+ else if (!Array.isArray(part) || typeof part[0] != "number" || part.some((e, i) => i && typeof e != "string")) {
+ throw new RangeError("Invalid JSON representation of ChangeSet");
+ }
+ else if (part.length == 1) {
+ sections.push(part[0], 0);
+ }
+ else {
+ while (inserted.length < i)
+ inserted.push(Text.empty);
+ inserted[i] = Text.of(part.slice(1));
+ sections.push(part[0], inserted[i].length);
+ }
+ }
+ return new ChangeSet(sections, inserted);
+ }
+ }
+ function addSection(sections, len, ins, forceJoin = false) {
+ if (len == 0 && ins <= 0)
+ return;
+ let last = sections.length - 2;
+ if (last >= 0 && ins <= 0 && ins == sections[last + 1])
+ sections[last] += len;
+ else if (len == 0 && sections[last] == 0)
+ sections[last + 1] += ins;
+ else if (forceJoin) {
+ sections[last] += len;
+ sections[last + 1] += ins;
+ }
+ else
+ sections.push(len, ins);
+ }
+ function addInsert(values, sections, value) {
+ if (value.length == 0)
+ return;
+ let index = (sections.length - 2) >> 1;
+ if (index < values.length) {
+ values[values.length - 1] = values[values.length - 1].append(value);
+ }
+ else {
+ while (values.length < index)
+ values.push(Text.empty);
+ values.push(value);
+ }
+ }
+ function iterChanges(desc, f, individual) {
+ let inserted = desc.inserted;
+ for (let posA = 0, posB = 0, i = 0; i < desc.sections.length;) {
+ let len = desc.sections[i++], ins = desc.sections[i++];
+ if (ins < 0) {
+ posA += len;
+ posB += len;
+ }
+ else {
+ let endA = posA, endB = posB, text = Text.empty;
+ for (;;) {
+ endA += len;
+ endB += ins;
+ if (ins && inserted)
+ text = text.append(inserted[(i - 2) >> 1]);
+ if (individual || i == desc.sections.length || desc.sections[i + 1] < 0)
+ break;
+ len = desc.sections[i++];
+ ins = desc.sections[i++];
+ }
+ f(posA, endA, posB, endB, text);
+ posA = endA;
+ posB = endB;
+ }
+ }
+ }
+ function mapSet(setA, setB, before, mkSet = false) {
+ let sections = [], insert = mkSet ? [] : null;
+ let a = new SectionIter(setA), b = new SectionIter(setB);
+ for (let posA = 0, posB = 0;;) {
+ if (a.ins == -1) {
+ posA += a.len;
+ a.next();
+ }
+ else if (b.ins == -1 && posB < posA) {
+ let skip = Math.min(b.len, posA - posB);
+ b.forward(skip);
+ addSection(sections, skip, -1);
+ posB += skip;
+ }
+ else if (b.ins >= 0 && (a.done || posB < posA || posB == posA && (b.len < a.len || b.len == a.len && !before))) {
+ addSection(sections, b.ins, -1);
+ while (posA > posB && !a.done && posA + a.len < posB + b.len) {
+ posA += a.len;
+ a.next();
+ }
+ posB += b.len;
+ b.next();
+ }
+ else if (a.ins >= 0) {
+ let len = 0, end = posA + a.len;
+ for (;;) {
+ if (b.ins >= 0 && posB > posA && posB + b.len < end) {
+ len += b.ins;
+ posB += b.len;
+ b.next();
+ }
+ else if (b.ins == -1 && posB < end) {
+ let skip = Math.min(b.len, end - posB);
+ len += skip;
+ b.forward(skip);
+ posB += skip;
+ }
+ else {
+ break;
+ }
+ }
+ addSection(sections, len, a.ins);
+ if (insert)
+ addInsert(insert, sections, a.text);
+ posA = end;
+ a.next();
+ }
+ else if (a.done && b.done) {
+ return insert ? new ChangeSet(sections, insert) : new ChangeDesc(sections);
+ }
+ else {
+ throw new Error("Mismatched change set lengths");
+ }
+ }
+ }
+ function composeSets(setA, setB, mkSet = false) {
+ let sections = [];
+ let insert = mkSet ? [] : null;
+ let a = new SectionIter(setA), b = new SectionIter(setB);
+ for (let open = false;;) {
+ if (a.done && b.done) {
+ return insert ? new ChangeSet(sections, insert) : new ChangeDesc(sections);
+ }
+ else if (a.ins == 0) { // Deletion in A
+ addSection(sections, a.len, 0, open);
+ a.next();
+ }
+ else if (b.len == 0 && !b.done) { // Insertion in B
+ addSection(sections, 0, b.ins, open);
+ if (insert)
+ addInsert(insert, sections, b.text);
+ b.next();
+ }
+ else if (a.done || b.done) {
+ throw new Error("Mismatched change set lengths");
+ }
+ else {
+ let len = Math.min(a.len2, b.len), sectionLen = sections.length;
+ if (a.ins == -1) {
+ let insB = b.ins == -1 ? -1 : b.off ? 0 : b.ins;
+ addSection(sections, len, insB, open);
+ if (insert && insB)
+ addInsert(insert, sections, b.text);
+ }
+ else if (b.ins == -1) {
+ addSection(sections, a.off ? 0 : a.len, len, open);
+ if (insert)
+ addInsert(insert, sections, a.textBit(len));
+ }
+ else {
+ addSection(sections, a.off ? 0 : a.len, b.off ? 0 : b.ins, open);
+ if (insert && !b.off)
+ addInsert(insert, sections, b.text);
+ }
+ open = (a.ins > len || b.ins >= 0 && b.len > len) && (open || sections.length > sectionLen);
+ a.forward2(len);
+ b.forward(len);
+ }
+ }
+ }
+ class SectionIter {
+ constructor(set) {
+ this.set = set;
+ this.i = 0;
+ this.next();
+ }
+ next() {
+ let { sections } = this.set;
+ if (this.i < sections.length) {
+ this.len = sections[this.i++];
+ this.ins = sections[this.i++];
+ }
+ else {
+ this.len = 0;
+ this.ins = -2;
+ }
+ this.off = 0;
+ }
+ get done() { return this.ins == -2; }
+ get len2() { return this.ins < 0 ? this.len : this.ins; }
+ get text() {
+ let { inserted } = this.set, index = (this.i - 2) >> 1;
+ return index >= inserted.length ? Text.empty : inserted[index];
+ }
+ textBit(len) {
+ let { inserted } = this.set, index = (this.i - 2) >> 1;
+ return index >= inserted.length && !len ? Text.empty
+ : inserted[index].slice(this.off, len == null ? undefined : this.off + len);
+ }
+ forward(len) {
+ if (len == this.len)
+ this.next();
+ else {
+ this.len -= len;
+ this.off += len;
+ }
+ }
+ forward2(len) {
+ if (this.ins == -1)
+ this.forward(len);
+ else if (len == this.ins)
+ this.next();
+ else {
+ this.ins -= len;
+ this.off += len;
+ }
+ }
+ }
+
+ /**
+ A single selection range. When
+ [`allowMultipleSelections`](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections)
+ is enabled, a [selection](https://codemirror.net/6/docs/ref/#state.EditorSelection) may hold
+ multiple ranges. By default, selections hold exactly one range.
+ */
+ class SelectionRange {
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ The lower boundary of the range.
+ */
+ from,
+ /**
+ The upper boundary of the range.
+ */
+ to, flags) {
+ this.from = from;
+ this.to = to;
+ this.flags = flags;
+ }
+ /**
+ The anchor of the range—the side that doesn't move when you
+ extend it.
+ */
+ get anchor() { return this.flags & 16 /* Inverted */ ? this.to : this.from; }
+ /**
+ The head of the range, which is moved when the range is
+ [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend).
+ */
+ get head() { return this.flags & 16 /* Inverted */ ? this.from : this.to; }
+ /**
+ True when `anchor` and `head` are at the same position.
+ */
+ get empty() { return this.from == this.to; }
+ /**
+ If this is a cursor that is explicitly associated with the
+ character on one of its sides, this returns the side. -1 means
+ the character before its position, 1 the character after, and 0
+ means no association.
+ */
+ get assoc() { return this.flags & 4 /* AssocBefore */ ? -1 : this.flags & 8 /* AssocAfter */ ? 1 : 0; }
+ /**
+ The bidirectional text level associated with this cursor, if
+ any.
+ */
+ get bidiLevel() {
+ let level = this.flags & 3 /* BidiLevelMask */;
+ return level == 3 ? null : level;
+ }
+ /**
+ The goal column (stored vertical offset) associated with a
+ cursor. This is used to preserve the vertical position when
+ [moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across
+ lines of different length.
+ */
+ get goalColumn() {
+ let value = this.flags >> 5 /* GoalColumnOffset */;
+ return value == 33554431 /* NoGoalColumn */ ? undefined : value;
+ }
+ /**
+ Map this range through a change, producing a valid range in the
+ updated document.
+ */
+ map(change, assoc = -1) {
+ let from = change.mapPos(this.from, assoc), to = change.mapPos(this.to, assoc);
+ return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags);
+ }
+ /**
+ Extend this range to cover at least `from` to `to`.
+ */
+ extend(from, to = from) {
+ if (from <= this.anchor && to >= this.anchor)
+ return EditorSelection.range(from, to);
+ let head = Math.abs(from - this.anchor) > Math.abs(to - this.anchor) ? from : to;
+ return EditorSelection.range(this.anchor, head);
+ }
+ /**
+ Compare this range to another range.
+ */
+ eq(other) {
+ return this.anchor == other.anchor && this.head == other.head;
+ }
+ /**
+ Return a JSON-serializable object representing the range.
+ */
+ toJSON() { return { anchor: this.anchor, head: this.head }; }
+ /**
+ Convert a JSON representation of a range to a `SelectionRange`
+ instance.
+ */
+ static fromJSON(json) {
+ if (!json || typeof json.anchor != "number" || typeof json.head != "number")
+ throw new RangeError("Invalid JSON representation for SelectionRange");
+ return EditorSelection.range(json.anchor, json.head);
+ }
+ }
+ /**
+ An editor selection holds one or more selection ranges.
+ */
+ class EditorSelection {
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ The ranges in the selection, sorted by position. Ranges cannot
+ overlap (but they may touch, if they aren't empty).
+ */
+ ranges,
+ /**
+ The index of the _main_ range in the selection (which is
+ usually the range that was added last).
+ */
+ mainIndex = 0) {
+ this.ranges = ranges;
+ this.mainIndex = mainIndex;
+ }
+ /**
+ Map a selection through a change. Used to adjust the selection
+ position for changes.
+ */
+ map(change, assoc = -1) {
+ if (change.empty)
+ return this;
+ return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex);
+ }
+ /**
+ Compare this selection to another selection.
+ */
+ eq(other) {
+ if (this.ranges.length != other.ranges.length ||
+ this.mainIndex != other.mainIndex)
+ return false;
+ for (let i = 0; i < this.ranges.length; i++)
+ if (!this.ranges[i].eq(other.ranges[i]))
+ return false;
+ return true;
+ }
+ /**
+ Get the primary selection range. Usually, you should make sure
+ your code applies to _all_ ranges, by using methods like
+ [`changeByRange`](https://codemirror.net/6/docs/ref/#state.EditorState.changeByRange).
+ */
+ get main() { return this.ranges[this.mainIndex]; }
+ /**
+ Make sure the selection only has one range. Returns a selection
+ holding only the main range from this selection.
+ */
+ asSingle() {
+ return this.ranges.length == 1 ? this : new EditorSelection([this.main]);
+ }
+ /**
+ Extend this selection with an extra range.
+ */
+ addRange(range, main = true) {
+ return EditorSelection.create([range].concat(this.ranges), main ? 0 : this.mainIndex + 1);
+ }
+ /**
+ Replace a given range with another range, and then normalize the
+ selection to merge and sort ranges if necessary.
+ */
+ replaceRange(range, which = this.mainIndex) {
+ let ranges = this.ranges.slice();
+ ranges[which] = range;
+ return EditorSelection.create(ranges, this.mainIndex);
+ }
+ /**
+ Convert this selection to an object that can be serialized to
+ JSON.
+ */
+ toJSON() {
+ return { ranges: this.ranges.map(r => r.toJSON()), main: this.mainIndex };
+ }
+ /**
+ Create a selection from a JSON representation.
+ */
+ static fromJSON(json) {
+ if (!json || !Array.isArray(json.ranges) || typeof json.main != "number" || json.main >= json.ranges.length)
+ throw new RangeError("Invalid JSON representation for EditorSelection");
+ return new EditorSelection(json.ranges.map((r) => SelectionRange.fromJSON(r)), json.main);
+ }
+ /**
+ Create a selection holding a single range.
+ */
+ static single(anchor, head = anchor) {
+ return new EditorSelection([EditorSelection.range(anchor, head)], 0);
+ }
+ /**
+ Sort and merge the given set of ranges, creating a valid
+ selection.
+ */
+ static create(ranges, mainIndex = 0) {
+ if (ranges.length == 0)
+ throw new RangeError("A selection needs at least one range");
+ for (let pos = 0, i = 0; i < ranges.length; i++) {
+ let range = ranges[i];
+ if (range.empty ? range.from <= pos : range.from < pos)
+ return normalized(ranges.slice(), mainIndex);
+ pos = range.to;
+ }
+ return new EditorSelection(ranges, mainIndex);
+ }
+ /**
+ Create a cursor selection range at the given position. You can
+ safely ignore the optional arguments in most situations.
+ */
+ static cursor(pos, assoc = 0, bidiLevel, goalColumn) {
+ return new SelectionRange(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 4 /* AssocBefore */ : 8 /* AssocAfter */) |
+ (bidiLevel == null ? 3 : Math.min(2, bidiLevel)) |
+ ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* NoGoalColumn */) << 5 /* GoalColumnOffset */));
+ }
+ /**
+ Create a selection range.
+ */
+ static range(anchor, head, goalColumn) {
+ let goal = (goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* NoGoalColumn */) << 5 /* GoalColumnOffset */;
+ return head < anchor ? new SelectionRange(head, anchor, 16 /* Inverted */ | goal) : new SelectionRange(anchor, head, goal);
+ }
+ }
+ function normalized(ranges, mainIndex = 0) {
+ let main = ranges[mainIndex];
+ ranges.sort((a, b) => a.from - b.from);
+ mainIndex = ranges.indexOf(main);
+ for (let i = 1; i < ranges.length; i++) {
+ let range = ranges[i], prev = ranges[i - 1];
+ if (range.empty ? range.from <= prev.to : range.from < prev.to) {
+ let from = prev.from, to = Math.max(range.to, prev.to);
+ if (i <= mainIndex)
+ mainIndex--;
+ ranges.splice(--i, 2, range.anchor > range.head ? EditorSelection.range(to, from) : EditorSelection.range(from, to));
+ }
+ }
+ return new EditorSelection(ranges, mainIndex);
+ }
+ function checkSelection(selection, docLength) {
+ for (let range of selection.ranges)
+ if (range.to > docLength)
+ throw new RangeError("Selection points outside of document");
+ }
+
+ let nextID = 0;
+ /**
+ A facet is a labeled value that is associated with an editor
+ state. It takes inputs from any number of extensions, and combines
+ those into a single output value.
+
+ Examples of facets are the [theme](https://codemirror.net/6/docs/ref/#view.EditorView^theme) styles
+ associated with an editor or the [tab
+ size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) (which is reduced to a single
+ value, using the input with the hightest precedence).
+ */
+ class Facet {
+ constructor(
+ /**
+ @internal
+ */
+ combine,
+ /**
+ @internal
+ */
+ compareInput,
+ /**
+ @internal
+ */
+ compare, isStatic,
+ /**
+ @internal
+ */
+ extensions) {
+ this.combine = combine;
+ this.compareInput = compareInput;
+ this.compare = compare;
+ this.isStatic = isStatic;
+ this.extensions = extensions;
+ /**
+ @internal
+ */
+ this.id = nextID++;
+ this.default = combine([]);
+ }
+ /**
+ Define a new facet.
+ */
+ static define(config = {}) {
+ return new Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray$1 : (a, b) => a === b), !!config.static, config.enables);
+ }
+ /**
+ Returns an extension that adds the given value for this facet.
+ */
+ of(value) {
+ return new FacetProvider([], this, 0 /* Static */, value);
+ }
+ /**
+ Create an extension that computes a value for the facet from a
+ state. You must take care to declare the parts of the state that
+ this value depends on, since your function is only called again
+ for a new state when one of those parts changed.
+
+ In most cases, you'll want to use the
+ [`provide`](https://codemirror.net/6/docs/ref/#state.StateField^define^config.provide) option when
+ defining a field instead.
+ */
+ compute(deps, get) {
+ if (this.isStatic)
+ throw new Error("Can't compute a static facet");
+ return new FacetProvider(deps, this, 1 /* Single */, get);
+ }
+ /**
+ Create an extension that computes zero or more values for this
+ facet from a state.
+ */
+ computeN(deps, get) {
+ if (this.isStatic)
+ throw new Error("Can't compute a static facet");
+ return new FacetProvider(deps, this, 2 /* Multi */, get);
+ }
+ from(field, get) {
+ if (!get)
+ get = x => x;
+ return this.compute([field], state => get(state.field(field)));
+ }
+ }
+ function sameArray$1(a, b) {
+ return a == b || a.length == b.length && a.every((e, i) => e === b[i]);
+ }
+ class FacetProvider {
+ constructor(dependencies, facet, type, value) {
+ this.dependencies = dependencies;
+ this.facet = facet;
+ this.type = type;
+ this.value = value;
+ this.id = nextID++;
+ }
+ dynamicSlot(addresses) {
+ var _a;
+ let getter = this.value;
+ let compare = this.facet.compareInput;
+ let idx = addresses[this.id] >> 1, multi = this.type == 2 /* Multi */;
+ let depDoc = false, depSel = false, depAddrs = [];
+ for (let dep of this.dependencies) {
+ if (dep == "doc")
+ depDoc = true;
+ else if (dep == "selection")
+ depSel = true;
+ else if ((((_a = addresses[dep.id]) !== null && _a !== void 0 ? _a : 1) & 1) == 0)
+ depAddrs.push(addresses[dep.id]);
+ }
+ return (state, tr) => {
+ if (!tr || tr.reconfigured) {
+ state.values[idx] = getter(state);
+ return 1 /* Changed */;
+ }
+ else {
+ let depChanged = (depDoc && tr.docChanged) || (depSel && (tr.docChanged || tr.selection)) ||
+ depAddrs.some(addr => (ensureAddr(state, addr) & 1 /* Changed */) > 0);
+ if (!depChanged)
+ return 0;
+ let newVal = getter(state), oldVal = tr.startState.values[idx];
+ if (multi ? compareArray(newVal, oldVal, compare) : compare(newVal, oldVal))
+ return 0;
+ state.values[idx] = newVal;
+ return 1 /* Changed */;
+ }
+ };
+ }
+ }
+ function compareArray(a, b, compare) {
+ if (a.length != b.length)
+ return false;
+ for (let i = 0; i < a.length; i++)
+ if (!compare(a[i], b[i]))
+ return false;
+ return true;
+ }
+ function dynamicFacetSlot(addresses, facet, providers) {
+ let providerAddrs = providers.map(p => addresses[p.id]);
+ let providerTypes = providers.map(p => p.type);
+ let dynamic = providerAddrs.filter(p => !(p & 1));
+ let idx = addresses[facet.id] >> 1;
+ return (state, tr) => {
+ let oldAddr = !tr ? null : tr.reconfigured ? tr.startState.config.address[facet.id] : idx << 1;
+ let changed = oldAddr == null;
+ for (let dynAddr of dynamic) {
+ if (ensureAddr(state, dynAddr) & 1 /* Changed */)
+ changed = true;
+ }
+ if (!changed)
+ return 0;
+ let values = [];
+ for (let i = 0; i < providerAddrs.length; i++) {
+ let value = getAddr(state, providerAddrs[i]);
+ if (providerTypes[i] == 2 /* Multi */)
+ for (let val of value)
+ values.push(val);
+ else
+ values.push(value);
+ }
+ let newVal = facet.combine(values);
+ if (oldAddr != null && facet.compare(newVal, getAddr(tr.startState, oldAddr)))
+ return 0;
+ state.values[idx] = newVal;
+ return 1 /* Changed */;
+ };
+ }
+ function maybeIndex(state, id) {
+ let found = state.config.address[id];
+ return found == null ? null : found >> 1;
+ }
+ const initField = Facet.define({ static: true });
+ /**
+ Fields can store additional information in an editor state, and
+ keep it in sync with the rest of the state.
+ */
+ class StateField {
+ constructor(
+ /**
+ @internal
+ */
+ id, createF, updateF, compareF,
+ /**
+ @internal
+ */
+ spec) {
+ this.id = id;
+ this.createF = createF;
+ this.updateF = updateF;
+ this.compareF = compareF;
+ this.spec = spec;
+ /**
+ @internal
+ */
+ this.provides = undefined;
+ }
+ /**
+ Define a state field.
+ */
+ static define(config) {
+ let field = new StateField(nextID++, config.create, config.update, config.compare || ((a, b) => a === b), config);
+ if (config.provide)
+ field.provides = config.provide(field);
+ return field;
+ }
+ create(state) {
+ let init = state.facet(initField).find(i => i.field == this);
+ return ((init === null || init === void 0 ? void 0 : init.create) || this.createF)(state);
+ }
+ /**
+ @internal
+ */
+ slot(addresses) {
+ let idx = addresses[this.id] >> 1;
+ return (state, tr) => {
+ if (!tr) {
+ state.values[idx] = this.create(state);
+ return 1 /* Changed */;
+ }
+ let oldVal, changed = 0;
+ if (tr.reconfigured) {
+ let oldIdx = maybeIndex(tr.startState, this.id);
+ oldVal = oldIdx == null ? this.create(tr.startState) : tr.startState.values[oldIdx];
+ changed = 1 /* Changed */;
+ }
+ else {
+ oldVal = tr.startState.values[idx];
+ }
+ let value = this.updateF(oldVal, tr);
+ if (!changed && !this.compareF(oldVal, value))
+ changed = 1 /* Changed */;
+ if (changed)
+ state.values[idx] = value;
+ return changed;
+ };
+ }
+ /**
+ Returns an extension that enables this field and overrides the
+ way it is initialized. Can be useful when you need to provide a
+ non-default starting value for the field.
+ */
+ init(create) {
+ return [this, initField.of({ field: this, create })];
+ }
+ /**
+ State field instances can be used as
+ [`Extension`](https://codemirror.net/6/docs/ref/#state.Extension) values to enable the field in a
+ given state.
+ */
+ get extension() { return this; }
+ }
+ const Prec_ = { fallback: 3, default: 2, extend: 1, override: 0 };
+ function prec(value) {
+ return (ext) => new PrecExtension(ext, value);
+ }
+ /**
+ By default extensions are registered in the order they are found
+ in the flattened form of nested array that was provided.
+ Individual extension values can be assigned a precedence to
+ override this. Extensions that do not have a precedence set get
+ the precedence of the nearest parent with a precedence, or
+ [`default`](https://codemirror.net/6/docs/ref/#state.Prec.default) if there is no such parent. The
+ final ordering of extensions is determined by first sorting by
+ precedence and then by order within each precedence.
+ */
+ const Prec = {
+ /**
+ A precedence below the default precedence, which will cause
+ default-precedence extensions to override it even if they are
+ specified later in the extension ordering.
+ */
+ fallback: prec(Prec_.fallback),
+ /**
+ The regular default precedence.
+ */
+ default: prec(Prec_.default),
+ /**
+ A higher-than-default precedence.
+ */
+ extend: prec(Prec_.extend),
+ /**
+ Precedence above the `default` and `extend` precedences.
+ */
+ override: prec(Prec_.override)
+ };
+ class PrecExtension {
+ constructor(inner, prec) {
+ this.inner = inner;
+ this.prec = prec;
+ }
+ }
+ /**
+ Extension compartments can be used to make a configuration
+ dynamic. By [wrapping](https://codemirror.net/6/docs/ref/#state.Compartment.of) part of your
+ configuration in a compartment, you can later
+ [replace](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure) that part through a
+ transaction.
+ */
+ class Compartment {
+ /**
+ Create an instance of this compartment to add to your [state
+ configuration](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions).
+ */
+ of(ext) { return new CompartmentInstance(this, ext); }
+ /**
+ Create an [effect](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) that
+ reconfigures this compartment.
+ */
+ reconfigure(content) {
+ return Compartment.reconfigure.of({ compartment: this, extension: content });
+ }
+ /**
+ Get the current content of the compartment in the state, or
+ `undefined` if it isn't present.
+ */
+ get(state) {
+ return state.config.compartments.get(this);
+ }
+ }
+ class CompartmentInstance {
+ constructor(compartment, inner) {
+ this.compartment = compartment;
+ this.inner = inner;
+ }
+ }
+ class Configuration {
+ constructor(base, compartments, dynamicSlots, address, staticValues) {
+ this.base = base;
+ this.compartments = compartments;
+ this.dynamicSlots = dynamicSlots;
+ this.address = address;
+ this.staticValues = staticValues;
+ this.statusTemplate = [];
+ while (this.statusTemplate.length < dynamicSlots.length)
+ this.statusTemplate.push(0 /* Uninitialized */);
+ }
+ staticFacet(facet) {
+ let addr = this.address[facet.id];
+ return addr == null ? facet.default : this.staticValues[addr >> 1];
+ }
+ static resolve(base, compartments, oldState) {
+ let fields = [];
+ let facets = Object.create(null);
+ let newCompartments = new Map();
+ for (let ext of flatten(base, compartments, newCompartments)) {
+ if (ext instanceof StateField)
+ fields.push(ext);
+ else
+ (facets[ext.facet.id] || (facets[ext.facet.id] = [])).push(ext);
+ }
+ let address = Object.create(null);
+ let staticValues = [];
+ let dynamicSlots = [];
+ for (let field of fields) {
+ address[field.id] = dynamicSlots.length << 1;
+ dynamicSlots.push(a => field.slot(a));
+ }
+ for (let id in facets) {
+ let providers = facets[id], facet = providers[0].facet;
+ if (providers.every(p => p.type == 0 /* Static */)) {
+ address[facet.id] = (staticValues.length << 1) | 1;
+ let value = facet.combine(providers.map(p => p.value));
+ let oldAddr = oldState ? oldState.config.address[facet.id] : null;
+ if (oldAddr != null) {
+ let oldVal = getAddr(oldState, oldAddr);
+ if (facet.compare(value, oldVal))
+ value = oldVal;
+ }
+ staticValues.push(value);
+ }
+ else {
+ for (let p of providers) {
+ if (p.type == 0 /* Static */) {
+ address[p.id] = (staticValues.length << 1) | 1;
+ staticValues.push(p.value);
+ }
+ else {
+ address[p.id] = dynamicSlots.length << 1;
+ dynamicSlots.push(a => p.dynamicSlot(a));
+ }
+ }
+ address[facet.id] = dynamicSlots.length << 1;
+ dynamicSlots.push(a => dynamicFacetSlot(a, facet, providers));
+ }
+ }
+ return new Configuration(base, newCompartments, dynamicSlots.map(f => f(address)), address, staticValues);
+ }
+ }
+ function flatten(extension, compartments, newCompartments) {
+ let result = [[], [], [], []];
+ let seen = new Map();
+ function inner(ext, prec) {
+ let known = seen.get(ext);
+ if (known != null) {
+ if (known >= prec)
+ return;
+ let found = result[known].indexOf(ext);
+ if (found > -1)
+ result[known].splice(found, 1);
+ if (ext instanceof CompartmentInstance)
+ newCompartments.delete(ext.compartment);
+ }
+ seen.set(ext, prec);
+ if (Array.isArray(ext)) {
+ for (let e of ext)
+ inner(e, prec);
+ }
+ else if (ext instanceof CompartmentInstance) {
+ if (newCompartments.has(ext.compartment))
+ throw new RangeError(`Duplicate use of compartment in extensions`);
+ let content = compartments.get(ext.compartment) || ext.inner;
+ newCompartments.set(ext.compartment, content);
+ inner(content, prec);
+ }
+ else if (ext instanceof PrecExtension) {
+ inner(ext.inner, ext.prec);
+ }
+ else if (ext instanceof StateField) {
+ result[prec].push(ext);
+ if (ext.provides)
+ inner(ext.provides, prec);
+ }
+ else if (ext instanceof FacetProvider) {
+ result[prec].push(ext);
+ if (ext.facet.extensions)
+ inner(ext.facet.extensions, prec);
+ }
+ else {
+ let content = ext.extension;
+ if (!content)
+ throw new Error(`Unrecognized extension value in extension set (${ext}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);
+ inner(content, prec);
+ }
+ }
+ inner(extension, Prec_.default);
+ return result.reduce((a, b) => a.concat(b));
+ }
+ function ensureAddr(state, addr) {
+ if (addr & 1)
+ return 2 /* Computed */;
+ let idx = addr >> 1;
+ let status = state.status[idx];
+ if (status == 4 /* Computing */)
+ throw new Error("Cyclic dependency between fields and/or facets");
+ if (status & 2 /* Computed */)
+ return status;
+ state.status[idx] = 4 /* Computing */;
+ let changed = state.config.dynamicSlots[idx](state, state.applying);
+ return state.status[idx] = 2 /* Computed */ | changed;
+ }
+ function getAddr(state, addr) {
+ return addr & 1 ? state.config.staticValues[addr >> 1] : state.values[addr >> 1];
+ }
+
+ const languageData = Facet.define();
+ const allowMultipleSelections = Facet.define({
+ combine: values => values.some(v => v),
+ static: true
+ });
+ const lineSeparator = Facet.define({
+ combine: values => values.length ? values[0] : undefined,
+ static: true
+ });
+ const changeFilter = Facet.define();
+ const transactionFilter = Facet.define();
+ const transactionExtender = Facet.define();
+
+ /**
+ Annotations are tagged values that are used to add metadata to
+ transactions in an extensible way. They should be used to model
+ things that effect the entire transaction (such as its [time
+ stamp](https://codemirror.net/6/docs/ref/#state.Transaction^time) or information about its
+ [origin](https://codemirror.net/6/docs/ref/#state.Transaction^userEvent)). For effects that happen
+ _alongside_ the other changes made by the transaction, [state
+ effects](https://codemirror.net/6/docs/ref/#state.StateEffect) are more appropriate.
+ */
+ class Annotation {
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ The annotation type.
+ */
+ type,
+ /**
+ The value of this annotation.
+ */
+ value) {
+ this.type = type;
+ this.value = value;
+ }
+ /**
+ Define a new type of annotation.
+ */
+ static define() { return new AnnotationType(); }
+ }
+ /**
+ Marker that identifies a type of [annotation](https://codemirror.net/6/docs/ref/#state.Annotation).
+ */
+ class AnnotationType {
+ /**
+ Create an instance of this annotation.
+ */
+ of(value) { return new Annotation(this, value); }
+ }
+ /**
+ Representation of a type of state effect. Defined with
+ [`StateEffect.define`](https://codemirror.net/6/docs/ref/#state.StateEffect^define).
+ */
+ class StateEffectType {
+ /**
+ @internal
+ */
+ constructor(
+ // The `any` types in these function types are there to work
+ // around TypeScript issue #37631, where the type guard on
+ // `StateEffect.is` mysteriously stops working when these properly
+ // have type `Value`.
+ /**
+ @internal
+ */
+ map) {
+ this.map = map;
+ }
+ /**
+ Create a [state effect](https://codemirror.net/6/docs/ref/#state.StateEffect) instance of this
+ type.
+ */
+ of(value) { return new StateEffect(this, value); }
+ }
+ /**
+ State effects can be used to represent additional effects
+ associated with a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction.effects). They
+ are often useful to model changes to custom [state
+ fields](https://codemirror.net/6/docs/ref/#state.StateField), when those changes aren't implicit in
+ document or selection changes.
+ */
+ class StateEffect {
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ @internal
+ */
+ type,
+ /**
+ The value of this effect.
+ */
+ value) {
+ this.type = type;
+ this.value = value;
+ }
+ /**
+ Map this effect through a position mapping. Will return
+ `undefined` when that ends up deleting the effect.
+ */
+ map(mapping) {
+ let mapped = this.type.map(this.value, mapping);
+ return mapped === undefined ? undefined : mapped == this.value ? this : new StateEffect(this.type, mapped);
+ }
+ /**
+ Tells you whether this effect object is of a given
+ [type](https://codemirror.net/6/docs/ref/#state.StateEffectType).
+ */
+ is(type) { return this.type == type; }
+ /**
+ Define a new effect type. The type parameter indicates the type
+ of values that his effect holds.
+ */
+ static define(spec = {}) {
+ return new StateEffectType(spec.map || (v => v));
+ }
+ /**
+ Map an array of effects through a change set.
+ */
+ static mapEffects(effects, mapping) {
+ if (!effects.length)
+ return effects;
+ let result = [];
+ for (let effect of effects) {
+ let mapped = effect.map(mapping);
+ if (mapped)
+ result.push(mapped);
+ }
+ return result;
+ }
+ }
+ /**
+ This effect can be used to reconfigure the root extensions of
+ the editor. Doing this will discard any extensions
+ [appended](https://codemirror.net/6/docs/ref/#state.StateEffect^appendConfig), but does not reset
+ the content of [reconfigured](https://codemirror.net/6/docs/ref/#state.Compartment.reconfigure)
+ compartments.
+ */
+ StateEffect.reconfigure = StateEffect.define();
+ /**
+ Append extensions to the top-level configuration of the editor.
+ */
+ StateEffect.appendConfig = StateEffect.define();
+ /**
+ Changes to the editor state are grouped into transactions.
+ Typically, a user action creates a single transaction, which may
+ contain any number of document changes, may change the selection,
+ or have other effects. Create a transaction by calling
+ [`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update).
+ */
+ class Transaction {
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ The state from which the transaction starts.
+ */
+ startState,
+ /**
+ The document changes made by this transaction.
+ */
+ changes,
+ /**
+ The selection set by this transaction, or undefined if it
+ doesn't explicitly set a selection.
+ */
+ selection,
+ /**
+ The effects added to the transaction.
+ */
+ effects,
+ /**
+ @internal
+ */
+ annotations,
+ /**
+ Whether the selection should be scrolled into view after this
+ transaction is dispatched.
+ */
+ scrollIntoView) {
+ this.startState = startState;
+ this.changes = changes;
+ this.selection = selection;
+ this.effects = effects;
+ this.annotations = annotations;
+ this.scrollIntoView = scrollIntoView;
+ /**
+ @internal
+ */
+ this._doc = null;
+ /**
+ @internal
+ */
+ this._state = null;
+ if (selection)
+ checkSelection(selection, changes.newLength);
+ if (!annotations.some((a) => a.type == Transaction.time))
+ this.annotations = annotations.concat(Transaction.time.of(Date.now()));
+ }
+ /**
+ The new document produced by the transaction. Contrary to
+ [`.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state)`.doc`, accessing this won't
+ force the entire new state to be computed right away, so it is
+ recommended that [transaction
+ filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) use this getter
+ when they need to look at the new document.
+ */
+ get newDoc() {
+ return this._doc || (this._doc = this.changes.apply(this.startState.doc));
+ }
+ /**
+ The new selection produced by the transaction. If
+ [`this.selection`](https://codemirror.net/6/docs/ref/#state.Transaction.selection) is undefined,
+ this will [map](https://codemirror.net/6/docs/ref/#state.EditorSelection.map) the start state's
+ current selection through the changes made by the transaction.
+ */
+ get newSelection() {
+ return this.selection || this.startState.selection.map(this.changes);
+ }
+ /**
+ The new state created by the transaction. Computed on demand
+ (but retained for subsequent access), so itis recommended not to
+ access it in [transaction
+ filters](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter) when possible.
+ */
+ get state() {
+ if (!this._state)
+ this.startState.applyTransaction(this);
+ return this._state;
+ }
+ /**
+ Get the value of the given annotation type, if any.
+ */
+ annotation(type) {
+ for (let ann of this.annotations)
+ if (ann.type == type)
+ return ann.value;
+ return undefined;
+ }
+ /**
+ Indicates whether the transaction changed the document.
+ */
+ get docChanged() { return !this.changes.empty; }
+ /**
+ Indicates whether this transaction reconfigures the state
+ (through a [configuration compartment](https://codemirror.net/6/docs/ref/#state.Compartment) or
+ with a top-level configuration
+ [effect](https://codemirror.net/6/docs/ref/#state.StateEffect^reconfigure).
+ */
+ get reconfigured() { return this.startState.config != this.state.config; }
+ }
+ /**
+ Annotation used to store transaction timestamps.
+ */
+ Transaction.time = Annotation.define();
+ /**
+ Annotation used to associate a transaction with a user interface
+ event. The view will set this to...
+
+ - `"input"` when the user types text
+ - `"delete"` when the user deletes the selection or text near the selection
+ - `"keyboardselection"` when moving the selection via the keyboard
+ - `"pointerselection"` when moving the selection through the pointing device
+ - `"paste"` when pasting content
+ - `"cut"` when cutting
+ - `"drop"` when content is inserted via drag-and-drop
+ */
+ Transaction.userEvent = Annotation.define();
+ /**
+ Annotation indicating whether a transaction should be added to
+ the undo history or not.
+ */
+ Transaction.addToHistory = Annotation.define();
+ function joinRanges(a, b) {
+ let result = [];
+ for (let iA = 0, iB = 0;;) {
+ let from, to;
+ if (iA < a.length && (iB == b.length || b[iB] >= a[iA])) {
+ from = a[iA++];
+ to = a[iA++];
+ }
+ else if (iB < b.length) {
+ from = b[iB++];
+ to = b[iB++];
+ }
+ else
+ return result;
+ if (!result.length || result[result.length - 1] < from)
+ result.push(from, to);
+ else if (result[result.length - 1] < to)
+ result[result.length - 1] = to;
+ }
+ }
+ function mergeTransaction(a, b, sequential) {
+ var _a;
+ let mapForA, mapForB, changes;
+ if (sequential) {
+ mapForA = b.changes;
+ mapForB = ChangeSet.empty(b.changes.length);
+ changes = a.changes.compose(b.changes);
+ }
+ else {
+ mapForA = b.changes.map(a.changes);
+ mapForB = a.changes.mapDesc(b.changes, true);
+ changes = a.changes.compose(mapForA);
+ }
+ return {
+ changes,
+ selection: b.selection ? b.selection.map(mapForB) : (_a = a.selection) === null || _a === void 0 ? void 0 : _a.map(mapForA),
+ effects: StateEffect.mapEffects(a.effects, mapForA).concat(StateEffect.mapEffects(b.effects, mapForB)),
+ annotations: a.annotations.length ? a.annotations.concat(b.annotations) : b.annotations,
+ scrollIntoView: a.scrollIntoView || b.scrollIntoView
+ };
+ }
+ function resolveTransactionInner(state, spec, docSize) {
+ let sel = spec.selection;
+ return {
+ changes: spec.changes instanceof ChangeSet ? spec.changes
+ : ChangeSet.of(spec.changes || [], docSize, state.facet(lineSeparator)),
+ selection: sel && (sel instanceof EditorSelection ? sel : EditorSelection.single(sel.anchor, sel.head)),
+ effects: asArray(spec.effects),
+ annotations: asArray(spec.annotations),
+ scrollIntoView: !!spec.scrollIntoView
+ };
+ }
+ function resolveTransaction(state, specs, filter) {
+ let s = resolveTransactionInner(state, specs.length ? specs[0] : {}, state.doc.length);
+ if (specs.length && specs[0].filter === false)
+ filter = false;
+ for (let i = 1; i < specs.length; i++) {
+ if (specs[i].filter === false)
+ filter = false;
+ let seq = !!specs[i].sequential;
+ s = mergeTransaction(s, resolveTransactionInner(state, specs[i], seq ? s.changes.newLength : state.doc.length), seq);
+ }
+ let tr = new Transaction(state, s.changes, s.selection, s.effects, s.annotations, s.scrollIntoView);
+ return extendTransaction(filter ? filterTransaction(tr) : tr);
+ }
+ // Finish a transaction by applying filters if necessary.
+ function filterTransaction(tr) {
+ let state = tr.startState;
+ // Change filters
+ let result = true;
+ for (let filter of state.facet(changeFilter)) {
+ let value = filter(tr);
+ if (value === false) {
+ result = false;
+ break;
+ }
+ if (Array.isArray(value))
+ result = result === true ? value : joinRanges(result, value);
+ }
+ if (result !== true) {
+ let changes, back;
+ if (result === false) {
+ back = tr.changes.invertedDesc;
+ changes = ChangeSet.empty(state.doc.length);
+ }
+ else {
+ let filtered = tr.changes.filter(result);
+ changes = filtered.changes;
+ back = filtered.filtered.invertedDesc;
+ }
+ tr = new Transaction(state, changes, tr.selection && tr.selection.map(back), StateEffect.mapEffects(tr.effects, back), tr.annotations, tr.scrollIntoView);
+ }
+ // Transaction filters
+ let filters = state.facet(transactionFilter);
+ for (let i = filters.length - 1; i >= 0; i--) {
+ let filtered = filters[i](tr);
+ if (filtered instanceof Transaction)
+ tr = filtered;
+ else if (Array.isArray(filtered) && filtered.length == 1 && filtered[0] instanceof Transaction)
+ tr = filtered[0];
+ else
+ tr = resolveTransaction(state, asArray(filtered), false);
+ }
+ return tr;
+ }
+ function extendTransaction(tr) {
+ let state = tr.startState, extenders = state.facet(transactionExtender), spec = tr;
+ for (let i = extenders.length - 1; i >= 0; i--) {
+ let extension = extenders[i](tr);
+ if (extension && Object.keys(extension).length)
+ spec = mergeTransaction(tr, resolveTransactionInner(state, extension, tr.changes.newLength), true);
+ }
+ return spec == tr ? tr : new Transaction(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView);
+ }
+ const none$5 = [];
+ function asArray(value) {
+ return value == null ? none$5 : Array.isArray(value) ? value : [value];
+ }
+
+ /**
+ The categories produced by a [character
+ categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer). These are used
+ do things like selecting by word.
+ */
+ var CharCategory;
+ (function (CharCategory) {
+ /**
+ Word characters.
+ */
+ CharCategory[CharCategory["Word"] = 0] = "Word";
+ /**
+ Whitespace.
+ */
+ CharCategory[CharCategory["Space"] = 1] = "Space";
+ /**
+ Anything else.
+ */
+ CharCategory[CharCategory["Other"] = 2] = "Other";
+ })(CharCategory || (CharCategory = {}));
+ const nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+ let wordChar;
+ try {
+ wordChar = new RegExp("[\\p{Alphabetic}\\p{Number}_]", "u");
+ }
+ catch (_) { }
+ function hasWordChar(str) {
+ if (wordChar)
+ return wordChar.test(str);
+ for (let i = 0; i < str.length; i++) {
+ let ch = str[i];
+ if (/\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)))
+ return true;
+ }
+ return false;
+ }
+ function makeCategorizer(wordChars) {
+ return (char) => {
+ if (!/\S/.test(char))
+ return CharCategory.Space;
+ if (hasWordChar(char))
+ return CharCategory.Word;
+ for (let i = 0; i < wordChars.length; i++)
+ if (char.indexOf(wordChars[i]) > -1)
+ return CharCategory.Word;
+ return CharCategory.Other;
+ };
+ }
+
+ /**
+ The editor state class is a persistent (immutable) data structure.
+ To update a state, you [create](https://codemirror.net/6/docs/ref/#state.EditorState.update) a
+ [transaction](https://codemirror.net/6/docs/ref/#state.Transaction), which produces a _new_ state
+ instance, without modifying the original object.
+
+ As such, _never_ mutate properties of a state directly. That'll
+ just break things.
+ */
+ class EditorState {
+ /**
+ @internal
+ */
+ constructor(
+ /**
+ @internal
+ */
+ config,
+ /**
+ The current document.
+ */
+ doc,
+ /**
+ The current selection.
+ */
+ selection, tr = null) {
+ this.config = config;
+ this.doc = doc;
+ this.selection = selection;
+ /**
+ @internal
+ */
+ this.applying = null;
+ this.status = config.statusTemplate.slice();
+ if (tr && tr.startState.config == config) {
+ this.values = tr.startState.values.slice();
+ }
+ else {
+ this.values = config.dynamicSlots.map(_ => null);
+ // Copy over old values for shared facets/fields if this is a reconfigure
+ if (tr)
+ for (let id in config.address) {
+ let cur = config.address[id], prev = tr.startState.config.address[id];
+ if (prev != null && (cur & 1) == 0)
+ this.values[cur >> 1] = getAddr(tr.startState, prev);
+ }
+ }
+ this.applying = tr;
+ // Fill in the computed state immediately, so that further queries
+ // for it made during the update return this state
+ if (tr)
+ tr._state = this;
+ for (let i = 0; i < this.config.dynamicSlots.length; i++)
+ ensureAddr(this, i << 1);
+ this.applying = null;
+ }
+ field(field, require = true) {
+ let addr = this.config.address[field.id];
+ if (addr == null) {
+ if (require)
+ throw new RangeError("Field is not present in this state");
+ return undefined;
+ }
+ ensureAddr(this, addr);
+ return getAddr(this, addr);
+ }
+ /**
+ Create a [transaction](https://codemirror.net/6/docs/ref/#state.Transaction) that updates this
+ state. Any number of [transaction specs](https://codemirror.net/6/docs/ref/#state.TransactionSpec)
+ can be passed. Unless
+ [`sequential`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.sequential) is set, the
+ [changes](https://codemirror.net/6/docs/ref/#state.TransactionSpec.changes) (if any) of each spec
+ are assumed to start in the _current_ document (not the document
+ produced by previous specs), and its
+ [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) and
+ [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) are assumed to refer
+ to the document created by its _own_ changes. The resulting
+ transaction contains the combined effect of all the different
+ specs. For [selection](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection), later
+ specs take precedence over earlier ones.
+ */
+ update(...specs) {
+ return resolveTransaction(this, specs, true);
+ }
+ /**
+ @internal
+ */
+ applyTransaction(tr) {
+ let conf = this.config, { base, compartments } = conf;
+ for (let effect of tr.effects) {
+ if (effect.is(Compartment.reconfigure)) {
+ if (conf) {
+ compartments = new Map;
+ conf.compartments.forEach((val, key) => compartments.set(key, val));
+ conf = null;
+ }
+ compartments.set(effect.value.compartment, effect.value.extension);
+ }
+ else if (effect.is(StateEffect.reconfigure)) {
+ conf = null;
+ base = effect.value;
+ }
+ else if (effect.is(StateEffect.appendConfig)) {
+ conf = null;
+ base = asArray(base).concat(effect.value);
+ }
+ }
+ new EditorState(conf || Configuration.resolve(base, compartments, this), tr.newDoc, tr.newSelection, tr);
+ }
+ /**
+ Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that
+ replaces every selection range with the given content.
+ */
+ replaceSelection(text) {
+ if (typeof text == "string")
+ text = this.toText(text);
+ return this.changeByRange(range => ({ changes: { from: range.from, to: range.to, insert: text },
+ range: EditorSelection.cursor(range.from + text.length) }));
+ }
+ /**
+ Create a set of changes and a new selection by running the given
+ function for each range in the active selection. The function
+ can return an optional set of changes (in the coordinate space
+ of the start document), plus an updated range (in the coordinate
+ space of the document produced by the call's own changes). This
+ method will merge all the changes and ranges into a single
+ changeset and selection, and return it as a [transaction
+ spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec), which can be passed to
+ [`update`](https://codemirror.net/6/docs/ref/#state.EditorState.update).
+ */
+ changeByRange(f) {
+ let sel = this.selection;
+ let result1 = f(sel.ranges[0]);
+ let changes = this.changes(result1.changes), ranges = [result1.range];
+ let effects = asArray(result1.effects);
+ for (let i = 1; i < sel.ranges.length; i++) {
+ let result = f(sel.ranges[i]);
+ let newChanges = this.changes(result.changes), newMapped = newChanges.map(changes);
+ for (let j = 0; j < i; j++)
+ ranges[j] = ranges[j].map(newMapped);
+ let mapBy = changes.mapDesc(newChanges, true);
+ ranges.push(result.range.map(mapBy));
+ changes = changes.compose(newMapped);
+ effects = StateEffect.mapEffects(effects, newMapped).concat(StateEffect.mapEffects(asArray(result.effects), mapBy));
+ }
+ return {
+ changes,
+ selection: EditorSelection.create(ranges, sel.mainIndex),
+ effects
+ };
+ }
+ /**
+ Create a [change set](https://codemirror.net/6/docs/ref/#state.ChangeSet) from the given change
+ description, taking the state's document length and line
+ separator into account.
+ */
+ changes(spec = []) {
+ if (spec instanceof ChangeSet)
+ return spec;
+ return ChangeSet.of(spec, this.doc.length, this.facet(EditorState.lineSeparator));
+ }
+ /**
+ Using the state's [line
+ separator](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator), create a
+ [`Text`](https://codemirror.net/6/docs/ref/#text.Text) instance from the given string.
+ */
+ toText(string) {
+ return Text.of(string.split(this.facet(EditorState.lineSeparator) || DefaultSplit));
+ }
+ /**
+ Return the given range of the document as a string.
+ */
+ sliceDoc(from = 0, to = this.doc.length) {
+ return this.doc.sliceString(from, to, this.lineBreak);
+ }
+ /**
+ Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet).
+ */
+ facet(facet) {
+ let addr = this.config.address[facet.id];
+ if (addr == null)
+ return facet.default;
+ ensureAddr(this, addr);
+ return getAddr(this, addr);
+ }
+ /**
+ Convert this state to a JSON-serializable object. When custom
+ fields should be serialized, you can pass them in as an object
+ mapping property names (in the resulting object, which should
+ not use `doc` or `selection`) to fields.
+ */
+ toJSON(fields) {
+ let result = {
+ doc: this.sliceDoc(),
+ selection: this.selection.toJSON()
+ };
+ if (fields)
+ for (let prop in fields) {
+ let value = fields[prop];
+ if (value instanceof StateField)
+ result[prop] = value.spec.toJSON(this.field(fields[prop]), this);
+ }
+ return result;
+ }
+ /**
+ Deserialize a state from its JSON representation. When custom
+ fields should be deserialized, pass the same object you passed
+ to [`toJSON`](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) when serializing as
+ third argument.
+ */
+ static fromJSON(json, config = {}, fields) {
+ if (!json || typeof json.doc != "string")
+ throw new RangeError("Invalid JSON representation for EditorState");
+ let fieldInit = [];
+ if (fields)
+ for (let prop in fields) {
+ let field = fields[prop], value = json[prop];
+ fieldInit.push(field.init(state => field.spec.fromJSON(value, state)));
+ }
+ return EditorState.create({
+ doc: json.doc,
+ selection: EditorSelection.fromJSON(json.selection),
+ extensions: config.extensions ? fieldInit.concat([config.extensions]) : fieldInit
+ });
+ }
+ /**
+ Create a new state. You'll usually only need this when
+ initializing an editor—updated states are created by applying
+ transactions.
+ */
+ static create(config = {}) {
+ let configuration = Configuration.resolve(config.extensions || [], new Map);
+ let doc = config.doc instanceof Text ? config.doc
+ : Text.of((config.doc || "").split(configuration.staticFacet(EditorState.lineSeparator) || DefaultSplit));
+ let selection = !config.selection ? EditorSelection.single(0)
+ : config.selection instanceof EditorSelection ? config.selection
+ : EditorSelection.single(config.selection.anchor, config.selection.head);
+ checkSelection(selection, doc.length);
+ if (!configuration.staticFacet(allowMultipleSelections))
+ selection = selection.asSingle();
+ return new EditorState(configuration, doc, selection);
+ }
+ /**
+ The size (in columns) of a tab in the document, determined by
+ the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet.
+ */
+ get tabSize() { return this.facet(EditorState.tabSize); }
+ /**
+ Get the proper [line-break](https://codemirror.net/6/docs/ref/#state.EditorState^lineSeparator)
+ string for this state.
+ */
+ get lineBreak() { return this.facet(EditorState.lineSeparator) || "\n"; }
+ /**
+ Look up a translation for the given phrase (via the
+ [`phrases`](https://codemirror.net/6/docs/ref/#state.EditorState^phrases) facet), or return the
+ original string if no translation is found.
+ */
+ phrase(phrase) {
+ for (let map of this.facet(EditorState.phrases))
+ if (Object.prototype.hasOwnProperty.call(map, phrase))
+ return map[phrase];
+ return phrase;
+ }
+ /**
+ Find the values for a given language data field, provided by the
+ the [`languageData`](https://codemirror.net/6/docs/ref/#state.EditorState^languageData) facet.
+ */
+ languageDataAt(name, pos) {
+ let values = [];
+ for (let provider of this.facet(languageData)) {
+ for (let result of provider(this, pos)) {
+ if (Object.prototype.hasOwnProperty.call(result, name))
+ values.push(result[name]);
+ }
+ }
+ return values;
+ }
+ /**
+ Return a function that can categorize strings (expected to
+ represent a single [grapheme cluster](https://codemirror.net/6/docs/ref/#text.findClusterBreak))
+ into one of:
+
+ - Word (contains an alphanumeric character or a character
+ explicitly listed in the local language's `"wordChars"`
+ language data, which should be a string)
+ - Space (contains only whitespace)
+ - Other (anything else)
+ */
+ charCategorizer(at) {
+ return makeCategorizer(this.languageDataAt("wordChars", at).join(""));
+ }
+ }
+ /**
+ A facet that, when enabled, causes the editor to allow multiple
+ ranges to be selected. Be careful though, because by default the
+ editor relies on the native DOM selection, which cannot handle
+ multiple selections. An extension like
+ [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) can be used to make
+ secondary selections visible to the user.
+ */
+ EditorState.allowMultipleSelections = allowMultipleSelections;
+ /**
+ Configures the tab size to use in this state. The first
+ (highest-precedence) value of the facet is used. If no value is
+ given, this defaults to 4.
+ */
+ EditorState.tabSize = Facet.define({
+ combine: values => values.length ? values[0] : 4
+ });
+ /**
+ The line separator to use. By default, any of `"\n"`, `"\r\n"`
+ and `"\r"` is treated as a separator when splitting lines, and
+ lines are joined with `"\n"`.
+
+ When you configure a value here, only that precise separator
+ will be used, allowing you to round-trip documents through the
+ editor without normalizing line separators.
+ */
+ EditorState.lineSeparator = lineSeparator;
+ /**
+ Registers translation phrases. The
+ [`phrase`](https://codemirror.net/6/docs/ref/#state.EditorState.phrase) method will look through
+ all objects registered with this facet to find translations for
+ its argument.
+ */
+ EditorState.phrases = Facet.define();
+ /**
+ A facet used to register [language
+ data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) providers.
+ */
+ EditorState.languageData = languageData;
+ /**
+ Facet used to register change filters, which are called for each
+ transaction (unless explicitly
+ [disabled](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter)), and can suppress
+ part of the transaction's changes.
+
+ Such a function can return `true` to indicate that it doesn't
+ want to do anything, `false` to completely stop the changes in
+ the transaction, or a set of ranges in which changes should be
+ suppressed. Such ranges are represented as an array of numbers,
+ with each pair of two number indicating the start and end of a
+ range. So for example `[10, 20, 100, 110]` suppresses changes
+ between 10 and 20, and between 100 and 110.
+ */
+ EditorState.changeFilter = changeFilter;
+ /**
+ Facet used to register a hook that gets a chance to update or
+ replace transaction specs before they are applied. This will
+ only be applied for transactions that don't have
+ [`filter`](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter) set to `false`. You
+ can either return a single (possibly the input transaction), or
+ an array of specs (which will be combined in the same way as the
+ arguments to [`EditorState.update`](https://codemirror.net/6/docs/ref/#state.EditorState.update)).
+
+ When possible, it is recommended to avoid accessing
+ [`Transaction.state`](https://codemirror.net/6/docs/ref/#state.Transaction.state) in a filter,
+ since it will force creation of a state that will then be
+ discarded again, if the transaction is actually filtered.
+
+ (This functionality should be used with care. Indiscriminately
+ modifying transaction is likely to break something or degrade
+ the user experience.)
+ */
+ EditorState.transactionFilter = transactionFilter;
+ /**
+ This is a more limited form of
+ [`transactionFilter`](https://codemirror.net/6/docs/ref/#state.EditorState^transactionFilter),
+ which can only add
+ [annotations](https://codemirror.net/6/docs/ref/#state.TransactionSpec.annotations) and
+ [effects](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects). _But_, this type
+ of filter runs even the transaction has disabled regular
+ [filtering](https://codemirror.net/6/docs/ref/#state.TransactionSpec.filter), making it suitable
+ for effects that don't need to touch the changes or selection,
+ but do want to process every transaction.
+
+ Extenders run _after_ filters, when both are applied.
+ */
+ EditorState.transactionExtender = transactionExtender;
+ Compartment.reconfigure = StateEffect.define();
+
+ /**
+ Utility function for combining behaviors to fill in a config
+ object from an array of provided configs. Will, by default, error
+ when a field gets two values that aren't `===`-equal, but you can
+ provide combine functions per field to do something else.
+ */
+ function combineConfig(configs, defaults, // Should hold only the optional properties of Config, but I haven't managed to express that
+ combine = {}) {
+ let result = {};
+ for (let config of configs)
+ for (let key of Object.keys(config)) {
+ let value = config[key], current = result[key];
+ if (current === undefined)
+ result[key] = value;
+ else if (current === value || value === undefined) ; // No conflict
+ else if (Object.hasOwnProperty.call(combine, key))
+ result[key] = combine[key](current, value);
+ else
+ throw new Error("Config merge conflict for field " + key);
+ }
+ for (let key in defaults)
+ if (result[key] === undefined)
+ result[key] = defaults[key];
+ return result;
+ }
+
+ const C = "\u037c";
+ const COUNT = typeof Symbol == "undefined" ? "__" + C : Symbol.for(C);
+ const SET = typeof Symbol == "undefined" ? "__styleSet" + Math.floor(Math.random() * 1e8) : Symbol("styleSet");
+ const top = typeof globalThis != "undefined" ? globalThis : typeof window != "undefined" ? window : {};
+
+ // :: - Style modules encapsulate a set of CSS rules defined from
+ // JavaScript. Their definitions are only available in a given DOM
+ // root after it has been _mounted_ there with `StyleModule.mount`.
+ //
+ // Style modules should be created once and stored somewhere, as
+ // opposed to re-creating them every time you need them. The amount of
+ // CSS rules generated for a given DOM root is bounded by the amount
+ // of style modules that were used. So to avoid leaking rules, don't
+ // create these dynamically, but treat them as one-time allocations.
+ class StyleModule {
+ // :: (Object