mirror of
https://github.com/flynx/pWiki.git
synced 2025-12-25 04:11:56 +00:00
Compare commits
5 Commits
ec04205bfa
...
97d6f86ab4
| Author | SHA1 | Date | |
|---|---|---|---|
| 97d6f86ab4 | |||
| 94f3d912c4 | |||
| e313a1a7e3 | |||
| ef666e91d0 | |||
| 7b755b413c |
@ -23,6 +23,20 @@ var atLine = function(elem, index){
|
|||||||
return false }
|
return false }
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
function clickPoint(x,y){
|
||||||
|
document
|
||||||
|
.elementFromPoint(x, y)
|
||||||
|
.dispatchEvent(
|
||||||
|
new MouseEvent( 'click', {
|
||||||
|
view: window,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
screenX: x,
|
||||||
|
screenY: y,
|
||||||
|
} )) }
|
||||||
|
//*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
@ -147,6 +161,39 @@ var quoted = {
|
|||||||
return text
|
return text
|
||||||
.replace(this.pre_pattern, this.pre.bind(this))
|
.replace(this.pre_pattern, this.pre.bind(this))
|
||||||
.replace(this.quote_pattern, this.quote.bind(this)) },
|
.replace(this.quote_pattern, this.quote.bind(this)) },
|
||||||
|
|
||||||
|
// XXX is this a good strategy???
|
||||||
|
__state: undefined,
|
||||||
|
__keydown__: function(evt, editor, elem){
|
||||||
|
// code editing...
|
||||||
|
if(elem.nodeName == 'CODE'
|
||||||
|
&& elem.getAttribute('contenteditable') == 'true'){
|
||||||
|
// XXX can keydown and keyup be triggered from different elements???
|
||||||
|
this.__state = elem.innerText
|
||||||
|
return false } },
|
||||||
|
// defined <plugin>.__editedview__(..) handler
|
||||||
|
__keyup__: function(evt, editor, elem){
|
||||||
|
var elem = evt.target
|
||||||
|
if(elem.nodeName == 'CODE'
|
||||||
|
&& elem.getAttribute('contenteditable') == 'true'){
|
||||||
|
// trigger if state actually changed..
|
||||||
|
this.__state != elem.innerText
|
||||||
|
&& editor.runPlugins('__editedview__', evt, editor, elem) } },
|
||||||
|
__focusout__: function(){
|
||||||
|
this.__state = undefined },
|
||||||
|
__editedview__: function(evt, editor, elem){
|
||||||
|
// editable code...
|
||||||
|
var block = editor.get(elem)
|
||||||
|
var code = block.querySelector('.code')
|
||||||
|
|
||||||
|
var update = elem.innerText
|
||||||
|
var i = [...block
|
||||||
|
.querySelectorAll('.view code[contenteditable=true]')]
|
||||||
|
.indexOf(elem)
|
||||||
|
// update element content...
|
||||||
|
code.value = quoted.replace(code.value, i, update)
|
||||||
|
|
||||||
|
return this },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -164,8 +211,22 @@ var syntax = {
|
|||||||
__setup__: function(editor){
|
__setup__: function(editor){
|
||||||
return this.update() },
|
return this.update() },
|
||||||
// XXX make a local update...
|
// XXX make a local update...
|
||||||
__changed__: function(evt, editor, node){
|
__editedcode__: function(evt, editor, elem){
|
||||||
return this.update() },
|
return this.update(elem) },
|
||||||
|
__editedview__: function(evt, editor, elem){
|
||||||
|
// XXX should we also clear the syntax???
|
||||||
|
delete elem.dataset.highlighted
|
||||||
|
return this },
|
||||||
|
// XXX this removes highlighting, can we make it update live???
|
||||||
|
__focusin__: function(evt, editor, elem){
|
||||||
|
if(elem.nodeName == 'CODE'
|
||||||
|
&& elem.getAttribute('contenteditable') == 'true'){
|
||||||
|
elem.classList.remove('hljs') } },
|
||||||
|
__focusout__: function(evt, editor, elem){
|
||||||
|
if(elem.nodeName == 'CODE'
|
||||||
|
&& elem.getAttribute('contenteditable') == 'true'){
|
||||||
|
this.update(elem) }
|
||||||
|
return this },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -194,7 +255,7 @@ var styling = {
|
|||||||
__parse__: function(text, editor, elem){
|
__parse__: function(text, editor, elem){
|
||||||
return text
|
return text
|
||||||
// markers...
|
// markers...
|
||||||
.replace(/(\s*)(?<!\\)(FEATURE:|Q:|Question:|Note:)(\s*)/gm,
|
.replace(/(\s*)(?<!\\)(FEATURE[:?]|Q:|Question:|Note:)(\s*)/gm,
|
||||||
'$1<b class="$2">$2</b>$3')
|
'$1<b class="$2">$2</b>$3')
|
||||||
.replace(/(\s*)(?<!\\)(ASAP|BUG|FIX|HACK|STUB|WARNING|CAUTION)(\s*)/gm,
|
.replace(/(\s*)(?<!\\)(ASAP|BUG|FIX|HACK|STUB|WARNING|CAUTION)(\s*)/gm,
|
||||||
'$1<span class="highlight $2">$2</span>$3')
|
'$1<span class="highlight $2">$2</span>$3')
|
||||||
@ -301,7 +362,7 @@ var tasks = {
|
|||||||
// completion...
|
// completion...
|
||||||
// XXX add support for being like a todo checkbox...
|
// XXX add support for being like a todo checkbox...
|
||||||
.replace(/(?<!\\)\[[%]\]/gm, '<span class="completion"></span>') },
|
.replace(/(?<!\\)\[[%]\]/gm, '<span class="completion"></span>') },
|
||||||
__changed__: function(evt, editor, node){
|
__editedcode__: function(evt, editor, node){
|
||||||
return this.updateBranch(editor, node) },
|
return this.updateBranch(editor, node) },
|
||||||
__click__: function(evt, editor, elem){
|
__click__: function(evt, editor, elem){
|
||||||
// toggle checkbox...
|
// toggle checkbox...
|
||||||
@ -341,6 +402,9 @@ var Outline = {
|
|||||||
carot_jump_edge_then_block: false,
|
carot_jump_edge_then_block: false,
|
||||||
|
|
||||||
|
|
||||||
|
// The order of plugins can be significant in the following cases:
|
||||||
|
// - parsing
|
||||||
|
// - event dropping
|
||||||
plugins: [
|
plugins: [
|
||||||
attributes,
|
attributes,
|
||||||
blocks,
|
blocks,
|
||||||
@ -355,11 +419,14 @@ var Outline = {
|
|||||||
// XXX revise -- should this be external???
|
// XXX revise -- should this be external???
|
||||||
escaping,
|
escaping,
|
||||||
],
|
],
|
||||||
|
// NOTE: if a handler returns false it will break plugin execution...
|
||||||
|
// XXX is this the right way to go???
|
||||||
runPlugins: function(method, ...args){
|
runPlugins: function(method, ...args){
|
||||||
for(var plugin of this.plugins){
|
for(var plugin of this.plugins){
|
||||||
method in plugin
|
if(method in plugin){
|
||||||
&& plugin[method](...args) }
|
if(plugin[method](...args) === false){
|
||||||
return this },
|
return false } } }
|
||||||
|
return true },
|
||||||
threadPlugins: function(method, value, ...args){
|
threadPlugins: function(method, value, ...args){
|
||||||
for(var plugin of this.plugins){
|
for(var plugin of this.plugins){
|
||||||
method in plugin
|
method in plugin
|
||||||
@ -577,6 +644,27 @@ var Outline = {
|
|||||||
return cur },
|
return cur },
|
||||||
deindent: function(node='focused', indent=false){
|
deindent: function(node='focused', indent=false){
|
||||||
return this.indent(node, indent) },
|
return this.indent(node, indent) },
|
||||||
|
shift: function(node='focused', direction){
|
||||||
|
if(node == 'up' || node == 'down'){
|
||||||
|
direction = node
|
||||||
|
node = 'focused' }
|
||||||
|
if(direction == null){
|
||||||
|
return }
|
||||||
|
node = this.get(node)
|
||||||
|
var focused = node.classList.contains('focused')
|
||||||
|
var siblings = this.get(node, 'siblings')
|
||||||
|
var i = siblings.indexOf(node)
|
||||||
|
if(direction == 'up'
|
||||||
|
&& i > 0){
|
||||||
|
siblings[i-1].before(node)
|
||||||
|
focused
|
||||||
|
&& this.focus()
|
||||||
|
} else if(direction == 'down'
|
||||||
|
&& i < siblings.length-1){
|
||||||
|
siblings[i+1].after(node)
|
||||||
|
focused
|
||||||
|
&& this.focus() }
|
||||||
|
return this },
|
||||||
show: function(node='focused', offset){
|
show: function(node='focused', offset){
|
||||||
var node = this.get(...arguments)
|
var node = this.get(...arguments)
|
||||||
var outline = this.outline
|
var outline = this.outline
|
||||||
@ -629,7 +717,6 @@ var Outline = {
|
|||||||
return this },
|
return this },
|
||||||
|
|
||||||
// block serialization...
|
// block serialization...
|
||||||
// XXX split this up into a generic handler + plugins...
|
|
||||||
// XXX need a way to filter input text...
|
// XXX need a way to filter input text...
|
||||||
// use-case: hidden attributes...
|
// use-case: hidden attributes...
|
||||||
// NOTE: this is auto-populated by .__code2html__(..)
|
// NOTE: this is auto-populated by .__code2html__(..)
|
||||||
@ -684,20 +771,13 @@ var Outline = {
|
|||||||
elem.text = run('post', text)
|
elem.text = run('post', text)
|
||||||
|
|
||||||
return elem },
|
return elem },
|
||||||
// XXX essentially here we need to remove service stuff like some
|
// output format...
|
||||||
// attributes (collapsed, id, ...)...
|
|
||||||
// XXX also need to quote leading '- ' in block text here...
|
|
||||||
// e.g.
|
|
||||||
// - block
|
|
||||||
// some text
|
|
||||||
// - text in the above block ('-' needs to be quoted)
|
|
||||||
// - next block
|
|
||||||
__code2text__: function(code){
|
__code2text__: function(code){
|
||||||
// XXX
|
return code
|
||||||
},
|
.replace(/(\n\s*)-/g, '$1\\-') },
|
||||||
__text2code__: function(text){
|
__text2code__: function(text){
|
||||||
// XXX
|
return text
|
||||||
},
|
.replace(/(\n\s*)\\-/g, '$1-') },
|
||||||
|
|
||||||
// serialization...
|
// serialization...
|
||||||
data: function(elem, deep=true){
|
data: function(elem, deep=true){
|
||||||
@ -711,8 +791,10 @@ var Outline = {
|
|||||||
} },
|
} },
|
||||||
json: function(node){
|
json: function(node){
|
||||||
var that = this
|
var that = this
|
||||||
node ??= this.outline
|
var children = [...(node ?
|
||||||
return [...node.lastChild.children]
|
node.lastChild.children
|
||||||
|
: this.outline.children)]
|
||||||
|
return children
|
||||||
.map(function(elem){
|
.map(function(elem){
|
||||||
return that.data(elem) }) },
|
return that.data(elem) }) },
|
||||||
// XXX add option to customize indent size...
|
// XXX add option to customize indent size...
|
||||||
@ -727,7 +809,7 @@ var Outline = {
|
|||||||
for(var elem of node){
|
for(var elem of node){
|
||||||
text.push(
|
text.push(
|
||||||
level +'- '
|
level +'- '
|
||||||
+ elem.text
|
+ this.__code2text__(elem.text)
|
||||||
.replace(/\n/g, '\n'+ level +' ')
|
.replace(/\n/g, '\n'+ level +' ')
|
||||||
+ (elem.collapsed ?
|
+ (elem.collapsed ?
|
||||||
'\n'+level+' ' + 'collapsed:: true'
|
'\n'+level+' ' + 'collapsed:: true'
|
||||||
@ -741,6 +823,7 @@ var Outline = {
|
|||||||
.join('\n') },
|
.join('\n') },
|
||||||
|
|
||||||
parse: function(text){
|
parse: function(text){
|
||||||
|
var that = this
|
||||||
text = text
|
text = text
|
||||||
.replace(/^\s*\n/, '')
|
.replace(/^\s*\n/, '')
|
||||||
text = ('\n' + text)
|
text = ('\n' + text)
|
||||||
@ -765,10 +848,10 @@ var Outline = {
|
|||||||
collapsed = value == 'true'
|
collapsed = value == 'true'
|
||||||
return '' })
|
return '' })
|
||||||
parent.push({
|
parent.push({
|
||||||
text: block
|
text: that.__text2code__(block
|
||||||
// normalize indent...
|
// normalize indent...
|
||||||
.split(new RegExp('\n'+sep+' ', 'g'))
|
.split(new RegExp('\n'+sep+' ', 'g'))
|
||||||
.join('\n'),
|
.join('\n')),
|
||||||
collapsed,
|
collapsed,
|
||||||
children: [],
|
children: [],
|
||||||
})
|
})
|
||||||
@ -850,6 +933,18 @@ var Outline = {
|
|||||||
&& (code.innerHTML = this.text())
|
&& (code.innerHTML = this.text())
|
||||||
return this },
|
return this },
|
||||||
|
|
||||||
|
|
||||||
|
// Actions...
|
||||||
|
prev: function(){},
|
||||||
|
next: function(){},
|
||||||
|
above: function(){},
|
||||||
|
below: function(){},
|
||||||
|
|
||||||
|
up: function(){},
|
||||||
|
down: function(){},
|
||||||
|
left: function(){},
|
||||||
|
right: function(){},
|
||||||
|
|
||||||
// XXX move the code here into methods/actions...
|
// XXX move the code here into methods/actions...
|
||||||
// XXX add scrollIntoView(..) to nav...
|
// XXX add scrollIntoView(..) to nav...
|
||||||
// XXX use keyboard.js...
|
// XXX use keyboard.js...
|
||||||
@ -862,16 +957,10 @@ var Outline = {
|
|||||||
var that = this
|
var that = this
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
if(edited){
|
if(edited){
|
||||||
var c = edited.selectionStart
|
var line = edited.getTextGeometry().line
|
||||||
var jump = function(){
|
if(line == 0){
|
||||||
if(edited.selectionStart == 0){
|
evt.preventDefault()
|
||||||
// needed to remember the position...
|
that.focus('edited', 'prev') }
|
||||||
edited.selectionStart = c
|
|
||||||
edited.selectionEnd = c
|
|
||||||
that.focus('edited', -1) } }
|
|
||||||
this.carot_jump_edge_then_block ?
|
|
||||||
jump()
|
|
||||||
: setTimeout(jump, 0)
|
|
||||||
} else {
|
} else {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.focus('focused', -1) } },
|
this.focus('focused', -1) } },
|
||||||
@ -879,16 +968,10 @@ var Outline = {
|
|||||||
var that = this
|
var that = this
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
if(edited){
|
if(edited){
|
||||||
var c = edited.selectionStart
|
var {line, lines} = edited.getTextGeometry()
|
||||||
var jump = function(){
|
if(line == lines -1){
|
||||||
if(edited.selectionStart == edited.value.length){
|
evt.preventDefault()
|
||||||
// needed to remember the position...
|
that.focus('edited', 'next') }
|
||||||
edited.selectionStart = c
|
|
||||||
edited.selectionEnd = c
|
|
||||||
that.focus('edited', 1) } }
|
|
||||||
this.carot_jump_edge_then_block ?
|
|
||||||
jump()
|
|
||||||
: setTimeout(jump, 0)
|
|
||||||
} else {
|
} else {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.focus('focused', 1) } },
|
this.focus('focused', 1) } },
|
||||||
@ -930,6 +1013,21 @@ var Outline = {
|
|||||||
this.toggleCollapse(false)
|
this.toggleCollapse(false)
|
||||||
: this.focus('next') } },
|
: this.focus('next') } },
|
||||||
|
|
||||||
|
PageUp: function(evt){
|
||||||
|
var edited = this.get('edited')
|
||||||
|
if(!edited
|
||||||
|
&& (evt.shiftKey
|
||||||
|
|| evt.ctrlKey)){
|
||||||
|
evt.preventDefault()
|
||||||
|
this.shift('up') } },
|
||||||
|
PageDown: function(evt){
|
||||||
|
var edited = this.get('edited')
|
||||||
|
if(!edited
|
||||||
|
&& (evt.shiftKey
|
||||||
|
|| evt.ctrlKey)){
|
||||||
|
evt.preventDefault()
|
||||||
|
this.shift('down') } },
|
||||||
|
|
||||||
// indent...
|
// indent...
|
||||||
Tab: function(evt){
|
Tab: function(evt){
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
@ -943,37 +1041,78 @@ var Outline = {
|
|||||||
|
|
||||||
// edit mode...
|
// edit mode...
|
||||||
O: function(evt){
|
O: function(evt){
|
||||||
if(evt.target.nodeName != 'TEXTAREA'){
|
if(!this.get('edited')){
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.Block('before')
|
this.edit(
|
||||||
?.querySelector('textarea')
|
this.Block('before')) } },
|
||||||
?.focus() } },
|
|
||||||
o: function(evt){
|
o: function(evt){
|
||||||
if(evt.target.nodeName != 'TEXTAREA'){
|
if(!this.get('edited')){
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.Block('next')
|
this.edit(
|
||||||
?.querySelector('textarea')
|
this.Block('next')) } },
|
||||||
?.focus() } },
|
|
||||||
Enter: function(evt){
|
Enter: function(evt){
|
||||||
|
|
||||||
|
var edited = this.get('edited')
|
||||||
|
// edit -> split text...
|
||||||
|
if(edited){
|
||||||
if(evt.ctrlKey
|
if(evt.ctrlKey
|
||||||
|| evt.shiftKey){
|
|| evt.shiftKey){
|
||||||
return }
|
return }
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
evt.target.nodeName == 'TEXTAREA' ?
|
var a = edited.selectionStart
|
||||||
|
var b = edited.selectionEnd
|
||||||
|
var prev = edited.value.slice(0, a)
|
||||||
|
var next = edited.value.slice(b)
|
||||||
|
edited.value = prev
|
||||||
this.Block('next')
|
this.Block('next')
|
||||||
?.querySelector('textarea')
|
edited = this.edit('next')
|
||||||
?.focus()
|
edited.value = next
|
||||||
: this.get()
|
edited.selectionStart = 0
|
||||||
?.querySelector('textarea')
|
edited.selectionEnd = 0
|
||||||
?.focus() },
|
return }
|
||||||
|
// view -> edit...
|
||||||
|
evt.preventDefault()
|
||||||
|
this.edit() },
|
||||||
Escape: function(evt){
|
Escape: function(evt){
|
||||||
this.outline.querySelector('textarea:focus')
|
this.focus() },
|
||||||
?.parentElement
|
|
||||||
?.focus() },
|
|
||||||
Delete: function(evt){
|
Delete: function(evt){
|
||||||
if(this.get('edited')){
|
var edited = this.get('edited')
|
||||||
|
if(edited){
|
||||||
|
if(edited.selectionStart == edited.value.length){
|
||||||
|
var next = this.get('edited', 'next')
|
||||||
|
// can't reclaim nested children...
|
||||||
|
if(this.get(next, 'children').length > 0){
|
||||||
|
return }
|
||||||
|
// do not delete past the top element...
|
||||||
|
if(this.get(0).querySelector('.code') === next){
|
||||||
|
return }
|
||||||
|
evt.preventDefault()
|
||||||
|
var i = edited.value.length
|
||||||
|
edited.value += next.value
|
||||||
|
edited.selectionStart = i
|
||||||
|
edited.selectionEnd = i
|
||||||
|
this.remove(next) }
|
||||||
return }
|
return }
|
||||||
this.remove() },
|
this.remove() },
|
||||||
|
Backspace: function(evt){
|
||||||
|
var edited = this.get('edited')
|
||||||
|
if(edited
|
||||||
|
&& edited.selectionEnd == 0
|
||||||
|
// can't reclaim nested children...
|
||||||
|
&& this.get(edited, 'children').length == 0){
|
||||||
|
var prev = this.get('edited', 'prev')
|
||||||
|
// do not delete past the bottom element...
|
||||||
|
if(this.get(-1).querySelector('.code') === prev){
|
||||||
|
return }
|
||||||
|
evt.preventDefault()
|
||||||
|
var i = prev.value.length
|
||||||
|
prev.value += edited.value
|
||||||
|
this.edit(prev)
|
||||||
|
prev.selectionStart = i
|
||||||
|
prev.selectionEnd = i
|
||||||
|
this.remove(edited)
|
||||||
|
return } },
|
||||||
|
|
||||||
// select...
|
// select...
|
||||||
// XXX add:
|
// XXX add:
|
||||||
@ -989,9 +1128,6 @@ var Outline = {
|
|||||||
: focused.setAttribute('selected', '') },
|
: focused.setAttribute('selected', '') },
|
||||||
},
|
},
|
||||||
|
|
||||||
// XXX might be a good idea to defer specific actions to event-like
|
|
||||||
// handlers...
|
|
||||||
// e.g. clicking left if block -> .blockleft(..) ... etc.
|
|
||||||
setup: function(dom){
|
setup: function(dom){
|
||||||
var that = this
|
var that = this
|
||||||
this.dom = dom
|
this.dom = dom
|
||||||
@ -1020,8 +1156,7 @@ var Outline = {
|
|||||||
return }
|
return }
|
||||||
|
|
||||||
// expand/collapse
|
// expand/collapse
|
||||||
if(elem.classList.contains('view')
|
if(elem.classList.contains('view')){
|
||||||
&& elem.parentElement.getAttribute('tabindex')){
|
|
||||||
// click: left of elem (outside)
|
// click: left of elem (outside)
|
||||||
if(evt.offsetX < 0){
|
if(evt.offsetX < 0){
|
||||||
// XXX item menu?
|
// XXX item menu?
|
||||||
@ -1038,7 +1173,7 @@ var Outline = {
|
|||||||
// edit of focus...
|
// edit of focus...
|
||||||
// NOTE: this is usefull if element text is hidden but the
|
// NOTE: this is usefull if element text is hidden but the
|
||||||
// frame is still visible...
|
// frame is still visible...
|
||||||
if(elem.getAttribute('tabindex')){
|
if(elem.classList.contains('block')){
|
||||||
elem.querySelector('.code').focus() }
|
elem.querySelector('.code').focus() }
|
||||||
|
|
||||||
that.runPlugins('__click__', evt, that, elem) })
|
that.runPlugins('__click__', evt, that, elem) })
|
||||||
@ -1046,12 +1181,10 @@ var Outline = {
|
|||||||
outline.addEventListener('keydown',
|
outline.addEventListener('keydown',
|
||||||
function(evt){
|
function(evt){
|
||||||
var elem = evt.target
|
var elem = evt.target
|
||||||
// code editing...
|
if(that.runPlugins('__keydown__', evt, that, evt.target) !== true){
|
||||||
if(elem.nodeName == 'CODE'
|
|
||||||
&& elem.getAttribute('contenteditable') == 'true'){
|
|
||||||
return }
|
return }
|
||||||
// update element state...
|
// update element state...
|
||||||
if(elem.nodeName == 'TEXTAREA'){
|
if(elem.classList.contains('code')){
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
that.update(elem.parentElement)
|
that.update(elem.parentElement)
|
||||||
elem.updateSize() }, 0) }
|
elem.updateSize() }, 0) }
|
||||||
@ -1061,30 +1194,14 @@ var Outline = {
|
|||||||
// update code block...
|
// update code block...
|
||||||
outline.addEventListener('keyup',
|
outline.addEventListener('keyup',
|
||||||
function(evt){
|
function(evt){
|
||||||
var elem = evt.target
|
that.runPlugins('__keyup__', evt, that, evt.target) })
|
||||||
// editable code...
|
|
||||||
if(elem.nodeName == 'CODE'
|
|
||||||
&& elem.getAttribute('contenteditable') == 'true'){
|
|
||||||
// XXX should we clear the syntax???
|
|
||||||
// XXX do this only if things changed...
|
|
||||||
delete elem.dataset.highlighted
|
|
||||||
|
|
||||||
var block = that.get(elem)
|
|
||||||
var code = block.querySelector('.code')
|
|
||||||
|
|
||||||
var update = elem.innerText
|
|
||||||
var i = [...block
|
|
||||||
.querySelectorAll('.view code[contenteditable=true]')]
|
|
||||||
.indexOf(elem)
|
|
||||||
// update element content...
|
|
||||||
code.value = quoted.replace(code.value, i, update)
|
|
||||||
|
|
||||||
return } })
|
|
||||||
// toggle view/code of nodes...
|
// toggle view/code of nodes...
|
||||||
outline.addEventListener('focusin',
|
outline.addEventListener('focusin',
|
||||||
function(evt){
|
function(evt){
|
||||||
var elem = evt.target
|
var elem = evt.target
|
||||||
|
|
||||||
|
// ignore children container...
|
||||||
if(elem.classList.contains('children')){
|
if(elem.classList.contains('children')){
|
||||||
return }
|
return }
|
||||||
|
|
||||||
@ -1103,15 +1220,18 @@ var Outline = {
|
|||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
})
|
})
|
||||||
//*/
|
//*/
|
||||||
})
|
|
||||||
|
// XXX do we need this???
|
||||||
|
that.runPlugins('__focusin__', evt, that, elem) })
|
||||||
outline.addEventListener('focusout',
|
outline.addEventListener('focusout',
|
||||||
function(evt){
|
function(evt){
|
||||||
var node = evt.target
|
var elem = evt.target
|
||||||
if(node.nodeName == 'TEXTAREA'
|
if(elem.classList.contains('code')){
|
||||||
&& node?.nextElementSibling?.nodeName == 'SPAN'){
|
var block = elem.parentElement
|
||||||
var block = node.parentElement
|
that.update(block, { text: elem.value })
|
||||||
that.update(block, { text: node.value })
|
that.runPlugins('__editedcode__', evt, that, elem) }
|
||||||
that.runPlugins('__changed__', evt, that, node) } })
|
|
||||||
|
that.runPlugins('__focusout__', evt, that, elem) })
|
||||||
// update .code...
|
// update .code...
|
||||||
var update_code_timeout
|
var update_code_timeout
|
||||||
outline.addEventListener('change',
|
outline.addEventListener('change',
|
||||||
@ -1121,7 +1241,8 @@ var Outline = {
|
|||||||
update_code_timeout = setTimeout(
|
update_code_timeout = setTimeout(
|
||||||
function(){
|
function(){
|
||||||
update_code_timeout = undefined
|
update_code_timeout = undefined
|
||||||
that.sync() },
|
that.sync()
|
||||||
|
that.runPlugins('__change__', evt, that) },
|
||||||
that.code_update_interval || 5000) })
|
that.code_update_interval || 5000) })
|
||||||
|
|
||||||
// toolbar...
|
// toolbar...
|
||||||
|
|||||||
@ -14,6 +14,59 @@ HTMLTextAreaElement.prototype.autoUpdateSize = function(){
|
|||||||
function(evt){
|
function(evt){
|
||||||
that.updateSize() })
|
that.updateSize() })
|
||||||
return this }
|
return this }
|
||||||
|
HTMLTextAreaElement.prototype.getTextGeometry = function(){
|
||||||
|
var offset = this.selectionStart
|
||||||
|
var text = this.value
|
||||||
|
|
||||||
|
// get the relevant styles...
|
||||||
|
var style = getComputedStyle(this)
|
||||||
|
var s = {}
|
||||||
|
for(var i=0; i < style.length; i++){
|
||||||
|
var k = style[i]
|
||||||
|
if(k.startsWith('font')
|
||||||
|
|| k.startsWith('line')
|
||||||
|
|| k.startsWith('white-space')){
|
||||||
|
s[k] = style[k] } }
|
||||||
|
|
||||||
|
var carret = document.createElement('span')
|
||||||
|
carret.innerText = '|'
|
||||||
|
carret.style.margin = '0px'
|
||||||
|
carret.style.padding = '0px'
|
||||||
|
|
||||||
|
var span = document.createElement('span')
|
||||||
|
span.innerText = text.slice(0, offset)
|
||||||
|
Object.assign(span.style, {
|
||||||
|
...s,
|
||||||
|
|
||||||
|
position: 'fixed',
|
||||||
|
display: 'block',
|
||||||
|
top: '-100%',
|
||||||
|
left: '-100%',
|
||||||
|
width: this.offsetWidth + 'px',
|
||||||
|
height: this.scrollHeight + 'px',
|
||||||
|
|
||||||
|
padding: style.padding,
|
||||||
|
|
||||||
|
outline: 'solid 1px red',
|
||||||
|
|
||||||
|
pointerEvents: 'none',
|
||||||
|
})
|
||||||
|
span.append(carret)
|
||||||
|
|
||||||
|
document.body.append(span)
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
length: text.length,
|
||||||
|
lines: Math.floor(this.offsetHeight / carret.offsetHeight),
|
||||||
|
line: Math.floor(carret.offsetTop / carret.offsetHeight),
|
||||||
|
offset: offset,
|
||||||
|
offsetLeft: carret.offsetLeft,
|
||||||
|
offsetTop: carret.offsetTop,
|
||||||
|
}
|
||||||
|
|
||||||
|
span.remove()
|
||||||
|
|
||||||
|
return res }
|
||||||
|
|
||||||
// calculate number of lines in text area (both wrapped and actual lines)
|
// calculate number of lines in text area (both wrapped and actual lines)
|
||||||
Object.defineProperty(HTMLTextAreaElement.prototype, 'heightLines', {
|
Object.defineProperty(HTMLTextAreaElement.prototype, 'heightLines', {
|
||||||
|
|||||||
@ -44,24 +44,16 @@ var setup = function(){
|
|||||||
- // Seems that I unintentionally implemented quite a chunk of the markdown spec ;)
|
- // Seems that I unintentionally implemented quite a chunk of the markdown spec ;)
|
||||||
-
|
-
|
||||||
- ## Bugs:
|
- ## Bugs:
|
||||||
- BUG: ASAP: editor: `-` at start of line is interpreted as block marker...
|
- BUG: last node seems to get trash tags added to it's end...
|
||||||
- need to either:
|
|
||||||
- quote the `-` in .text() -- _preferreed_
|
|
||||||
- split the lines starting with `-` into nested nodes (a-la .load())
|
|
||||||
- BUG? pressing down from a longer line will jump over a shorter line
|
|
||||||
- to reproduce\:
|
|
||||||
- here is the line to jump from, for example from here
|
|
||||||
an we'll not get here...
|
|
||||||
-
|
-
|
||||||
- ## ToDo:
|
- ## ToDo:
|
||||||
- ASAP: editor: backsapce/del at start/end of a block should join it with prev/next
|
|
||||||
- ASAP: editor: pressing enter in text edit mode should split text into two blocks
|
|
||||||
- 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: 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(..)`
|
||||||
|
- a way to make a block monospace (???)
|
||||||
|
- show list bullet if node is empty but edited... (???)
|
||||||
- Code blocks and bullets:
|
- Code blocks and bullets:
|
||||||
- ```
|
- ```
|
||||||
code
|
code
|
||||||
@ -76,6 +68,7 @@ var setup = function(){
|
|||||||
- delete node
|
- delete node
|
||||||
- indent/deindent
|
- indent/deindent
|
||||||
- edit node
|
- edit node
|
||||||
|
- auto-shift done blocks to the end of siblings... (option?)
|
||||||
- FEATURE? block templates...
|
- FEATURE? block templates...
|
||||||
collapsed:: true
|
collapsed:: true
|
||||||
- something like: `TPL: [_] <editable/> -- <editable/>`
|
- something like: `TPL: [_] <editable/> -- <editable/>`
|
||||||
@ -92,7 +85,7 @@ var setup = function(){
|
|||||||
- #### Click in this line and see where the cursor goes
|
- #### Click in this line and see where the cursor goes
|
||||||
- _not sure how..._
|
- _not sure how..._
|
||||||
- Q: persistent empty first/last node (a button to create a new node)?
|
- Q: persistent empty first/last node (a button to create a new node)?
|
||||||
- Q: should bullets be on the same level as nodes or offset??
|
- Q: should list bullets be on the same level as nodes or offset??
|
||||||
collapsed:: true
|
collapsed:: true
|
||||||
- A) justified to bullet:
|
- A) justified to bullet:
|
||||||
* list item
|
* list item
|
||||||
@ -104,6 +97,9 @@ var setup = function(){
|
|||||||
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???
|
||||||
- empty item height is a bit off...
|
- empty item height is a bit off...
|
||||||
|
- DONE editor: backsapce/del at start/end of a block should join it with prev/next
|
||||||
|
- DONE editor: pressing enter in text edit mode should split text into two blocks
|
||||||
|
- DONE editor: shifting nodes up/down
|
||||||
- DONE Q: can we edit code in a code block directly? (a-la Logseq)
|
- DONE Q: can we edit code in a code block directly? (a-la Logseq)
|
||||||
- DONE "percentage complete" in parent blocks with todo's nested
|
- DONE "percentage complete" in parent blocks with todo's nested
|
||||||
- DONE `.editor .outline:empty` view and behavior...
|
- DONE `.editor .outline:empty` view and behavior...
|
||||||
@ -122,14 +118,16 @@ var setup = function(){
|
|||||||
-
|
-
|
||||||
- ## Refactoring:
|
- ## Refactoring:
|
||||||
- Plugin architecture
|
- Plugin architecture
|
||||||
- Item parser (`.__code2html__(..)`)
|
- DONE basic structure
|
||||||
- ~split out~
|
- plugin handler sequencing (see: `.setup(..)`)
|
||||||
- ~define~/doc api
|
- plugin handler canceling
|
||||||
- ~define a way to extend/stack parsers~
|
- DONE Item parser (`.__code2html__(..)`)
|
||||||
|
- DONE split out
|
||||||
|
- DONE define a way to extend/stack parsers
|
||||||
- Format parser/generator
|
- Format parser/generator
|
||||||
- split out
|
- split out
|
||||||
- define api
|
- define api
|
||||||
- experiment with clean markdown as format
|
- experiment with clean _markdown_ as format
|
||||||
- CSS
|
- CSS
|
||||||
- separate out theming
|
- separate out theming
|
||||||
- separate out settings
|
- separate out settings
|
||||||
@ -139,8 +137,25 @@ var setup = function(){
|
|||||||
- Q: do we need `features.js` and/or `actions.js`
|
- Q: do we need `features.js` and/or `actions.js`
|
||||||
- Q: do we need a concatenative API??
|
- Q: do we need a concatenative API??
|
||||||
- `<block>.get() -> <block>`
|
- `<block>.get() -> <block>`
|
||||||
|
- Docs
|
||||||
-
|
-
|
||||||
- ## TEST
|
- ## TEST
|
||||||
|
- ### Controls
|
||||||
|
- ASAP: these need updating...
|
||||||
|
- | Key | Action |
|
||||||
|
| up | focus node above |
|
||||||
|
| down | focus node below |
|
||||||
|
| left | focus parent node |
|
||||||
|
| right | focus first child node |
|
||||||
|
| tab | indent node |
|
||||||
|
| s-tab | deindent node |
|
||||||
|
| s-pgup | shift node up |
|
||||||
|
| s-pgdown | shift node down |
|
||||||
|
| s-left | collapse node |
|
||||||
|
| s-right | expand node |
|
||||||
|
| enter | normal mode: edit node |
|
||||||
|
| | edit mode: create node below |
|
||||||
|
| esc | exit edit mode |
|
||||||
- ### Formatting:
|
- ### Formatting:
|
||||||
- Styles
|
- Styles
|
||||||
- # Heading 1
|
- # Heading 1
|
||||||
@ -235,7 +250,8 @@ var setup = function(){
|
|||||||
- This is a line of text
|
- This is a line of text
|
||||||
- This is a set
|
- This is a set
|
||||||
text lines
|
text lines
|
||||||
- Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text </pre>
|
- Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text Lots of text
|
||||||
|
- </pre>
|
||||||
<!-- outline -->
|
<!-- outline -->
|
||||||
<div class="outline"></div>
|
<div class="outline"></div>
|
||||||
<!-- toolbar (optional) -->
|
<!-- toolbar (optional) -->
|
||||||
@ -248,28 +264,13 @@ var setup = function(){
|
|||||||
<button onclick="editor.toggleCollapse()?.focus()">˅˄</button>
|
<button onclick="editor.toggleCollapse()?.focus()">˅˄</button>
|
||||||
<button onclick="editor.remove()">×</button>
|
<button onclick="editor.remove()">×</button>
|
||||||
</div-->
|
</div-->
|
||||||
|
<span class="__textarea"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<button onclick="editor.dom.classList.toggle('show-click-zones')">show/hide click zones</button>
|
<button onclick="editor.dom.classList.toggle('show-click-zones')">show/hide click zones</button>
|
||||||
|
|
||||||
<pre>
|
|
||||||
|
|
||||||
Controls:
|
|
||||||
up - focus node above
|
|
||||||
down - focus node below
|
|
||||||
left - focus parent node
|
|
||||||
right - focus first child node
|
|
||||||
tab - indent node
|
|
||||||
s-tab - deindent node
|
|
||||||
s-left - collapse node
|
|
||||||
s-right - expand node
|
|
||||||
enter - normal mode: edit node
|
|
||||||
- edit mode: create node below
|
|
||||||
esc - exit edit mode
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user