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: ` - - `, - }); - } -} -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
    \n
    \n
    \n \n
    \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 "})}}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