mirror of
https://github.com/flynx/pWiki.git
synced 2025-10-29 10:00:08 +00:00
refactoring...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
34105a355c
commit
9a1d851112
@ -465,6 +465,11 @@ editor .outline .block:focus {
|
|||||||
-1 * var(--checkbox-size)
|
-1 * var(--checkbox-size)
|
||||||
- var(--checkbox-margin));
|
- var(--checkbox-margin));
|
||||||
}
|
}
|
||||||
|
/* status... */
|
||||||
|
.editor .outline .block>.view .completion[completion]:before {
|
||||||
|
content: "(" attr(completion) ")";
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------- Code ---*/
|
/*---------------------------------------------------------- Code ---*/
|
||||||
|
|||||||
@ -27,37 +27,106 @@ var atLine = function(elem, index){
|
|||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
var codeBlock = {
|
var plugin = {
|
||||||
// can be used in:
|
// XXX make this more generic...
|
||||||
// <string>.replace(codeBlock.pattern, codeBlock.handler)
|
style: function(editor, elem, style, code=undefined){
|
||||||
// or:
|
style = [style].flat()
|
||||||
// codeBlock
|
editor.__styles = [...new Set([
|
||||||
pattern: /(?<!\\)```(.*\s*\n)((\n|.)*?)\h*(?<!\\)```/g,
|
...(editor.__styles ?? []),
|
||||||
handler: function(_, language, code){
|
...style,
|
||||||
var quote = this?.quote
|
])]
|
||||||
|| codeBlock.quote
|
return function(_, text){
|
||||||
language = language.trim()
|
elem.style ??= []
|
||||||
language = language ?
|
elem.style.push(...style)
|
||||||
'language-'+language
|
return code
|
||||||
: language
|
?? text } },
|
||||||
return `<pre>`
|
}
|
||||||
+`<code contenteditable="true" class="${language}">${
|
|
||||||
quote ?
|
|
||||||
quote(code)
|
|
||||||
: code
|
|
||||||
}</code>`
|
|
||||||
+`</pre>` },
|
|
||||||
|
|
||||||
quote: function(text){
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var attributes = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
__pre_parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
// hidden attributes...
|
||||||
|
// XXX make this generic...
|
||||||
|
// collapsed...
|
||||||
|
.replace(/(\n|^)\s*collapsed::\s*(.*)\s*(\n|$)/,
|
||||||
|
function(_, value){
|
||||||
|
elem.collapsed = value.trim() == 'true'
|
||||||
|
return '' })
|
||||||
|
// id...
|
||||||
|
.replace(/(\n|^)\s*id::\s*(.*)\s*(\n|$)/,
|
||||||
|
function(_, value){
|
||||||
|
elem.id = value.trim()
|
||||||
|
return '' }) },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var blocks = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
__pre_parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
// markdown...
|
||||||
|
// style: headings...
|
||||||
|
.replace(/^(?<!\\)######\s+(.*)$/m, this.style(editor, elem, 'heading-6'))
|
||||||
|
.replace(/^(?<!\\)#####\s+(.*)$/m, this.style(editor, elem, 'heading-5'))
|
||||||
|
.replace(/^(?<!\\)####\s+(.*)$/m, this.style(editor, elem, 'heading-4'))
|
||||||
|
.replace(/^(?<!\\)###\s+(.*)$/m, this.style(editor, elem, 'heading-3'))
|
||||||
|
.replace(/^(?<!\\)##\s+(.*)$/m, this.style(editor, elem, 'heading-2'))
|
||||||
|
.replace(/^(?<!\\)#\s+(.*)$/m, this.style(editor, elem, 'heading-1'))
|
||||||
|
// style: list...
|
||||||
|
//.replace(/^(?<!\\)[-\*]\s+(.*)$/m, style('list-item'))
|
||||||
|
.replace(/^\s*(.*)(?<!\\):\s*$/m, this.style(editor, elem, 'list'))
|
||||||
|
.replace(/^\s*(.*)(?<!\\)#\s*$/m, this.style(editor, elem, 'numbered-list'))
|
||||||
|
// style: misc...
|
||||||
|
.replace(/^\s*(?<!\\)>\s+(.*)$/m, this.style(editor, elem, 'quote'))
|
||||||
|
.replace(/^\s*(?<!\\)((\/\/|;)\s+.*)$/m, this.style(editor, elem, 'comment'))
|
||||||
|
.replace(/^\s*(?<!\\)NOTE:?\s*(.*)$/m, this.style(editor, elem, 'NOTE'))
|
||||||
|
.replace(/^\s*(?<!\\)XXX\s+(.*)$/m, this.style(editor, elem, 'XXX'))
|
||||||
|
.replace(/^(.*)\s*(?<!\\)XXX$/m, this.style(editor, elem, 'XXX')) } ,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// XXX add actions...
|
||||||
|
var quoted = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
encode: function(text){
|
||||||
return text
|
return text
|
||||||
.replace(/(?<!\\)&/g, '&')
|
.replace(/(?<!\\)&/g, '&')
|
||||||
.replace(/(?<!\\)</g, '<')
|
.replace(/(?<!\\)</g, '<')
|
||||||
.replace(/(?<!\\)>/g, '>')
|
.replace(/(?<!\\)>/g, '>')
|
||||||
.replace(/\\(?!`)/g, '\\\\') },
|
.replace(/\\(?!`)/g, '\\\\') },
|
||||||
|
|
||||||
map: function(text, func){
|
// can be used in:
|
||||||
return text.replace(this.pattern, func) },
|
// <string>.replace(quoted.pattern, quoted.handler)
|
||||||
|
quote_pattern: /(?<!\\)`(?=[^\s])(([^`]|\\`)*[^\s])(?<!\\)`/gm,
|
||||||
|
quote: function(_, code){
|
||||||
|
return `<code>${ this.encode(code) }</code>` },
|
||||||
|
|
||||||
|
pre_pattern: /(?<!\\)```(.*\s*\n)((\n|.)*?)\h*(?<!\\)```/g,
|
||||||
|
pre: function(_, language, code){
|
||||||
|
language = language.trim()
|
||||||
|
language = language ?
|
||||||
|
'language-'+language
|
||||||
|
: language
|
||||||
|
return `<pre>`
|
||||||
|
+`<code contenteditable="true" class="${language}">${
|
||||||
|
this.encode(code)
|
||||||
|
}</code>`
|
||||||
|
+`</pre>` },
|
||||||
|
|
||||||
|
map: function(text, func){
|
||||||
|
return text.replace(this.pre_pattern, func) },
|
||||||
replace: function(text, index, updated){
|
replace: function(text, index, updated){
|
||||||
return this.map(text,
|
return this.map(text,
|
||||||
function(match, language, code){
|
function(match, language, code){
|
||||||
@ -68,9 +137,169 @@ var codeBlock = {
|
|||||||
updated(code)
|
updated(code)
|
||||||
: updated)
|
: updated)
|
||||||
+'```') }) },
|
+'```') }) },
|
||||||
|
|
||||||
toHTML: function(text){
|
toHTML: function(text){
|
||||||
return this.map(text, this.handler) },
|
return this.map(text, this.handler) },
|
||||||
|
|
||||||
|
__pre_parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
.replace(this.pre_pattern, this.pre.bind(this))
|
||||||
|
.replace(this.quote_pattern, this.quote.bind(this)) },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// XXX Hackish...
|
||||||
|
var syntax = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
update: function(){
|
||||||
|
window.hljs
|
||||||
|
&& hljs.highlightAll()
|
||||||
|
return this },
|
||||||
|
|
||||||
|
__setup__: function(editor){
|
||||||
|
return this.update() },
|
||||||
|
// XXX make a local update...
|
||||||
|
__changed__: function(editor, node){
|
||||||
|
return this.update() },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var tables = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
__parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
.replace(/^\s*(?<!\\)\|\s*((.|\n)*)\s*\|\s*$/,
|
||||||
|
function(_, body){
|
||||||
|
return `<table><tr><td>${
|
||||||
|
body
|
||||||
|
.replace(/\s*\|\s*\n\s*\|\s*/gm, '</td></tr>\n<tr><td>')
|
||||||
|
.replace(/\s*\|\s*/gm, '</td><td>')
|
||||||
|
}</td></td></table>` }) },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var styling = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
__parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
// markers...
|
||||||
|
.replace(/(\s*)(?<!\\)(FEATURE:|Q:|Question:|Note:)(\s*)/gm,
|
||||||
|
'$1<b class="$2">$2</b>$3')
|
||||||
|
.replace(/(\s*)(?<!\\)(ASAP|BUG|FIX|HACK|STUB|WARNING|CAUTION)(\s*)/gm,
|
||||||
|
'$1<span class="highlight $2">$2</span>$3')
|
||||||
|
// elements...
|
||||||
|
.replace(/(\n|^)(?<!\\)---*\h*(\n|$)/m, '$1<hr>')
|
||||||
|
// basic styling...
|
||||||
|
// XXX revise...
|
||||||
|
.replace(/(?<!\\)\*(?=[^\s*])(([^*]|\\\*)*[^\s*])(?<!\\)\*/gm, '<b>$1</b>')
|
||||||
|
.replace(/(?<!\\)~(?=[^\s~])(([^~]|\\~)*[^\s~])(?<!\\)~/gm, '<s>$1</s>')
|
||||||
|
.replace(/(?<!\\)_(?=[^\s_])(([^_]|\\_)*[^\s_])(?<!\\)_/gm, '<i>$1</i>')
|
||||||
|
// code/quoting...
|
||||||
|
//.replace(/(?<!\\)`(?=[^\s])(([^`]|\\`)*[^\s])(?<!\\)`/gm, quote)
|
||||||
|
// XXX support "\==" in mark...
|
||||||
|
.replace(/(?<!\\)==(?=[^\s])(.*[^\s])(?<!\\)==/gm, '<mark>$1</mark>')
|
||||||
|
// links...
|
||||||
|
.replace(/(?<!\\)\[([^\]]*)\]\(([^)]*)\)/g, '<a href="$2">$1</a>')
|
||||||
|
.replace(/((?:https?:|ftps?:)[^\s]*)(\s*)/g, '<a href="$1">$1</a>$2') },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// XXX use ligatures for these???
|
||||||
|
var symbols = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
__parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
// characters...
|
||||||
|
.replace(/(?<!\\)\(i\)/gm, '🛈')
|
||||||
|
.replace(/(?<!\\)\(c\)/gm, '©')
|
||||||
|
.replace(/(?<!\\)\/!\\/gm, '⚠')
|
||||||
|
.replace(/(?<!\\)---(?!-)/gm, '—')
|
||||||
|
.replace(/(?<!\\)--(?!-)/gm, '–') },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
var escaping = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
__post_parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
// quoting...
|
||||||
|
// NOTE: this must be last...
|
||||||
|
.replace(/(?<!\\)\\(.)/gm, '$1') },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// XXX add actions...
|
||||||
|
var tasks = {
|
||||||
|
__proto__: plugin,
|
||||||
|
|
||||||
|
updateStatus: function(editor, node){
|
||||||
|
node = editor.get(node)
|
||||||
|
if(node == null){
|
||||||
|
return this }
|
||||||
|
var state = node
|
||||||
|
.querySelector('.view')
|
||||||
|
.querySelector('.completion')
|
||||||
|
if(state){
|
||||||
|
var c =
|
||||||
|
((node.querySelectorAll('input[type=checkbox]:checked').length
|
||||||
|
/ node.querySelectorAll('input[type=checkbox]').length)
|
||||||
|
* 100)
|
||||||
|
.toFixed(0)
|
||||||
|
!isNaN(c)
|
||||||
|
&& state.setAttribute('completion', c +'%') }
|
||||||
|
return this },
|
||||||
|
updateBranch: function(editor, node){
|
||||||
|
if(!node){
|
||||||
|
return this }
|
||||||
|
var outline = editor.outline
|
||||||
|
var p = node
|
||||||
|
while(p !== outline){
|
||||||
|
this.updateStatus(editor, p)
|
||||||
|
p = editor.get(p, 'parent') }
|
||||||
|
return this },
|
||||||
|
updateAll: function(editor){
|
||||||
|
for(var e of [...editor.outline.querySelectorAll('.block>.view .completion')]){
|
||||||
|
this.updateStatus(editor, e) }
|
||||||
|
return this },
|
||||||
|
|
||||||
|
|
||||||
|
__setup__: function(editor){
|
||||||
|
return this.updateAll(editor) },
|
||||||
|
__changed__: function(editor, node){
|
||||||
|
return this.updateBranch(editor, node) },
|
||||||
|
__parse__: function(text, editor, elem){
|
||||||
|
return text
|
||||||
|
// block checkboxes...
|
||||||
|
// NOTE: these are separate as we need to align block text
|
||||||
|
// to leading chekbox...
|
||||||
|
.replace(/^\s*(?<!\\)\[[_ ]\]\s*/m,
|
||||||
|
this.style(editor, elem, 'todo', '<input type="checkbox">'))
|
||||||
|
.replace(/^\s*(?<!\\)\[[Xx]\]\s*/m,
|
||||||
|
this.style(editor, elem, 'todo', '<input type="checkbox" checked>'))
|
||||||
|
// inline checkboxes...
|
||||||
|
.replace(/\s*(?<!\\)\[[_ ]\]\s*/gm,
|
||||||
|
this.style(editor, elem, 'check', '<input type="checkbox">'))
|
||||||
|
.replace(/\s*(?<!\\)\[[Xx]\]\s*/gm,
|
||||||
|
this.style(editor, elem, 'check', '<input type="checkbox" checked>'))
|
||||||
|
// completion...
|
||||||
|
// XXX add support for being like a todo checkbox...
|
||||||
|
.replace(/(?<!\\)\[[%]\]/gm, '<span class="completion"></span>') },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,6 +320,32 @@ var Outline = {
|
|||||||
carot_jump_edge_then_block: false,
|
carot_jump_edge_then_block: false,
|
||||||
|
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
attributes,
|
||||||
|
blocks,
|
||||||
|
quoted,
|
||||||
|
styling,
|
||||||
|
tables,
|
||||||
|
symbols,
|
||||||
|
syntax,
|
||||||
|
tasks,
|
||||||
|
|
||||||
|
// keep this last...
|
||||||
|
// XXX revise -- should this be external???
|
||||||
|
escaping,
|
||||||
|
],
|
||||||
|
runPlugins: function(method, ...args){
|
||||||
|
for(var plugin of this.plugins){
|
||||||
|
method in plugin
|
||||||
|
&& plugin[method](...args) }
|
||||||
|
return this },
|
||||||
|
threadPlugins: function(method, value, ...args){
|
||||||
|
for(var plugin of this.plugins){
|
||||||
|
method in plugin
|
||||||
|
&& (value = plugin[method](value, ...args)) }
|
||||||
|
return value },
|
||||||
|
|
||||||
|
|
||||||
get code(){
|
get code(){
|
||||||
return this.dom.querySelector('.code') },
|
return this.dom.querySelector('.code') },
|
||||||
get outline(){
|
get outline(){
|
||||||
@ -363,154 +618,49 @@ var Outline = {
|
|||||||
var elem = {
|
var elem = {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// only whitespace -> keep element blank...
|
// only whitespace -> keep element blank...
|
||||||
if(code.trim() == ''){
|
if(code.trim() == ''){
|
||||||
elem.text = ''
|
elem.text = ''
|
||||||
return elem }
|
return elem }
|
||||||
|
|
||||||
// helpers...
|
// helpers...
|
||||||
var style = function(style, code=undefined){
|
var run = function(stage, text){
|
||||||
style = [style].flat()
|
var meth = {
|
||||||
that.__styles = [...new Set([
|
pre: '__pre_parse__',
|
||||||
...(that.__styles ?? []),
|
main: '__parse__',
|
||||||
...style,
|
post: '__post_parse__',
|
||||||
])]
|
}[stage]
|
||||||
return function(_, text){
|
return that.threadPlugins(meth, text, that, elem) }
|
||||||
elem.style ??= []
|
|
||||||
elem.style.push(...style)
|
|
||||||
return code
|
|
||||||
?? text } }
|
|
||||||
var quoteText = function(text){
|
|
||||||
return text
|
|
||||||
.replace(/(?<!\\)&/g, '&')
|
|
||||||
.replace(/(?<!\\)</g, '<')
|
|
||||||
.replace(/(?<!\\)>/g, '>')
|
|
||||||
.replace(/\\(?!`)/g, '\\\\') }
|
|
||||||
var quote = function(_, code){
|
|
||||||
return `<code>${quoteText(code)}</code>` }
|
|
||||||
var table = function(_, body){
|
|
||||||
return `<table><tr><td>${
|
|
||||||
body
|
|
||||||
.replace(/\s*\|\s*\n\s*\|\s*/gm, '</td></tr>\n<tr><td>')
|
|
||||||
.replace(/\s*\|\s*/gm, '</td><td>')
|
|
||||||
}</td></td></table>` }
|
|
||||||
|
|
||||||
var preParse = function(text){
|
// stage: pre...
|
||||||
return text
|
var text = run('pre',
|
||||||
// hidden attributes...
|
// pre-sanitize...
|
||||||
// XXX make this generic...
|
code.replace(/\x00/g, ''))
|
||||||
// collapsed...
|
// split text into parsable and non-parsable sections...
|
||||||
.replace(/(\n|^)\s*collapsed::\s*(.*)\s*(\n|$)/,
|
var sections = text
|
||||||
function(_, value){
|
|
||||||
elem.collapsed = value.trim() == 'true'
|
|
||||||
return '' })
|
|
||||||
// id...
|
|
||||||
.replace(/(\n|^)\s*id::\s*(.*)\s*(\n|$)/,
|
|
||||||
function(_, value){
|
|
||||||
elem.id = value.trim()
|
|
||||||
return '' }) }
|
|
||||||
var blockParse = function(text){
|
|
||||||
return text
|
|
||||||
// markdown...
|
|
||||||
// style: headings...
|
|
||||||
.replace(/^(?<!\\)######\s+(.*)$/m, style('heading-6'))
|
|
||||||
.replace(/^(?<!\\)#####\s+(.*)$/m, style('heading-5'))
|
|
||||||
.replace(/^(?<!\\)####\s+(.*)$/m, style('heading-4'))
|
|
||||||
.replace(/^(?<!\\)###\s+(.*)$/m, style('heading-3'))
|
|
||||||
.replace(/^(?<!\\)##\s+(.*)$/m, style('heading-2'))
|
|
||||||
.replace(/^(?<!\\)#\s+(.*)$/m, style('heading-1'))
|
|
||||||
// style: list...
|
|
||||||
//.replace(/^(?<!\\)[-\*]\s+(.*)$/m, style('list-item'))
|
|
||||||
.replace(/^\s*(.*)(?<!\\):\s*$/m, style('list'))
|
|
||||||
.replace(/^\s*(.*)(?<!\\)#\s*$/m, style('numbered-list'))
|
|
||||||
|
|
||||||
// style: misc...
|
|
||||||
.replace(/^\s*(?<!\\)>\s+(.*)$/m, style('quote'))
|
|
||||||
.replace(/^\s*(?<!\\)((\/\/|;)\s+.*)$/m, style('comment'))
|
|
||||||
.replace(/^\s*(?<!\\)NOTE:?\s*(.*)$/m, style('NOTE'))
|
|
||||||
.replace(/^\s*(?<!\\)XXX\s+(.*)$/m, style('XXX'))
|
|
||||||
.replace(/^(.*)\s*(?<!\\)XXX$/m, style('XXX')) }
|
|
||||||
var quoteParse = function(text){
|
|
||||||
return text
|
|
||||||
.replace(codeBlock.pattern, codeBlock.handler)
|
|
||||||
.replace(/(?<!\\)`(?=[^\s])(([^`]|\\`)*[^\s])(?<!\\)`/gm, quote) }
|
|
||||||
var inlineParse = function(text){
|
|
||||||
return text
|
|
||||||
.replace(/(\s*)(?<!\\)(FEATURE:|Q:|Question:|Note:)(\s*)/gm,
|
|
||||||
'$1<b class="$2">$2</b>$3')
|
|
||||||
.replace(/(\s*)(?<!\\)(ASAP|BUG|FIX|HACK|STUB|WARNING|CAUTION)(\s*)/gm,
|
|
||||||
'$1<span class="highlight $2">$2</span>$3')
|
|
||||||
// elements...
|
|
||||||
.replace(/(\n|^)(?<!\\)---*\h*(\n|$)/m, '$1<hr>')
|
|
||||||
// ToDo...
|
|
||||||
// NOTE: these are separate as we need to align block text
|
|
||||||
// to leading chekbox...
|
|
||||||
.replace(/^\s*(?<!\\)\[[_ ]\]\s*/m,
|
|
||||||
style('todo', '<input type="checkbox">'))
|
|
||||||
.replace(/^\s*(?<!\\)\[[Xx]\]\s*/m,
|
|
||||||
style('todo', '<input type="checkbox" checked>'))
|
|
||||||
// inline checkboxes...
|
|
||||||
.replace(/\s*(?<!\\)\[[_ ]\]\s*/gm,
|
|
||||||
style('check', '<input type="checkbox">'))
|
|
||||||
.replace(/\s*(?<!\\)\[[Xx]\]\s*/gm,
|
|
||||||
style('check', '<input type="checkbox" checked>'))
|
|
||||||
// tables...
|
|
||||||
.replace(/^\s*(?<!\\)\|\s*((.|\n)*)\s*\|\s*$/, table)
|
|
||||||
// basic styling...
|
|
||||||
// XXX revise...
|
|
||||||
.replace(/(?<!\\)\*(?=[^\s*])(([^*]|\\\*)*[^\s*])(?<!\\)\*/gm, '<b>$1</b>')
|
|
||||||
.replace(/(?<!\\)~(?=[^\s~])(([^~]|\\~)*[^\s~])(?<!\\)~/gm, '<s>$1</s>')
|
|
||||||
.replace(/(?<!\\)_(?=[^\s_])(([^_]|\\_)*[^\s_])(?<!\\)_/gm, '<i>$1</i>')
|
|
||||||
// code/quoting...
|
|
||||||
//.replace(/(?<!\\)`(?=[^\s])(([^`]|\\`)*[^\s])(?<!\\)`/gm, quote)
|
|
||||||
// XXX support "\==" in mark...
|
|
||||||
.replace(/(?<!\\)==(?=[^\s])(.*[^\s])(?<!\\)==/gm, '<mark>$1</mark>')
|
|
||||||
// links...
|
|
||||||
.replace(/(?<!\\)\[([^\]]*)\]\(([^)]*)\)/g, '<a href="$2">$1</a>')
|
|
||||||
.replace(/((?:https?:|ftps?:)[^\s]*)(\s*)/g, '<a href="$1">$1</a>$2')
|
|
||||||
// characters...
|
|
||||||
// XXX use ligatures for these???
|
|
||||||
.replace(/(?<!\\)\(i\)/gm, '🛈')
|
|
||||||
.replace(/(?<!\\)\(c\)/gm, '©')
|
|
||||||
.replace(/(?<!\\)\/!\\/gm, '⚠')
|
|
||||||
.replace(/(?<!\\)---(?!-)/gm, '—')
|
|
||||||
.replace(/(?<!\\)--(?!-)/gm, '–') }
|
|
||||||
var postParse = function(text){
|
|
||||||
return text
|
|
||||||
// quoting...
|
|
||||||
// NOTE: this must be last...
|
|
||||||
.replace(/(?<!\\)\\(.)/gm, '$1') }
|
|
||||||
|
|
||||||
var parse = function(text){
|
|
||||||
// split text into parsable and non-parsable sections...
|
|
||||||
// split fomat:
|
// split fomat:
|
||||||
// [ text <match> <type> <body>, ... ]
|
// [ text <match> <type> <body>, ... ]
|
||||||
var pattern = /(<(pre|code)(?:|\s[^>]*)>((?:\n|.)*)<\/\2>)/g
|
.split(/(<(pre|code)(?:|\s[^>]*)>((?:\n|.)*)<\/\2>)/g)
|
||||||
var sections =
|
// sort out the sections...
|
||||||
quoteParse(
|
var parsable = []
|
||||||
blockParse(
|
var quoted = []
|
||||||
preParse(text
|
while(sections.length > 0){
|
||||||
.replace(/\x00/g, ''))))
|
var [section, match] = sections.splice(0, 4)
|
||||||
.split(pattern)
|
parsable.push(section)
|
||||||
// sort out the sections...
|
quoted.push(match) }
|
||||||
var parsable = []
|
// stage: main...
|
||||||
var quoted = []
|
text = run('main',
|
||||||
while(sections.length > 0){
|
// parse only the parsable sections...
|
||||||
var [section, match] = sections.splice(0, 4)
|
parsable.join('\x00'))
|
||||||
parsable.push(section)
|
.split(/\x00/g)
|
||||||
quoted.push(match) }
|
// merge the quoted sections back in...
|
||||||
// parse only the parsable sections...
|
.map(function(section){
|
||||||
return postParse(
|
return [section, quoted.shift() ?? ''] })
|
||||||
inlineParse(
|
.flat()
|
||||||
parsable
|
.join('')
|
||||||
.join('\x00'))
|
// stage: post...
|
||||||
.split(/\x00/g)
|
elem.text = run('post', text)
|
||||||
.map(function(section){
|
|
||||||
return [section, quoted.shift() ?? ''] })
|
|
||||||
.flat()
|
|
||||||
.join('')) }
|
|
||||||
|
|
||||||
elem.text = parse(code)
|
|
||||||
|
|
||||||
return elem },
|
return elem },
|
||||||
// XXX essentially here we need to remove service stuff like some
|
// XXX essentially here we need to remove service stuff like some
|
||||||
@ -835,6 +985,7 @@ var Outline = {
|
|||||||
function(evt){
|
function(evt){
|
||||||
var elem = evt.target
|
var elem = evt.target
|
||||||
|
|
||||||
|
// prevent focusing parent by clicking between blocks...
|
||||||
if(elem.classList.contains('children')){
|
if(elem.classList.contains('children')){
|
||||||
return }
|
return }
|
||||||
|
|
||||||
@ -882,7 +1033,9 @@ var Outline = {
|
|||||||
return i-- == 0 ?
|
return i-- == 0 ?
|
||||||
to
|
to
|
||||||
: m }
|
: m }
|
||||||
text.value = text.value.replace(/\[[Xx_]\]/g, toggle) } })
|
text.value = text.value.replace(/\[[Xx_]\]/g, toggle)
|
||||||
|
// update status...
|
||||||
|
tasks.updateBranch(that, node) } })
|
||||||
// keyboard handling...
|
// keyboard handling...
|
||||||
outline.addEventListener('keydown',
|
outline.addEventListener('keydown',
|
||||||
function(evt){
|
function(evt){
|
||||||
@ -918,7 +1071,7 @@ var Outline = {
|
|||||||
.querySelectorAll('.view code[contenteditable=true]')]
|
.querySelectorAll('.view code[contenteditable=true]')]
|
||||||
.indexOf(elem)
|
.indexOf(elem)
|
||||||
// update element content...
|
// update element content...
|
||||||
code.value = codeBlock.replace(code.value, i, update)
|
code.value = quoted.replace(code.value, i, update)
|
||||||
|
|
||||||
return } })
|
return } })
|
||||||
// toggle view/code of nodes...
|
// toggle view/code of nodes...
|
||||||
@ -951,12 +1104,10 @@ var Outline = {
|
|||||||
if(node.nodeName == 'TEXTAREA'
|
if(node.nodeName == 'TEXTAREA'
|
||||||
&& node?.nextElementSibling?.nodeName == 'SPAN'){
|
&& node?.nextElementSibling?.nodeName == 'SPAN'){
|
||||||
var block = node.parentElement
|
var block = node.parentElement
|
||||||
that.update(block, { text: node.value }) }
|
that.update(block, { text: node.value })
|
||||||
|
|
||||||
// XXX do a plugin...
|
that.runPlugins('__changed__', that, node)
|
||||||
window.hljs
|
} })
|
||||||
&& hljs.highlightAll()
|
|
||||||
})
|
|
||||||
// update .code...
|
// update .code...
|
||||||
var update_code_timeout
|
var update_code_timeout
|
||||||
outline.addEventListener('change',
|
outline.addEventListener('change',
|
||||||
@ -997,9 +1148,7 @@ var Outline = {
|
|||||||
.replace(/>/g, '>'))
|
.replace(/>/g, '>'))
|
||||||
console.log(`Parse: ${Date.now() - t}ms`)}
|
console.log(`Parse: ${Date.now() - t}ms`)}
|
||||||
|
|
||||||
// XXX do a plugin...
|
this.runPlugins('__setup__', this)
|
||||||
window.hljs
|
|
||||||
&& hljs.highlightAll()
|
|
||||||
|
|
||||||
return this },
|
return this },
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,8 +57,6 @@ var setup = function(){
|
|||||||
- ASAP: editor: shifting nodes up/down
|
- ASAP: editor: shifting nodes up/down
|
||||||
- ASAP: scroll into view is bad...
|
- ASAP: scroll into view is bad...
|
||||||
- ASAP: need to reach checkboxes via keyboard
|
- ASAP: need to reach checkboxes via keyboard
|
||||||
- FEATURE: "percentage complete" in parent blocks with todo's nested
|
|
||||||
- _...use `[%]` (preferred), `%%`, or something similar..._
|
|
||||||
- FEATURE: read-only mode
|
- FEATURE: read-only mode
|
||||||
- FEATURE: `collapse-children:: true` block option -- when loading collapse all immediate children
|
- FEATURE: `collapse-children:: true` block option -- when loading collapse all immediate children
|
||||||
- FF: figure out a way to draw expand/collapse bullets without the use of CSS' `:has(..)`
|
- FF: figure out a way to draw expand/collapse bullets without the use of CSS' `:has(..)`
|
||||||
@ -67,7 +65,7 @@ var setup = function(){
|
|||||||
code
|
code
|
||||||
```
|
```
|
||||||
- _bullet should be either in the middle of the block or at the first line of code (preferred)..._
|
- _bullet should be either in the middle of the block or at the first line of code (preferred)..._
|
||||||
- custom element...
|
- editor as a custom element...
|
||||||
- Nerd fonts (option???)
|
- Nerd fonts (option???)
|
||||||
- multiple node selection
|
- multiple node selection
|
||||||
- copy/paste nodes/trees
|
- copy/paste nodes/trees
|
||||||
@ -76,6 +74,13 @@ var setup = function(){
|
|||||||
- delete node
|
- delete node
|
||||||
- indent/deindent
|
- indent/deindent
|
||||||
- edit node
|
- edit node
|
||||||
|
- FEATURE? block templates...
|
||||||
|
collapsed:: true
|
||||||
|
- something like: `TPL: [_] <editable/> -- <editable/>`
|
||||||
|
- `TPL:` -- template marker
|
||||||
|
- `<editable/>` -- field marker
|
||||||
|
- each child node will copy the template and allow editing of only fields
|
||||||
|
- not clear how to handle template changes...
|
||||||
- Q: can we get the caret line in a textarea???
|
- Q: can we get the caret line in a textarea???
|
||||||
- _...this will fix a lot of issues with moving between blocks in edit mode..._
|
- _...this will fix a lot of issues with moving between blocks in edit mode..._
|
||||||
- Q: do we use \\t for indent? (option???)
|
- Q: do we use \\t for indent? (option???)
|
||||||
@ -96,8 +101,9 @@ var setup = function(){
|
|||||||
* list item
|
* list item
|
||||||
block text
|
block text
|
||||||
- NOTE: this is only a problem if making list-items manually -- disable???
|
- NOTE: this is only a problem if making list-items manually -- disable???
|
||||||
- ~Q: can we edit code in a code block directly? (a-la Logseq)~
|
|
||||||
- empty item height is a bit off...
|
- empty item height is a bit off...
|
||||||
|
- ~Q: can we edit code in a code block directly? (a-la Logseq)~
|
||||||
|
- ~"percentage complete" in parent blocks with todo's nested~
|
||||||
- ~`.editor .outline:empty` view and behavior...~
|
- ~`.editor .outline:empty` view and behavior...~
|
||||||
- ~editor: semi-live update styles~
|
- ~editor: semi-live update styles~
|
||||||
- ~do a better expand/collapse icons~
|
- ~do a better expand/collapse icons~
|
||||||
@ -113,11 +119,11 @@ var setup = function(){
|
|||||||
- ~add optional text styling to nodes~
|
- ~add optional text styling to nodes~
|
||||||
-
|
-
|
||||||
- ## Refactoring:
|
- ## Refactoring:
|
||||||
|
- Plugin architecture
|
||||||
- Item parser (`.__code2html__(..)`)
|
- Item parser (`.__code2html__(..)`)
|
||||||
- split out
|
- ~split out~
|
||||||
- define api
|
- ~define~/doc api
|
||||||
- define a way to extend/stack parsers
|
- ~define a way to extend/stack parsers~
|
||||||
_...add wikiwords, ..._
|
|
||||||
- Format parser/generator
|
- Format parser/generator
|
||||||
- split out
|
- split out
|
||||||
- define api
|
- define api
|
||||||
@ -193,12 +199,13 @@ var setup = function(){
|
|||||||
- ---
|
- ---
|
||||||
- Markers: ASAP, BUG, FIX, HACK, STUB, WARNING, and CAUTION
|
- Markers: ASAP, BUG, FIX, HACK, STUB, WARNING, and CAUTION
|
||||||
- Basic task management
|
- Basic task management
|
||||||
- Inline [X] checkboxes [_]
|
- [%] Completion status
|
||||||
- To do items/blocks
|
- Inline [X] checkboxes [_]
|
||||||
- [_] undone item
|
- To do items/blocks
|
||||||
_(clicking the checkbox updates the item)_
|
- [_] undone item
|
||||||
- [X] done item
|
_(clicking the checkbox updates the item)_
|
||||||
- [_] we can also add inline [x] checkboxes
|
- [X] done item
|
||||||
|
- [_] we can also add inline [x] checkboxes and states: [%]
|
||||||
- links
|
- links
|
||||||
- [example](about:blank)
|
- [example](about:blank)
|
||||||
- https://example.com
|
- https://example.com
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user