mirror of
				https://github.com/flynx/pWiki.git
				synced 2025-11-03 20:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			610 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			HTML
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			610 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			HTML
		
	
	
		
			Executable File
		
	
	
	
	
<!DOCTYPE html>
 | 
						|
<html>
 | 
						|
<!--html manifest="pwiki.appcache"-->
 | 
						|
<head>
 | 
						|
<title>pWiki</title>
 | 
						|
 | 
						|
<meta charset="utf-8">
 | 
						|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
 | 
						|
 | 
						|
<link rel="manifest" href="manifest.json">
 | 
						|
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-Bold.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-BoldItalic.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-ExtraBold.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-ExtraBoldItalic.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-Italic.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-Light.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-LightItalic.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-Regular.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-Semibold.ttf">
 | 
						|
<link rel="prefetch" href="css/fonts/Open_Sans/OpenSans-SemiboldItalic.ttf">
 | 
						|
 | 
						|
<link rel="stylesheet" href="css/fonts.css">
 | 
						|
 | 
						|
 | 
						|
<!-- NativeMarkdown -->
 | 
						|
<script src="ext-lib/showdown.min.js"></script>
 | 
						|
<script>
 | 
						|
var MarkdownPage = {
 | 
						|
}
 | 
						|
</script>
 | 
						|
<!-- NativeMarkdown -->
 | 
						|
 | 
						|
 | 
						|
 | 
						|
<!-- MediumEditor -->
 | 
						|
<!--link rel="stylesheet" href="experiments/medium-editor/css/medium-editor.css">
 | 
						|
<link rel="stylesheet" href="experiments/medium-editor/css/themes/default.css">
 | 
						|
<script src="ext-lib/showdown.min.js"></script>
 | 
						|
<script src="experiments/medium-editor/js/medium-editor.js"></script>
 | 
						|
<script src="experiments/medium-editor/js/me-markdown.standalone.js"></script>
 | 
						|
<script>
 | 
						|
var setupMediumEditor = async function () {
 | 
						|
	var editorelem = document.querySelector('.medium-editor')
 | 
						|
	if(editorelem){
 | 
						|
		console.log('MediumEditor: setup...')
 | 
						|
		var page = pwiki.get('..')
 | 
						|
 | 
						|
		// load the initial state...
 | 
						|
		var converter = new showdown.Converter()
 | 
						|
		editorelem.innerHTML = converter.makeHtml(await page.raw)
 | 
						|
 | 
						|
		var elem = document.querySelector('.medium-markdown')
 | 
						|
		editor = new MediumEditor(editorelem, {
 | 
						|
			extensions: {
 | 
						|
				markdown: new MeMarkdown(function(code) {
 | 
						|
					saveLiveContent(page.path, code)
 | 
						|
					// XXX DEBUG...
 | 
						|
					elem 
 | 
						|
						&& (elem.textContent = code) }) } }) } }
 | 
						|
</script-->
 | 
						|
<!-- MediumEditor -->
 | 
						|
 | 
						|
<!-- ToastUIEditor -->
 | 
						|
<!-- ToastUIEditor -->
 | 
						|
 | 
						|
 | 
						|
 | 
						|
</head>
 | 
						|
 | 
						|
<style>
 | 
						|
 | 
						|
body {
 | 
						|
	font-size: 1.1em;
 | 
						|
}
 | 
						|
 | 
						|
h1:empty {
 | 
						|
	display: none;
 | 
						|
}
 | 
						|
 | 
						|
a {
 | 
						|
	text-decoration: none;
 | 
						|
}
 | 
						|
a:hover {
 | 
						|
	text-decoration: underline;
 | 
						|
}
 | 
						|
 | 
						|
.show-on-hover {
 | 
						|
	opacity: 0;
 | 
						|
}
 | 
						|
:hover>.show-on-hover {
 | 
						|
	opacity: 0.4;
 | 
						|
}
 | 
						|
.show-on-hover:hover {
 | 
						|
	opacity: 0.8;
 | 
						|
}
 | 
						|
 | 
						|
/* Spinner... */
 | 
						|
