This commit is contained in:
杜恒 2021-03-29 11:02:01 +08:00
parent e97641ca99
commit fc470d46ea
9 changed files with 2489 additions and 2435 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
import { undo, redo } from '@codemirror/history'; import { undo, redo } from '@codemirror/history';
export default class JoeAction { export default class JoeAction {
constructor() { constructor() {
$('body').append(` $('body').append(`
<div class="cm-modal"> <div class="cm-modal">
<div class="cm-modal__wrapper"> <div class="cm-modal__wrapper">
<div class="cm-modal__wrapper-header"> <div class="cm-modal__wrapper-header">
@ -16,186 +16,186 @@ export default class JoeAction {
</div> </div>
</div> </div>
`); `);
$('.cm-modal__wrapper-footer--cancle, .cm-modal__wrapper-header--close').on('click', () => $('.cm-modal').removeClass('active')); $('.cm-modal__wrapper-footer--cancle, .cm-modal__wrapper-header--close').on('click', () => $('.cm-modal').removeClass('active'));
$('.cm-modal__wrapper-footer--confirm').on('click', () => { $('.cm-modal__wrapper-footer--confirm').on('click', () => {
this.options.confirm(); this.options.confirm();
$('.cm-modal').removeClass('active'); $('.cm-modal').removeClass('active');
}); });
} }
_openModal(options = {}) { _openModal(options = {}) {
const _options = { const _options = {
title: '提示', title: '提示',
innerHtml: '内容', innerHtml: '内容',
hasFooter: true, hasFooter: true,
confirm: () => {}, confirm: () => {},
handler: () => {} handler: () => {}
}; };
this.options = Object.assign(_options, options); this.options = Object.assign(_options, options);
$('.cm-modal__wrapper-header--text').html(this.options.title); $('.cm-modal__wrapper-header--text').html(this.options.title);
$('.cm-modal__wrapper-bodyer').html(this.options.innerHtml); $('.cm-modal__wrapper-bodyer').html(this.options.innerHtml);
this.options.hasFooter ? $('.cm-modal__wrapper-footer').show() : $('.cm-modal__wrapper-footer').hide(); this.options.hasFooter ? $('.cm-modal__wrapper-footer').show() : $('.cm-modal__wrapper-footer').hide();
$('.cm-modal').addClass('active'); $('.cm-modal').addClass('active');
this.options.handler(); this.options.handler();
} }
_getLineCh(cm) { _getLineCh(cm) {
const head = cm.state.selection.main.head; const head = cm.state.selection.main.head;
const line = cm.state.doc.lineAt(head); const line = cm.state.doc.lineAt(head);
return head - line.from; return head - line.from;
} }
_replaceSelection(cm, str) { _replaceSelection(cm, str) {
cm.dispatch(cm.state.replaceSelection(str)); cm.dispatch(cm.state.replaceSelection(str));
} }
_setCursor(cm, pos) { _setCursor(cm, pos) {
cm.dispatch({ selection: { anchor: pos } }); cm.dispatch({ selection: { anchor: pos } });
} }
_getSelection(cm) { _getSelection(cm) {
return cm.state.sliceDoc(cm.state.selection.main.from, cm.state.selection.main.to); return cm.state.sliceDoc(cm.state.selection.main.from, cm.state.selection.main.to);
} }
_insetAmboText(cm, str) { _insetAmboText(cm, str) {
const cursor = cm.state.selection.main.head; const cursor = cm.state.selection.main.head;
const selection = this._getSelection(cm); const selection = this._getSelection(cm);
this._replaceSelection(cm, ` ${str + selection + str} `); this._replaceSelection(cm, ` ${str + selection + str} `);
if (selection === '') this._setCursor(cm, cursor + str.length + 1); if (selection === '') this._setCursor(cm, cursor + str.length + 1);
cm.focus(); cm.focus();
} }
_createTableLists(cm, url, activeTab = '', modalTitle) { _createTableLists(cm, url, activeTab = '', modalTitle) {
$.ajax({ $.ajax({
url, url,
dataType: 'json', dataType: 'json',
success: res => { success: res => {
let tabbarStr = ''; let tabbarStr = '';
let listsStr = ''; let listsStr = '';
for (let key in res) { for (let key in res) {
const arr = res[key].split(' '); const arr = res[key].split(' ');
tabbarStr += `<div class="tabbar-item ${key === activeTab ? 'active' : ''}" data-show="${key}">${key}</div>`; tabbarStr += `<div class="tabbar-item ${key === activeTab ? 'active' : ''}" data-show="${key}">${key}</div>`;
listsStr += `<div class="lists ${key === activeTab ? 'active' : ''}" data-show="${key}">${arr.map(item => `<div class="lists-item" data-text="${item}">${item}</div>`).join(' ')}</div>`; listsStr += `<div class="lists ${key === activeTab ? 'active' : ''}" data-show="${key}">${arr.map(item => `<div class="lists-item" data-text="${item}">${item}</div>`).join(' ')}</div>`;
} }
this._openModal({ this._openModal({
title: modalTitle, title: modalTitle,
hasFooter: false, hasFooter: false,
innerHtml: `<div class="tabbar">${tabbarStr}</div>${listsStr}`, innerHtml: `<div class="tabbar">${tabbarStr}</div>${listsStr}`,
handler: () => { handler: () => {
$('.cm-modal__wrapper-bodyer .tabbar-item').on('click', function () { $('.cm-modal__wrapper-bodyer .tabbar-item').on('click', function () {
const activeTab = $(this); const activeTab = $(this);
const show = activeTab.attr('data-show'); const show = activeTab.attr('data-show');
const tabbar = $('.cm-modal__wrapper-bodyer .tabbar'); const tabbar = $('.cm-modal__wrapper-bodyer .tabbar');
activeTab.addClass('active').siblings().removeClass('active'); activeTab.addClass('active').siblings().removeClass('active');
tabbar.stop().animate({ tabbar.stop().animate({
scrollLeft: activeTab[0].offsetLeft - tabbar[0].offsetWidth / 2 + activeTab[0].offsetWidth / 2 - 15 scrollLeft: activeTab[0].offsetLeft - tabbar[0].offsetWidth / 2 + activeTab[0].offsetWidth / 2 - 15
}); });
$('.cm-modal__wrapper-bodyer .lists').removeClass('active'); $('.cm-modal__wrapper-bodyer .lists').removeClass('active');
$(".cm-modal__wrapper-bodyer .lists[data-show='" + show + "']").addClass('active'); $(".cm-modal__wrapper-bodyer .lists[data-show='" + show + "']").addClass('active');
}); });
const _this = this; const _this = this;
$('.cm-modal__wrapper-bodyer .lists-item').on('click', function () { $('.cm-modal__wrapper-bodyer .lists-item').on('click', function () {
const text = $(this).attr('data-text'); const text = $(this).attr('data-text');
_this._replaceSelection(cm, ` ${text} `); _this._replaceSelection(cm, ` ${text} `);
$('.cm-modal').removeClass('active'); $('.cm-modal').removeClass('active');
cm.focus(); cm.focus();
}); });
} }
}); });
} }
}); });
} }
handleFullScreen(el) { handleFullScreen(el) {
el.toggleClass('active'); el.toggleClass('active');
$('body').toggleClass('fullscreen'); $('body').toggleClass('fullscreen');
$('.cm-container').toggleClass('fullscreen'); $('.cm-container').toggleClass('fullscreen');
$('.cm-preview').width(0); $('.cm-preview').width(0);
} }
handlePublish() { handlePublish() {
$('#btn-submit').click(); $('#btn-submit').click();
} }
handleUndo(cm) { handleUndo(cm) {
undo(cm); undo(cm);
cm.focus(); cm.focus();
} }
handleRedo(cm) { handleRedo(cm) {
redo(cm); redo(cm);
cm.focus(); cm.focus();
} }
handleIndent(cm) { handleIndent(cm) {
this._replaceSelection(cm, ' '); this._replaceSelection(cm, ' ');
cm.focus(); cm.focus();
} }
handleTime(cm) { handleTime(cm) {
const time = new Date(); const time = new Date();
const _Year = time.getFullYear(); const _Year = time.getFullYear();
const _Month = String(time.getMonth() + 1).padStart(2, 0); const _Month = String(time.getMonth() + 1).padStart(2, 0);
const _Date = String(time.getDate()).padStart(2, 0); const _Date = String(time.getDate()).padStart(2, 0);
const _Hours = String(time.getHours()).padStart(2, 0); const _Hours = String(time.getHours()).padStart(2, 0);
const _Minutes = String(time.getMinutes()).padStart(2, 0); const _Minutes = String(time.getMinutes()).padStart(2, 0);
const _Seconds = String(time.getSeconds()).padStart(2, 0); const _Seconds = String(time.getSeconds()).padStart(2, 0);
const _Day = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][time.getDay()]; const _Day = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][time.getDay()];
const _time = `${this._getLineCh(cm) ? '\n' : ''}${_Year}-${_Month}-${_Date} ${_Hours}:${_Minutes}:${_Seconds} ${_Day}\n`; const _time = `${this._getLineCh(cm) ? '\n' : ''}${_Year}-${_Month}-${_Date} ${_Hours}:${_Minutes}:${_Seconds} ${_Day}\n`;
this._replaceSelection(cm, _time); this._replaceSelection(cm, _time);
cm.focus(); cm.focus();
} }
handleHr(cm) { handleHr(cm) {
const str = `${this._getLineCh(cm) ? '\n' : ''}\n------------\n\n`; const str = `${this._getLineCh(cm) ? '\n' : ''}\n------------\n\n`;
this._replaceSelection(cm, str); this._replaceSelection(cm, str);
cm.focus(); cm.focus();
} }
handleClean(cm) { handleClean(cm) {
cm.dispatch({ changes: { from: 0, to: cm.state.doc.length, insert: '' } }); cm.dispatch({ changes: { from: 0, to: cm.state.doc.length, insert: '' } });
cm.focus(); cm.focus();
} }
handleOrdered(cm) { handleOrdered(cm) {
const selection = this._getSelection(cm); const selection = this._getSelection(cm);
if (selection === '') { if (selection === '') {
const str = (this._getLineCh(cm) ? '\n\n' : '') + '1. '; const str = (this._getLineCh(cm) ? '\n\n' : '') + '1. ';
this._replaceSelection(cm, str); this._replaceSelection(cm, str);
} else { } else {
const selectionText = selection.split('\n'); const selectionText = selection.split('\n');
for (let i = 0, len = selectionText.length; i < len; i++) { for (let i = 0, len = selectionText.length; i < len; i++) {
selectionText[i] = selectionText[i] === '' ? '' : i + 1 + '. ' + selectionText[i]; selectionText[i] = selectionText[i] === '' ? '' : i + 1 + '. ' + selectionText[i];
} }
const str = (this._getLineCh(cm) ? '\n' : '') + selectionText.join('\n'); const str = (this._getLineCh(cm) ? '\n' : '') + selectionText.join('\n');
this._replaceSelection(cm, str); this._replaceSelection(cm, str);
} }
cm.focus(); cm.focus();
} }
handleUnordered(cm) { handleUnordered(cm) {
const selection = this._getSelection(cm); const selection = this._getSelection(cm);
if (selection === '') { if (selection === '') {
const str = (this._getLineCh(cm) ? '\n' : '') + '- '; const str = (this._getLineCh(cm) ? '\n' : '') + '- ';
this._replaceSelection(cm, str); this._replaceSelection(cm, str);
} else { } else {
const selectionText = selection.split('\n'); const selectionText = selection.split('\n');
for (let i = 0, len = selectionText.length; i < len; i++) { for (let i = 0, len = selectionText.length; i < len; i++) {
selectionText[i] = selectionText[i] === '' ? '' : '- ' + selectionText[i]; selectionText[i] = selectionText[i] === '' ? '' : '- ' + selectionText[i];
} }
const str = (this._getLineCh(cm) ? '\n' : '') + selectionText.join('\n'); const str = (this._getLineCh(cm) ? '\n' : '') + selectionText.join('\n');
this._replaceSelection(cm, str); this._replaceSelection(cm, str);
} }
cm.focus(); cm.focus();
} }
handleQuote(cm) { handleQuote(cm) {
const selection = this._getSelection(cm); const selection = this._getSelection(cm);
if (selection === '') { if (selection === '') {
this._replaceSelection(cm, `${this._getLineCh(cm) ? '\n' : ''}> `); this._replaceSelection(cm, `${this._getLineCh(cm) ? '\n' : ''}> `);
} else { } else {
const selectionText = selection.split('\n'); const selectionText = selection.split('\n');
for (let i = 0, len = selectionText.length; i < len; i++) { for (let i = 0, len = selectionText.length; i < len; i++) {
selectionText[i] = selectionText[i] === '' ? '' : '> ' + selectionText[i]; selectionText[i] = selectionText[i] === '' ? '' : '> ' + selectionText[i];
} }
const str = (this._getLineCh(cm) ? '\n' : '') + selectionText.join('\n'); const str = (this._getLineCh(cm) ? '\n' : '') + selectionText.join('\n');
this._replaceSelection(cm, str); this._replaceSelection(cm, str);
} }
cm.focus(); cm.focus();
} }
handleDownload(cm) { handleDownload(cm) {
const title = $('#title').val() || '新文章'; const title = $('#title').val() || '新文章';
const aTag = document.createElement('a'); const aTag = document.createElement('a');
let blob = new Blob([cm.state.doc.toString()]); let blob = new Blob([cm.state.doc.toString()]);
aTag.download = title + '.md'; aTag.download = title + '.md';
aTag.href = URL.createObjectURL(blob); aTag.href = URL.createObjectURL(blob);
aTag.click(); aTag.click();
URL.revokeObjectURL(blob); URL.revokeObjectURL(blob);
} }
handleTitle(cm, tool) { handleTitle(cm, tool) {
const item = $(` const item = $(`
<div class="cm-tools-item" title="${tool.title}"> <div class="cm-tools-item" title="${tool.title}">
${tool.innerHTML} ${tool.innerHTML}
<div class="cm-tools__dropdown"> <div class="cm-tools__dropdown">
@ -208,26 +208,26 @@ export default class JoeAction {
</div> </div>
</div> </div>
`); `);
item.on('click', function (e) { item.on('click', function (e) {
e.stopPropagation(); e.stopPropagation();
$(this).toggleClass('active'); $(this).toggleClass('active');
}); });
const _this = this; const _this = this;
item.on('click', '.cm-tools__dropdown-item', function (e) { item.on('click', '.cm-tools__dropdown-item', function (e) {
e.stopPropagation(); e.stopPropagation();
const text = $(this).attr('data-text'); const text = $(this).attr('data-text');
if (_this._getLineCh(cm)) _this._replaceSelection(cm, '\n\n' + text); if (_this._getLineCh(cm)) _this._replaceSelection(cm, '\n\n' + text);
else _this._replaceSelection(cm, text); else _this._replaceSelection(cm, text);
item.removeClass('active'); item.removeClass('active');
cm.focus(); cm.focus();
}); });
$(document).on('click', () => item.removeClass('active')); $(document).on('click', () => item.removeClass('active'));
$('.cm-tools').append(item); $('.cm-tools').append(item);
} }
handleLink(cm) { handleLink(cm) {
this._openModal({ this._openModal({
title: '插入链接', title: '插入链接',
innerHtml: ` innerHtml: `
<div class="fitem"> <div class="fitem">
<label>链接标题</label> <label>链接标题</label>
<input autocomplete="off" name="title" placeholder="请输入链接标题"/> <input autocomplete="off" name="title" placeholder="请输入链接标题"/>
@ -237,18 +237,18 @@ export default class JoeAction {
<input autocomplete="off" name="url" placeholder="请输入链接地址"/> <input autocomplete="off" name="url" placeholder="请输入链接地址"/>
</div> </div>
`, `,
confirm: () => { confirm: () => {
const title = $(".cm-modal input[name='title']").val() || 'Test'; const title = $(".cm-modal input[name='title']").val() || 'Test';
const url = $(".cm-modal input[name='url']").val() || 'http://'; const url = $(".cm-modal input[name='url']").val() || 'http://';
this._replaceSelection(cm, ` [${title}](${url}) `); this._replaceSelection(cm, ` [${title}](${url}) `);
cm.focus(); cm.focus();
} }
}); });
} }
handleImage(cm) { handleImage(cm) {
this._openModal({ this._openModal({
title: '插入图片', title: '插入图片',
innerHtml: ` innerHtml: `
<div class="fitem"> <div class="fitem">
<label>图片名称</label> <label>图片名称</label>
<input autocomplete="off" name="title" placeholder="请输入图片名称"/> <input autocomplete="off" name="title" placeholder="请输入图片名称"/>
@ -258,18 +258,18 @@ export default class JoeAction {
<input autocomplete="off" name="url" placeholder="请输入图片地址"/> <input autocomplete="off" name="url" placeholder="请输入图片地址"/>
</div> </div>
`, `,
confirm: () => { confirm: () => {
const title = $(".cm-modal input[name='title']").val() || 'Test'; const title = $(".cm-modal input[name='title']").val() || 'Test';
const url = $(".cm-modal input[name='url']").val() || 'http://'; const url = $(".cm-modal input[name='url']").val() || 'http://';
this._replaceSelection(cm, ` ![${title}](${url}) `); this._replaceSelection(cm, ` ![${title}](${url}) `);
cm.focus(); cm.focus();
} }
}); });
} }
handleTable(cm) { handleTable(cm) {
this._openModal({ this._openModal({
title: '插入表格', title: '插入表格',
innerHtml: ` innerHtml: `
<div class="fitem"> <div class="fitem">
<label>表格行</label> <label>表格行</label>
<input style="width: 50px; flex: none; margin-right: 10px;" value="3" autocomplete="off" name="row"/> <input style="width: 50px; flex: none; margin-right: 10px;" value="3" autocomplete="off" name="row"/>
@ -277,33 +277,33 @@ export default class JoeAction {
<input style="width: 50px; flex: none;" value="3" autocomplete="off" name="column"/> <input style="width: 50px; flex: none;" value="3" autocomplete="off" name="column"/>
</div> </div>
`, `,
confirm: () => { confirm: () => {
let row = $(".cm-modal input[name='row']").val(); let row = $(".cm-modal input[name='row']").val();
let column = $(".cm-modal input[name='column']").val(); let column = $(".cm-modal input[name='column']").val();
if (isNaN(row)) row = 3; if (isNaN(row)) row = 3;
if (isNaN(column)) column = 3; if (isNaN(column)) column = 3;
let rowStr = ''; let rowStr = '';
let rangeStr = ''; let rangeStr = '';
let columnlStr = ''; let columnlStr = '';
for (let i = 0; i < column; i++) { for (let i = 0; i < column; i++) {
rowStr += '| 表头 '; rowStr += '| 表头 ';
rangeStr += '| :--: '; rangeStr += '| :--: ';
} }
for (let i = 0; i < row; i++) { for (let i = 0; i < row; i++) {
for (let j = 0; j < column; j++) columnlStr += '| 表格 '; for (let j = 0; j < column; j++) columnlStr += '| 表格 ';
columnlStr += '|\n'; columnlStr += '|\n';
} }
const htmlStr = `${rowStr}|\n${rangeStr}|\n${columnlStr}\n`; const htmlStr = `${rowStr}|\n${rangeStr}|\n${columnlStr}\n`;
if (this._getLineCh(cm)) this._replaceSelection(cm, '\n\n' + htmlStr); if (this._getLineCh(cm)) this._replaceSelection(cm, '\n\n' + htmlStr);
else this._replaceSelection(cm, htmlStr); else this._replaceSelection(cm, htmlStr);
cm.focus(); cm.focus();
} }
}); });
} }
handleCodeBlock(cm) { handleCodeBlock(cm) {
this._openModal({ this._openModal({
title: '插入代码块', title: '插入代码块',
innerHtml: ` innerHtml: `
<div class="fitem"> <div class="fitem">
<label>语言类型</label> <label>语言类型</label>
<select name="type"> <select name="type">
@ -347,25 +347,30 @@ export default class JoeAction {
</select> </select>
</div> </div>
`, `,
confirm: () => { confirm: () => {
const type = $(".cm-modal select[name='type']").val() || 'html'; const type = $(".cm-modal select[name='type']").val() || 'html';
const htmlStr = `\`\`\`${type}\ncode here...\n\`\`\``; const htmlStr = `\`\`\`${type}\ncode here...\n\`\`\``;
if (this._getLineCh(cm)) this._replaceSelection(cm, '\n\n' + htmlStr); if (this._getLineCh(cm)) this._replaceSelection(cm, '\n\n' + htmlStr);
else this._replaceSelection(cm, htmlStr); else this._replaceSelection(cm, htmlStr);
cm.focus(); cm.focus();
} }
}); });
} }
handleAbout() { handleAbout() {
this._openModal({ this._openModal({
title: '关于', title: '关于',
innerHtml: ` innerHtml: `
<ul> <ul>
<li>短代码功能正在开发中...</li> <li>短代码功能正在开发中...</li>
<li>仅支持网络图片粘贴上传截图等</li> <li>仅支持网络图片粘贴上传截图等</li>
<li>本编辑器仅供Joe主题使用未经允许不得移植至其他主题</li> <li>本编辑器仅供Joe主题使用未经允许不得移植至其他主题</li>
</ul> </ul>
` `
}); });
} }
handleTask(cm, type) {
const str = type ? '{x}' : '{ }';
this._replaceSelection(cm, ` ${str} `);
cm.focus();
}
} }

View File

@ -0,0 +1,10 @@
const parser = new HyperDown();
export default function createPreviewHtml(str) {
str = parser.makeHtml(str);
str = str.replace(/{x}/g, '<input type="checkbox" class="task" checked disabled></input>')
str = str.replace(/{ }/g, '<input type="checkbox" class="task" disabled></input>')
$('.cm-preview-content').html(str);
$('.cm-preview-content pre code').each((i, el) => Prism.highlightElement(el));
}

View File

@ -84,6 +84,16 @@ export default [
title: '符号表情', title: '符号表情',
innerHTML: '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="19" height="19"><path d="M512 56.889A455.111 455.111 0 0 0 56.889 512 455.111 455.111 0 0 0 512 967.111 455.111 455.111 0 0 0 967.111 512 455.111 455.111 0 0 0 512 56.889zm111.047 270.336A69.086 69.086 0 0 1 671.29 307.2c17.863 0 35.67 7.396 48.242 20.025 12.629 12.572 20.025 30.379 20.025 48.242 0 17.863-7.396 35.669-20.025 48.241-12.8 12.744-30.151 19.912-48.242 20.025a68.95 68.95 0 0 1-48.242-20.025 68.95 68.95 0 0 1-20.025-48.241c0-17.863 7.396-35.67 20.025-48.242zm-318.578 0a69.086 69.086 0 0 1 48.242-20.025c17.863 0 35.67 7.396 48.242 20.025 12.63 12.572 20.025 30.379 20.025 48.242 0 17.863-7.396 35.669-20.025 48.241-12.8 12.744-30.151 19.912-48.242 20.025a68.95 68.95 0 0 1-48.242-20.025 68.95 68.95 0 0 1-20.025-48.241c0-17.863 7.396-35.67 20.025-48.242zM786.375 566.67c-10.24 132.893-118.556 236.544-270.563 235.975-156.331 1.707-264.704-107.178-270.507-235.975a23.324 23.324 0 0 1-2.446-10.41c0-13.597 11.605-24.633 26.282-24.52h493.796c14.336 0 26.055 11.037 26.055 24.52a24.292 24.292 0 0 1-2.617 10.41z"/></svg>' innerHTML: '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="19" height="19"><path d="M512 56.889A455.111 455.111 0 0 0 56.889 512 455.111 455.111 0 0 0 512 967.111 455.111 455.111 0 0 0 967.111 512 455.111 455.111 0 0 0 512 56.889zm111.047 270.336A69.086 69.086 0 0 1 671.29 307.2c17.863 0 35.67 7.396 48.242 20.025 12.629 12.572 20.025 30.379 20.025 48.242 0 17.863-7.396 35.669-20.025 48.241-12.8 12.744-30.151 19.912-48.242 20.025a68.95 68.95 0 0 1-48.242-20.025 68.95 68.95 0 0 1-20.025-48.241c0-17.863 7.396-35.67 20.025-48.242zm-318.578 0a69.086 69.086 0 0 1 48.242-20.025c17.863 0 35.67 7.396 48.242 20.025 12.63 12.572 20.025 30.379 20.025 48.242 0 17.863-7.396 35.669-20.025 48.241-12.8 12.744-30.151 19.912-48.242 20.025a68.95 68.95 0 0 1-48.242-20.025 68.95 68.95 0 0 1-20.025-48.241c0-17.863 7.396-35.67 20.025-48.242zM786.375 566.67c-10.24 132.893-118.556 236.544-270.563 235.975-156.331 1.707-264.704-107.178-270.507-235.975a23.324 23.324 0 0 1-2.446-10.41c0-13.597 11.605-24.633 26.282-24.52h493.796c14.336 0 26.055 11.037 26.055 24.52a24.292 24.292 0 0 1-2.617 10.41z"/></svg>'
}, },
{
type: 'task-no',
title: '任务 - 未完成',
innerHTML: '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M831.55 128.531c38.35 0 63.911 25.568 63.911 63.91v639.104c0 38.355-25.561 63.916-63.91 63.916H192.44c-38.34 0-63.908-25.56-63.908-63.916V192.442c0-38.343 25.567-63.91 63.908-63.91h639.11m0-63.91H192.44c-70.3 0-127.816 57.518-127.816 127.82v639.103c0 70.308 57.515 127.833 127.816 127.833h639.11c70.294 0 127.822-57.525 127.822-127.833V192.442c0-70.302-57.527-127.82-127.823-127.82zm0 0"/></svg>'
},
{
type: 'task-yes',
title: '任务 - 已完成',
innerHTML: '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M831.551 64.623h-639.11c-70.3 0-127.816 57.517-127.816 127.819v639.103c0 70.308 57.515 127.833 127.816 127.833h639.11c70.294 0 127.822-57.525 127.822-127.833V192.442c0-70.302-57.527-127.82-127.822-127.82zM646.217 486.44c-108.652 159.779-204.52 345.115-204.52 345.115L192.443 550.351l63.916-70.303 153.385 146.994s76.695-127.822 178.95-236.469c102.261-108.652 223.689-198.127 223.689-198.127l19.17 63.916c0-.001-102.255 108.646-185.337 230.078z"/></svg>'
},
{ {
type: 'code-block', type: 'code-block',
title: '代码块', title: '代码块',

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -7,36 +7,29 @@ import { history, historyKeymap } from '@codemirror/history';
import { classHighlightStyle } from '@codemirror/highlight'; import { classHighlightStyle } from '@codemirror/highlight';
import tools from './_tools'; import tools from './_tools';
import JoeAction from './_actions'; import JoeAction from './_actions';
import createPreviewHtml from './_create';
class Joe extends JoeAction { class Joe extends JoeAction {
constructor() { constructor() {
super(); super();
this.plugins = [history(), classHighlightStyle, bracketMatching(), closeBrackets()]; this.plugins = [history(), classHighlightStyle, bracketMatching(), closeBrackets()];
this.parser = new HyperDown(); this._isPasting = false;
this._isPasting = false; this.init_ViewPort();
this.init_Editor();
this.init_Preview();
this.init_Tools();
this.init_Insert();
}
this.init_ViewPort(); /* 已测 √ */
this.init_Editor(); init_ViewPort() {
this.init_Preview(); if ($('meta[name="viewport"]').length > 0) $('meta[name="viewport"]').attr('content', 'width=device-width, user-scalable=no, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover');
this.init_Tools(); else $('head').append('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover">');
this.init_Insert(); }
}
_createPreviewHtml(str) { /* 已测 √ */
str = this.parser.makeHtml(str); init_Editor() {
$('.cm-preview-content').html(str); $('#text').before(`
$('.cm-preview-content pre code').each((i, el) => Prism.highlightElement(el));
}
/* 已测 √ */
init_ViewPort() {
if ($('meta[name="viewport"]').length > 0) $('meta[name="viewport"]').attr('content', 'width=device-width, user-scalable=no, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover');
else $('head').append('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover">');
}
/* 已测 √ */
init_Editor() {
$('#text').before(`
<div class="cm-container"> <div class="cm-container">
<div class="cm-tools"></div> <div class="cm-tools"></div>
<div class="cm-mainer"> <div class="cm-mainer">
@ -47,225 +40,231 @@ class Joe extends JoeAction {
<div class="cm-progress-right"></div> <div class="cm-progress-right"></div>
</div> </div>
`); `);
this._createPreviewHtml($('#text').val()); createPreviewHtml($('#text').val());
const cm = new EditorView({ const cm = new EditorView({
state: EditorState.create({ state: EditorState.create({
doc: $('#text').val(), doc: $('#text').val(),
extensions: [ extensions: [
...this.plugins, ...this.plugins,
keymap.of([defaultTabBinding, ...defaultKeymap, ...historyKeymap, ...closeBracketsKeymap]), keymap.of([defaultTabBinding, ...defaultKeymap, ...historyKeymap, ...closeBracketsKeymap]),
EditorView.updateListener.of(update => { EditorView.updateListener.of(update => {
if (!update.docChanged) return; if (!update.docChanged) return;
this._createPreviewHtml(update.state.doc.toString()); createPreviewHtml(update.state.doc.toString());
}), }),
EditorView.domEventHandlers({ EditorView.domEventHandlers({
paste: e => { paste: e => {
const clipboardData = e.clipboardData; const clipboardData = e.clipboardData;
if (!clipboardData || !clipboardData.items) return; if (!clipboardData || !clipboardData.items) return;
const items = clipboardData.items; const items = clipboardData.items;
if (!items.length) return; if (!items.length) return;
let blob = null; let blob = null;
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) { if (items[i].type.indexOf('image') !== -1) {
e.preventDefault(); e.preventDefault();
blob = items[i].getAsFile(); blob = items[i].getAsFile();
break; break;
} }
} }
if (!blob) return; if (!blob) return;
let api = window.JoeConfig.uploadAPI; let api = window.JoeConfig.uploadAPI;
if (!api) return; if (!api) return;
const cid = $('input[name="cid"]').val(); const cid = $('input[name="cid"]').val();
cid && (api = api + '&cid=' + cid); cid && (api = api + '&cid=' + cid);
if (this._isPasting) return; if (this._isPasting) return;
this._isPasting = true; this._isPasting = true;
const fileName = Date.now().toString(36) + '.png'; const fileName = Date.now().toString(36) + '.png';
let formData = new FormData(); let formData = new FormData();
formData.append('name', fileName); formData.append('name', fileName);
formData.append('file', blob, fileName); formData.append('file', blob, fileName);
$.ajax({ $.ajax({
url: api, url: api,
method: 'post', method: 'post',
data: formData, data: formData,
contentType: false, contentType: false,
processData: false, processData: false,
dataType: 'json', dataType: 'json',
xhr: () => { xhr: () => {
const xhr = $.ajaxSettings.xhr(); const xhr = $.ajaxSettings.xhr();
if (!xhr.upload) return; if (!xhr.upload) return;
xhr.upload.addEventListener( xhr.upload.addEventListener(
'progress', 'progress',
e => { e => {
let percent = (e.loaded / e.total) * 100; let percent = (e.loaded / e.total) * 100;
$('.cm-progress-left').width(percent / 2 + '%'); $('.cm-progress-left').width(percent / 2 + '%');
$('.cm-progress-right').width(percent / 2 + '%'); $('.cm-progress-right').width(percent / 2 + '%');
}, },
false false
); );
return xhr; return xhr;
}, },
success: res => { success: res => {
$('.cm-progress-left').width(0); $('.cm-progress-left').width(0);
$('.cm-progress-right').width(0); $('.cm-progress-right').width(0);
this._isPasting = false; this._isPasting = false;
const str = `${super._getLineCh(cm) ? '\n' : ''}![${res[1].title}](${res[0]})\n`; const str = `${super._getLineCh(cm) ? '\n' : ''}![${res[1].title}](${res[0]})\n`;
super._replaceSelection(cm, str); super._replaceSelection(cm, str);
cm.focus(); cm.focus();
}, },
error: () => { error: () => {
$('.cm-progress-left').width(0); $('.cm-progress-left').width(0);
$('.cm-progress-right').width(0); $('.cm-progress-right').width(0);
this._isPasting = false; this._isPasting = false;
} }
}); });
} }
}) })
], ],
tabSize: 4 tabSize: 4
}) })
}); });
$('.cm-mainer').prepend(cm.dom); $('.cm-mainer').prepend(cm.dom);
$('#text')[0].form && $('#text')[0].form.addEventListener('submit', () => $('#text').val(cm.state.doc.toString())); $('#text')[0].form && $('#text')[0].form.addEventListener('submit', () => $('#text').val(cm.state.doc.toString()));
this.cm = cm; this.cm = cm;
} }
/* 已测 √ */ /* 已测 √ */
init_Preview() { init_Preview() {
const move = (nowClientX, nowWidth, clientX) => { const move = (nowClientX, nowWidth, clientX) => {
let moveX = nowClientX - clientX; let moveX = nowClientX - clientX;
let moveWidth = nowWidth + moveX; let moveWidth = nowWidth + moveX;
if (moveWidth <= 0) moveWidth = 0; if (moveWidth <= 0) moveWidth = 0;
if (moveWidth >= $('.cm-mainer').outerWidth() - 16) moveWidth = $('.cm-mainer').outerWidth() - 16; if (moveWidth >= $('.cm-mainer').outerWidth() - 16) moveWidth = $('.cm-mainer').outerWidth() - 16;
$('.cm-preview').width(moveWidth); $('.cm-preview').width(moveWidth);
}; };
$('.cm-resize').on({ $('.cm-resize').on({
mousedown: e => { mousedown: e => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const nowWidth = $('.cm-preview').outerWidth(); const nowWidth = $('.cm-preview').outerWidth();
const nowClientX = e.clientX; const nowClientX = e.clientX;
document.onmousemove = _e => { document.onmousemove = _e => {
if (window.requestAnimationFrame) requestAnimationFrame(() => move(nowClientX, nowWidth, _e.clientX)); if (window.requestAnimationFrame) requestAnimationFrame(() => move(nowClientX, nowWidth, _e.clientX));
else move(nowClientX, nowWidth, _e.clientX); else move(nowClientX, nowWidth, _e.clientX);
}; };
document.onmouseup = () => { document.onmouseup = () => {
document.onmousemove = null; document.onmousemove = null;
document.onmouseup = null; document.onmouseup = null;
}; };
return false; return false;
}, },
touchstart: e => { touchstart: e => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const nowWidth = $('.cm-preview').outerWidth(); const nowWidth = $('.cm-preview').outerWidth();
const nowClientX = e.originalEvent.targetTouches[0].clientX; const nowClientX = e.originalEvent.targetTouches[0].clientX;
document.ontouchmove = _e => { document.ontouchmove = _e => {
if (window.requestAnimationFrame) requestAnimationFrame(() => move(nowClientX, nowWidth, _e.targetTouches[0].clientX)); if (window.requestAnimationFrame) requestAnimationFrame(() => move(nowClientX, nowWidth, _e.targetTouches[0].clientX));
else move(nowClientX, nowWidth, _e.targetTouches[0].clientX); else move(nowClientX, nowWidth, _e.targetTouches[0].clientX);
}; };
document.ontouchend = () => { document.ontouchend = () => {
document.ontouchmove = null; document.ontouchmove = null;
document.ontouchend = null; document.ontouchend = null;
}; };
return false; return false;
} }
}); });
} }
/* 已测 √ */ /* 已测 √ */
init_Tools() { init_Tools() {
tools.forEach(item => { tools.forEach(item => {
if (item.type === 'title') { if (item.type === 'title') {
super.handleTitle(this.cm, item); super.handleTitle(this.cm, item);
} else { } else {
const el = $(`<div class="cm-tools-item" title="${item.title}">${item.innerHTML}</div>`); const el = $(`<div class="cm-tools-item" title="${item.title}">${item.innerHTML}</div>`);
el.on('click', e => { el.on('click', e => {
e.preventDefault(); e.preventDefault();
switch (item.type) { switch (item.type) {
case 'fullScreen': case 'fullScreen':
super.handleFullScreen(el); super.handleFullScreen(el);
break; break;
case 'publish': case 'publish':
super.handlePublish(); super.handlePublish();
break; break;
case 'undo': case 'undo':
super.handleUndo(this.cm); super.handleUndo(this.cm);
break; break;
case 'redo': case 'redo':
super.handleRedo(this.cm); super.handleRedo(this.cm);
break; break;
case 'time': case 'time':
super.handleTime(this.cm); super.handleTime(this.cm);
break; break;
case 'bold': case 'bold':
super._insetAmboText(this.cm, '**'); super._insetAmboText(this.cm, '**');
break; break;
case 'italic': case 'italic':
super._insetAmboText(this.cm, '*'); super._insetAmboText(this.cm, '*');
break; break;
case 'delete': case 'delete':
super._insetAmboText(this.cm, '~~'); super._insetAmboText(this.cm, '~~');
break; break;
case 'code-inline': case 'code-inline':
super._insetAmboText(this.cm, '`'); super._insetAmboText(this.cm, '`');
break; break;
case 'indent': case 'indent':
super.handleIndent(this.cm); super.handleIndent(this.cm);
break; break;
case 'hr': case 'hr':
super.handleHr(this.cm); super.handleHr(this.cm);
break; break;
case 'clean': case 'clean':
super.handleClean(this.cm); super.handleClean(this.cm);
break; break;
case 'ordered-list': case 'ordered-list':
super.handleOrdered(this.cm); super.handleOrdered(this.cm);
break; break;
case 'unordered-list': case 'unordered-list':
super.handleUnordered(this.cm); super.handleUnordered(this.cm);
break; break;
case 'quote': case 'quote':
super.handleQuote(this.cm); super.handleQuote(this.cm);
break; break;
case 'download': case 'download':
super.handleDownload(this.cm); super.handleDownload(this.cm);
break; break;
case 'link': case 'link':
super.handleLink(this.cm); super.handleLink(this.cm);
break; break;
case 'image': case 'image':
super.handleImage(this.cm); super.handleImage(this.cm);
break; break;
case 'table': case 'table':
super.handleTable(this.cm); super.handleTable(this.cm);
break; break;
case 'code-block': case 'code-block':
super.handleCodeBlock(this.cm); super.handleCodeBlock(this.cm);
break; break;
case 'about': case 'about':
super.handleAbout(); super.handleAbout();
break; break;
case 'character': case 'character':
super._createTableLists(this.cm, JoeConfig.characterAPI, '星星符号', '字符大全'); super._createTableLists(this.cm, JoeConfig.characterAPI, '星星符号', '字符大全');
break; break;
case 'emoji': case 'emoji':
super._createTableLists(this.cm, JoeConfig.emojiAPI, '表情', '符号表情(需数据库支持)'); super._createTableLists(this.cm, JoeConfig.emojiAPI, '表情', '符号表情(需数据库支持)');
break; break;
} case 'task-no':
}); super.handleTask(this.cm, false);
$('.cm-tools').append(el); break;
} case 'task-yes':
}); super.handleTask(this.cm, true);
} break;
}
});
$('.cm-tools').append(el);
}
});
}
/* 已测 √ */ /* 已测 √ */
init_Insert() { init_Insert() {
Typecho.insertFileToEditor = (file, url, isImage) => { Typecho.insertFileToEditor = (file, url, isImage) => {
const str = `${super._getLineCh(this.cm) ? '\n' : ''}${isImage ? '!' : ''}[${file}](${url})\n`; const str = `${super._getLineCh(this.cm) ? '\n' : ''}${isImage ? '!' : ''}[${file}](${url})\n`;
super._replaceSelection(this.cm, str); super._replaceSelection(this.cm, str);
this.cm.focus(); this.cm.focus();
}; };
} }
} }
document.addEventListener('DOMContentLoaded', () => new Joe()); document.addEventListener('DOMContentLoaded', () => new Joe());

View File

@ -1,10 +1,10 @@
import { nodeResolve } from '@rollup/plugin-node-resolve'; import { nodeResolve } from '@rollup/plugin-node-resolve';
import { uglify } from 'rollup-plugin-uglify'; import { uglify } from 'rollup-plugin-uglify';
export default { export default {
input: './js/joe.write.js', input: './js/joe.write.js',
output: { output: {
file: './js/joe.write.chunk.js', file: './js/joe.write.chunk.js',
format: 'iife' format: 'iife'
}, },
plugins: [nodeResolve(), uglify()] plugins: [nodeResolve(), uglify()]
}; };