pWiki/pwiki2.js
Alex A. Naanou 136cd294f4 added render rependency tracking + note cleanup...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2022-08-21 18:10:39 +03:00

360 lines
12 KiB
JavaScript
Executable File

/**********************************************************************
*
*
* XXX need a uniform way to track state in pwiki for things like paging
* and the like with simple user/macro access...
* ...the simplest that comes to mind is to store in in path
* somehow:
* - <path>?<arg>=<value>&...
* traditional "query string"...
* - <path>/<arg>:<valu>/<arg>:<value>/.../<action>
* stack-style arguments...
* + simple to implement
* - goes thrugh page search???
* - <path>:<value>:<name>=<value>:...
* - ...
* the general idea is to be:
* - flexible enough to allow the basics done
* - restrictive enough to prevent missuse
* ...the rest of the state can simply be stored in the root pwiki
* object in one of the following ways:
* - directly (attrs/dict)
* - a special page
* - virtual (path-specific)
* e.g.
* /some/path/@state/page -> 4
* ...might be fun to implement a basic json editor
* and viewer with this api...
* ...controlled via js
* ...or special actions:
* /some/path/@state/page/next (increment)
* /some/path/@state/page/prev (decrement)
* /some/path/@state/page=10 (assign)
* ...
* - session
* - stored (config)
* ...css selector as path....
* XXX CACHE cache rendered pages and invalidate cache based to changes
* to page dependencies...
* XXX GENERATOR make pattern path parsing a generator...
* ...experiment with a controllable iterator/range thing...
* This would require:
* 1) the rendering infrastructure to support generation and
* partial rendering starting from .expand(..) and up...
* input output
* .expand(..) DONE DONE
* .resolve(..) partial DONE
* XXX for-await-of (vs. for-of-await) breaks things
* (see: /test_slots, ..?)
* .parse(..) DONE NO
* XXX with the current implementation of filters
* this can't work as a generator...
* ...might be a good idea to make filters local only...
* XXX slots/macros might also pose a problem...
* 2) all the macros that can source pages to produce generators (DONE)
* XXX BUG: .move(..) behaves in an odd way...
* see: System/move page action
* ...deletes the original and moves an empty page -- sync error???
* XXX differences in behaviour between _abc and abc, either need to make
* them the same or document the differences and the reasons behind
* them...
* XXX add support for <join> tag in include/source/quote???
* XXX BUG?: markdown: when parsing chunks each chunk gets an open/closed
* <p> inserted at start/end -- this breaks stuff returned by macros...
* ...there are two ways to dance around this:
* - make filters run a bit more globaly -- per block...
* - find a local parser...
* XXX introspection:
* /stores
* list stores...
* /info
* list page/store info
* /storage
* list storage usage / limits
* XXX OPTIMIZE: /tree is really slow...
* ...is the problem purely in the async/await playing ping-pong???
* XXX BUG: FF: conflict between object.run and PouchDB...
* XXX add action to reset overloaded (bootstrap) pages...
* - per page
* - global
* XXX Q: can we access fs from a pwa???
* ...looks like no :|
* XXX DEPENDS @now() makes the template uncachable, to we actually need it???
* XXX CHECK: @macro(..) and @slot(..) must overload in the same way...
*
*
*
* XXX ROADMAP:
* - run in browser
* - basics, loading -- DONE
* - test localStorage / sessionStorage -- DONE
* - test pouch -- DONE
* - render page -- DONE
* - navigation -- DONE
* - hash/anchor -- DONE
* - action redirects (see: System/delete) -- DONE
* - basic editor and interactivity -- DONE
* - export
* - json -- DONE
* - zip (json/tree) --
* - page actions
* - delete -- DONE
* - copy/move -- DONE
* - resolved (async) --
* - migrate/rewrite bootstrap --
* - store topology --
* - sync and sync conf --
* - images
* - get --
* - download --
* - upload --
* - search
* - paths
* - text
* - markdown -- DONE
* - WikiWord -- DONE
* - dom filter mechanics -- DONE
* - filters
* - markdown (???) -- ???
* this can be done in one of two ways:
* - wrapping blocks in elemens
* ...requires negative filter calls, either on -wikiword
* or a different filter like nowikiwords...
* - tags (current)
* - raw / code -- DONE?
* - nl2br --
* - nowhitespace --
* clear extra whitespace from text elements
* - dom filters:
* - editor
* basic -- DONE
* see: /System/edit
* MediumEditor (markdown-plugin)
* https://github.com/yabwe/medium-editor
* https://github.com/IonicaBizau/medium-editor-markdown
* - heavy-ish markdown plugin
* ToastUI (markdown)
* https://github.com/nhn/tui.editor
* - quite heavy
* Pen (markdown)
* https://github.com/sofish/pen
* - no npm module
* - not sure if it works on mobile
* + small
* tiptap (no-markdown, investigate y.js)
* - wikiword / path2link --
* ..do we need to be able to control this???
* - templates
* - all (tree) -- DONE
* - configuration
* - defaults
* - System/config (global)
* - pwa
* - service worker ???
* ...handle relative urls (???)
* - cli
* - basic wiki manipulations (1:1 methods)
* - import/export/sync
* - introspection/repl
* - archive old code
* - update docs
* - refactor and cleanup
* - module structure -- REVISE
* - pack as electron app (???)
*
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Architecture:
* store
* page
* renderer
*
* Modules:
* page - base pages and page APIs (XXX should this be in lib???)
* parser - pWiki macro parser (XXX should this be in lib???)
* store - stores
* base - memory store and store utils
* file - file storage
* localstorage - localStorage / sessionStorage stores
* pouchdb -
* ...
* filter - page filters
* base - base filters incl. wikiword
* markdown - markdown renderer
* ...
* pwiki2 - main cli / node entry point
* browser - browser entry point
* pwiki2-test - testing and experimenting (XXX move to test.js)
*
*
* Q: can we make this a single module with +/- some plugins??
* ...this would make things quite a bit simpler but will negate the
* use of high level libs like types...
*
*
* XXX DOC:
* - paths in pWiki behave a bit differently than traditional
* file-system paths, this is due to one key distinction:
* in pWiki there is no distinction between a file and a
* directory
* i.e. each path can both contain data as a file and at the same
* time support sub-paths etc.
* for this reason behaviour of some APIs will differ, all paths
* within a page (a-la file) are relative to its children and not
* to it's siblings. For example, for page "/a/b/c" a link to "./x"
* will resolve to "/a/b/c/x" and this is independent of whether
* the base path is given as "/a/b/c/" or "/a/b/c"
*
* NOTE: implementing things in a traditional manner would
* introduce lots of edge-cases and subtle ways to make
* mistakes, bugs and inconsistencies.
* - types of recursion
* (see: pwiki/page.js: Page.macros.include(..) notes)
* - slot <content/> order --
* (see: page.js: Page's .macros.slot(..) notes)
*
*
* XXX weaknesses to review:
* - <store>.paths() as an index...
* + decouples the search logic from the store backend
* - requires the whole list of pages to be in memory
* ...need to be independent of the number of pages if at
* all possible -- otherwise this will hinder long-term use...
* .paths() should be cached to make all path searches away from
* async waits as we can test quite a number of paths before we
* find a page...
* ...another solution is to offload the search to the store backend
* but this would require the stores to reimplement most of pwiki/path
* -
*
*
* TODO?:
* - <page>.then() -- resolve when all pending write operations done ???
* - an async REPL???
* - custom element???
* - might be a good idea to try signature based security:
* - sign changes
* - sign sync session
* - refuse changes with wrong signatures
* - public keys available on client and on server
* - check signatures localy
* - check signatures remotely
* - private key available only with author
* - keep both the last signed and superceding unsigned version
* - on sync ask to overwrite unsigned with signed
* - check if we can use the same mechanics as ssh...
* - in this view a user in the system is simply a set of keys and
* a signature (a page =))
*
* XXX RELATIVE relative urls are a bit odd...
* Path/to/page opens Moo -> Path/to/Page/Moo
* should be (???):
* Path/to/page opens Moo -> Path/to/Moo
* this boils down to how path.relative(..) works, treating the base
* as a directory always (current) vs. only if '/' is at the end, on
* one hand the current approach is more uniform with less subtle ways
* to make mistakes but on the other hand this may introduce a lot
* of complexity to the user writing links, e.g. how should the
* following be interpreted?
* page: /SomePage
* link: SomeOtherPage
* -> /SomeOtherPage
* -> /SomePage/SomeOtherPage (current)
* the current approach does not seem to be intuitive...
* can this be fixed uniformly across the whole system???
* XXX document this!
*
* XXX need to think about search -- page function argument syntax???
*
* XXX might need to get all the links (macro-level) from a page...
* ...would be useful for caching...
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
(function(require){ var module={} // make module AMD/node compatible...
/*********************************************************************/
// XXX
//var object = require('lib/object')
var object = require('ig-object')
var types = require('ig-types')
var pwpath =
module.path =
require('./pwiki/path')
var page = require('./pwiki/page')
var basestore = require('./pwiki/store/base')
//var localstoragestore = require('./store/localstorage')
// XXX for some reason this does not run quietly in browser
//var pouchdbstore = require('./store/pouchdb')
//var filestore = require('./store/file')
//---------------------------------------------------------------------
// Basic setup...
//
//
// Store topology:
//
// root (BaseStore) ---next--- main (MetaStore)
// |
// +-- System/... (BaseStore)
//
// Alternative store topology:
//
// root (BaseStore) ---next--- main (MetaStore)
// System/... (static)
//
//
var store =
module.store =
//BaseStore.nest()
// XXX clone...
{ __proto__: basestore.BaseStore }
.nest({ __proto__: basestore.MetaStore })
// XXX these are async...
// ...see browser.js for a way to deal with this...
// XXX note sure how to organize the system actions -- there can be two
// options:
// - a root ram store with all the static stuff and nest the rest
// - a nested store (as is the case here)
// XXX nested system store...
store.update('System',
Object.create(basestore.BaseStore).load(page.System))
store.update('Settings',
Object.create(basestore.BaseStore).load(page.Settings))
// NOTE: in general the root wiki api is simply a page instance.
// XXX not yet sure how to organize the actual client -- UI, hooks, .. etc
var pwiki =
module.pwiki =
page.Page('/', '/', store)
//---------------------------------------------------------------------
// comandline...
if(typeof(__filename) != 'undefined'
&& __filename == (require.main || {}).filename){
}
/**********************************************************************
* vim:set ts=4 sw=4 nowrap : */ return module })