.spinner {
 | 
						|
	position: fixed;
 | 
						|
	display: flex;
 | 
						|
	text-align: center;
 | 
						|
	left: 50%;
 | 
						|
	top: 50%;
 | 
						|
	width: 100px;
 | 
						|
	height: 100px;
 | 
						|
	margin-top: -50px;
 | 
						|
	margin-left: -50px;
 | 
						|
	white-space: nowrap;
 | 
						|
 | 
						|
	background: transparent;
 | 
						|
	pointer-events: none;
 | 
						|
 | 
						|
	animation: fadein 2s 1;
 | 
						|
}
 | 
						|
.spinner span {
 | 
						|
	position: relative;
 | 
						|
	display: inline-block;
 | 
						|
	font-size: 2em;
 | 
						|
 | 
						|
	animation: loading 2s infinite ;
 | 
						|
	animation-delay: calc(0.2s * var(--i));
 | 
						|
}
 | 
						|
body:not(.loading) .page.spinner {
 | 
						|
	display: none;
 | 
						|
}
 | 
						|
body.loading .page.spinner {
 | 
						|
	opacity: 0.9;
 | 
						|
	animation: none;
 | 
						|
}
 | 
						|
body.loading .page.spinner span {
 | 
						|
	font-size: 4em;
 | 
						|
	/*
 | 
						|
	animation: loading 2s infinite ;
 | 
						|
	animation-delay: calc(0.2s * var(--i));
 | 
						|
	*/
 | 
						|
	transform: rotateY(90deg);
 | 
						|
	animation: loading-ninty 2s infinite ;
 | 
						|
	animation-delay: calc(0.2s * var(--i));
 | 
						|
}
 | 
						|
@keyframes fadein {
 | 
						|
	from {
 | 
						|
		opacity: 0;
 | 
						|
	}
 | 
						|
	50% {
 | 
						|
		opacity: 0.8;
 | 
						|
	}
 | 
						|
	to {
 | 
						|
		opacity: 1;
 | 
						|
	}
 | 
						|
}
 | 
						|
@keyframes loading {
 | 
						|
	0%, 60% {
 | 
						|
		transform: rotateY(360deg);
 | 
						|
	}
 | 
						|
}
 | 
						|
