mirror of
https://github.com/flynx/pWiki.git
synced 2025-12-25 12:21:58 +00:00
Compare commits
7 Commits
9489220ec3
...
6c484194ae
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c484194ae | |||
| dc30fdfec5 | |||
| 0511ac879f | |||
| 477e5be961 | |||
| d86273c484 | |||
| 250b07be11 | |||
| 7828a10bf9 |
@ -138,23 +138,25 @@ var plugin = {
|
|||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
// XXX style attributes...
|
||||||
var attributes = {
|
var attributes = {
|
||||||
__proto__: plugin,
|
__proto__: plugin,
|
||||||
|
|
||||||
__pre_parse__: function(text, editor, elem){
|
__parse__: function(text, editor, elem){
|
||||||
|
var skip = new Set([
|
||||||
|
'text',
|
||||||
|
'focused',
|
||||||
|
'collapsed',
|
||||||
|
'id',
|
||||||
|
'children',
|
||||||
|
'style',
|
||||||
|
])
|
||||||
return text
|
return text
|
||||||
// hidden attributes...
|
+ Object.entries(elem)
|
||||||
// XXX make this generic...
|
.reduce(function(res, [key, value]){
|
||||||
// collapsed...
|
return skip.has(key) ?
|
||||||
.replace(/(\n|^)\s*collapsed::\s*(.*)\s*(\n|$)/,
|
res
|
||||||
function(_, value){
|
: res + `<br>${key}: ${value}` }, '') },
|
||||||
elem.collapsed = value.trim() == 'true'
|
|
||||||
return '' })
|
|
||||||
// id...
|
|
||||||
.replace(/(\n|^)\s*id::\s*(.*)\s*(\n|$)/,
|
|
||||||
function(_, value){
|
|
||||||
elem.id = value.trim()
|
|
||||||
return '' }) },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -614,7 +616,6 @@ var Outline = {
|
|||||||
// - parsing
|
// - parsing
|
||||||
// - event dropping
|
// - event dropping
|
||||||
plugins: [
|
plugins: [
|
||||||
attributes,
|
|
||||||
blocks,
|
blocks,
|
||||||
quoted,
|
quoted,
|
||||||
|
|
||||||
@ -622,6 +623,8 @@ var Outline = {
|
|||||||
// treating '[_] ... [_]' as italic...
|
// treating '[_] ... [_]' as italic...
|
||||||
tasks,
|
tasks,
|
||||||
styling,
|
styling,
|
||||||
|
// XXX
|
||||||
|
//attributes,
|
||||||
tables,
|
tables,
|
||||||
symbols,
|
symbols,
|
||||||
//syntax,
|
//syntax,
|
||||||
@ -838,26 +841,28 @@ var Outline = {
|
|||||||
update: function(node='focused', data){
|
update: function(node='focused', data){
|
||||||
var node = this.get(node)
|
var node = this.get(node)
|
||||||
data ??= this.data(node, false)
|
data ??= this.data(node, false)
|
||||||
for(var [attr, value] of Object.entries(data)){
|
|
||||||
if(attr == 'children'){
|
|
||||||
continue }
|
|
||||||
|
|
||||||
if(attr == 'text'){
|
var parsed = {}
|
||||||
var text = node.querySelector('textarea')
|
if('text' in data){
|
||||||
var html = node.querySelector('span')
|
var text = node.querySelector('textarea')
|
||||||
if(this.__code2html__){
|
var html = node.querySelector('span')
|
||||||
// NOTE: we are ignoring the .collapsed attr here
|
if(this.__code2html__){
|
||||||
var parsed = this.__code2html__(data.text)
|
// NOTE: we are ignoring the .collapsed attr here
|
||||||
html.innerHTML = parsed.text
|
parsed = this.__code2html__(data.text, {...data})
|
||||||
// heading...
|
html.innerHTML = parsed.text
|
||||||
node.classList.remove(...this.__styles)
|
// heading...
|
||||||
parsed.style
|
node.classList.remove(...this.__styles)
|
||||||
&& node.classList.add(...parsed.style)
|
parsed.style
|
||||||
} else {
|
&& node.classList.add(...parsed.style)
|
||||||
html.innerHTML = data.text }
|
delete parsed.style
|
||||||
text.value = data.text
|
} else {
|
||||||
// XXX this does not seem to work until we click in the textarea...
|
html.innerHTML = data.text }
|
||||||
text.autoUpdateSize()
|
text.value = data.text
|
||||||
|
// XXX this does not seem to work until we click in the textarea...
|
||||||
|
text.autoUpdateSize() }
|
||||||
|
|
||||||
|
for(var [attr, value] of Object.entries({...data, ...parsed})){
|
||||||
|
if(attr == 'children' || attr == 'text'){
|
||||||
continue }
|
continue }
|
||||||
|
|
||||||
var type = this.__block_attrs__[attr]
|
var type = this.__block_attrs__[attr]
|
||||||
@ -872,8 +877,7 @@ var Outline = {
|
|||||||
(value ?
|
(value ?
|
||||||
node.setAttribute(attr, '')
|
node.setAttribute(attr, '')
|
||||||
: node.removeAttribute(attr))
|
: node.removeAttribute(attr))
|
||||||
: (attr in data
|
: value != null ?
|
||||||
&& value != null) ?
|
|
||||||
node.setAttribute(attr, value)
|
node.setAttribute(attr, value)
|
||||||
: node.removeAttribute(attr) } }
|
: node.removeAttribute(attr) } }
|
||||||
this.__change__()
|
this.__change__()
|
||||||
@ -988,16 +992,13 @@ var Outline = {
|
|||||||
this.__change__()
|
this.__change__()
|
||||||
return this },
|
return this },
|
||||||
|
|
||||||
// block serialization...
|
// block render...
|
||||||
// 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__(..)
|
||||||
__styles: undefined,
|
__styles: undefined,
|
||||||
__code2html__: function(code){
|
__code2html__: function(code, elem={}){
|
||||||
var that = this
|
var that = this
|
||||||
var elem = {
|
|
||||||
collapsed: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// only whitespace -> keep element blank...
|
// only whitespace -> keep element blank...
|
||||||
if(code.trim() == ''){
|
if(code.trim() == ''){
|
||||||
@ -1013,6 +1014,9 @@ var Outline = {
|
|||||||
}[stage]
|
}[stage]
|
||||||
return that.threadPlugins(meth, text, that, elem) }
|
return that.threadPlugins(meth, text, that, elem) }
|
||||||
|
|
||||||
|
elem = this.parseBlockAttrs(code, elem)
|
||||||
|
code = elem.text
|
||||||
|
|
||||||
// stage: pre...
|
// stage: pre...
|
||||||
var text = run('pre',
|
var text = run('pre',
|
||||||
// pre-sanitize...
|
// pre-sanitize...
|
||||||
@ -1115,6 +1119,54 @@ var Outline = {
|
|||||||
.flat()
|
.flat()
|
||||||
.join('\n') },
|
.join('\n') },
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parse attrs...
|
||||||
|
// .parseBlockAttrs(<text>[, <elem>])
|
||||||
|
// -> <elem>
|
||||||
|
//
|
||||||
|
// Parse attrs keeping non-system attrs in .text...
|
||||||
|
// .parseBlockAttrs(<text>, true[, <elem>])
|
||||||
|
// -> <elem>
|
||||||
|
//
|
||||||
|
// Parse attrs keeping all attrs in .text...
|
||||||
|
// .parseBlockAttrs(<text>, 'all'[, <elem>])
|
||||||
|
// -> <elem>
|
||||||
|
//
|
||||||
|
parseBlockAttrs: function(text, keep=false, elem={}){
|
||||||
|
if(typeof(keep) == 'object'){
|
||||||
|
elem = keep
|
||||||
|
keep = typeof(elem) == 'boolean' ?
|
||||||
|
elem
|
||||||
|
: false }
|
||||||
|
var system = this.__block_attrs__
|
||||||
|
var clean = text
|
||||||
|
// XXX for some reason changing the first group into (?<= .. )
|
||||||
|
// still eats up the whitespace...
|
||||||
|
// ...putting the same pattern in a normal group and
|
||||||
|
// returning it works fine...
|
||||||
|
//.replace(/(?<=[\n\h]*)(?:(?:\n|^)\s*\w*\s*::\s*[^\n]*\s*)*$/,
|
||||||
|
.replace(/([\n\t ]*)(?:(?:\n|^)[\t ]*\w*[\t ]*::[\t ]*[^\n]*[\t ]*)+$/,
|
||||||
|
function(match, ws){
|
||||||
|
var attrs = match
|
||||||
|
.trim()
|
||||||
|
.split(/(?:[\t ]*::[\t ]*|[\t ]*\n[\t ]*)/g)
|
||||||
|
while(attrs.length > 0){
|
||||||
|
var [name, val] = attrs.splice(0, 2)
|
||||||
|
elem[name] =
|
||||||
|
val == 'true' ?
|
||||||
|
true
|
||||||
|
: val == 'false' ?
|
||||||
|
false
|
||||||
|
: val
|
||||||
|
// keep non-system attrs...
|
||||||
|
if(keep
|
||||||
|
&& !(name in system)){
|
||||||
|
ws += `\n${name}::${val}` } }
|
||||||
|
return ws })
|
||||||
|
elem.text = keep == 'all' ?
|
||||||
|
text
|
||||||
|
: clean
|
||||||
|
return elem },
|
||||||
parse: function(text){
|
parse: function(text){
|
||||||
var that = this
|
var that = this
|
||||||
text = text
|
text = text
|
||||||
@ -1134,29 +1186,14 @@ var Outline = {
|
|||||||
// same level...
|
// same level...
|
||||||
if(sep.length == prev_sep.length){
|
if(sep.length == prev_sep.length){
|
||||||
var [_, block] = lst.splice(0, 2)
|
var [_, block] = lst.splice(0, 2)
|
||||||
var attrs = {
|
var attrs = that.parseBlockAttrs(block)
|
||||||
|
attrs.text = that.__text2code__(attrs.text
|
||||||
|
// normalize indent...
|
||||||
|
.split(new RegExp('\n'+sep+' ', 'g'))
|
||||||
|
.join('\n'))
|
||||||
|
parent.push({
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
focused: false,
|
focused: false,
|
||||||
}
|
|
||||||
block = block
|
|
||||||
// XXX generalize attr processing...
|
|
||||||
.replace(/\n\s*id::\s*(.*)\s*$/,
|
|
||||||
function(_, value){
|
|
||||||
attrs.id = value
|
|
||||||
return '' })
|
|
||||||
.replace(/\n\s*focused::\s*(.*)\s*$/,
|
|
||||||
function(_, value){
|
|
||||||
attrs.focused = value == 'true'
|
|
||||||
return '' })
|
|
||||||
.replace(/\n\s*collapsed::\s*(.*)\s*$/,
|
|
||||||
function(_, value){
|
|
||||||
attrs.collapsed = value == 'true'
|
|
||||||
return '' })
|
|
||||||
parent.push({
|
|
||||||
text: that.__text2code__(block
|
|
||||||
// normalize indent...
|
|
||||||
.split(new RegExp('\n'+sep+' ', 'g'))
|
|
||||||
.join('\n')),
|
|
||||||
...attrs,
|
...attrs,
|
||||||
children: [],
|
children: [],
|
||||||
})
|
})
|
||||||
@ -1190,6 +1227,7 @@ var Outline = {
|
|||||||
children.classList.add('children')
|
children.classList.add('children')
|
||||||
children.setAttribute('tabindex', '-1')
|
children.setAttribute('tabindex', '-1')
|
||||||
block.append(code, html, children)
|
block.append(code, html, children)
|
||||||
|
|
||||||
this.update(block, data)
|
this.update(block, data)
|
||||||
|
|
||||||
// place...
|
// place...
|
||||||
@ -1326,6 +1364,18 @@ var Outline = {
|
|||||||
this.toggleCollapse(false)
|
this.toggleCollapse(false)
|
||||||
: this.focus('next') } },
|
: this.focus('next') } },
|
||||||
|
|
||||||
|
Home: function(evt){
|
||||||
|
if(this.get('edited')
|
||||||
|
&& !evt.ctrlKey){
|
||||||
|
return }
|
||||||
|
evt.preventDefault()
|
||||||
|
this.focus(0) },
|
||||||
|
End: function(evt){
|
||||||
|
if(this.get('edited')
|
||||||
|
&& !evt.ctrlKey){
|
||||||
|
return }
|
||||||
|
evt.preventDefault()
|
||||||
|
this.focus(-1) },
|
||||||
PageUp: function(evt){
|
PageUp: function(evt){
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
if(!edited
|
if(!edited
|
||||||
@ -1341,7 +1391,7 @@ var Outline = {
|
|||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
this.shift('down') } },
|
this.shift('down') } },
|
||||||
|
|
||||||
// indent...
|
// indent..
|
||||||
Tab: function(evt){
|
Tab: function(evt){
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
var edited = this.get('edited')
|
var edited = this.get('edited')
|
||||||
@ -1519,7 +1569,7 @@ var Outline = {
|
|||||||
// update element state...
|
// update element state...
|
||||||
if(elem.classList.contains('code')){
|
if(elem.classList.contains('code')){
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
that.update(elem.parentElement)
|
that.update(elem.parentElement)
|
||||||
elem.updateSize() }, 0) }
|
elem.updateSize() }, 0) }
|
||||||
// handle keyboard...
|
// handle keyboard...
|
||||||
evt.key in that.keyboard
|
evt.key in that.keyboard
|
||||||
@ -1561,7 +1611,9 @@ var Outline = {
|
|||||||
var elem = evt.target
|
var elem = evt.target
|
||||||
if(elem.classList.contains('code')){
|
if(elem.classList.contains('code')){
|
||||||
var block = elem.parentElement
|
var block = elem.parentElement
|
||||||
that.update(block, { text: elem.value })
|
// clean out attrs...
|
||||||
|
elem.value = that.parseBlockAttrs(elem.value).text
|
||||||
|
that.update(block)
|
||||||
// give the browser a chance to update the DOM...
|
// give the browser a chance to update the DOM...
|
||||||
// XXX revise...
|
// XXX revise...
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
|
|||||||
@ -38,24 +38,18 @@ var setup = function(){
|
|||||||
- An outline-based markdown editor experiment
|
- An outline-based markdown editor experiment
|
||||||
- ### Infuences::
|
- ### Infuences::
|
||||||
- Logseq
|
- Logseq
|
||||||
- Tomboy
|
- Conboy (Nokia N900's Tomboy clone)
|
||||||
- Bonsai
|
- Bonsai
|
||||||
-
|
-
|
||||||
- // 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: editor: FF seems to update the style every other key press -- should be live...
|
- BUG: editor: FF seems to update the style every other key press -- should be live...
|
||||||
|
- BUG: scrolling into view needs tuning...
|
||||||
|
- BUG: mobile browsers behave quite chaotically ignoring parts of the styling...
|
||||||
-
|
-
|
||||||
- ## ToDo:
|
- ## ToDo:
|
||||||
- ASAP: unify attr parsing
|
- pgup/pgdown/~home/end~ buttons
|
||||||
- _now duplicated in `.parse(..)` and `.__code2html__(..)`_
|
|
||||||
- might be a good idea to add a special text parse stage and use in on both branches...
|
|
||||||
- ASAP: attrs in editor are not parsed correctly (see: [attrs](#attributes))
|
|
||||||
- ASAP: multiple attrs are not handled correctly (see: [attrs](#attributes))
|
|
||||||
- ASAP: style attrs (see: [attrs](#attributes))
|
|
||||||
- ASAP: scroll into view is bad...
|
|
||||||
- ASAP: mobile browsers behave quite chaotically ignoring parts of the styling...
|
|
||||||
- pgup/pgdown/home/end buttons
|
|
||||||
- identify a block (index, id, ...)
|
- identify a block (index, id, ...)
|
||||||
- FEATURE: "crop" -- view block tree separately...
|
- FEATURE: "crop" -- view block tree separately...
|
||||||
- focus
|
- focus
|
||||||
@ -81,6 +75,7 @@ var setup = function(){
|
|||||||
- embed css
|
- embed css
|
||||||
- cleanup html
|
- cleanup html
|
||||||
- generate ideomatic html (???)
|
- generate ideomatic html (???)
|
||||||
|
- style attrs (see: [attrs](#attributes))
|
||||||
- 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(..)`
|
||||||
- table inline editing a-la code editing -- click cell and edit directly...
|
- table inline editing a-la code editing -- click cell and edit directly...
|
||||||
@ -111,6 +106,12 @@ 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 unify attr parsing
|
||||||
|
collapsed:: true
|
||||||
|
- _now duplicated in `.parse(..)` and `.__code2html__(..)`_
|
||||||
|
- might be a good idea to add a special text parse stage and use in on both branches...
|
||||||
|
- DONE attrs in editor are not parsed correctly (see: [attrs](#attributes))
|
||||||
|
- DONE multiple attrs are not handled correctly (see: [attrs](#attributes))
|
||||||
- DONE call `.sync()` on all changes...
|
- DONE call `.sync()` on all changes...
|
||||||
- DONE show list bullet if node is empty but edited...
|
- DONE show list bullet if node is empty but edited...
|
||||||
collapsed:: true
|
collapsed:: true
|
||||||
@ -263,7 +264,8 @@ var setup = function(){
|
|||||||
- [_] we can also add inline [x] checkboxes and states: [%]
|
- [_] we can also add inline [x] checkboxes and states: [%]
|
||||||
- navigating checkboxes in view mode can be done via `ctrl-left` / `ctrl-right` and toggling is done via `space`
|
- navigating checkboxes in view mode can be done via `ctrl-left` / `ctrl-right` and toggling is done via `space`
|
||||||
- links
|
- links
|
||||||
- [example](about:blank)
|
- [link](about:blank)
|
||||||
|
- [local links](#attributes)
|
||||||
- https://example.com
|
- https://example.com
|
||||||
- ./path/to/file /path/to -- _not supported yet_
|
- ./path/to/file /path/to -- _not supported yet_
|
||||||
- Tables
|
- Tables
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user