split out v3...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
52
v3/Makefile
Executable file
@ -0,0 +1,52 @@
|
||||
|
||||
|
||||
|
||||
BOOTSTRAP_FILES := \
|
||||
$(wildcard bootstrap/*) \
|
||||
$(wildcard bootstrap/*/*) \
|
||||
README.md
|
||||
|
||||
LOCAL_MODULES := \
|
||||
node_modules/ig-doc/doc.js \
|
||||
node_modules/ig-stoppable/stoppable.js \
|
||||
node_modules/ig-object/object.js \
|
||||
node_modules/ig-actions/actions.js \
|
||||
node_modules/ig-features/features.js
|
||||
|
||||
EXT_MODULES := \
|
||||
$(wildcard node_modules/pouchdb/dist/*) \
|
||||
$(wildcard node_modules/jszip/dist/*) \
|
||||
$(wildcard node_modules/idb-keyval/dist/*.js) \
|
||||
$(wildcard node_modules/showdown/dist/*)
|
||||
|
||||
POUCH_DB := \
|
||||
$(wildcard node_modules/pouchdb/dist/*)
|
||||
|
||||
|
||||
|
||||
lib/types: node_modules
|
||||
mkdir -p $@
|
||||
cp node_modules/ig-types/*js $@
|
||||
|
||||
|
||||
bootstrap.js: scripts/bootstrap.js $(BOOTSTRAP_FILES)
|
||||
node $<
|
||||
|
||||
|
||||
.PHONY: bootstrap
|
||||
bootstrap: bootstrap.js
|
||||
|
||||
|
||||
node_modules:
|
||||
npm install
|
||||
|
||||
|
||||
dev: node_modules lib/types $(EXT_MODULES) $(LOCAL_MODULES) bootstrap
|
||||
cp $(LOCAL_MODULES) lib/
|
||||
cp $(EXT_MODULES) ext-lib/
|
||||
|
||||
|
||||
clean:
|
||||
rm -f bootstrap.js
|
||||
|
||||
|
||||
6
v3/bootstrap.js
vendored
Normal file
1
v3/bootstrap/Doc.html
Executable file
@ -0,0 +1 @@
|
||||
@include(./About)
|
||||
1
v3/bootstrap/Doc/About.md
Executable file
@ -0,0 +1 @@
|
||||
Placeholder file, this will be replaced with /README.md in bootstrap data...
|
||||
252
v3/bootstrap/Doc/Macros.md
Executable file
@ -0,0 +1,252 @@
|
||||
#  pWiki Macros
|
||||
|
||||
## Syntax
|
||||
|
||||
Any macro can be used in any of the two forms, either _inline_ or _HTML-like_.
|
||||
|
||||
Inline:
|
||||
```
|
||||
@macro-name(value)
|
||||
```
|
||||
|
||||
HTML-style:
|
||||
```
|
||||
<macro-name arg="value"/>
|
||||
|
||||
<macro-name arg="value">
|
||||
...text...
|
||||
</macro-name>
|
||||
```
|
||||
|
||||
The two forms are almost identical, with the only difference being that the
|
||||
inline form does not support body text (note that some macros may provide
|
||||
this functionality as an argument, namely `slot`).
|
||||
|
||||
The two forms exist to fill two distinct functions:
|
||||
- inline: compatible with attribute values and short
|
||||
- html-like: element-like, simpler when dealing with html
|
||||
|
||||
|
||||
|
||||
### Escaping macros
|
||||
|
||||
Macros can be escaped for inclusion in the page, the two types of macros
|
||||
are escaped a bit differently:
|
||||
|
||||
- inline macros -- escaped by preceding with a "\"
|
||||
|
||||
```
|
||||
\\@include(\SomePage)
|
||||
```
|
||||
|
||||
Displayed in page as:
|
||||
|
||||
\@include(\SomePage)
|
||||
|
||||
<pwiki-comment>
|
||||
_NOTE: if displayed on github, this will show an extra "\" in both
|
||||
cases, this should be ignored as pWiki will consume the escaping "\"
|
||||
in both the code example and the preview._
|
||||
</pwiki-comment>
|
||||
|
||||
|
||||
- html-like macros -- escaped _the HTML way_
|
||||
|
||||
```
|
||||
<include src="\SomePage"\>
|
||||
```
|
||||
|
||||
Displayed in page as:
|
||||
|
||||
<include src="\SomePage"\\>
|
||||
|
||||
|
||||
|
||||
### Conditional comments
|
||||
|
||||
In addition to HTML and filter-specific comments pWiki provides two types
|
||||
of conditional comments that serve two specific functions:
|
||||
|
||||
Show something in pWiki but hide it in HTML:
|
||||
```
|
||||
<!--\[pWiki[ ... ]]-->
|
||||
```
|
||||
|
||||
Show something in HTML but hide in pWiki:
|
||||
<pre>
|
||||
<pwiki-comment> ... </pwiki-comment>
|
||||
</pre>
|
||||
|
||||
|
||||
This will enable writing documents (mainly in _markdown_) that are usable
|
||||
bot from within pWiki as well as outside.
|
||||
|
||||
|
||||
## Macros
|
||||
|
||||
### now ()
|
||||
|
||||
Get current date in seconds since epoch, this is equivalet Javascript's
|
||||
`Date.now()`.
|
||||
|
||||
This is mostly used for automatically creating paths (see: todo / outline)
|
||||
|
||||
This is different from `$NOW` in path (see: Doc/Path) in that this gets
|
||||
the date once per page load, i.e. the date changes on page load, while
|
||||
`$NOW` is set every time the path is used, i.e. on every click or script
|
||||
use.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
\@now()
|
||||
```
|
||||
|
||||
<pwiki-comment>Will produce: `1471389217848`</pwiki-comment>
|
||||
|
||||
<!--[pWiki[ Will produce: `@now()` ]]-->
|
||||
|
||||
|
||||
|
||||
### filter (name)
|
||||
|
||||
Enable or disable a page filter.
|
||||
|
||||
A filter is a way to transform the page source.
|
||||
|
||||
Arguments:
|
||||
- `name` -- filter name. If name is preceded with a '-' then it
|
||||
will be forced off. This is useful for disabling _default_ filters, or
|
||||
filters added previously in templates.
|
||||
|
||||
Filters:
|
||||
- wikiword (default)
|
||||
- markdown
|
||||
|
||||
**Example:**
|
||||
|
||||
|
||||
<!--[pWiki[
|
||||
- `[Templates/_edit/_edit]` – _see the macro at the end of the page._
|
||||
]]-->
|
||||
|
||||
<pwiki-comment>
|
||||
- [bootstrap \_edit](/bootstrap/Templates/_edit.html) – _see the
|
||||
macro at the end of the page._
|
||||
</pwiki-comment>
|
||||
|
||||
|
||||
|
||||
### 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)
|
||||
```
|
||||
]]-->
|
||||
|
||||
|
||||
### slot (name text)
|
||||
|
||||
Define or fill a slot.
|
||||
|
||||
First occurrence of a `name` will _define_ a slot and fill it with `text`.
|
||||
Each new occurrence of a name will change slot content.
|
||||
|
||||
**Example:**
|
||||
|
||||
<pwiki-comment>
|
||||
- [bootstrap view](/bootstrap/Templates/_view.html)
|
||||
- [bootstrap edit](/bootstrap/Templates/_edit.html)
|
||||
</pwiki-comment>
|
||||
|
||||
<!--[pWiki[
|
||||
[Templates/\_view] / [bootstrap view](bootstrap/Templates/_view.html):
|
||||
```
|
||||
@source(Templates/_view)
|
||||
```
|
||||
|
||||
[Templates/\_edit] / [bootstrap edit](bootstrap/Templates/_edit.html):
|
||||
```
|
||||
@source(Templates/_edit)
|
||||
```
|
||||
]]-->
|
||||
|
||||
|
||||
### macro (name src sort) / else ()
|
||||
|
||||
Apply macro to source page and include the result.
|
||||
|
||||
This is similar to include but does not require a separate page.
|
||||
|
||||
Both `name` and `src` are optional.
|
||||
|
||||
If `name` is given a _named macro_ is defined. This macro can be later
|
||||
referenced (used) by name. A named macro can be redefined/overridden.
|
||||
|
||||
If `src` is given a macro is applied to a specific page or range of pages
|
||||
(see: WikiPath).
|
||||
|
||||
For a macro to be useful it must have a body (`text`), either defined as
|
||||
a named macro or in the current macro.
|
||||
|
||||
Arguments:
|
||||
- `name` -- macro name (optional).
|
||||
- `src` -- path to source page (optional).
|
||||
- `sort` -- space separated list of methods to use for item sorting
|
||||
|
||||
|
||||
`else` macro is applicable inside `macro`. it is used when the `src` path
|
||||
of `macro` matches no pages.
|
||||
|
||||
**Example:**
|
||||
|
||||
<pwiki-comment>
|
||||
- [bootstrap pages](/bootstrap/Templates/pages.html)
|
||||
</pwiki-comment>
|
||||
|
||||
<!--[pWiki[
|
||||
[Templates/pages] / [bootstrap pages](bootstrap/Templates/pages.html):
|
||||
```
|
||||
@source(Templates/pages)
|
||||
```
|
||||
]]-->
|
||||
|
||||
|
||||
<!-- @filter(markdown) -->
|
||||
<!-- vim:set ts=4 sw=4 ft=markdown : -->
|
||||
267
v3/bootstrap/Doc/Path.md
Executable file
@ -0,0 +1,267 @@
|
||||
#  pWiki Path
|
||||
|
||||
A Wiki is a set of _pages_, each uniquely defined by it's _title_. Titles
|
||||
are traditionally formatted as WikiWords. pWiki closely follows this
|
||||
culture, while not restricting either page title formatting nor page
|
||||
nesting (nested paths), though in the general case following the Wiki
|
||||
style is recommended.
|
||||
|
||||
|
||||
|
||||
## Basic terminology
|
||||
|
||||
**Path**
|
||||
_One or more strings (or parts) separated by "/" that identifies a view._
|
||||
|
||||
We call the last _part_ in a path sequence a _title_.
|
||||
|
||||
We call the sub-path without the _title_ a _basedir_ or simply _dir_.
|
||||
|
||||
In pWiki, there is no distinction between a page and a _directory_, thus
|
||||
we do not use the later term, instead, we may use the term _sub-page_.
|
||||
|
||||
Paths are case sensitive.
|
||||
|
||||
|
||||
**Page**
|
||||
_A set of data associated with a path._
|
||||
|
||||
A page is identified by it's path, but this does not require every
|
||||
sub-path of that path to exist -- the full path is the identifier.
|
||||
|
||||
Not every path _identifies_ a page, but every path _resolves_ to a page.
|
||||
|
||||
Some pages are _bootstrapped_, i.e. are predefined in pWiki, these pages
|
||||
can be overridden but can not be removed. This is done to make it simple
|
||||
to revert to the default state if something goes wrong.
|
||||
|
||||
|
||||
**View**
|
||||
_A path that resolves to a page that may or may not be at (identified by)
|
||||
that specific path._
|
||||
|
||||
A _view's_ path may match that of a specific page or may not match any
|
||||
page directly, but any view will resolve to a page via the _acquisition
|
||||
process_
|
||||
|
||||
Any page is a view, every view resolves to a page, but not every view
|
||||
is a page.
|
||||
|
||||
(see: _Page acquisiton_ below)
|
||||
|
||||
|
||||
**\WikiWord**
|
||||
_A WikiWork is a specially formated string that is treated as a link in
|
||||
page text_
|
||||
|
||||
In pWiki a simple WikiWord is any string starting with a capital letter,
|
||||
must contain at least and one more capital letter and can consist of
|
||||
letters, numbers, underscores (WikiWord itself is a good example)
|
||||
|
||||
A WikiWord path (_WikiPath_) is a set of path parts separated by '/' the
|
||||
first part must start with a capital letter and the rest can contain
|
||||
letters, numbers and/or underscores (example: Path/to/somepage).
|
||||
|
||||
_Note that this is not actually a part of the path specification but it
|
||||
is part of the Wiki culture and a convenient way to automatically link
|
||||
to pages (handled by pWiki macros when rendering pages)._
|
||||
|
||||
|
||||
|
||||
**Special path characters**
|
||||
Titles of _user pages_ must not contain the leading underscore `_`
|
||||
character, such paths are used internally.
|
||||
|
||||
|
||||
|
||||
## Page acquisition
|
||||
|
||||
pWiki path system differs from how traditional file system paths are
|
||||
handled. In pWiki if a path does not reference a page directly (i.e.
|
||||
it's a _view_), a search is conducted to find an alternative page. This
|
||||
search is called _page acquisition_.
|
||||
|
||||
**Acquisition process:**
|
||||
_A set of rules defining how a page is retrieved via a path._
|
||||
|
||||
|
||||
This is used as a simple and uniform mechanism to:
|
||||
- Get default pages for specific situations
|
||||
Like [Templates/EmptyPage] to handle the _page not found_ condition.
|
||||
- Define generic templates/pages accessible by multiple pages in path
|
||||
A good example would be the viewer used to show this page [Templates/\_view]
|
||||
and all of it's _chrome_ like the path in the header and links in the
|
||||
footer <pwiki-comment>(seen: when viewing through pWiki)</pwiki-comment>
|
||||
- Overload default templates/pages
|
||||
|
||||
|
||||
### The acquisition order/rules:
|
||||
|
||||
1. if _path_ matches a specific page, target _page_ is found
|
||||
1. if _path_ does not match a page:
|
||||
1. if _title_ matches a page in the parent _path_, _page_ is found
|
||||
1. repeat until we either have a match or reach root (empty _basedir_)
|
||||
1. if no match is found, check if title exists in [Templates] in _basedir_
|
||||
1. if no match is found, check if title exists in [/System]
|
||||
1. if no match is found, repeat process for `EmptyPage` instead of _title_
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
For path `Path/To/Page` the following paths are checked in order
|
||||
and the first matching page is returned:
|
||||
|
||||
- _Check path as-is then go up:_
|
||||
- `Path/To/Page`
|
||||
- `Path/Page`
|
||||
- `Page`
|
||||
- _Check in `Templates`, in path and up:_
|
||||
- `Path/To/Templates/Page`
|
||||
- `Path/Templates/Page`
|
||||
- `Templates/Page`
|
||||
- _Check root `System`:_
|
||||
- `System/Page`
|
||||
- _Check `EmptyPage` in path, then in templates:_
|
||||
- `Path/To/EmptyPage`
|
||||
- `Path/EmptyPage`
|
||||
- `EmptyPage`
|
||||
- `Path/To/Templates/EmptyPage`
|
||||
- `Path/Templates/EmptyPage`
|
||||
- `Templates/EmptyPage` _(This is guaranteed to exist)_
|
||||
|
||||
|
||||
**Exceptions:**
|
||||
|
||||
- `System/settings` is global and _can not be overloaded_ for use as
|
||||
system configuration. This is done for security reasons.
|
||||
|
||||
|
||||
|
||||
## Default pages
|
||||
|
||||
**EmptyPage**
|
||||
A page resolved when a page does not exist. Used as a template for
|
||||
new/empty pages.
|
||||
|
||||
This page is guaranteed to exist by the system.
|
||||
|
||||
Located at: Templates/EmptyPage
|
||||
|
||||
|
||||
**EmptyToDo**
|
||||
Used as a template for new/empty ToDo pages.
|
||||
|
||||
Located at: Templates/EmptyToDo
|
||||
|
||||
|
||||
**EmptyOutline**
|
||||
Used as a template for new/empty outline pages.
|
||||
|
||||
Located at: Templates/EmptyOutline
|
||||
|
||||
|
||||
**NoMatch**
|
||||
Returned when pattern matches no pages.
|
||||
|
||||
|
||||
|
||||
## Relative and absolute paths
|
||||
|
||||
pWiki follows the traditional path semantics with one addition, the ">>"
|
||||
that is similar to ".." but works in the opposite direction, consuming
|
||||
the next, i.e. child, path element instead of parent.
|
||||
|
||||
To illustrate the relative and absolute mechanics:
|
||||
|
||||
| Title | Source Page | Path | Resolves to |
|
||||
|-------------------|-------------|-----------------------|-------------------------|
|
||||
| "." - current | \SourcePage | \\./Target/Page | \SourcePage/Target/Page |
|
||||
| ".." - parent | \SourcePage | \\../Target/Page | \Target/Page |
|
||||
| ">>" | \SourcePage | >>\/Target/Page | \SourcePage/Page |
|
||||
| "/" - root dir | \SourcePage | \/Target/Page | \/Target/Page |
|
||||
|
||||
|
||||
_Note that neither a leading ".." at root level, nor a trailing ">>"
|
||||
in any path, will have any effect, and will simply be ignored (e.g.
|
||||
"\/../Page" is same as "/Page" and "\Path/>>" is the same as "Path")_
|
||||
|
||||
|
||||
|
||||
## Path patterns
|
||||
|
||||
Path patterns are used to match/iterate multiple pages. The syntax is
|
||||
similar to path glob patterns.
|
||||
|
||||
- "\*" - matches any page in a sub-path on one level
|
||||
- "\*\*" - matches any page in a sub-path recursively
|
||||
|
||||
Note that neither will match parts of paths that do not explicitly
|
||||
identify pages.
|
||||
|
||||
|
||||
**Example:**
|
||||
|
||||
XXX revise...
|
||||
|
||||
For the following paths:
|
||||
|
||||
```
|
||||
WikiHome
|
||||
SomePage
|
||||
Path/to/OtherPage
|
||||
Path/Page
|
||||
```
|
||||
|
||||
Patterns and their matches:
|
||||
- `*` will match:
|
||||
- WikiHome
|
||||
- SomePage
|
||||
- `**` will match:
|
||||
- WikiHome
|
||||
- SomePage
|
||||
- Path/to/OtherPage
|
||||
- Path/Page
|
||||
- `\Path/*` will match:
|
||||
- Path/Page
|
||||
|
||||
Note that neither `\Path` nor `\Path/to` does not refer to any real pages,
|
||||
thus neither is matched by any of the patterns explicitly.
|
||||
|
||||
|
||||
|
||||
## Path actions
|
||||
|
||||
XXX path elements that perform actions on pages but do not actually
|
||||
correspond to actual pages.
|
||||
|
||||
|
||||
|
||||
## Path variables
|
||||
|
||||
Path variables are resolved when path is resolved or accessed.
|
||||
|
||||
**`$NOW`**
|
||||
Replaced with the current time.
|
||||
|
||||
_Also see the `\@now()` macro: [Doc/Macros]._
|
||||
|
||||
|
||||
**`$PATH`**
|
||||
Replaced with current page path.
|
||||
|
||||
|
||||
**`$BASE`**
|
||||
Replaced with current page basedir.
|
||||
|
||||
|
||||
**`$TITLE`**
|
||||
Replaced with current page title.
|
||||
|
||||
|
||||
**`$INDEX`**
|
||||
Replaced with current page index in pattern matching.
|
||||
|
||||
|
||||
|
||||
<!-- @filter(markdown) -->
|
||||
<!-- vim:set ts=4 sw=4 ft=markdown spell : -->
|
||||
1
v3/bootstrap/Doc/Templates.md
Executable file
@ -0,0 +1 @@
|
||||
XXX Document the template structure here XXX
|
||||
34
v3/bootstrap/System/settings.json
Executable file
@ -0,0 +1,34 @@
|
||||
/**********************************************************************
|
||||
* !EXPERIMENTAL!
|
||||
*
|
||||
* These filters are required for .code to be JSON compatible:
|
||||
* @filter(json) @filter(-wikiword)
|
||||
*
|
||||
* NOTE: currently inline editing may mess this up.
|
||||
* NOTE: all the comments will be removed before parsing.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
// The actual root data to be parsed...
|
||||
{
|
||||
// Wiki config...
|
||||
"HomePage": "WikiHome",
|
||||
"ShowSystemPages": false,
|
||||
"ShowBasePages": true,
|
||||
// NOTE: setting this to true will effectively allow pages to control
|
||||
// your wiki. This is a potential threat if you allow untrusted
|
||||
// content on your wiki...
|
||||
// You are doing this at your own risk!
|
||||
"AllowScripts": false,
|
||||
|
||||
// PeerJS API key...
|
||||
"PeerJS-API-key": "XXX",
|
||||
// PeerJS server URL (leave blank for default)...
|
||||
"PeerJS-Server": null,
|
||||
|
||||
// XXX
|
||||
"CouchDB-Server": null
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
99
v3/bootstrap/System/style.css
Executable file
@ -0,0 +1,99 @@
|
||||
body {
|
||||
font-family: /*worksans,*/ opensans, sans-serif;
|
||||
|
||||
}
|
||||
|
||||
.title img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
border-bottom: solid 1px rgba(0, 0, 0, 0.1);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
h2, h3 {
|
||||
border-bottom: solid 1px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
|
||||
/* tables */
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
table, td, th {
|
||||
border-bottom: solid 1px gray;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td, th {
|
||||
text-align: left;
|
||||
padding: 5px;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
|
||||
.raw,
|
||||
.text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item.checked {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.item.checked:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.item.checked .item-content * {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.button {
|
||||
text-decoration: none;
|
||||
}
|
||||
.button:last-child {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.separator~* {
|
||||
float: right;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
padding: 10px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.item .button {
|
||||
display: none;
|
||||
}
|
||||
.item:hover .button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sort-handle {
|
||||
opacity: 0.1;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.item:hover .sort-handle {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.sort-placeholder {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* @filter(-wikiword) */
|
||||
/* @filter(text) */
|
||||
/* vim:set ts=4 sw=4 ft=css : */
|
||||
20
v3/bootstrap/Templates.html
Executable file
@ -0,0 +1,20 @@
|
||||
<p>
|
||||
XXX Genereal template description...
|
||||
</p>
|
||||
|
||||
<macro name="show-source" src="./**" sort="path">
|
||||
<hr>
|
||||
<h2>
|
||||
<a href="#@source(./path)/_edit">@source(./path)</a>
|
||||
</h2>
|
||||
<p>
|
||||
<pre><code>@quote(./raw)</code></pre>
|
||||
</p>
|
||||
</macro>
|
||||
|
||||
<macro name="show-source" src="Templates"/>
|
||||
|
||||
<macro name="show-source" src="System/style"/>
|
||||
|
||||
<!-- @filter(-wikiword) -->
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
1
v3/bootstrap/Templates/EmptyOutline.html
Executable file
@ -0,0 +1 @@
|
||||
@include(./outline)
|
||||
8
v3/bootstrap/Templates/EmptyPage.html
Executable file
@ -0,0 +1,8 @@
|
||||
<!-- place filters here so as not to takup page space: ... -->
|
||||
|
||||
Page @include(./path) is empty.<br><br>
|
||||
|
||||
Links to this page:<br>
|
||||
@include(./links)<br><br>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
1
v3/bootstrap/Templates/EmptyToDo.html
Executable file
@ -0,0 +1 @@
|
||||
@include(./todo)
|
||||
3
v3/bootstrap/Templates/_css.html
Executable file
@ -0,0 +1,3 @@
|
||||
<style>
|
||||
@source(..)
|
||||
</style>
|
||||
12
v3/bootstrap/Templates/_edit.html
Executable file
@ -0,0 +1,12 @@
|
||||
<include src="../_view"/>
|
||||
|
||||
<slot name="toggle-edit-link">(<a href="#..">view</a>)</slot>
|
||||
|
||||
<slot name="title" class="title" contenteditable saveto="..">@source(../title)</slot>
|
||||
|
||||
<slot name="page-content">
|
||||
<code><pre><quote src="../raw" class="raw" saveto=".." contenteditable/></pre></code>
|
||||
</slot>
|
||||
|
||||
<!-- @filter(-wikiword) -->
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
9
v3/bootstrap/Templates/_outline.html
Executable file
@ -0,0 +1,9 @@
|
||||
<include src="../_view"/>
|
||||
|
||||
<slot name="title" class="title" contenteditable saveto="..">@source(../title)</slot>
|
||||
|
||||
<slot name="page-content">
|
||||
@include(../outline)
|
||||
</slot>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
1
v3/bootstrap/Templates/_raw.txt
Executable file
@ -0,0 +1 @@
|
||||
@source(..)
|
||||
9
v3/bootstrap/Templates/_todo.html
Executable file
@ -0,0 +1,9 @@
|
||||
<include src="../_view"/>
|
||||
|
||||
<slot name="title" class="title" contenteditable saveto="..">@source(../title)</slot>
|
||||
|
||||
<slot name="page-content">
|
||||
@include(../todo)
|
||||
</slot>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
31
v3/bootstrap/Templates/_view.html
Executable file
@ -0,0 +1,31 @@
|
||||
|
||||
@include(./style/_css)
|
||||
|
||||
<div>
|
||||
<a href="#pages" class="pages-list-button button">☰</a>
|
||||
|
||||
[@source(../path)]
|
||||
|
||||
<slot name="toggle-edit-link"> (<a href="#./_edit">edit</a>) </slot>
|
||||
|
||||
<span class="separator"/>
|
||||
|
||||
<a href="#NewPage/_edit" class="new-page-button button">+</a>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h1 saveto="..">
|
||||
<slot name="title" class="title">@source(../title)</slot>
|
||||
</h1>
|
||||
<br>
|
||||
|
||||
<div>
|
||||
<slot name="page-content">
|
||||
<include src=".." class="text" saveto=".." tabindex="0"/>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<a href="#/">home</a>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
12
v3/bootstrap/Templates/all_pages.html
Executable file
@ -0,0 +1,12 @@
|
||||
<macro src="../**">
|
||||
<div class="item">
|
||||
[@source(./path)]
|
||||
<span class="separator"/>
|
||||
<a class="button" href="#@source(./path)/delete">×</a>
|
||||
</div>
|
||||
<else>
|
||||
No pages...
|
||||
</else>
|
||||
</macro>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
6
v3/bootstrap/Templates/macros.html
Executable file
@ -0,0 +1,6 @@
|
||||
<macro name="remove-button">
|
||||
<span page="./path" class="remove-button button">×</span>
|
||||
</macro>
|
||||
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
47
v3/bootstrap/Templates/outline.html
Executable file
@ -0,0 +1,47 @@
|
||||
<macro name="item-pre-controls"/>
|
||||
|
||||
<macro name="item-content" class="item-content">
|
||||
<include
|
||||
class="raw"
|
||||
contenteditable
|
||||
tabindex="0"
|
||||
style="display:inline-block"
|
||||
saveto="@source(./path)"
|
||||
src="." />
|
||||
</macro>
|
||||
|
||||
<macro name="item-post-controls">
|
||||
<a class="button" href="#@source(./path)/delete">×</a>
|
||||
</macro>
|
||||
|
||||
|
||||
<div>
|
||||
<span
|
||||
class="raw"
|
||||
contenteditable
|
||||
tabindex="0"
|
||||
saveto="@source(../path)/@now()"
|
||||
style="display:inline-block" >
|
||||
+
|
||||
</span>
|
||||
</div>
|
||||
<div class="sortable">
|
||||
<macro src="../*" sort="checked order -title">
|
||||
<div class="item">
|
||||
<div>
|
||||
<span class="sort-handle">☰</span>
|
||||
<macro name="item-pre-controls" src="."/>
|
||||
<macro name="item-content" src="." />
|
||||
<span class="separator"/>
|
||||
<macro name="item-post-controls" src="."/>
|
||||
</div>
|
||||
<div style="padding-left: 30px">
|
||||
<include
|
||||
style="display:block"
|
||||
src="@source(./path)/outline" />
|
||||
</div>
|
||||
</div>
|
||||
</macro>
|
||||
</div>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
12
v3/bootstrap/Templates/pages.html
Executable file
@ -0,0 +1,12 @@
|
||||
<macro src="../*">
|
||||
<div class="item">
|
||||
[@source(./path)]
|
||||
<span class="separator"/>
|
||||
<a class="button" href="#@source(./path)/delete">×</a>
|
||||
</div>
|
||||
<else>
|
||||
No pages...
|
||||
</else>
|
||||
</macro>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
7
v3/bootstrap/Templates/todo.html
Executable file
@ -0,0 +1,7 @@
|
||||
<macro name="item-pre-controls">
|
||||
<input type="checkbox" class="state" saveto="@source(./path)"/>
|
||||
</macro>
|
||||
|
||||
<include src="../outline">
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
15
v3/bootstrap/Templates/tree.html
Executable file
@ -0,0 +1,15 @@
|
||||
<div class="sortable">
|
||||
<macro src="../*">
|
||||
<div class="item">
|
||||
<span class="sort-handle">☰</span>
|
||||
<a href="#@source(./path)">@source(./title)</a>
|
||||
<span class="separator"/>
|
||||
<a class="button" href="#@source(./path)/delete">×</a>
|
||||
</div>
|
||||
<div style="padding-left: 30px">
|
||||
<include style="display:block" src="@source(./path)/tree" />
|
||||
</div>
|
||||
</macro>
|
||||
</div>
|
||||
|
||||
<!-- vim:set ts=4 sw=4 : -->
|
||||
3
v3/bootstrap/Test/slot.html
Executable file
@ -0,0 +1,3 @@
|
||||
|
||||
<slot name=first text="first slot default"/>
|
||||
|
||||
3
v3/bootstrap/TestTodo.md
Executable file
@ -0,0 +1,3 @@
|
||||
[Templates/outline/_edit]
|
||||
|
||||
@include(./todo)
|
||||
1
v3/bootstrap/TestTodo/01.html
Executable file
@ -0,0 +1 @@
|
||||
item 1
|
||||
1
v3/bootstrap/TestTodo/02.html
Executable file
@ -0,0 +1 @@
|
||||
item 2
|
||||
1
v3/bootstrap/TestTodo/03.html
Executable file
@ -0,0 +1 @@
|
||||
item 3
|
||||
1
v3/bootstrap/TestTodo/04.html
Executable file
@ -0,0 +1 @@
|
||||
item 4
|
||||
2
v3/bootstrap/Theme/CLI/tree.txt
Executable file
@ -0,0 +1,2 @@
|
||||
@source(../name)<macro src="../*">
|
||||
<include src="@source(./path)/tree" recursive="..."/></macro>
|
||||
BIN
v3/img/pWiki-192px.jpg
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
v3/img/pWiki-48px.jpg
Executable file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
v3/img/pWiki-96px.jpg
Executable file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
v3/img/pWiki-i.jpg
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
v3/img/pWiki-s.jpg
Executable file
|
After Width: | Height: | Size: 56 KiB |
BIN
v3/img/pWiki.jpg
Executable file
|
After Width: | Height: | Size: 210 KiB |
BIN
v3/img/pWiki.kra
Executable file
BIN
v3/img/pWiki.png
Executable file
|
After Width: | Height: | Size: 468 KiB |
347
v3/package-lock.json
generated
Normal file
@ -0,0 +1,347 @@
|
||||
{
|
||||
"name": "pWiki",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pWiki",
|
||||
"version": "0.0.1",
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"glob": "*",
|
||||
"ig-actions": "*",
|
||||
"ig-features": "*",
|
||||
"ig-object": "*",
|
||||
"ig-test": "*",
|
||||
"ig-types": "*",
|
||||
"requirejs": "*",
|
||||
"uuid": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "13.0.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.2.2",
|
||||
"minipass": "^7.1.3",
|
||||
"path-scurry": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-actions": {
|
||||
"version": "3.26.0",
|
||||
"resolved": "https://registry.npmjs.org/ig-actions/-/ig-actions-3.26.0.tgz",
|
||||
"integrity": "sha512-GYwip0mgj1KkwqLeXelxifRog04FQpKsvT0V8G09MyWdZHKVbRw+3oXQnrAwba/+bJhJpQhSBXi6ClNAb7ExHA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ig-object": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-argv": {
|
||||
"version": "2.17.2",
|
||||
"resolved": "https://registry.npmjs.org/ig-argv/-/ig-argv-2.17.2.tgz",
|
||||
"integrity": "sha512-EGm3Sx1vo9gnQKh2CJKdyNyY0IsKljz2zTUt1SV3BrErtRvFz3WuH5ltoD0ErSEUmyY5mtOklTrFSmXqA2joLQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ig-object": "^5.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-argv/node_modules/ig-object": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.6.0.tgz",
|
||||
"integrity": "sha512-5MAUWSwfHKQNrgLroXxBHjlhrhVbhzlVqvUcfMDjUeK/ufWQ9THE0HDcvhfu+YrPfRjTR2QpD2Ygp+2H4O0C6g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ig-doc": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ig-doc/-/ig-doc-1.0.0.tgz",
|
||||
"integrity": "sha512-3+wAFWcvXv4czAk7pNa9+DEMDjlj+aJ7AQcB1ZnK49B+49nuJX7JkKJnBSF38C1YoEoxs12jvjuaqtSrvQa3KA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ig-features": {
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npmjs.org/ig-features/-/ig-features-3.4.7.tgz",
|
||||
"integrity": "sha512-ejwYjnP1K5mXzbnLTrsuuB0lGSeUtO6UtBxYcGbXGdu9rgKjz3Q46qC9XYiM01zYw3MltivLCdc2kN6/RiOvZw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ig-actions": "^3.24.28",
|
||||
"ig-object": "^5.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-features/node_modules/ig-object": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.6.0.tgz",
|
||||
"integrity": "sha512-5MAUWSwfHKQNrgLroXxBHjlhrhVbhzlVqvUcfMDjUeK/ufWQ9THE0HDcvhfu+YrPfRjTR2QpD2Ygp+2H4O0C6g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ig-object": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-6.2.1.tgz",
|
||||
"integrity": "sha512-mVGmqd+yiIf1hUo/hkqhc0JApXApuaFU4chmtO4nudL87bQ6jmJQ2qqSdUfe2hauNOHXNqG226qltCp39FHqXA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ig-doc": "*",
|
||||
"ig-stoppable": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-stoppable": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.4.tgz",
|
||||
"integrity": "sha512-KxS8AGsjelRAmbbQuASj+XRuk99P4OOprd+lIUMU2nuRKPQItNQK/apls8IlR3kNp5ZdQqBdV+zVJmYGrxofnA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ig-test": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/ig-test/-/ig-test-1.6.7.tgz",
|
||||
"integrity": "sha512-+gHKSemPMbMk+UjDaY74BwYmxWh1tRPa6vnoDf8aauXaFdoYkYLKM6lhEB5koerTXK69M9aPw609o7r91O5Z6Q==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"colors": "1.4.0",
|
||||
"glob": "^7.1.6",
|
||||
"ig-argv": "^2.16.3",
|
||||
"ig-object": "^5.4.16"
|
||||
},
|
||||
"bin": {
|
||||
"runtests": "test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-test/node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ig-test/node_modules/brace-expansion": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
|
||||
"integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-test/node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-test/node_modules/ig-object": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.6.0.tgz",
|
||||
"integrity": "sha512-5MAUWSwfHKQNrgLroXxBHjlhrhVbhzlVqvUcfMDjUeK/ufWQ9THE0HDcvhfu+YrPfRjTR2QpD2Ygp+2H4O0C6g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ig-test/node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ig-types": {
|
||||
"version": "6.26.2",
|
||||
"resolved": "https://registry.npmjs.org/ig-types/-/ig-types-6.26.2.tgz",
|
||||
"integrity": "sha512-VGg8MVmpblVCmZK52bJcDtPG3uRFiEyPnlDEIJv3MymTm+aUNWR/Th20NU2wfGV/Ux3KlsbGlhe+0c9ZKTYPDw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ig-object": "^6.0.0",
|
||||
"ig-stoppable": "^2.0.0",
|
||||
"object-run": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.5.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
|
||||
"integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/object-run": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/object-run/-/object-run-1.1.0.tgz",
|
||||
"integrity": "sha512-evzNuK7N0AZBjZDNwWfX8xJoHYYWOuoXn0A0lHxddCsKQcmU8K68HQW7eloHwc6TGlNa3CCXHxdFnO+46qtJUA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||
"integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/requirejs": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.8.tgz",
|
||||
"integrity": "sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"r_js": "bin/r.js",
|
||||
"r.js": "bin/r.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz",
|
||||
"integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist-node/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
}
|
||||
38
v3/package.json
Executable file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "pWiki",
|
||||
"version": "0.0.1",
|
||||
"author": "Alex A. Naanou <alex.nanou@gmail.com>",
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"requirejs": "*",
|
||||
"uuid": "*",
|
||||
"glob": "*",
|
||||
|
||||
"ig-actions": "*",
|
||||
"ig-features": "*",
|
||||
"ig-object": "*",
|
||||
"ig-types": "*",
|
||||
"ig-test": "*"
|
||||
},
|
||||
"disabled-dependencies": {
|
||||
"jszip": "*",
|
||||
"any-date-parser": "*",
|
||||
"file-saver": "*",
|
||||
"flexsearch": "*",
|
||||
"idb-keyval": "*",
|
||||
"peer": "*",
|
||||
"pouch-replicate-webrtc": "*",
|
||||
"pouchdb": "*",
|
||||
"showdown": "*",
|
||||
"xss": "*",
|
||||
"@toast-ui/editor": "*",
|
||||
"medium-editor": "*",
|
||||
"medium-editor-markdown": "*",
|
||||
"pouchdb": "*",
|
||||
"pouchdb-browser": "*",
|
||||
"showdown": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"bootstrap": "node scripts/bootstrap.js"
|
||||
}
|
||||
}
|
||||
14
v3/pwiki/_module.js
Normal file
@ -0,0 +1,14 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
**********************************************************************/
|
||||
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||
(function(require){ var module={} // make module AMD/node compatible...
|
||||
/*********************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* vim:set ts=4 sw=4 nowrap : */ return module })
|
||||
452
v3/pwiki/index.js
Executable file
@ -0,0 +1,452 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
**********************************************************************/
|
||||
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||
(function(require){ var module={} // make module AMD/node compatible...
|
||||
/*********************************************************************/
|
||||
|
||||
var object = require('ig-object')
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
// makeIndex(<name>[, <options>])
|
||||
// makeIndex(<name>, <generate>[, <options>])
|
||||
// -> <index-handler>
|
||||
//
|
||||
// Call/get
|
||||
// <index-handler>()
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
//
|
||||
// Call the index handler method...
|
||||
// <index-handler>('__call__', ..)
|
||||
// -> ...
|
||||
// -> <promise>
|
||||
//
|
||||
// Get merged data (cached)
|
||||
// <index-handler>('get')
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
// NOTE: when a getter is pending (promise), all consecutive calls
|
||||
// will resolve the original getter return value...
|
||||
//
|
||||
// Get sync or cached result and do "lazy" background update...
|
||||
// <index-handler>('lazy')
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
// NOTE: if <index-handler>(..) is synchronous, this will wait till
|
||||
// it returns and will return the result.
|
||||
// NOTE: 'lazy' mode is generally faster as it does all the checks and
|
||||
// updating (if needed) in a background promise, but can return
|
||||
// outdated cached results.
|
||||
// NOTE: as a side-effect this avoids returning promises if a cached
|
||||
// value is available. i.e. a promise is returned only when
|
||||
// getting/generating a value for the first time.
|
||||
//
|
||||
// Get cached result and trigger a background update...
|
||||
// <index-handler>('cached')
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
// -> undefined
|
||||
// NOTE: this is like 'lazy' but will not wait for <index-handler>)(..)
|
||||
// to return, making it even faster but as a trade off it will
|
||||
// return the cached and possibly outdated result even if
|
||||
// <index-handler>(..) is synchronous.
|
||||
//
|
||||
// Get local data (uncached)...
|
||||
// <index-handler>('local')
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
//
|
||||
// Clear cache...
|
||||
// <index-handler>('clear')
|
||||
//
|
||||
// Reset cache (clear then get)...
|
||||
// <index-handler>('reset')
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
//
|
||||
// Get index status...
|
||||
// <index-handler>('status')
|
||||
// -> 'empty'
|
||||
// -> 'pending'
|
||||
// -> 'cached'
|
||||
// -> 'outdated'
|
||||
//
|
||||
// Run custom action...
|
||||
// <index-handler>(<action-name>), ...)
|
||||
// -> <data>
|
||||
// -> <promise>
|
||||
//
|
||||
// NOTE: the main differences between the 'get', 'lazy' and 'cached' actions:
|
||||
// 'get'
|
||||
// generate/merge are all sync/async as defined
|
||||
// when cached value available validate and return either the cached value or generate
|
||||
// 'lazy'
|
||||
// XXX
|
||||
// 'cached'
|
||||
// call get in background
|
||||
// return cached value or undefined
|
||||
//
|
||||
//
|
||||
//
|
||||
// Special methods:
|
||||
//
|
||||
// Special method to generate local <data>...
|
||||
// .__<name>__()
|
||||
// -> <data>
|
||||
//
|
||||
// Merge local data with other sources...
|
||||
// .__<name>_merge__(<data>)
|
||||
// -> <data>
|
||||
//
|
||||
// Test if cache is valid...
|
||||
// .__<name>_isvalid__(<timestamp>)
|
||||
// -> <bool>
|
||||
//
|
||||
// Handle custom action...
|
||||
// .__<name>_<action-name>__(<data>. ...)
|
||||
// -> <data>
|
||||
//
|
||||
//
|
||||
//
|
||||
// Special attributes:
|
||||
//
|
||||
// Cached data...
|
||||
// .__<name>_cache / .<name>
|
||||
//
|
||||
// Modification time...
|
||||
// .__<name>_modified
|
||||
//
|
||||
// Pending generator promise...
|
||||
// .__<name>_promise
|
||||
//
|
||||
//
|
||||
// Options format:
|
||||
// {
|
||||
// // XXX
|
||||
// attr: false
|
||||
// | true
|
||||
// | <name>,
|
||||
//
|
||||
// // list of dependencies that when changed will trigger a cache
|
||||
// // drop on current index...
|
||||
// // NOTE: dependency checking is done via .modified time, if value
|
||||
// // is changed manually and not via an action then the system
|
||||
// // will not catch the change.
|
||||
// depends: [
|
||||
// <index-name>,
|
||||
// ...
|
||||
// ],
|
||||
//
|
||||
// // custom action...
|
||||
// // NOTE: this is the same as defining .__<name>_<action-name>__(..)
|
||||
// // method...
|
||||
// <action-name>: <func>,
|
||||
// }
|
||||
//
|
||||
//
|
||||
// XXX do we separate internal methods and actions???
|
||||
// i.e. __<name>_merge__(..) / __<name>_isvalid__(..) and the rest...
|
||||
var makeIndex =
|
||||
module.makeIndex =
|
||||
function(name, generate, options={}){
|
||||
// makeIndex(<name>, <options>)
|
||||
if(generate
|
||||
&& typeof(generate) != 'function'){
|
||||
options = generate
|
||||
generate = options.generate }
|
||||
|
||||
// attr names...
|
||||
var cache =
|
||||
typeof(options.attr) == 'string' ?
|
||||
options.attr
|
||||
// XXX revise default...
|
||||
: !!options.attr ?
|
||||
name
|
||||
: `__${name}_cache`
|
||||
var modified = `__${name}_modified`
|
||||
var promise = `__${name}_promise`
|
||||
var test = `__${name}_isvalid__`
|
||||
var merge = `__${name}_merge__`
|
||||
var special = `__${name}__`
|
||||
|
||||
// set modified time...
|
||||
var _stamp = function(that, res){
|
||||
res instanceof Promise ?
|
||||
res.then(function(){
|
||||
that[modified] = Date.now() })
|
||||
: (that[modified] = Date.now())
|
||||
return res }
|
||||
// make local cache...
|
||||
var _make = function(that){
|
||||
return that[special] != null ?
|
||||
that[special]()
|
||||
: (generate
|
||||
&& generate.call(that)) }
|
||||
var _smake = function(that){
|
||||
return _stamp(that, _make(that)) }
|
||||
// unwrap a promised value into cache...
|
||||
var _await = function(obj, val){
|
||||
if(val instanceof Promise){
|
||||
// NOTE: this avoids a race condition when a getter is called
|
||||
// while a previous getter is still pending...
|
||||
if(obj[promise] == null){
|
||||
obj[promise] = val
|
||||
val.then(
|
||||
function(value){
|
||||
delete obj[promise]
|
||||
obj[cache] = value },
|
||||
function(err){
|
||||
// XXX should we report this???
|
||||
delete obj[promise] }) }
|
||||
val = obj[promise] }
|
||||
return val }
|
||||
var _deferred = async function(obj, ...args){
|
||||
return meth.call(obj, ...args) }
|
||||
|
||||
// build the method...
|
||||
var meth
|
||||
return (meth = Object.assign(
|
||||
function(action, ...args){
|
||||
var that = this
|
||||
|
||||
action = action === undefined ?
|
||||
('__call__' in options ?
|
||||
'__call__'
|
||||
: 'get')
|
||||
: action
|
||||
|
||||
// action: status...
|
||||
if(action == 'status'){
|
||||
if(this[cache] instanceof Promise){
|
||||
return 'pending' }
|
||||
if(cache in this){
|
||||
var cur = this[modified]
|
||||
// user test...
|
||||
if(test in this
|
||||
&& !this[test](cur)){
|
||||
return 'outdated'
|
||||
// check dependencies...
|
||||
} else if(meth.options.depends){
|
||||
for(var dep of meth.options.depends){
|
||||
if(this[`__${this[dep].index}_modified`] > cur){
|
||||
return 'outdated' } } }
|
||||
return 'cached' }
|
||||
return 'empty' }
|
||||
|
||||
// action: lazy...
|
||||
if(action == 'lazy'){
|
||||
if(this[cache] instanceof Promise){
|
||||
return this[cache] }
|
||||
var res = meth.call(this, 'get')
|
||||
return (this[cache]
|
||||
&& res instanceof Promise) ?
|
||||
this[cache]
|
||||
: res }
|
||||
// action: cached...
|
||||
if(action == 'cached'){
|
||||
_deferred(this, 'get')
|
||||
return this[cache] }
|
||||
// action: local...
|
||||
// NOTE: this is intentionally not cached...
|
||||
if(action == 'local'){
|
||||
return _make(this) }
|
||||
|
||||
// action: clear/reset...
|
||||
if(action == 'clear'
|
||||
|| action == 'reset'){
|
||||
delete this[cache]
|
||||
'reset' in options
|
||||
&& options['reset'].call(this, null, name) }
|
||||
if(action == 'clear'){
|
||||
return }
|
||||
|
||||
// validate cache...
|
||||
if(cache in this
|
||||
&& meth.call(this, 'status') == 'outdated'){
|
||||
delete this[cache] }
|
||||
|
||||
// action: other...
|
||||
if(action != 'get'
|
||||
&& action != '__call__'
|
||||
&& action != 'reset'){
|
||||
var action_meth = `__${name}_${action}__`
|
||||
// generate cache if not available...
|
||||
var cur = cache in this ?
|
||||
this[cache]
|
||||
: meth.call(this, 'reset')
|
||||
var res = _await(this,
|
||||
this[cache] =
|
||||
// NOTE: this[action_meth] will fully shadow options[action]...
|
||||
action_meth in this ?
|
||||
this[action_meth](cur, ...args)
|
||||
: (action in options
|
||||
&& typeof(options[action]) == 'function') ?
|
||||
//options[action].call(this, cur, ...args)
|
||||
options[action].call(this, cur, name, ...args)
|
||||
: cur)
|
||||
res !== cur
|
||||
&& _stamp(this, res)
|
||||
return res }
|
||||
|
||||
// get/generate the data...
|
||||
var res = _await(this,
|
||||
this[cache] =
|
||||
// cached...
|
||||
this[cache] != null ?
|
||||
this[cache]
|
||||
// generate + merge...
|
||||
: this[merge] != null ?
|
||||
// NOTE: need to set the timestamp after the merge...
|
||||
_stamp(this,
|
||||
this[merge](_make(this)))
|
||||
// generate...
|
||||
: _smake(this))
|
||||
|
||||
// action: call...
|
||||
// NOTE: this directly returns the result to user but will
|
||||
// not automatically influence the stored value...
|
||||
if(action == '__call__'){
|
||||
return options.__call__.call(this, res, name, ...args) }
|
||||
|
||||
// action: get...
|
||||
return res },
|
||||
{
|
||||
index: name,
|
||||
indexed: true,
|
||||
options,
|
||||
})) }
|
||||
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
// XXX
|
||||
var iter =
|
||||
module.iter =
|
||||
function*(obj){
|
||||
for(var key of object.deepKeys(obj)){
|
||||
var d = object.values(obj, key, true).next().value.value
|
||||
// XXX should makeIndex(..) be a constructor -- i.e. an instanceof test???
|
||||
if(typeof(d) == 'function'
|
||||
&& d.indexed){
|
||||
yield key } } }
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//
|
||||
// .index(obj)
|
||||
// .index(obj, 'get')
|
||||
// -> <indexi>
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// .index(obj, <action>, ...)
|
||||
// -> <indexi>
|
||||
//
|
||||
//
|
||||
// .index('obj, new', <name>, <generate>[, <options>])
|
||||
// -> <index-handler>
|
||||
//
|
||||
// XXX
|
||||
var index =
|
||||
module.index =
|
||||
async function(obj, action='get', ...args){
|
||||
// create a new index...
|
||||
if(action == 'new'){
|
||||
var res = module.makeIndex(...args)
|
||||
var [name, _, options={}] = args
|
||||
var attr = name
|
||||
if(options.attr){
|
||||
var attr = `__${name}`
|
||||
Object.defineProperty(obj, name, {
|
||||
get: function(){
|
||||
return obj[attr] }, }) }
|
||||
return (obj[attr] = res) }
|
||||
// propagate action...
|
||||
return Object.fromEntries(
|
||||
await Promise.all(
|
||||
module.iter(obj)
|
||||
.map(async function(name){
|
||||
return [
|
||||
obj[name].index,
|
||||
await obj[name](action, ...args),
|
||||
] }))) }
|
||||
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
var IndexManagerMixin =
|
||||
module.IndexManagerMixin =
|
||||
object.Mixin('IndexManagerMixin', {
|
||||
// List of index handler attribute names...
|
||||
//
|
||||
// XXX rename???
|
||||
get index_attrs(){
|
||||
return [...module.iter(this)] },
|
||||
index: async function(action='get', ...args){
|
||||
return module.index(this, ...arguments) },
|
||||
})
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
var indexTest =
|
||||
module.indexTest =
|
||||
IndexManagerMixin({
|
||||
// tests...
|
||||
//
|
||||
moo: module.makeIndex('moo', () => 123),
|
||||
|
||||
foo_index: module.makeIndex('foo', () => 123, {
|
||||
attr: true,
|
||||
add: function(cur, val){
|
||||
return cur + val },
|
||||
}),
|
||||
|
||||
__boo_add__: function(cur, val){
|
||||
return cur + val },
|
||||
boo: module.makeIndex('boo', () => 123),
|
||||
|
||||
__soo_add__: async function(cur, val){
|
||||
return await cur + val },
|
||||
__soo: module.makeIndex('soo', async () => 123),
|
||||
get soo(){
|
||||
return this.__soo() },
|
||||
|
||||
__sum: module.makeIndex('sum',
|
||||
async function(){
|
||||
return await this.moo()
|
||||
+ await this.foo_index()
|
||||
+ await this.boo()
|
||||
+ await this.soo },
|
||||
{ depends: [
|
||||
'moo',
|
||||
'foo_index',
|
||||
'boo',
|
||||
'__soo',
|
||||
], }),
|
||||
get sum(){
|
||||
return this.__sum() },
|
||||
|
||||
__merged__: function(){
|
||||
return 777 },
|
||||
__merged_merge__: async function(data){
|
||||
return (await data) + 777 },
|
||||
__merged: module.makeIndex('merged'),
|
||||
get merged(){
|
||||
return this.__merged() },
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* vim:set ts=4 sw=4 nowrap : */ return module })
|
||||
2066
v3/pwiki/page.js
Executable file
1838
v3/pwiki/parser.js
Normal file
464
v3/pwiki/path.js
Executable file
@ -0,0 +1,464 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
**********************************************************************/
|
||||
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||
(function(require){ var module={} // make module AMD/node compatible...
|
||||
/*********************************************************************/
|
||||
|
||||
var types = require('ig-types')
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
var makeEncoder = function(name, chars){
|
||||
var pattern_attr = '__'+name+'_pattern'
|
||||
return function(str){
|
||||
var pattern = this[pattern_attr] =
|
||||
this[pattern_attr]
|
||||
?? RegExp(`[${
|
||||
this[chars]
|
||||
.replace(/\\/g, '\\\\') }]`, 'g')
|
||||
return str
|
||||
.replace(pattern,
|
||||
function(s){
|
||||
return ('%'+s.charCodeAt().toString(16)).toUpperCase() }) } }
|
||||
var makeDecoder = function(name, encode, chars){
|
||||
var pattern_attr = '__'+name+'_pattern'
|
||||
return function(str){
|
||||
var pattern = this[pattern_attr] =
|
||||
this[pattern_attr]
|
||||
?? RegExp(`%(${
|
||||
this[encode](this[chars])
|
||||
.slice(1)
|
||||
.replace(/%/g, '|') })`, 'gi')
|
||||
return str
|
||||
.replace(pattern, function(_, c){
|
||||
return String.fromCharCode('0x'+c) }) } }
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Path...
|
||||
|
||||
module = {
|
||||
|
||||
// Page returned when listing a path ending with '/'...
|
||||
//
|
||||
// If set to false treat dirs the same as pages (default)
|
||||
//INDEX_PAGE: 'index',
|
||||
INDEX_PAGE: false,
|
||||
|
||||
// The page returned when getting the '/' path...
|
||||
//
|
||||
// NOTE: this is the same as .INDEX_PAGE but only for '/'
|
||||
ROOT_PAGE: 'WikiHome',
|
||||
|
||||
ALTERNATIVE_PAGES: [
|
||||
'EmptyPage',
|
||||
'NotFoundError',
|
||||
],
|
||||
|
||||
// Default alternate search locations...
|
||||
//
|
||||
// NOTE: if a path here is relative it is also searched relative to
|
||||
// the target path.
|
||||
SEARCH_PATHS: [
|
||||
'.templates',
|
||||
],
|
||||
|
||||
// System path...
|
||||
//
|
||||
// This acts the same as elements in .SEARCH_PATHS but should contain
|
||||
// all the default and fallback templates.
|
||||
//
|
||||
// NOTE: we can't use .pwiki here as it will be in conflict with the
|
||||
// fs store's directory structure.
|
||||
// XXX or can we?
|
||||
SYSTEM_PATH: '/.system',
|
||||
|
||||
// XXX EXPERIMENTAL
|
||||
|
||||
ENCODED_PATH: '#:*%',
|
||||
encode: makeEncoder('encode', 'ENCODED_PATH'),
|
||||
decode: makeDecoder('decode', 'encode', 'ENCODED_PATH'),
|
||||
|
||||
ENCODED_ELEM: '#:*%\\/',
|
||||
encodeElem: makeEncoder('encode_elem', 'ENCODED_ELEM'),
|
||||
decodeElem: makeDecoder('decode_elem', 'encodeElem', 'ENCODED_ELEM'),
|
||||
|
||||
// chars we keep in path as-is -- decode on normalize...
|
||||
//
|
||||
UNENCODED_PATH: '\'"',
|
||||
quote: makeEncoder('quote', 'UNENCODED_PATH'),
|
||||
unquote: makeDecoder('unquote', 'quote', 'UNENCODED_PATH'),
|
||||
|
||||
quoteHTML: function(str){
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<') },
|
||||
unquoteHTML: function(str){
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<') },
|
||||
|
||||
|
||||
// Path utils...
|
||||
//
|
||||
// Path can be in one of two formats:
|
||||
// string
|
||||
// array
|
||||
//
|
||||
// NOTE: trailing/leading '/' are represented by '' at end/start of
|
||||
// path list...
|
||||
normalize: function(path='.', format='auto'){
|
||||
format = format == 'auto' ?
|
||||
(path instanceof Array ?
|
||||
'array'
|
||||
: 'string')
|
||||
: format
|
||||
var root = path[0] == ''
|
||||
|| path[0] == '/'
|
||||
path = this.unquote(
|
||||
path instanceof Array ?
|
||||
path.join('/')
|
||||
: path)
|
||||
// NOTE: this will also trim the path elements...
|
||||
.split(/\s*[\\\/]+\s*/)
|
||||
.reduce(function(res, e, i, L){
|
||||
// special case: leading '..' / '.'
|
||||
if(res.length == 0
|
||||
&& e == '..'){
|
||||
return [e] }
|
||||
// multiple leading '..'...
|
||||
;(e == '..'
|
||||
&& res.at(-1) == '..' ?
|
||||
res.push(e)
|
||||
: e == '.'
|
||||
// keep explicit '/' only at start/end of path...
|
||||
|| (e == ''
|
||||
&& i != 0
|
||||
&& i != L.length-1)) ?
|
||||
undefined
|
||||
: e == '..'
|
||||
|| res.at(-1) == '>>' ?
|
||||
((res.length > 1
|
||||
|| res[0] != '')
|
||||
&& res.pop())
|
||||
// NOTE: the last '>>' will be retained...
|
||||
: res.push(e)
|
||||
return res }, [])
|
||||
// clear the trailing '/'...
|
||||
path.at(-1) == ''
|
||||
&& path.pop()
|
||||
// trim trailing ':'...
|
||||
path.at(-1)
|
||||
&& path.at(-1).endsWith(':')
|
||||
&& (path.push(
|
||||
path.pop()
|
||||
.replace(/:*$/, '')))
|
||||
return format == 'string' ?
|
||||
// special case: root -> keep '/'
|
||||
((root
|
||||
&& path.length == 1
|
||||
&& path[0] == '') ?
|
||||
('/'+ path.join('/'))
|
||||
: path.join('/'))
|
||||
: path },
|
||||
sanitize: function(path, format='auto'){
|
||||
format = format == 'auto' ?
|
||||
(path instanceof Array ?
|
||||
'array'
|
||||
: 'string')
|
||||
: format
|
||||
path = this.split(path)
|
||||
// leading: '/', '.' and '..'...
|
||||
while(path[0] == ''
|
||||
|| path[0] == '.'
|
||||
|| path[0] == '..'){
|
||||
path.shift() }
|
||||
//trailing '/'
|
||||
path.at(-1) == ''
|
||||
&& path.pop()
|
||||
return format == 'string' ?
|
||||
path.join('/')
|
||||
: path },
|
||||
split: function(path){
|
||||
return this.normalize(path, 'array') },
|
||||
join: function(...parts){
|
||||
return this.normalize(
|
||||
(parts[0] instanceof Array ?
|
||||
parts[0]
|
||||
: parts),
|
||||
'string') },
|
||||
basename: function(path){
|
||||
path = this.split(path)
|
||||
return path.length == 0 ?
|
||||
''
|
||||
: path.length == 1 ?
|
||||
path[0]
|
||||
: (path.at(-1) == '' ?
|
||||
path.at(-2)
|
||||
: path.at(-1)) },
|
||||
dirname: function(path){
|
||||
path = this.split(path)
|
||||
path = path.length == 1 ?
|
||||
'/'
|
||||
: path.length == 2 ?
|
||||
path[0]
|
||||
: (path.at(-1) == '' ?
|
||||
path.slice(0, -2)
|
||||
: path.slice(0, -1))
|
||||
.join('/')
|
||||
return path == '' ?
|
||||
'/'
|
||||
: path },
|
||||
|
||||
// XXX BUG? which is more correct??
|
||||
// .relative('a/b/c', 'x')
|
||||
// -> 'a/b/c/x' (current)
|
||||
// or:
|
||||
// .relative('a/b/c', 'x')
|
||||
// -> 'a/b/x'
|
||||
// ...not sure about this yet...
|
||||
// XXX REVISE...
|
||||
relative: function(parent, path, format='auto'){
|
||||
format = format == 'auto' ?
|
||||
(path instanceof Array ?
|
||||
'array'
|
||||
: 'string')
|
||||
: format
|
||||
// root path...
|
||||
if(path[0] == '' || path[0] == '/'){
|
||||
return this.normalize(path, format) }
|
||||
// unify parent/path types...
|
||||
parent = parent instanceof Array ?
|
||||
parent
|
||||
: parent.split(/\s*[\\\/]+\s*/)
|
||||
path = path instanceof Array ?
|
||||
path
|
||||
: path.split(/\s*[\\\/]+\s*/)
|
||||
// NOTE: relative paths are siblings and not children unless the
|
||||
// parent is explicitly a directory (i.e. ends in '/')...
|
||||
/* XXX RELATIVE -- leading @ in path is the same as a trailing / in parent...
|
||||
path[0] == '@' ?
|
||||
path.shift()
|
||||
: parent.pop()
|
||||
//*/
|
||||
return this.normalize([...parent, ...path], format) },
|
||||
|
||||
// Build alternative paths for page acquisition...
|
||||
//
|
||||
// NOTE: if seen is given (when called recursively) this will not
|
||||
// search for .ALTERNATIVE_PAGES...
|
||||
// XXX should we keep the trailing '/'???
|
||||
// XXX EXPERIMENTAL...
|
||||
paths: function*(path='/', strict=false, seen=new Set()){
|
||||
if(path === true || path === false){
|
||||
strict = path
|
||||
path = '/' }
|
||||
if(strict instanceof Set){
|
||||
seen = strict
|
||||
strict = false }
|
||||
var alt_pages = !strict
|
||||
path = this.normalize(path, 'string')
|
||||
// special case: root...
|
||||
if(path == '/' || path == ''){
|
||||
// normalize...
|
||||
path = '/'
|
||||
// as-is...
|
||||
seen.add(path)
|
||||
yield path
|
||||
// special case: root page...
|
||||
if(this.ROOT_PAGE){
|
||||
yield* this.paths(this.normalize('/'+ this.ROOT_PAGE, 'string'), seen) }}
|
||||
// NOTE: since path is already normalized we can trust the delimiter...
|
||||
path = path.split(/\//g)
|
||||
// normalize relative paths to root...
|
||||
path[0] != ''
|
||||
&& path.unshift('')
|
||||
// paths ending in '/'...
|
||||
if(path[path.length-1] == ''){
|
||||
path.pop()
|
||||
this.INDEX_PAGE
|
||||
&& path.push(this.INDEX_PAGE) }
|
||||
|
||||
var searchPath = function*(path, search_paths){
|
||||
// full path...
|
||||
var p = this.normalize(['', ...path], 'string')
|
||||
if(!seen.has(p)){
|
||||
seen.add(p)
|
||||
yield p }
|
||||
|
||||
path = path.slice()
|
||||
search_paths = search_paths instanceof Array ?
|
||||
search_paths
|
||||
: [search_paths]
|
||||
// search up the levels...
|
||||
var cur, sub, sub_cur
|
||||
do{
|
||||
cur = [path.pop(), ...(cur ?? [])]
|
||||
sub = cur.slice()
|
||||
sub_cur = []
|
||||
// template paths...
|
||||
for(var tpl of search_paths){
|
||||
// search the sub paths in the template dir...
|
||||
var parent = ['', ...path]
|
||||
do{
|
||||
sub_cur = [sub.pop(), ...(sub_cur ?? [])]
|
||||
p = this.relative(parent, tpl +'/'+ sub_cur.join('/'), 'string')
|
||||
if(!seen.has(p)){
|
||||
seen.add(p)
|
||||
yield p }
|
||||
}while(sub.length > 0)
|
||||
}
|
||||
// search the sub paths in the parent dir...
|
||||
parent = parent.length > 1 ?
|
||||
parent.slice(0, -1)
|
||||
: parent
|
||||
sub = cur.slice()
|
||||
sub_cur = []
|
||||
do{
|
||||
sub_cur = [sub.pop(), ...(sub_cur ?? [])]
|
||||
p = this.relative(parent, sub_cur.join('/'), 'string')
|
||||
if(!seen.has(p)){
|
||||
seen.add(p)
|
||||
yield p }
|
||||
}while(sub.length > 0)
|
||||
}while(path.length > 0) }.bind(this)
|
||||
|
||||
yield* searchPath(path, this.SEARCH_PATHS)
|
||||
yield* searchPath(path, this.SYSTEM_PATH)
|
||||
if(alt_pages){
|
||||
for(var page of [...this.ALTERNATIVE_PAGES]){
|
||||
yield* this.paths(path.slice(0, -1).concat(page), true, seen) } } },
|
||||
/*/
|
||||
paths: function*(path='/', strict=false){
|
||||
if(path === true || path === false){
|
||||
strict = path
|
||||
path = '/' }
|
||||
var alt_pages = !strict
|
||||
var seen = strict instanceof Set ?
|
||||
strict
|
||||
: new Set()
|
||||
path = this.normalize(path, 'string')
|
||||
// special case: root...
|
||||
if(path == '/' || path == ''){
|
||||
// normalize...
|
||||
path = '/'
|
||||
// as-is...
|
||||
seen.add(path)
|
||||
yield path
|
||||
// special case: root page...
|
||||
if(this.ROOT_PAGE){
|
||||
yield* this.paths(this.normalize('/'+ this.ROOT_PAGE, 'string'), seen) }}
|
||||
// NOTE: since path is already normalized we can trust the delimiter...
|
||||
path = path.split(/\//g)
|
||||
// normalize relative paths to root...
|
||||
path[0] != ''
|
||||
&& path.unshift('')
|
||||
// paths ending in '/'...
|
||||
if(path[path.length-1] == ''){
|
||||
path.pop()
|
||||
this.INDEX_PAGE
|
||||
&& path.push(this.INDEX_PAGE) }
|
||||
// search for page...
|
||||
var page = path.pop()
|
||||
for(var tpl of ['.', ...this.SEARCH_PATHS, this.SYSTEM_PATH]){
|
||||
// search for page up the path...
|
||||
var pg = page
|
||||
var base = path.slice()
|
||||
while(base.length > 0){
|
||||
var p = base.slice()
|
||||
while(p.length > 0){
|
||||
// NOTE: we are adding '' to path here to get things
|
||||
// relative to it and not relative to basedir...
|
||||
var cur = this.relative([...p, ''], tpl +'/'+ pg, 'string')
|
||||
if(!seen.has(cur)){
|
||||
seen.add(cur)
|
||||
yield cur }
|
||||
// special case: non-relative template/page path...
|
||||
if(tpl[0] == '/'){
|
||||
break }
|
||||
p.pop() }
|
||||
// next search for tail sub-path...
|
||||
// for a/b/c
|
||||
// c in a/b -> b/c in a
|
||||
pg = base.pop() +'/'+ pg } }
|
||||
// alternative pages...
|
||||
if(alt_pages){
|
||||
for(var page of [...this.ALTERNATIVE_PAGES]){
|
||||
yield* this.paths(path.concat(page), seen) }} },
|
||||
//*/
|
||||
|
||||
names: function(path='/'){
|
||||
path = path == '' ?
|
||||
'/'
|
||||
: path
|
||||
path = this.normalize(path, 'string')
|
||||
var name = path == '/' ?
|
||||
['', this.ROOT_PAGE]
|
||||
: [this.basename(path)]
|
||||
return name == '' ?
|
||||
this.ALTERNATIVE_PAGES.slice()
|
||||
: [...name, ...this.ALTERNATIVE_PAGES] },
|
||||
|
||||
|
||||
//
|
||||
// .splitArgs(<path>)
|
||||
// -> <spec>
|
||||
//
|
||||
// Format:
|
||||
// {
|
||||
// path: <path>
|
||||
// args: {
|
||||
// <name>:<value>,
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Syntax:
|
||||
// <path> ::= <path>:<args>
|
||||
// <args> ::=
|
||||
// <arg> | <arg>:<args>
|
||||
// <arg> ::=
|
||||
// <value>
|
||||
// | <name>=<value>
|
||||
//
|
||||
// XXX the problem here is that we could legitimately create path
|
||||
// items containing ":" -- it either needs to be a reserved char
|
||||
// or this scheme will not work...
|
||||
splitArgs: function(path){
|
||||
path = this.normalize(path, 'string')
|
||||
var [path, ...args] = path.split(/(?<!\\):/g)
|
||||
return {
|
||||
path,
|
||||
args: args.reduce(function(res, arg){
|
||||
var [name, value] = arg.split(/=(.*)/)
|
||||
res[name.trim()] = value ?? true
|
||||
return res }, {}),
|
||||
} },
|
||||
obj2args: function(args){
|
||||
return args instanceof Object ?
|
||||
Object.entries(args)
|
||||
.map(function([key, value]){
|
||||
return value === true ?
|
||||
key
|
||||
: key +'='+ ((value + '').replace(/:/g, '\\:')) })
|
||||
.join(':')
|
||||
: args },
|
||||
joinArgs: function(path, args={}){
|
||||
path = this.join(path)
|
||||
args = this.obj2args(args)
|
||||
return args == '' ?
|
||||
path
|
||||
: path +':'+ args },
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* vim:set ts=4 sw=4 : */ return module })
|
||||
65
v3/scripts/bootstrap.js
vendored
Executable file
@ -0,0 +1,65 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
var fs = require('fs')
|
||||
var glob = require('glob')
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
var bootstrap = {}
|
||||
|
||||
var BOOTSTRAP_TEMPLATE =
|
||||
`// This file is generated automatically, all changes made here will be lost.
|
||||
|
||||
var Bootstrap = $BOOTSTRAP
|
||||
|
||||
typeof(module) != "undefined"
|
||||
&& (module.exports = Bootstrap)`
|
||||
|
||||
|
||||
// XXX add support for json...
|
||||
new glob.Glob('bootstrap/**/*.@(tpl|md|css|html|txt)', {})
|
||||
.stream()
|
||||
.on('data', function(path){
|
||||
var p = path
|
||||
.replace('bootstrap/', '')
|
||||
.replace(/\.(json|txt|md|css|html|txt)/, '')
|
||||
console.log('Found:', p)
|
||||
bootstrap[p] = {
|
||||
text: fs.readFileSync(path).toString(),
|
||||
} })
|
||||
.on('end', function(){
|
||||
|
||||
// extra root stuff...
|
||||
if(fs.existsSync('README.md')){
|
||||
console.log('Setting:', 'About')
|
||||
bootstrap['Doc/About'] = {
|
||||
text: fs.readFileSync('README.md').toString(),
|
||||
} }
|
||||
if(!bootstrap.WikiHome){
|
||||
console.log('Setting:', 'WikiHome')
|
||||
bootstrap.WikiHome = {
|
||||
text: '@include(Doc/About)'
|
||||
} }
|
||||
if(fs.existsSync('LICENSE')){
|
||||
console.log('Setting:', 'LICENSE')
|
||||
bootstrap['LICENSE'] = {
|
||||
text: `${
|
||||
fs.readFileSync('LICENSE').toString()
|
||||
}<!-- @filter(text) -->`,
|
||||
} }
|
||||
|
||||
var txt = BOOTSTRAP_TEMPLATE
|
||||
.replace(/\$BOOTSTRAP/g, JSON.stringify(bootstrap))
|
||||
|
||||
console.log('Writing:', 'bootstrap.js')
|
||||
fs.writeFileSync('bootstrap.js', txt) })
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* vim:set ts=4 sw=4 : */
|
||||