@keyframes loading-ninty {
 | 
						|
	0% {
 | 
						|
		transform: rotateY(90deg);
 | 
						|
	}
 | 
						|
	98% {
 | 
						|
		transform: rotateY(360deg);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
.placeholder {
 | 
						|
	opacity: 0.4;
 | 
						|
}
 | 
						|
 | 
						|
.new-page-indicator {
 | 
						|
	position: absolute;	
 | 
						|
	font-size: small;
 | 
						|
	font-style: italic;
 | 
						|
	opacity: 0.5;
 | 
						|
}
 | 
						|
 | 
						|
/* TOC */
 | 
						|
toc {
 | 
						|
	--toc-level-offset: 2em;
 | 
						|
}
 | 
						|
toc a {
 | 
						|
	display: block;
 | 
						|
}
 | 
						|
toc .h1 {
 | 
						|
	margin-left: 0em;
 | 
						|
}
 | 
						|
toc .h2 {
 | 
						|
	margin-left: calc(var(--toc-level-offset) * 1);
 | 
						|
}
 | 
						|
toc .h3 {
 | 
						|
	margin-left: calc(var(--toc-level-offset) * 2);
 | 
						|
}
 | 
						|
toc .h4 {
 | 
						|
	margin-left: calc(var(--toc-level-offset) * 3);
 | 
						|
}
 | 
						|
toc .h5 {
 | 
						|
	margin-left: calc(var(--toc-level-offset) * 4);
 | 
						|
}
 | 
						|
toc .h5 {
 | 
						|
	margin-left: calc(var(--toc-level-offset) * 5);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
.error .msg {
 | 
						|
	font-weight: bold;
 | 
						|
	color: red;
 | 
						|
	margin-bottom: 1em;
 | 
						|
}
 | 
						|
.error {
 | 
						|
	background: lightyellow;
 | 
						|
	padding: 1em;
 | 
						|
	box-shadow: inset 3px 5px 15px 5px rgba(0,0,0,0.03);
 | 
						|
	border: dashed red 1px;
 | 
						|
}
 | 
						|
 | 
						|
textarea {
 | 
						|
	font-size: 1.2em;
 | 
						|
	border: none;
 | 
						|
	resize: none;
 | 
						|
}
 | 
						|
[contenteditable] {
 | 
						|
	outline: 0px solid transparent;
 | 
						|
}
 | 
						|
textarea:empty:after,
 | 
						|
[contenteditable]:empty:after {
 | 
						|
	display: block;
 | 
						|
	content: 'Empty';
 | 
						|
	opacity: 0.3;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
.tree-page-title:empty:after {
 | 
						|
	content: "/";
 | 
						|
}
 | 
						|
 | 
						|
</style>
 | 
						|
<!-- 
 | 
						|
Do not edit here...
 | 
						|
This is loaded with the style defined by the system 
 | 
						|
-->
 | 
						|
<style id="style"></style>
 | 
						|
 | 
						|
<!-- XXX do we need this??? -->
 | 
						|
<script src="bootstrap.js"></script>
 | 
						|
 | 
						|
<!--script data-main="pwiki2" src="ext-lib/require.js"></script-->
 | 
						|
<script src="ext-lib/require.js"></script>
 | 
						|
<script>
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
var DEBUG = true
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
// Module loading...
 | 
						|
 | 
						|
var makeFallbacks = 
 | 
						|
function(paths, search=['lib']){
 | 
						|
	return Object.entries(paths)
 | 
						|
		.map(function([key, path]){
 | 
						|
			// package...
 | 
						|
			if(path.endsWith('/')){
 | 
						|
				return [key, path] }
 | 
						|
			return [
 | 
						|
				key,
 | 
						|
				[
 | 
						|
					path,
 | 
						|
					...search
 | 
						|
						.map(function(base){
 | 
						|
							return base +'/'+ path.split(/[\\\/]+/g).pop() }),
 | 
						|
				]
 | 
						|
			] })
 | 
						|
		.reduce(function(res, [key, value]){
 | 
						|
			res[key] = value
 | 
						|
			return res }, {}) }
 | 
						|
 | 
						|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
						|
require.config({
 | 
						|
	paths: {
 | 
						|
		...makeFallbacks({
 | 
						|
			'ig-doc': 'node_modules/ig-doc/doc',
 | 
						|
			'ig-stoppable': 'node_modules/ig-stoppable/stoppable',
 | 
						|
			'object-run': 'node_modules/object-run/run',
 | 
						|
			'ig-object': 'node_modules/ig-object/object',
 | 
						|
		}),
 | 
						|
 | 
						|
		// packages...
 | 
						|
		'ig-types': [
 | 
						|
			'node_modules/ig-types',
 | 
						|
			'lib/types',
 | 
						|
		],
 | 
						|
 | 
						|
		// external stuff...
 | 
						|
		...makeFallbacks({
 | 
						|
			'jszip': 'node_modules/jszip/dist/jszip',
 | 
						|
			'pouchdb': 'node_modules/pouchdb/dist/pouchdb',
 | 
						|
			'showdown': 'node_modules/showdown/dist/showdown',
 | 
						|
			'idb-keyval': 'node_modules/idb-keyval/dist/umd',
 | 
						|
			'flexsearch': 'node_modules/flexsearch/dist/flexsearch.bundle',
 | 
						|
			'any-date-parser': 'node_modules/any-date-parser/dist/browser-bundle',
 | 
						|
		}, ['ext-lib']),
 | 
						|
	},
 | 
						|
	packages: [
 | 
						|
		'ig-types',
 | 
						|
	]
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
// Editor -- save changes...
 | 
						|
 | 
						|
/* XXX GLOBAL_STYLE
 | 
						|
// XXX might be a good idea to make this a method of pwiki???
 | 
						|
var STYLE_UPDATED = false
 | 
						|
var updateStyle = async function(){
 | 
						|
	document.querySelector('#style').innerHTML = 
 | 
						|
		await pwiki.get('/.config/Style/_text').text }
 | 
						|
//*/
 | 
						|
 | 
						|
// XXX might be a good idea to make this a method of pwiki???
 | 
						|
// XXX the page seems to be broken...
 | 
						|
var CONFIG_UPDATED = false
 | 
						|
var updateConfig = async function(){
 | 
						|
	// XXX need to set this to something...
 | 
						|
	// XXX
 | 
						|
	// XXX need to use a parser that supports comments and stuff...
 | 
						|
	return JSON.parse(await pwiki.get('/.config/Config/_text').text) }
 | 
						|
 | 
						|
 | 
						|
// XXX versioning??
 | 
						|
var SAVE_LIVE_TIMEOUT = 5000
 | 
						|
var SAVE_LIVE_QUEUE = {}
 | 
						|
 | 
						|
var saveLiveContent = 
 | 
						|
function(path, text){
 | 
						|
	SAVE_LIVE_QUEUE[path] = text 
 | 
						|
	// clear editor page cache...
 | 
						|
	pwiki.cache = null }
 | 
						|
 | 
						|
var SAVE_QUEUE = {}
 | 
						|
var saveContent =
 | 
						|
function(path, text){
 | 
						|
	SAVE_QUEUE[path] = text }
 | 
						|
 | 
						|
var saveAll =
 | 
						|
function(){
 | 
						|
	saveNow()
 | 
						|
	var queue = Object.entries(SAVE_QUEUE)
 | 
						|
	SAVE_QUEUE = {}
 | 
						|
	queue
 | 
						|
		.forEach(function([path, text]){
 | 
						|
			console.log('saving changes to:', path)
 | 
						|
			pwiki.get(path).raw = text }) }
 | 
						|
 | 
						|
var saveNow = 
 | 
						|
function(){
 | 
						|
	var queue = Object.entries(SAVE_LIVE_QUEUE)
 | 
						|
	SAVE_LIVE_QUEUE = {}
 | 
						|
	NEW_TITLE = undefined
 | 
						|
	queue
 | 
						|
		.forEach(function([path, text]){
 | 
						|
			console.log('saving changes to:', path)
 | 
						|
			pwiki.get(path).raw = text }) }
 | 
						|
setInterval(saveNow, SAVE_LIVE_TIMEOUT)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
// Spinner/loading...
 | 
						|
 | 
						|
// loading spinner...
 | 
						|
window.startSpinner = function(){
 | 
						|
	document.body.classList.add('loading') }
 | 
						|
window.stopSpinner = function(){
 | 
						|
	document.body.classList.remove('loading') }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
// General setup...
 | 
						|
 | 
						|
document.pwikiloaded = new Event('pwikiloaded')
 | 
						|
 | 
						|
var logTime = async function(promise, msg=''){
 | 
						|
	var t = Date.now()
 | 
						|
	var res = await promise
 | 
						|
	t = Date.now() - t
 | 
						|
	DEBUG 
 | 
						|
		&& console.log(`## ${
 | 
						|
				typeof(msg) == 'function' ? 
 | 
						|
					msg(res) 
 | 
						|
					: msg
 | 
						|
			} (${t}ms)`)
 | 
						|
	return res }
 | 
						|
 | 
						|
 | 
						|
REFRESH_DELAY = 20
 | 
						|
 | 
						|
var refresh = async function(){
 | 
						|
	pwiki.__prev_path = pwiki.path
 | 
						|
	startSpinner()
 | 
						|
	setTimeout(function(){
 | 
						|
		logTime(
 | 
						|
			pwiki.refresh(), 
 | 
						|
			pwiki.location) }, REFRESH_DELAY) }
 | 
						|
 | 
						|
 | 
						|
history.scrollRestoration = 'manual'
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
						|
// start loading pWiki...
 | 
						|
require(['./browser'], function(browser){ 
 | 
						|
	var pwiki = window.pwiki = browser.pwiki 
 | 
						|
	//var pwpath = window.path = browser.pwpath
 | 
						|
	pwiki.dom = document.querySelector('#pWiki')
 | 
						|
 | 
						|
	// handle location.hash/history (both directions)
 | 
						|
	window.addEventListener('hashchange', function(evt){
 | 
						|
		evt.preventDefault()
 | 
						|
		var [path, hash] = decodeURI(location.hash).slice(1).split('#')
 | 
						|
		path = path.trim() == '' ? 
 | 
						|
			pwiki.location
 | 
						|
			//'/'
 | 
						|
			: path
 | 
						|
		// treat links as absolute unless explicitly relative...
 | 
						|
		path = /^\.\.?([\\\/].*)?$/.test(path) ?
 | 
						|
			path
 | 
						|
			: '/'+path
 | 
						|
		startSpinner()
 | 
						|
		// NOTE: setTimeout(..) to allow the spinner to start...
 | 
						|
		// NOTE: this seems not to work if the REFRESH_DELAY is too small...
 | 
						|
		setTimeout(function(){
 | 
						|
			pwiki.location = [path, hash] }, REFRESH_DELAY) })
 | 
						|
 | 
						|
	// scroll...
 | 
						|
	// NOTE: we restore scroll position only on history navigation...
 | 
						|
	var save_scroll = async function(){ 
 | 
						|
		history.replaceState({ 
 | 
						|
			path: pwiki.location,
 | 
						|
			// XXX HACK this will work only on full page...
 | 
						|
			scroll: document.scrollingElement.scrollTop,
 | 
						|
		}, '', window.location.hash) }
 | 
						|
	// save scroll position just after scroll is done...
 | 
						|
	var _scrolling
 | 
						|
	window.addEventListener('scroll', function(evt){
 | 
						|
		_scrolling
 | 
						|
			&& clearTimeout(_scrolling)
 | 
						|
		_scrolling = setTimeout(save_scroll, 200) })
 | 
						|
	// get scroll position from history state...
 | 
						|
	window.addEventListener('popstate', function(evt){
 | 
						|
		pwiki.__scroll = (evt.state ?? {}).scroll })
 | 
						|
 | 
						|
	pwiki
 | 
						|
		.onBeforeNavigate(function(){
 | 
						|
			this.__prev_path = this.path
 | 
						|
			saveAll() })
 | 
						|
		.navigate(async function(){
 | 
						|
			// NOTE: we do not need to directly update location.hash here as
 | 
						|
			//		that will push an extra history item...
 | 
						|
			history.replaceState(
 | 
						|
				{ path: this.location }, 
 | 
						|
				'',
 | 
						|
				'#'+this.location 
 | 
						|
					+(this.hash ? 
 | 
						|
						'#'+this.hash 
 | 
						|
						: ''))
 | 
						|
 | 
						|
			/* XXX GLOBAL_STYLE
 | 
						|
			// style...
 | 
						|
			if(STYLE_UPDATED){
 | 
						|
				STYLE_UPDATED = false
 | 
						|
				await updateStyle() }
 | 
						|
			// config...
 | 
						|
			if(CONFIG_UPDATED){
 | 
						|
				CONFIG_UPDATED = false
 | 
						|
				await updateConfig() }
 | 
						|
			//*/
 | 
						|
			// NOTE: we are intentionally not awaiting for this -- this 
 | 
						|
			//		separates the navigate and load events...
 | 
						|
			logTime(
 | 
						|
				this.refresh(), 
 | 
						|
				this.location) })
 | 
						|
		.onLoad(function(evt){
 | 
						|
			var that = this
 | 
						|
			// stop spinner...
 | 
						|
			stopSpinner()
 | 
						|
			// handle title...
 | 
						|
			// NOTE: we set the global title to either the last <title> 
 | 
						|
			//		tag value or the attr .title 
 | 
						|
			var titles = [...document.querySelectorAll('title')]
 | 
						|
			titles[0].innerHTML = 
 | 
						|
				`${titles.length > 1 ? 
 | 
						|
					titles.at(-1).innerText
 | 
						|
					: this.title} — pWiki`
 | 
						|
			// scroll...
 | 
						|
			this.hash ?
 | 
						|
				// to anchor element...
 | 
						|
				this.dom
 | 
						|
					.querySelector('#'+ this.hash)
 | 
						|
					.scrollIntoView() 
 | 
						|
				// restore history position...
 | 
						|
				// NOTE: only on navigate to new page...
 | 
						|
				// XXX HACK this will work only on full page pWiki and 
 | 
						|
				//		not on a element/nested pWiki...
 | 
						|
				: (this.__prev_path != this.path 
 | 
						|
					&& (document.scrollingElement.scrollTop = this.__scroll ?? 0))
 | 
						|
 | 
						|
			// handle refresh...
 | 
						|
			// NOTE: we need to do this as hashchange is only triggered 
 | 
						|
			//		when the hash is actually changed...
 | 
						|
			for(var lnk of this.dom.querySelectorAll(`a[href="${location.hash}"]`)){
 | 
						|
				lnk.addEventListener('click', refresh) } })
 | 
						|
		.delete(refresh)
 | 
						|
 | 
						|
	// handle special file updates...
 | 
						|
	// NOTE: the actual updates are done .navigate(..)
 | 
						|
	pwiki.store
 | 
						|
		.update(function(_, path){
 | 
						|
			// XXX GLOBAL_STYLE
 | 
						|
			//if(path == '/.config/Style'){
 | 
						|
			//	STYLE_UPDATED = true }
 | 
						|
			if(path == '/.config/Config'){
 | 
						|
				CONFIG_UPDATED = true } })
 | 
						|
 | 
						|
	// wait for stuff to finish...
 | 
						|
	browser.setup.then(async function(){
 | 
						|
		// index...
 | 
						|
		await logTime(
 | 
						|
			pwiki.store.index(), 
 | 
						|
			'Indexing')
 | 
						|
		// setup global stuff...
 | 
						|
		// XXX GLOBAL_STYLE
 | 
						|
		//updateStyle()
 | 
						|
		//updateConfig()
 | 
						|
		// show current page...
 | 
						|
		pwiki.location = decodeURI(location.hash).slice(1) }) }) 
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
// Export/Import...
 | 
						|
 | 
						|
// XXX
 | 
						|
var importData = function(evt){
 | 
						|
	var files = event.target.files
 | 
						|
	var reader = new FileReader()
 | 
						|
	reader.addEventListener('load', function(evt){
 | 
						|
		var json = JSON.parse(evt.target.result)
 | 
						|
		console.log('LOADING JSON:', json)
 | 
						|
		pwiki.store
 | 
						|
			.load(json)
 | 
						|
			.then(function(){
 | 
						|
				location.reload() }) })
 | 
						|
	reader.readAsText(files[0]) }
 | 
						|
 | 
						|
// XXX
 | 
						|
var exportData = async function(options={}){
 | 
						|
	var filename
 | 
						|
	if(typeof(options) == 'string'){
 | 
						|
		filename = options
 | 
						|
		options = arguments[1] ?? {} }
 | 
						|
	var blob = new Blob(
 | 
						|
		[await pwiki.store.json({stringify: true, space: 4, ...options})], 
 | 
						|
		{type: "text/plain;charset=utf-8"});
 | 
						|
 | 
						|
	var a = document.createElement('a')
 | 
						|
    var blobURL = a.href = URL.createObjectURL(blob)
 | 
						|
    a.download = filename
 | 
						|
		?? options.filename 
 | 
						|
		?? (Date.timeStamp() +'.pWiki-export.pwiki')
 | 
						|
 | 
						|
    //document.body.appendChild(a)
 | 
						|
    a.dispatchEvent(new MouseEvent("click"))
 | 
						|
    //document.body.removeChild(a)
 | 
						|
    //URL.revokeObjectURL(blobURL)
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
</script>
 | 
						|
 | 
						|
<body>
 | 
						|
 | 
						|
<!-- XXX need to add something passive but animated here... -->
 | 
						|
<div id="pWiki">
 | 
						|
	<div class="spinner">
 | 
						|
		<span style="--i:0">p</span>
 | 
						|
		<span style="--i:1">W</span>
 | 
						|
		<span style="--i:2">i</span>
 | 
						|
		<span style="--i:3">k</span>
 | 
						|
		<span style="--i:4">i</span>
 | 
						|
	</div>
 | 
						|
</div>
 | 
						|
 | 
						|
<div class="page spinner">
 | 
						|
	<span style="--i:0">.</span>
 | 
						|
	<span style="--i:1">.</span>
 | 
						|
	<span style="--i:2">.</span>
 | 
						|
</div>
 | 
						|
 | 
						|
</body>
 | 
						|
</html>
 | 
						|
 | 
						|
<!-- vim:set sw=4 ts=4 : -->
 |