mirror of
https://github.com/flynx/pWiki.git
synced 2026-10-03 12:51:40 +00:00
Compare commits
5 Commits
e8b0cb435c
...
79ffe58b46
| Author | SHA1 | Date | |
|---|---|---|---|
| 79ffe58b46 | |||
| 7075dce017 | |||
| 7b0aab07da | |||
| 97fb6ee856 | |||
| 12d77832a5 |
@ -157,7 +157,7 @@ use.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### slot (<name> <text> shown|hidden) / content
|
### slot (_name_ _text_ shown|hidden) / content
|
||||||
|
|
||||||
```
|
```
|
||||||
@slot(<name>)
|
@slot(<name>)
|
||||||
@ -229,6 +229,107 @@ Will resolve to `This is: [[ some text ]]`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### include (_src_ _recursive_ _join_ isolated) / content
|
||||||
|
|
||||||
|
```
|
||||||
|
@include(<src>)
|
||||||
|
@include(<src> [recursive=".."] [join=".."] [isolated])
|
||||||
|
@include(<src> [recursive=".."] [join=".."] [isolated="partial"])
|
||||||
|
|
||||||
|
<include <src>>
|
||||||
|
...
|
||||||
|
<content/>
|
||||||
|
...
|
||||||
|
</include>
|
||||||
|
```
|
||||||
|
|
||||||
|
Include a page. The included page is rendered relative to the spurce page
|
||||||
|
resolved from `<src>`, independently from current page, but in the current
|
||||||
|
namespace, and is inserted as-is.
|
||||||
|
|
||||||
|
If body is not empty, it will render instead of included page content.
|
||||||
|
If body contains `<content/>` macro, it will be replaced with included
|
||||||
|
page content.
|
||||||
|
If more than one page matches `<src>`, the body will be copied once per
|
||||||
|
page and the page's content will be inserted into each respective `<content/>`
|
||||||
|
macro.
|
||||||
|
|
||||||
|
`join` is inserted between pages if more than one page is matched by `<src>`.
|
||||||
|
|
||||||
|
If `isolated` is given, prevent slots from included page from affecting
|
||||||
|
the including page.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
`/some/page`:
|
||||||
|
```
|
||||||
|
<slot X value/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Normal case: render included page but in view of current namespace:
|
||||||
|
```
|
||||||
|
<slot X original/><include src=/some/page> [ <content/> ] </include>
|
||||||
|
```
|
||||||
|
Will render to: `value [ ]`
|
||||||
|
|
||||||
|
|
||||||
|
Completely isolated:
|
||||||
|
```
|
||||||
|
<slot X original/><include src=/some/page isolated> [ <content/> ] </include>
|
||||||
|
```
|
||||||
|
Will render to: `original [ value ]`
|
||||||
|
|
||||||
|
|
||||||
|
Isolated up, i.e. the included page sees local state but can not affect it:
|
||||||
|
```
|
||||||
|
<slot X original/><include src=/some/page isolated=partial> [ <content/> ] </include>
|
||||||
|
```
|
||||||
|
Will render to: `original [ ]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### source (src)
|
||||||
|
|
||||||
|
```
|
||||||
|
@source(<src>)
|
||||||
|
|
||||||
|
<source <src>>
|
||||||
|
...
|
||||||
|
<content/>
|
||||||
|
...
|
||||||
|
</source>
|
||||||
|
```
|
||||||
|
|
||||||
|
Load page source into the current page.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### quote (src)
|
||||||
|
|
||||||
|
Insert a page without rendering. This is similar to include but will not
|
||||||
|
render the page.
|
||||||
|
|
||||||
|
The difference between `source` and `quote` is:
|
||||||
|
- _source_ includes the page as-is
|
||||||
|
- _quotes_ escapes the page (i.e. _quotes_ it's source) for its code to
|
||||||
|
display in the rendered HTML correctly.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- `src` -- path to source page.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
<pwiki-comment>
|
||||||
|
- [bootstrap css](/bootstrap/Templates/_css.html)
|
||||||
|
</pwiki-comment>
|
||||||
|
|
||||||
|
<!--[pWiki[
|
||||||
|
[Templates/\_css] / [bootstrap css](bootstrap/Templates/_css.html):
|
||||||
|
```
|
||||||
|
@source(Templates/_css)
|
||||||
|
```
|
||||||
|
]]-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -261,51 +362,6 @@ macro at the end of the page._
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### include (src isolated text)
|
|
||||||
|
|
||||||
Include a page. The included page is rendered independently from current
|
|
||||||
page and is inserted as-is in macro body.
|
|
||||||
|
|
||||||
Note that this will produce a `include` tag in the code that contains
|
|
||||||
the included page, this makes this tag not suitable for use anywhere
|
|
||||||
but an html element body.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
- `src` -- path to source page.
|
|
||||||
- `isolated` -- prevent slots from included page from affecting the including page.
|
|
||||||
- `text` -- is used when recursive include is detected and ignored otherwise.
|
|
||||||
|
|
||||||
_For examples see `slot` macro exaples below._
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### source (src) / quote (src)
|
|
||||||
|
|
||||||
Insert a page without rendering. This is similar to include but will not
|
|
||||||
render the page.
|
|
||||||
|
|
||||||
The difference between `source` and `quote` is:
|
|
||||||
- _source_ includes the page as-is
|
|
||||||
- _quotes_ escapes the page (i.e. _quotes_ it's source) for its code to
|
|
||||||
display in the rendered HTML correctly.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
- `src` -- path to source page.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
|
|
||||||
<pwiki-comment>
|
|
||||||
- [bootstrap css](/bootstrap/Templates/_css.html)
|
|
||||||
</pwiki-comment>
|
|
||||||
|
|
||||||
<!--[pWiki[
|
|
||||||
[Templates/\_css] / [bootstrap css](bootstrap/Templates/_css.html):
|
|
||||||
```
|
|
||||||
@source(Templates/_css)
|
|
||||||
```
|
|
||||||
]]-->
|
|
||||||
|
|
||||||
|
|
||||||
### macro (name src sort) / else ()
|
### macro (name src sort) / else ()
|
||||||
|
|
||||||
Apply macro to source page and include the result.
|
Apply macro to source page and include the result.
|
||||||
|
|||||||
@ -278,6 +278,7 @@ module.BaseParser = {
|
|||||||
|
|
||||||
return blocks },
|
return blocks },
|
||||||
|
|
||||||
|
|
||||||
// Strip comments...
|
// Strip comments...
|
||||||
//
|
//
|
||||||
stripComments: function(str){
|
stripComments: function(str){
|
||||||
@ -442,7 +443,7 @@ module.BaseParser = {
|
|||||||
//
|
//
|
||||||
// NOTE: this internaly uses .macros to check for propper nesting
|
// NOTE: this internaly uses .macros to check for propper nesting
|
||||||
//group: function*(page, lex, to=false){
|
//group: function*(page, lex, to=false){
|
||||||
group: function*(lex, to=false, parent, context){
|
group: function*(lex, to=false, context){
|
||||||
lex = typeof(lex) != 'object' ?
|
lex = typeof(lex) != 'object' ?
|
||||||
this.lex(lex)
|
this.lex(lex)
|
||||||
: lex
|
: lex
|
||||||
@ -510,7 +511,6 @@ module.BaseParser = {
|
|||||||
value.args.body
|
value.args.body
|
||||||
?? value.args.text,
|
?? value.args.text,
|
||||||
false,
|
false,
|
||||||
parent,
|
|
||||||
value.name)] }
|
value.name)] }
|
||||||
delete value.args.body
|
delete value.args.body
|
||||||
delete value.args.text
|
delete value.args.text
|
||||||
@ -597,7 +597,7 @@ module.BaseParser = {
|
|||||||
// sync (text) and async (DOM) rendering
|
// sync (text) and async (DOM) rendering
|
||||||
// in the simplest form: take the expanded AST and merge
|
// in the simplest form: take the expanded AST and merge
|
||||||
// into a single string
|
// into a single string
|
||||||
expand: function(page, ast, state={}){
|
expand: function(page, ast, state={}, nested_handlers={}){
|
||||||
var that = this
|
var that = this
|
||||||
ast = typeof(ast) != 'object' ?
|
ast = typeof(ast) != 'object' ?
|
||||||
this.group(ast)
|
this.group(ast)
|
||||||
@ -607,8 +607,9 @@ module.BaseParser = {
|
|||||||
|
|
||||||
var elems = []
|
var elems = []
|
||||||
for(let elem of ast){
|
for(let elem of ast){
|
||||||
|
|
||||||
// text block...
|
// text block...
|
||||||
if(typeof(elem) == 'string'){
|
if(typeof(elem) != 'object'){
|
||||||
elems.push(elem)
|
elems.push(elem)
|
||||||
continue }
|
continue }
|
||||||
// do not re-expand expanded elements...
|
// do not re-expand expanded elements...
|
||||||
@ -618,7 +619,6 @@ module.BaseParser = {
|
|||||||
continue }
|
continue }
|
||||||
|
|
||||||
// cleanup...
|
// cleanup...
|
||||||
//elem = {...elem}
|
|
||||||
elem = serialize.partialDeepCopy(elem)
|
elem = serialize.partialDeepCopy(elem)
|
||||||
delete elem.error
|
delete elem.error
|
||||||
delete elem.value
|
delete elem.value
|
||||||
@ -626,9 +626,15 @@ module.BaseParser = {
|
|||||||
|
|
||||||
var {name, args, body} = elem
|
var {name, args, body} = elem
|
||||||
|
|
||||||
// nested macro -- skip...
|
// nested macro...
|
||||||
if(that.macros[name] instanceof Array){
|
if(that.macros[name] instanceof Array){
|
||||||
elems.push(elem)
|
// call handler...
|
||||||
|
if(nested_handlers[name]){
|
||||||
|
elems.push(
|
||||||
|
nested_handlers[name].call(that, elem))
|
||||||
|
// skip...
|
||||||
|
} else {
|
||||||
|
elems.push(elem) }
|
||||||
continue }
|
continue }
|
||||||
// drop non-macros/aliases...
|
// drop non-macros/aliases...
|
||||||
if(typeof(that.macros[name]) != 'function'
|
if(typeof(that.macros[name]) != 'function'
|
||||||
@ -639,7 +645,7 @@ module.BaseParser = {
|
|||||||
// XXX cache this as an AST
|
// XXX cache this as an AST
|
||||||
body
|
body
|
||||||
&& !that.macros[name].lazy
|
&& !that.macros[name].lazy
|
||||||
&& (body = this.expand(page, body, state))
|
&& (body = this.expand(page, body, state, nested_handlers))
|
||||||
|
|
||||||
// call macro...
|
// call macro...
|
||||||
var res = that.callMacro(page, name, args, body, state)
|
var res = that.callMacro(page, name, args, body, state)
|
||||||
@ -731,10 +737,10 @@ module.BaseParser = {
|
|||||||
// XXX it looks like the first slot is resolved before the last
|
// XXX it looks like the first slot is resolved before the last
|
||||||
// slot has a chance to set the value as it is waiting for
|
// slot has a chance to set the value as it is waiting for
|
||||||
// the first slot to finish...
|
// the first slot to finish...
|
||||||
resolve: function(page, ast, state={}){
|
resolve: function(page, ast, state={}, nested_handlers={}){
|
||||||
var that = this
|
var that = this
|
||||||
ast = typeof(ast) != 'object' ?
|
ast = typeof(ast) != 'object' ?
|
||||||
this.expand(page, ast, state)
|
this.expand(page, ast, state, nested_handlers)
|
||||||
: ast instanceof types.Generator ?
|
: ast instanceof types.Generator ?
|
||||||
ast
|
ast
|
||||||
: ast.iter()
|
: ast.iter()
|
||||||
@ -798,21 +804,21 @@ module.BaseParser = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// XXX
|
// XXX
|
||||||
parse: function(page, ast, state={}, wait='wait'){
|
parse: function(page, ast, state={}, nested_handlers={}, wait='wait'){
|
||||||
var that = this
|
var that = this
|
||||||
var reresolve = !!state.wait
|
var reresolve = !!state.wait
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.resolve(page, ast, state),
|
this.resolve(page, ast, state, nested_handlers),
|
||||||
state[wait],
|
state[wait],
|
||||||
function(ast, reresolve){
|
function(ast, reresolve){
|
||||||
ast = reresolve ?
|
ast = reresolve ?
|
||||||
that.resolve(page, ast, state)
|
that.resolve(page, ast, state, nested_handlers)
|
||||||
: ast
|
: ast
|
||||||
return (ast ?? '').join('') }) },
|
return (ast ?? '').join('') }) },
|
||||||
// XXX how should this play with filters???
|
// XXX how should this play with filters???
|
||||||
// ...should filters be client-side only??
|
// ...should filters be client-side only??
|
||||||
parseNested: function(page, ast, state={}){
|
parseNested: function(page, ast, state={}, nested_handlers={}){
|
||||||
return this.parse(page, ast, state, 'waitNestedi') },
|
return this.parse(page, ast, state, nested_handlers, 'waitNestedi') },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1409,7 +1415,8 @@ module.parser = {
|
|||||||
slot.splice(0, slot.length, placeholder)
|
slot.splice(0, slot.length, placeholder)
|
||||||
// expand body...
|
// expand body...
|
||||||
body = body ?
|
body = body ?
|
||||||
that.expand(page, body ?? [], state)
|
// XXX can we pass content handler here???
|
||||||
|
that.expand(page, body ?? [], state, {})
|
||||||
: body
|
: body
|
||||||
// if slot not overriden, write our value...
|
// if slot not overriden, write our value...
|
||||||
if(slot[0] === placeholder){
|
if(slot[0] === placeholder){
|
||||||
@ -1438,7 +1445,7 @@ module.parser = {
|
|||||||
return ((st ?? state).slots ?? {})[name] },
|
return ((st ?? state).slots ?? {})[name] },
|
||||||
{slot: name}) }) })),
|
{slot: name}) }) })),
|
||||||
// XXX do not like this name...
|
// XXX do not like this name...
|
||||||
content: ['slot'],
|
content: ['slot', 'include', 'quote'],
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -1448,9 +1455,13 @@ module.parser = {
|
|||||||
// @include(src=<path> isolated recursive=<text>)
|
// @include(src=<path> isolated recursive=<text>)
|
||||||
//
|
//
|
||||||
// <include src=<path> .. >
|
// <include src=<path> .. >
|
||||||
// <text>
|
// ...
|
||||||
|
// <concent/>
|
||||||
|
// ...
|
||||||
// </include>
|
// </include>
|
||||||
//
|
//
|
||||||
|
// NOTE: if body is not empty and <content/> is not present, the
|
||||||
|
// included page will not be placed.
|
||||||
// NOTE: there can be two ways of recursion in pWiki:
|
// NOTE: there can be two ways of recursion in pWiki:
|
||||||
// - flat recursion
|
// - flat recursion
|
||||||
// /A -> /A -> /A -> ..
|
// /A -> /A -> /A -> ..
|
||||||
@ -1465,15 +1476,12 @@ module.parser = {
|
|||||||
// not 100% correct manner focusing on path depth and ignoring
|
// not 100% correct manner focusing on path depth and ignoring
|
||||||
// the context, this potentially can lead to false positives.
|
// the context, this potentially can lead to false positives.
|
||||||
//
|
//
|
||||||
// XXX might be a good idea to add a <content/> tag to place the
|
// XXX do we need the <context/> nested macro???
|
||||||
// loaded text...
|
// XXX should this resolve join and/or body in the context of the
|
||||||
|
// included page or the outer page (current)???
|
||||||
// XXX add path recursion test to data -- fail if two paths resolve
|
// XXX add path recursion test to data -- fail if two paths resolve
|
||||||
// to the same context...
|
// to the same context...
|
||||||
// XXX need a way to make encode option transparent...
|
// XXX need a way to make encode option transparent...
|
||||||
// XXX need a way to wrap the included page...
|
|
||||||
// - template page...
|
|
||||||
// - prefix/sufix...
|
|
||||||
// XXX store a page cache in state...
|
|
||||||
// XXX do we want to load a specific slot/block???
|
// XXX do we want to load a specific slot/block???
|
||||||
// XXX UPDATE...
|
// XXX UPDATE...
|
||||||
include: Macro(
|
include: Macro(
|
||||||
@ -1512,18 +1520,14 @@ module.parser = {
|
|||||||
this.parseNested(page, src, state),
|
this.parseNested(page, src, state),
|
||||||
function(src){
|
function(src){
|
||||||
// check for recursion...
|
// check for recursion...
|
||||||
// XXX do we do this for pattern paths???
|
// XXX will this work for @source(..)???
|
||||||
// XXX this does not catch the /A/A/A/... recursion...
|
|
||||||
var stack = state.include_stack ??= []
|
var stack = state.include_stack ??= []
|
||||||
// XXX is this the right separator???
|
if(stack.includes(src)){
|
||||||
// ...need something that can't be in a path...
|
|
||||||
var base_src = base +'|'+ src
|
|
||||||
if(stack.includes(base_src)){
|
|
||||||
if(recursive){
|
if(recursive){
|
||||||
return that.expand(page, recursive, state) }
|
return that.expand(page, recursive, state) }
|
||||||
throw new Error('Recursion:\n\t'+
|
throw new Error('Recursion:\n\t'+
|
||||||
[...stack, base_src].join('\n\t\t-> ')) }
|
[...stack, src].join('\n\t\t-> ')) }
|
||||||
stack.push(base_src)
|
stack.push(src)
|
||||||
|
|
||||||
var cache = state.cache ??= {}
|
var cache = state.cache ??= {}
|
||||||
if(cache[src]){
|
if(cache[src]){
|
||||||
@ -1541,28 +1545,40 @@ module.parser = {
|
|||||||
|
|
||||||
// content handler...
|
// content handler...
|
||||||
handler ??=
|
handler ??=
|
||||||
function(page, text, state){
|
function(page, src, text, state){
|
||||||
// XXX should this be the difference between
|
page = page.get(src)
|
||||||
// @include(..) and @source(..)???
|
|
||||||
// i.e.
|
|
||||||
// .include(..) -> .resolve(..)
|
|
||||||
// .source(..) -> .expand(..)
|
|
||||||
return args.isolated ?
|
return args.isolated ?
|
||||||
this.resolve(page, text,
|
this.resolve(
|
||||||
|
page,
|
||||||
|
text,
|
||||||
|
Object.assign(
|
||||||
args.isolated == 'partial' ?
|
args.isolated == 'partial' ?
|
||||||
// XXX
|
|
||||||
serialize.partialDeepCopy(state)
|
serialize.partialDeepCopy(state)
|
||||||
: {})
|
: {},
|
||||||
: this.expand(page, text, state)}
|
{stack: state.stack ?? []}))
|
||||||
|
: this.expand(page, text, state) }
|
||||||
|
|
||||||
var pageHandler =
|
var pageHandler =
|
||||||
function(text){
|
function(text){
|
||||||
// XXX handle body / <content/>...
|
var content_handled = false
|
||||||
// XXX
|
return body ?
|
||||||
return handler.call(that, page, text, state) }
|
// handle body / <content/>...
|
||||||
|
Promise.awaitOrRun(
|
||||||
|
that.expand(page, body, state,
|
||||||
|
{ content: function(){
|
||||||
|
content_handled = true
|
||||||
|
return text = handler.call(that, page, src, text, state) } }),
|
||||||
|
function(text){
|
||||||
|
// if no <content/> present we still
|
||||||
|
// need to handle the included page...
|
||||||
|
content_handled
|
||||||
|
|| handler.call(that, page, src, text, state)
|
||||||
|
return text })
|
||||||
|
// place as-is...
|
||||||
|
: handler.call(that, page, src, text, state) }
|
||||||
var resultHandler =
|
var resultHandler =
|
||||||
function(pages){
|
function(pages){
|
||||||
state.include_stack.at(-1) == base_src
|
state.include_stack.at(-1) == src
|
||||||
&& state.include_stack.pop()
|
&& state.include_stack.pop()
|
||||||
// cleanup...
|
// cleanup...
|
||||||
if(state.include_stack.length == 0){
|
if(state.include_stack.length == 0){
|
||||||
@ -1607,25 +1623,51 @@ module.parser = {
|
|||||||
var that = this
|
var that = this
|
||||||
return this.macros['include'].call(this,
|
return this.macros['include'].call(this,
|
||||||
page, args, body, state,
|
page, args, body, state,
|
||||||
function(page, text, state){
|
function(page, src, text, state){
|
||||||
return that.expand(page, text, state) }) }),
|
return that.expand(page, text, state) }) }),
|
||||||
|
|
||||||
// Load macro and slot definitions but ignore the page text...
|
// Load macro and slot definitions but ignore the page text...
|
||||||
//
|
//
|
||||||
// NOTE: this is essentially the same as @source(..) but returns ''.
|
// NOTE: this is essentially the same as @source(..) but returns ''.
|
||||||
// XXX revise name...
|
|
||||||
load: Macro(
|
load: Macro(
|
||||||
['src'],
|
['src'],
|
||||||
function(args, body, state){
|
function(page, args, body, state){
|
||||||
var that = this
|
var that = this
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.macros['include'].call(this, page, args, body, state),
|
this.macros['include'].call(this, page, args, body, state),
|
||||||
function(){
|
function(){
|
||||||
return '' }) }),
|
return '' }) }),
|
||||||
|
|
||||||
// XXX a naive version, see ._quote(..) for reference...
|
//
|
||||||
|
// @quote(<src>)
|
||||||
|
//
|
||||||
|
// <quote src=<src>[ filter="<filter> ..."]/>
|
||||||
|
//
|
||||||
|
// <quote text=" .. "[ filter="<filter> ..."]/>
|
||||||
|
//
|
||||||
|
// <quote[ filter="<filter> ..."]>
|
||||||
|
// ..
|
||||||
|
// </quote>
|
||||||
|
//
|
||||||
|
// This has two modes of operation, i.e. the body can be treated
|
||||||
|
// in two destinct ways:
|
||||||
|
// - if both src and body are given -- like @include(..) body is
|
||||||
|
// used as a wrapper for the quoted page
|
||||||
|
// - if only body is given -- body is inderted as is without
|
||||||
|
// any processing.
|
||||||
|
// XXX not sure I like this, this might change in the futore...
|
||||||
|
// one way to split the two is to split this into two macros,
|
||||||
|
// but I can't make the split logical/obvious...
|
||||||
|
//
|
||||||
|
// NOTE: src ant text arguments are mutually exclusive, src takes
|
||||||
|
// priority.
|
||||||
|
// NOTE: the filter argument has the same semantics as the filter
|
||||||
|
// macro with one exception, when used in quote, the body is
|
||||||
|
// not expanded...
|
||||||
|
// NOTE: the filter argument uses the same filters as @filter(..)
|
||||||
// XXX might be a good idea to do an auto-filter that would be
|
// XXX might be a good idea to do an auto-filter that would be
|
||||||
// apropriately selected according to format -- md, html, ...
|
// apropriately selected according to format -- md, html, ...
|
||||||
|
// XXX filter...
|
||||||
quote: Macro(
|
quote: Macro(
|
||||||
['src', 'join', 'filter'],
|
['src', 'join', 'filter'],
|
||||||
quoting(
|
quoting(
|
||||||
@ -1644,6 +1686,20 @@ module.parser = {
|
|||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
text,
|
text,
|
||||||
function(text){
|
function(text){
|
||||||
|
// XXX not sure I like that this has two "modes"...
|
||||||
|
text = src && body ?
|
||||||
|
// XXX do we need to account for generators???
|
||||||
|
(text instanceof Array ?
|
||||||
|
text
|
||||||
|
: [text])
|
||||||
|
.map(function(text){
|
||||||
|
return that.expand(
|
||||||
|
page,
|
||||||
|
that.ast(body.join(''), false, 'quote'),
|
||||||
|
state,
|
||||||
|
{ content: function(){
|
||||||
|
return text }, }) })
|
||||||
|
: text
|
||||||
return that.joinBlocks(
|
return that.joinBlocks(
|
||||||
page,
|
page,
|
||||||
that.filterBlocks(
|
that.filterBlocks(
|
||||||
@ -1654,133 +1710,7 @@ module.parser = {
|
|||||||
args.join,
|
args.join,
|
||||||
state) }) }) })),
|
state) }) }) })),
|
||||||
|
|
||||||
//
|
/* XXX
|
||||||
// @quote(<src>)
|
|
||||||
//
|
|
||||||
// <quote src=<src>[ filter="<filter> ..."]/>
|
|
||||||
//
|
|
||||||
// <quote text=" .. "[ filter="<filter> ..."]/>
|
|
||||||
//
|
|
||||||
// <quote[ filter="<filter> ..."]>
|
|
||||||
// ..
|
|
||||||
// </quote>
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: src ant text arguments are mutually exclusive, src takes
|
|
||||||
// priority.
|
|
||||||
// NOTE: the filter argument has the same semantics as the filter
|
|
||||||
// macro with one exception, when used in quote, the body is
|
|
||||||
// not expanded...
|
|
||||||
// NOTE: the filter argument uses the same filters as @filter(..)
|
|
||||||
// NOTE: else argument implies strict mode...
|
|
||||||
// XXX need a way to escape macros -- i.e. include </quote> in a quoted text...
|
|
||||||
// XXX should join/else be sub-tags???
|
|
||||||
// XXX UPDATE...
|
|
||||||
_quote: Macro(
|
|
||||||
['src', 'filter', 'text', 'join', 'else',
|
|
||||||
['s', 'expandactions', 'strict']],
|
|
||||||
async function*(args, body, state){
|
|
||||||
var src = args.src //|| args[0]
|
|
||||||
var base = this.get(this.path.split(/\*/).shift())
|
|
||||||
var text = args.text
|
|
||||||
?? body
|
|
||||||
?? []
|
|
||||||
var strict = !!(args.strict
|
|
||||||
?? args['else']
|
|
||||||
?? false)
|
|
||||||
// parse arg values...
|
|
||||||
src = src ?
|
|
||||||
await base.parse(src, state)
|
|
||||||
: src
|
|
||||||
// XXX INHERIT_ARGS special-case: inherit args by default...
|
|
||||||
if(this.actions_inherit_args
|
|
||||||
&& this.actions_inherit_args.has(pwpath.basename(src))
|
|
||||||
&& this.get(pwpath.dirname(src)).path == this.path){
|
|
||||||
src += ':$ARGS' }
|
|
||||||
var expandactions =
|
|
||||||
args.expandactions
|
|
||||||
?? true
|
|
||||||
// XXX EXPERIMENTAL
|
|
||||||
var strquotes = args.s
|
|
||||||
|
|
||||||
var depends = state.depends =
|
|
||||||
state.depends
|
|
||||||
?? new Set()
|
|
||||||
// XXX DEPENDS_PATTERN
|
|
||||||
depends.add(src)
|
|
||||||
|
|
||||||
var pages = src ?
|
|
||||||
(!expandactions
|
|
||||||
&& await this.get(src).type == 'action' ?
|
|
||||||
base.get(this.QUOTE_ACTION_PAGE)
|
|
||||||
: await this.get(src).asPages(strict))
|
|
||||||
: text instanceof Array ?
|
|
||||||
[text.join('')]
|
|
||||||
: typeof(text) == 'string' ?
|
|
||||||
[text]
|
|
||||||
: text
|
|
||||||
// else...
|
|
||||||
pages = ((!pages
|
|
||||||
|| pages.length == 0)
|
|
||||||
&& args['else']) ?
|
|
||||||
[await base.parse(args['else'], state)]
|
|
||||||
: pages
|
|
||||||
// empty...
|
|
||||||
if(!pages || pages.length == 0){
|
|
||||||
return }
|
|
||||||
|
|
||||||
var join = args.join
|
|
||||||
&& await base.parse(args.join, state)
|
|
||||||
var first = true
|
|
||||||
for await (var page of pages){
|
|
||||||
if(join && !first){
|
|
||||||
yield join }
|
|
||||||
first = false
|
|
||||||
|
|
||||||
text = typeof(page) == 'string' ?
|
|
||||||
page
|
|
||||||
: (!expandactions
|
|
||||||
&& await page.type == 'action') ?
|
|
||||||
base.get(this.QUOTE_ACTION_PAGE).raw
|
|
||||||
: await page.raw
|
|
||||||
text = strquotes ?
|
|
||||||
text
|
|
||||||
.replace(/["']/g, function(c){
|
|
||||||
return '%'+ c.charCodeAt().toString(16) })
|
|
||||||
: text
|
|
||||||
|
|
||||||
page.path
|
|
||||||
&& depends.add(page.path)
|
|
||||||
|
|
||||||
var filters =
|
|
||||||
args.filter
|
|
||||||
&& args.filter
|
|
||||||
.trim()
|
|
||||||
.split(/\s+/g)
|
|
||||||
|
|
||||||
// NOTE: we are delaying .quote_filters handling here to
|
|
||||||
// make their semantics the same as general filters...
|
|
||||||
// ...and since we are internally calling .filter(..)
|
|
||||||
// macro we need to dance around it's architecture too...
|
|
||||||
// NOTE: since the body of quote(..) only has filters applied
|
|
||||||
// to it doing the first stage of .filter(..) as late
|
|
||||||
// as the second stage here will have no ill effect...
|
|
||||||
// NOTE: this uses the same filters as @filter(..)
|
|
||||||
// NOTE: the function wrapper here isolates text in
|
|
||||||
// a closure per function...
|
|
||||||
yield (function(text){
|
|
||||||
return async function(state){
|
|
||||||
// add global quote-filters...
|
|
||||||
filters =
|
|
||||||
(state.quote_filters
|
|
||||||
&& !(filters ?? []).includes(this.ISOLATED_FILTERS)) ?
|
|
||||||
[...state.quote_filters, ...(filters ?? [])]
|
|
||||||
: filters
|
|
||||||
return filters ?
|
|
||||||
await this.__parser__.callMacro(
|
|
||||||
this, 'filter', filters, text, state, false)
|
|
||||||
.call(this, state)
|
|
||||||
: text } })(text) } }),
|
|
||||||
// very similar to @filter(..) but will affect @quote(..) filters...
|
// very similar to @filter(..) but will affect @quote(..) filters...
|
||||||
'quote-filter': function(args, body, state){
|
'quote-filter': function(args, body, state){
|
||||||
var filters = state.quote_filters =
|
var filters = state.quote_filters =
|
||||||
|
|||||||
@ -27,6 +27,20 @@ module.exports.PAGES = {
|
|||||||
'/async/recursive/SelfOther': Promise.resolve('<< @include(/async/recursive/OtherSelf) >>'),
|
'/async/recursive/SelfOther': Promise.resolve('<< @include(/async/recursive/OtherSelf) >>'),
|
||||||
|
|
||||||
'/multi/page': [ 'A', 'B', 'C' ],
|
'/multi/page': [ 'A', 'B', 'C' ],
|
||||||
|
'/async/multi/page': Promise.resolve([ 'A', 'B', 'C' ]),
|
||||||
|
/* XXX are these actually possible???
|
||||||
|
// ...and should these be handlable??
|
||||||
|
'/multi/async/page': [
|
||||||
|
Promise.resolve('A'),
|
||||||
|
Promise.resolve('B'),
|
||||||
|
Promise.resolve('C'),
|
||||||
|
],
|
||||||
|
'/multi/async/multi/page': Promise.resolve([
|
||||||
|
Promise.resolve('A'),
|
||||||
|
Promise.resolve('B'),
|
||||||
|
Promise.resolve('C'),
|
||||||
|
]),
|
||||||
|
//*/
|
||||||
}
|
}
|
||||||
|
|
||||||
var P =
|
var P =
|
||||||
@ -225,8 +239,17 @@ test.Setups({
|
|||||||
'original overloaded',
|
'original overloaded',
|
||||||
],
|
],
|
||||||
} },
|
} },
|
||||||
|
// content...
|
||||||
|
include_content: function(assert){
|
||||||
|
return {
|
||||||
|
page: P,
|
||||||
|
code: [
|
||||||
|
'<include "/page">[[ <content/> ]]</include>',
|
||||||
|
'[[ '+ P.get('/page').raw +' ]]', ], } },
|
||||||
|
// XXX
|
||||||
|
|
||||||
// recursion...
|
// recursion...
|
||||||
// XXX test path recursion: /A -> /A/A -> /A/A/A -> ...
|
// XXX handle test path recursion: /A -> /A/A -> /A/A/A -> ...
|
||||||
include_recursive_a: function(assert){
|
include_recursive_a: function(assert){
|
||||||
return {
|
return {
|
||||||
page: P,
|
page: P,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user