1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-26 12:53:42 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Jermolene
a32fc33dcc Version number update for 5.1.16 2018-04-25 18:36:28 +01:00
981 changed files with 18687 additions and 44309 deletions

View File

@@ -1,34 +0,0 @@
language: node_js
- "node"
stages:
- name: test
- name: build-prerelease
if: branch = "master"
- name: build-tiddlywiki-com
if: branch = "tiddlywiki-com"
jobs:
include:
- stage: test
script: ./bin/test.sh
- stage: build-prerelease
script:
- ./bin/travis-pre-build.sh
- export TW5_BUILD_TIDDLYWIKI='./tiddlywiki.js'
- export TW5_BUILD_VERSION=$(./bin/get-plugin-library-version-number)
- export TW5_BUILD_DETAILS="Prerelease built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
- export TW5_BUILD_MAIN_EDITION='./editions/prerelease'
- export TW5_BUILD_OUTPUT='./output/prerelease'
- ./bin/build-site.sh
- ./bin/travis-push.sh
- stage: build-tiddlywiki-com
script:
- ./bin/travis-pre-build.sh
- export TW5_BUILD_TIDDLYWIKI='./node_modules/tiddlywiki/tiddlywiki.js'
- export TW5_BUILD_VERSION=$(./bin/get-plugin-library-version-number)
- export TW5_BUILD_DETAILS="Built from branch '$TRAVIS_BRANCH' at commit $(git rev-parse HEAD) of $(git remote get-url origin) at $(date +'%F %T %Z')"
- export TW5_BUILD_MAIN_EDITION='./editions/tw5.com'
- export TW5_BUILD_OUTPUT='./output'
- ./bin/build-site.sh
- ./bin/travis-push.sh

15
bin/2bld.cmd Normal file
View File

@@ -0,0 +1,15 @@
@echo off
rem build TiddlyWiki 2.x
rem cook the TiddlyWiki 2.x.x index file
node .\tiddlywiki.js ^
editions\tw2 ^
--verbose ^
--output tmp\tw2 ^
--load editions\tw2\source\tiddlywiki.com\index.html.recipe ^
--rendertiddler $:/core/templates/tiddlywiki2.template.html index.html text/plain ^
|| exit 1
fc tmp\tw2\index.html editions\tw2\target\prebuilt.html

15
bin/2bld.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
# build TiddlyWiki 2.x
# cook the TiddlyWiki 2.x.x index file
node ./tiddlywiki.js \
editions/tw2 \
--verbose \
--output tmp/tw2 \
--load editions/tw2/source/tiddlywiki.com/index.html.recipe \
--rendertiddler $:/core/templates/tiddlywiki2.template.html index.html text/plain \
|| exit 1
diff -q tmp/tw2/index.html editions/tw2/target/prebuilt.html

View File

@@ -1,449 +0,0 @@
#!/bin/bash
# Build all tiddlywiki.com assets.
# Default to the current version number for building the plugin library
if [ -z "$TW5_BUILD_VERSION" ]; then
TW5_BUILD_VERSION=v5.1.19
fi
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
# Default to using tw5.com as the main edition for /index.html
if [ -z "$TW5_BUILD_MAIN_EDITION" ]; then
TW5_BUILD_MAIN_EDITION=./editions/tw5.com
fi
echo "Using TW5_BUILD_MAIN_EDITION as [$TW5_BUILD_MAIN_EDITION]"
# Default to the version of TiddlyWiki installed in this repo
if [ -z "$TW5_BUILD_TIDDLYWIKI" ]; then
TW5_BUILD_TIDDLYWIKI=./tiddlywiki.js
fi
echo "Using TW5_BUILD_TIDDLYWIKI as [$TW5_BUILD_TIDDLYWIKI]"
# Set up the build details
if [ -z "$TW5_BUILD_DETAILS" ]; then
TW5_BUILD_DETAILS="$(git symbolic-ref --short HEAD)-$(git rev-parse HEAD) from $(git remote get-url origin)"
fi
echo "Using TW5_BUILD_DETAILS as [$TW5_BUILD_DETAILS]"
if [ -z "$TW5_BUILD_COMMIT" ]; then
TW5_BUILD_COMMIT="$(git rev-parse HEAD)"
fi
echo "Using TW5_BUILD_COMMIT as [$TW5_BUILD_COMMIT]"
# Set up the build output directory
if [ -z "$TW5_BUILD_OUTPUT" ]; then
TW5_BUILD_OUTPUT=./output
fi
mkdir -p $TW5_BUILD_OUTPUT
if [ ! -d "$TW5_BUILD_OUTPUT" ]; then
echo 'A valid TW5_BUILD_OUTPUT environment variable must be set'
exit 1
fi
echo "Using TW5_BUILD_OUTPUT as [$TW5_BUILD_OUTPUT]"
echo "Build details: $TW5_BUILD_DETAILS"
# Make the CNAME file that GitHub Pages requires
echo "tiddlywiki.com" > $TW5_BUILD_OUTPUT/CNAME
# Delete any existing static content
mkdir -p $TW5_BUILD_OUTPUT/static
mkdir -p $TW5_BUILD_OUTPUT/dev
mkdir -p $TW5_BUILD_OUTPUT/dev/static
rm $TW5_BUILD_OUTPUT/static/*
rm $TW5_BUILD_OUTPUT/dev/static/*
# Redirects
echo "<a href='./plugins/tiddlywiki/tw2parser/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tw2parser/index.html</a>" > $TW5_BUILD_OUTPUT/classicparserdemo.html
echo "<a href='./plugins/tiddlywiki/codemirror/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/codemirror/index.html</a>" > $TW5_BUILD_OUTPUT/codemirrordemo.html
echo "<a href='./plugins/tiddlywiki/d3/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/d3/index.html</a>" > $TW5_BUILD_OUTPUT/d3demo.html
echo "<a href='./plugins/tiddlywiki/highlight/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/highlight/index.html</a>" > $TW5_BUILD_OUTPUT/highlightdemo.html
echo "<a href='./plugins/tiddlywiki/markdown/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/markdown/index.html</a>" > $TW5_BUILD_OUTPUT/markdowndemo.html
echo "<a href='./plugins/tiddlywiki/tahoelafs/index.html'>Moved to http://tiddlywiki.com/plugins/tiddlywiki/tahoelafs/index.html</a>" > $TW5_BUILD_OUTPUT/tahoelafs.html
# Put the build details into a .tid file so that it can be included in each build (deleted at the end of this script)
echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n" > $TW5_BUILD_OUTPUT/build.tid
######################################################
#
# Core distribution
#
######################################################
# /index.html Main site
# /favicon.ico Favicon for main site
# /static.html Static rendering of default tiddlers
# /alltiddlers.html Static rendering of all tiddlers
# /static/* Static single tiddlers
# /static/static.css Static stylesheet
# /static/favicon.ico Favicon for static pages
node $TW5_BUILD_TIDDLYWIKI \
$TW5_BUILD_MAIN_EDITION \
--verbose \
--version \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--build favicon static index \
|| exit 1
# /empty.html Empty
# /empty.hta For Internet Explorer
node $TW5_BUILD_TIDDLYWIKI \
./editions/empty \
--verbose \
--output $TW5_BUILD_OUTPUT \
--build empty \
|| exit 1
# /test.html Test edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/test \
--verbose \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all test.html text/plain \
|| exit 1
# /dev/index.html Developer docs
# /dev/favicon.ico Favicon for dev site
# /dev/static.html Static rendering of default tiddlers
# /dev/alltiddlers.html Static rendering of all tiddlers
# /dev/static/* Static single tiddlers
# /dev/static/static.css Static stylesheet
node $TW5_BUILD_TIDDLYWIKI \
./editions/dev \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/dev \
--build index favicon static \
|| exit 1
# /upgrade.html Custom edition for performing upgrades
node $TW5_BUILD_TIDDLYWIKI \
./editions/upgrade \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--build upgrade \
|| exit 1
# /encrypted.html Copy of the main file encrypted with the password "password"
node $TW5_BUILD_TIDDLYWIKI \
$TW5_BUILD_MAIN_EDITION \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--build encrypted \
|| exit 1
######################################################
#
# Editions
#
######################################################
# /editions/xlsx-utils/index.html xlsx-utils edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/xlsx-utils \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/xlsx-utils/ \
--build index \
|| exit 1
# /editions/resumebuilder/index.html Resume builder edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/resumebuilder \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/resumebuilder/ \
--build index \
|| exit 1
# /editions/text-slicer/index.html Text slicer edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/text-slicer \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/text-slicer/ \
--build index \
|| exit 1
# /editions/translators/index.html Translators edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/translators \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/translators/ \
--build index \
|| exit 1
# /editions/introduction/index.html Introduction edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/introduction \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/introduction/ \
--build index \
|| exit 1
# /editions/full/index.html Full edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/full \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/full/ \
--build index \
|| exit 1
# /editions/tw5.com-docs/index.html tiddlywiki.com docs edition
node $TW5_BUILD_TIDDLYWIKI \
./editions/tw5.com-docs \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/editions/tw5.com-docs/ \
--build index \
|| exit 1
######################################################
#
# Plugin demos
#
######################################################
# /plugins/tiddlywiki/innerwiki/index.html Demo wiki with Innerwiki plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/innerwikidemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/innerwiki/index.html text/plain \
|| exit 1
# /plugins/tiddlywiki/dynaview/index.html Demo wiki with DynaView plugin
# /plugins/tiddlywiki/dynaview/empty.html Empty wiki with DynaView plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/dynaviewdemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/dynaview/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/dynaview/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/katex/index.html Demo wiki with KaTeX plugin
# /plugins/tiddlywiki/katex/empty.html Empty wiki with KaTeX plugin
# TODO: Build the static file with the release of 5.1.3
# --rendertiddler $:/core/templates/static.template.html plugins/tiddlywiki/katex/static.html text/plain \
node $TW5_BUILD_TIDDLYWIKI \
./editions/katexdemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/katex/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/katex/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/tahoelafs/index.html Demo wiki with Tahoe-LAFS plugin
# /plugins/tiddlywiki/tahoelafs/empty.html Empty wiki with Tahoe-LAFS plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/tahoelafs \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/tahoelafs/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tahoelafs/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/d3/index.html Demo wiki with D3 plugin
# /plugins/tiddlywiki/d3/empty.html Empty wiki with D3 plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/d3demo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/d3/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/d3/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/codemirror/index.html Demo wiki with codemirror plugin
# /plugins/tiddlywiki/codemirror/empty.html Empty wiki with codemirror plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/codemirrordemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/codemirror/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/codemirror/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/markdown/index.html Demo wiki with Markdown plugin
# /plugins/tiddlywiki/markdown/empty.html Empty wiki with Markdown plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/markdowndemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/markdown/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/markdown/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/tw2parser/index.html Demo wiki with tw2parser plugin
# /plugins/tiddlywiki/tw2parser/empty.html Empty wiki with tw2parser plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/classicparserdemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/tw2parser/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/tw2parser/empty.html text/plain \
|| exit 1
# /plugins/tiddlywiki/highlight/index.html Demo wiki with highlight plugin
# /plugins/tiddlywiki/highlight/empty.html Empty wiki with highlight plugin
node $TW5_BUILD_TIDDLYWIKI \
./editions/highlightdemo \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT \
--rendertiddler $:/core/save/all plugins/tiddlywiki/highlight/index.html text/plain \
--rendertiddler $:/core/save/empty plugins/tiddlywiki/highlight/empty.html text/plain \
|| exit 1
######################################################
#
# Language editions
#
######################################################
# Delete any existing static content
rm $TW5_BUILD_OUTPUT/languages/de-AT/static/*
rm $TW5_BUILD_OUTPUT/languages/de-DE/static/*
rm $TW5_BUILD_OUTPUT/languages/es-ES/static/*
rm $TW5_BUILD_OUTPUT/languages/fr-FR/static/*
rm $TW5_BUILD_OUTPUT/languages/ja-JP/static/*
rm $TW5_BUILD_OUTPUT/languages/ko-KR/static/*
rm $TW5_BUILD_OUTPUT/languages/zh-Hans/static/*
rm $TW5_BUILD_OUTPUT/languages/zh-Hant/static/*
# /languages/de-AT/index.html Demo wiki with de-AT language
# /languages/de-AT/empty.html Empty wiki with de-AT language
node $TW5_BUILD_TIDDLYWIKI \
./editions/de-AT \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/de-AT \
--build favicon empty static index \
|| exit 1
# /languages/de-DE/index.html Demo wiki with de-DE language
# /languages/de-DE/empty.html Empty wiki with de-DE language
node $TW5_BUILD_TIDDLYWIKI \
./editions/de-DE \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/de-DE \
--build favicon empty static index \
|| exit 1
# /languages/es-ES/index.html Demo wiki with es-ES language
# /languages/es-ES/empty.html Empty wiki with es-ES language
node $TW5_BUILD_TIDDLYWIKI \
./editions/es-ES \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/es-ES \
--build favicon empty static index \
|| exit 1
# /languages/fr-FR/index.html Demo wiki with fr-FR language
# /languages/fr-FR/empty.html Empty wiki with fr-FR language
node $TW5_BUILD_TIDDLYWIKI \
./editions/fr-FR \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/fr-FR \
--build favicon empty static index \
|| exit 1
# /languages/ja-JP/index.html Demo wiki with ja-JP language
# /languages/ja-JP/empty.html Empty wiki with ja-JP language
node $TW5_BUILD_TIDDLYWIKI \
./editions/ja-JP \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/ja-JP \
--build empty index \
|| exit 1
# /languages/ko-KR/index.html Demo wiki with ko-KR language
# /languages/ko-KR/empty.html Empty wiki with ko-KR language
node $TW5_BUILD_TIDDLYWIKI \
./editions/ko-KR \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/ko-KR \
--build favicon empty static index \
|| exit 1
# /languages/zh-Hans/index.html Demo wiki with zh-Hans language
# /languages/zh-Hans/empty.html Empty wiki with zh-Hans language
node $TW5_BUILD_TIDDLYWIKI \
./editions/zh-Hans \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/zh-Hans \
--build empty index \
|| exit 1
# /languages/zh-Hant/index.html Demo wiki with zh-Hant language
# /languages/zh-Hant/empty.html Empty wiki with zh-Hant language
node $TW5_BUILD_TIDDLYWIKI \
./editions/zh-Hant \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/languages/zh-Hant \
--build empty index \
|| exit 1
######################################################
#
# Plugin library
#
######################################################
node $TW5_BUILD_TIDDLYWIKI \
./editions/pluginlibrary \
--verbose \
--load $TW5_BUILD_OUTPUT/build.tid \
--output $TW5_BUILD_OUTPUT/library/$TW5_BUILD_VERSION \
--build \
|| exit 1
# Delete the temporary build tiddler
rm $TW5_BUILD_OUTPUT/build.tid || exit 1

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env node
// Extract raw version number from package.json (without the optional "-prerelease" suffix)
if(!process.env["TW5_BUILD_TIDDLYWIKI"]) {
throw "TW5_BUILD_TIDDLYWIKI environment variable not set";
}
var fs = require("fs"),
path = require("path");
var filename = path.resolve(path.dirname(process.env["TW5_BUILD_TIDDLYWIKI"]),"./package.json"),
json = JSON.parse(fs.readFileSync(filename,"utf8"));
if(!json.version) {
throw "Missing version number in package.json";
}
process.stdout.write("v" + json.version.split("-")[0]);

View File

@@ -1,7 +0,0 @@
#!/bin/bash
# publish to npm
./bin/clean.sh
npm publish || exit 1

View File

@@ -1,8 +0,0 @@
#!/bin/bash
# Abbreviated build script for building prerelease
tiddlywiki editions/prerelease \
--verbose \
--build favicon index \
|| exit 1

View File

@@ -1,17 +0,0 @@
#!/bin/bash
# Build readmes from corresponding tiddlers
# Default to the version of TiddlyWiki installed in this repo
if [ -z "$TW5_BUILD_TIDDLYWIKI" ]; then
TW5_BUILD_TIDDLYWIKI=./tiddlywiki.js
fi
# tw5.com readmes
node $TW5_BUILD_TIDDLYWIKI \
editions/tw5.com \
--verbose \
--output . \
--build readmes \
|| exit 1

View File

@@ -1,4 +1,3 @@
<h1 class="">Script Files</h1><p>The <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a> repository contains several scripts in the <code>bin</code> folder that you can use to automate common tasks, or as a useful starting point for your own scripts. See <a class="tc-tiddlylink tc-tiddlylink-missing" href="https://tiddlywiki.com/static/Scripts%2520for%2520building%2520tiddlywiki.com.html">Scripts for building tiddlywiki.com</a> for details of the scripts used to build and release <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/</a>.</p><p>All the scripts expect to be run from the root folder of the repository.</p><h2 class=""><code>serve</code>: serves tw5.com</h2><pre><code>./bin/serve.sh -h
./bin/serve.sh [edition dir] [username] [password] [host] [port]</code></pre><p>Or:</p><pre><code>./bin/serve.cmd -h
./bin/serve.cmd [edition dir] [username] [password] [host] [port]</code></pre><p>This script starts <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a> running as an HTTP server, defaulting to the content from the <code>tw5.com-server</code> edition. By default, the Node.js serves on port 8080. If the optional <code>username</code> parameter is provided, it is used for signing edits. If the <code>password</code> is provided then HTTP basic authentication is used. Run the script with the <code>-h</code> parameter to see online help.</p><p>To experiment with this configuration, run the script and then visit <code>http://127.0.0.1:8080</code> in a browser.</p><p>Changes made in the browser propagate to the server over HTTP (use the browser developer console to see these requests). The server then syncs changes to the file system (and logs each change to the screen).</p><h2 class=""><code>test</code>: build and run tests</h2><p>This script runs the <code>test</code> edition of <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> on the server to perform the server-side tests and to build <code>test.html</code> for running the tests in the browser.</p><h2 class=""><code>lazy</code>: serves tw5.com with lazily loaded images</h2><pre><code>./bin/lazy.sh &lt;username&gt; [&lt;password&gt;]</code></pre><p>Or:</p><pre><code>./bin/lazy.cmd &lt;username&gt; [&lt;password&gt;]</code></pre><p>This script serves the <code>tw5.com-server</code> edition content with <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/LazyLoading.html">LazyLoading</a> applied to images.
</p>
./bin/serve.cmd [edition dir] [username] [password] [host] [port]</code></pre><p>This script starts <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a> running as an HTTP server, defaulting to the content from the <code>tw5.com-server</code> edition. By default, the Node.js serves on port 8080. If the optional <code>username</code> parameter is provided, it is used for signing edits. If the <code>password</code> is provided then HTTP basic authentication is used. Run the script with the <code>-h</code> parameter to see online help.</p><p>To experiment with this configuration, run the script and then visit <code>http://127.0.0.1:8080</code> in a browser.</p><p>Changes made in the browser propagate to the server over HTTP (use the browser developer console to see these requests). The server then syncs changes to the file system (and logs each change to the screen).</p><h2 class=""><code>test</code>: build and run tests</h2><p>This script runs the <code>test</code> edition of <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> on the server to perform the server-side tests and to build <code>test.html</code> for running the tests in the browser.</p><h2 class=""><code>lazy</code>: serves tw5.com with lazily loaded images</h2><pre><code>./bin/lazy.sh &lt;username&gt; [&lt;password&gt;]</code></pre><p>Or:</p><pre><code>./bin/lazy.cmd &lt;username&gt; [&lt;password&gt;]</code></pre><p>This script serves the <code>tw5.com-server</code> edition content with <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/LazyLoading.html">LazyLoading</a> applied to images.</p><h2 class=""><code>2bld</code>: builds <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> 2.6.5</h2><p>This script builds <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> 2.6.5 from the original source and then displays the differences between them (<code>diff</code> is used for *nix, <code>fc</code> for Windows).</p>

View File

@@ -7,7 +7,6 @@
node ./tiddlywiki.js \
./editions/test \
--verbose \
--version \
--rendertiddler $:/core/save/all test.html text/plain \
|| exit 1

View File

@@ -1,10 +0,0 @@
#!/bin/bash
# Install latest current release from npm
# (we need to force because otherwise npm will refuse to install a module of the same name)
npm --force install tiddlywiki || exit 1
# Pull existing GitHub pages content
git clone --depth=1 --branch=master "https://github.com/Jermolene/jermolene.github.io.git" output

View File

@@ -1,20 +0,0 @@
#!/bin/bash
# Push output back to GitHub
cd output || exit 1
git config --global user.email "travis@travis-ci.org" || exit 1
git config --global user.name "Travis CI" || exit 1
git add -A . || exit 1
git commit --message "Travis build: $TRAVIS_BUILD_NUMBER of $TRAVIS_BRANCH ($(date +'%F %T %Z'))" || exit 1
git remote add deploy "https://$GH_TOKEN@github.com/Jermolene/jermolene.github.io.git" &>/dev/null || exit 1
git push deploy master &>/dev/null || exit 1
cd .. || exit 1

View File

@@ -1,17 +0,0 @@
#!/bin/bash
# Bump to a new version number
if [ -z "$1" ]
then
echo "Missing version (eg '5.1.38-prerelease')"
exit 1
fi
# Set the new version number (will also commit and tag the release)
npm version $1 -m "Version number update for $1" || exit 1
# Make sure our tags are pushed to the origin server
git push origin --tags || exit 1

View File

@@ -9,50 +9,37 @@ Basic styles used before we boot up the parsing engine
Error message and password prompt
*/
.tc-error-form {
font-family: sans-serif;
color: #fff;
z-index: 20000;
position: fixed;
background-color: rgb(255, 75, 75);
border: 8px solid rgb(255, 0, 0);
border-radius: 8px;
width: 50%;
margin-left: 25%;
margin-top: 4em;
padding: 0 2em 1em 2em;
}
.tc-error-form h1 {
text-align: center;
}
.tc-error-prompt {
text-align: center;
color: #000;
}
.tc-error-message {
overflow: auto;
max-height: 40em;
padding-right: 1em;
margin: 1em 0;
white-space: pre-line;
}
.tc-password-wrapper {
.tc-password-wrapper, .tc-error-form {
font-family: sans-serif;
z-index: 20000;
position: fixed;
text-align: center;
width: 200px;
top: 4em;
left: 50%;
margin-left: -144px; /* - width/2 - paddingHorz/2 - border */
left: 50%;
margin-left: -144px; /* - width/2 - paddingHorz/2 - border */
padding: 16px 16px 16px 16px;
border-radius: 8px;
}
.tc-error-form {
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
background-color: rgb(255, 75, 75);
border: 8px solid rgb(255, 0, 0);
width: 480px;
margin-left: -244px; /* - width/2 - paddingHorz/2 - border */
}
.tc-error-form div {
padding-bottom: 1em;
}
.tc-error-prompt {
color: #000;
text-shadow: none;
}
.tc-password-wrapper {
color: #000;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);

View File

@@ -51,63 +51,6 @@ $tw.utils.isArray = function(value) {
return Object.prototype.toString.call(value) == "[object Array]";
};
/*
Check if an array is equal by value and by reference.
*/
$tw.utils.isArrayEqual = function(array1,array2) {
if(array1 === array2) {
return true;
}
array1 = array1 || [];
array2 = array2 || [];
if(array1.length !== array2.length) {
return false;
}
return array1.every(function(value,index) {
return value === array2[index];
});
};
/*
Push entries onto an array, removing them first if they already exist in the array
array: array to modify (assumed to be free of duplicates)
value: a single value to push or an array of values to push
*/
$tw.utils.pushTop = function(array,value) {
var t,p;
if($tw.utils.isArray(value)) {
// Remove any array entries that are duplicated in the new values
if(value.length !== 0) {
if(array.length !== 0) {
if(value.length < array.length) {
for(t=0; t<value.length; t++) {
p = array.indexOf(value[t]);
if(p !== -1) {
array.splice(p,1);
}
}
} else {
for(t=array.length-1; t>=0; t--) {
p = value.indexOf(array[t]);
if(p !== -1) {
array.splice(t,1);
}
}
}
}
// Push the values on top of the main array
array.push.apply(array,value);
}
} else {
p = array.indexOf(value);
if(p !== -1) {
array.splice(p,1);
}
array.push(value);
}
return array;
};
/*
Determine if a value is a date
*/
@@ -146,9 +89,7 @@ Helper for making DOM elements
tag: tag name
options: see below
Options include:
namespace: defaults to http://www.w3.org/1999/xhtml
attributes: hashmap of attribute values
style: hashmap of styles
text: text to add as a child node
children: array of further child nodes
innerHTML: optional HTML for element
@@ -158,7 +99,7 @@ eventListeners: array of event listeners (this option won't work until $tw.utils
*/
$tw.utils.domMaker = function(tag,options) {
var doc = options.document || document;
var element = doc.createElementNS(options.namespace || "http://www.w3.org/1999/xhtml",tag);
var element = doc.createElement(tag);
if(options["class"]) {
element.className = options["class"];
}
@@ -174,9 +115,6 @@ $tw.utils.domMaker = function(tag,options) {
$tw.utils.each(options.attributes,function(attribute,name) {
element.setAttribute(name,attribute);
});
$tw.utils.each(options.style,function(value,name) {
element.style[name] = value;
});
if(options.eventListeners) {
$tw.utils.addEventListeners(element,options.eventListeners);
}
@@ -197,8 +135,8 @@ $tw.utils.error = function(err) {
var dm = $tw.utils.domMaker,
heading = dm("h1",{text: errHeading}),
prompt = dm("div",{text: promptMsg, "class": "tc-error-prompt"}),
message = dm("div",{text: err, "class":"tc-error-message"}),
button = dm("div",{children: [dm("button",{text: ( $tw.language == undefined ? "close" : $tw.language.getString("Buttons/Close/Caption") )})], "class": "tc-error-prompt"}),
message = dm("div",{text: err}),
button = dm("button",{text: ( $tw.language == undefined ? "close" : $tw.language.getString("Buttons/Close/Caption") )}),
form = dm("form",{children: [heading,prompt,message,button], "class": "tc-error-form"});
document.body.insertBefore(form,document.body.firstChild);
form.addEventListener("submit",function(event) {
@@ -330,7 +268,7 @@ $tw.utils.stringifyList = function(value) {
};
// Parse a string array from a bracketted list. For example "OneTiddler [[Another Tiddler]] LastOne"
$tw.utils.parseStringArray = function(value, allowDuplicate) {
$tw.utils.parseStringArray = function(value) {
if(typeof value === "string") {
var memberRegExp = /(?:^|[^\S\xA0])(?:\[\[(.*?)\]\])(?=[^\S\xA0]|$)|([\S\xA0]+)/mg,
results = [], names = {},
@@ -339,7 +277,7 @@ $tw.utils.parseStringArray = function(value, allowDuplicate) {
match = memberRegExp.exec(value);
if(match) {
var item = match[1] || match[2];
if(item !== undefined && (!$tw.utils.hop(names,item) || allowDuplicate)) {
if(item !== undefined && !$tw.utils.hop(names,item)) {
results.push(item);
names[item] = true;
}
@@ -924,61 +862,6 @@ $tw.Tiddler.prototype.hasField = function(field) {
return $tw.utils.hop(this.fields,field);
};
/*
Compare two tiddlers for equality
tiddler: the tiddler to compare
excludeFields: array of field names to exclude from the comparison
*/
$tw.Tiddler.prototype.isEqual = function(tiddler,excludeFields) {
if(!(tiddler instanceof $tw.Tiddler)) {
return false;
}
excludeFields = excludeFields || [];
var self = this,
differences = []; // Fields that have differences
// Add to the differences array
function addDifference(fieldName) {
// Check for this field being excluded
if(excludeFields.indexOf(fieldName) === -1) {
// Save the field as a difference
$tw.utils.pushTop(differences,fieldName);
}
}
// Returns true if the two values of this field are equal
function isFieldValueEqual(fieldName) {
var valueA = self.fields[fieldName],
valueB = tiddler.fields[fieldName];
// Check for identical string values
if(typeof(valueA) === "string" && typeof(valueB) === "string" && valueA === valueB) {
return true;
}
// Check for identical array values
if($tw.utils.isArray(valueA) && $tw.utils.isArray(valueB) && $tw.utils.isArrayEqual(valueA,valueB)) {
return true;
}
// Check for identical date values
if($tw.utils.isDate(valueA) && $tw.utils.isDate(valueB) && valueA.getTime() === valueB.getTime()) {
return true;
}
// Otherwise the fields must be different
return false;
}
// Compare our fields
for(var fieldName in this.fields) {
if(!isFieldValueEqual(fieldName)) {
addDifference(fieldName);
}
}
// There's a difference for every field in the other tiddler that we don't have
for(fieldName in tiddler.fields) {
if(!(fieldName in this.fields)) {
addDifference(fieldName);
}
}
// Return whether there were any differences
return differences.length === 0;
};
/*
Register and install the built in tiddler field modules
*/
@@ -1013,7 +896,7 @@ $tw.modules.define("$:/boot/tiddlerfields/list","tiddlerfield",{
/*
Wiki constructor. State is stored in private members that only a small number of privileged accessor methods have direct access. Methods added via the prototype have to use these accessors and cannot access the state data directly.
options include:
enableIndexers - Array of indexer names to enable, or null to use all available indexers
shadowTiddlers: Array of shadow tiddlers to be added
*/
$tw.Wiki = function(options) {
options = options || {};
@@ -1028,31 +911,14 @@ $tw.Wiki = function(options) {
},
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
shadowTiddlers = Object.create(null), // Hashmap by title of {source:, tiddler:}
shadowTiddlers = options.shadowTiddlers || Object.create(null), // Hashmap by title of {source:, tiddler:}
shadowTiddlerTitles = null,
getShadowTiddlerTitles = function() {
if(!shadowTiddlerTitles) {
shadowTiddlerTitles = Object.keys(shadowTiddlers);
}
return shadowTiddlerTitles;
},
enableIndexers = options.enableIndexers || null,
indexers = [],
indexersByName = Object.create(null);
this.addIndexer = function(indexer,name) {
// Bail if this indexer is not enabled
if(enableIndexers && enableIndexers.indexOf(name) === -1) {
return;
}
indexers.push(indexer);
indexersByName[name] = indexer;
indexer.init();
};
this.getIndexer = function(name) {
return indexersByName[name] || null;
};
};
// Add a tiddler to the store
this.addTiddler = function(tiddler) {
@@ -1065,33 +931,12 @@ $tw.Wiki = function(options) {
if(title) {
// Uncomment the following line for detailed logs of all tiddler writes
// console.log("Adding",title,tiddler)
// Record the old tiddler state
var updateDescriptor = {
old: {
tiddler: this.getTiddler(title),
shadow: this.isShadowTiddler(title),
exists: this.tiddlerExists(title)
}
}
// Save the new tiddler
tiddlers[title] = tiddler;
// Check we've got it's title
if(tiddlerTitles && tiddlerTitles.indexOf(title) === -1) {
tiddlerTitles.push(title);
}
// Record the new tiddler state
updateDescriptor["new"] = {
tiddler: tiddler,
shadow: this.isShadowTiddler(title),
exists: this.tiddlerExists(title)
}
// Update indexes
this.clearCache(title);
this.clearGlobalCache();
$tw.utils.each(indexers,function(indexer) {
indexer.update(updateDescriptor);
});
// Queue a change event
this.enqueueTiddlerEvent(title);
}
}
@@ -1102,36 +947,15 @@ $tw.Wiki = function(options) {
// Uncomment the following line for detailed logs of all tiddler deletions
// console.log("Deleting",title)
if($tw.utils.hop(tiddlers,title)) {
// Record the old tiddler state
var updateDescriptor = {
old: {
tiddler: this.getTiddler(title),
shadow: this.isShadowTiddler(title),
exists: this.tiddlerExists(title)
}
}
// Delete the tiddler
delete tiddlers[title];
// Delete it from the list of titles
if(tiddlerTitles) {
var index = tiddlerTitles.indexOf(title);
if(index !== -1) {
tiddlerTitles.splice(index,1);
}
}
// Record the new tiddler state
updateDescriptor["new"] = {
tiddler: this.getTiddler(title),
shadow: this.isShadowTiddler(title),
exists: this.tiddlerExists(title)
}
// Update indexes
this.clearCache(title);
this.clearGlobalCache();
$tw.utils.each(indexers,function(indexer) {
indexer.update(updateDescriptor);
});
// Queue a change event
this.enqueueTiddlerEvent(title,true);
}
};
@@ -1218,6 +1042,7 @@ $tw.Wiki = function(options) {
callback(tiddlers[title],title);
}
}
};
// Test for the existence of a tiddler (excludes shadow tiddlers)
@@ -1331,14 +1156,8 @@ $tw.Wiki = function(options) {
shadowTiddlerTitles = null;
this.clearCache(null);
this.clearGlobalCache();
$tw.utils.each(indexers,function(indexer) {
indexer.rebuild();
});
};
if(this.addIndexersToWiki) {
this.addIndexersToWiki();
}
};
// Dummy methods that will be filled in after boot
@@ -1462,7 +1281,8 @@ $tw.Wiki.prototype.deserializeTiddlers = function(type,text,srcFields,options) {
/*
Register the built in tiddler deserializer modules
*/
var deserializeHeaderComment = function(text,fields) {
$tw.modules.define("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{
"application/javascript": function(text,fields) {
var headerCommentRegExp = new RegExp($tw.config.jsModuleHeaderRegExpString,"mg"),
match = headerCommentRegExp.exec(text);
fields.text = text;
@@ -1470,12 +1290,7 @@ var deserializeHeaderComment = function(text,fields) {
fields = $tw.utils.parseFields(match[1].split(/\r?\n\r?\n/mg)[0],fields);
}
return [fields];
};
$tw.modules.define("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{
"application/javascript": deserializeHeaderComment
});
$tw.modules.define("$:/boot/tiddlerdeserializer/css","tiddlerdeserializer",{
"text/css": deserializeHeaderComment
}
});
$tw.modules.define("$:/boot/tiddlerdeserializer/tid","tiddlerdeserializer",{
"application/x-tiddler": function(text,fields) {
@@ -1533,40 +1348,8 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/html","tiddlerdeserializer",{
});
$tw.modules.define("$:/boot/tiddlerdeserializer/json","tiddlerdeserializer",{
"application/json": function(text,fields) {
var isTiddlerValid = function(data) {
// Not valid if it's not an object with a title property
if(typeof(data) !== "object" || !$tw.utils.hop(data,"title")) {
return false;
}
for(var f in data) {
if($tw.utils.hop(data,f)) {
// Check field name doesn't contain whitespace or control characters
if(typeof(data[f]) !== "string" || /[\x00-\x1F\s]/.test(f)) {
return false;
}
}
}
return true;
},
isTiddlerArrayValid = function(data) {
for(var t=0; t<data.length; t++) {
if(!isTiddlerValid(data[t])) {
return false;
}
}
return true;
},
data = JSON.parse(text);
if($tw.utils.isArray(data) && isTiddlerArrayValid(data)) {
return data;
} else if(isTiddlerValid(data)) {
return [data];
} else {
// Plain JSON file
fields.text = text;
fields.type = "application/json";
return [fields];
}
var data = JSON.parse(text);
return $tw.utils.isArray(data) ? data : [data];
}
});
@@ -1719,11 +1502,9 @@ $tw.loadTiddlersFromFile = function(filepath,fields) {
typeInfo = type ? $tw.config.contentTypeInfo[type] : null,
data = fs.readFileSync(filepath,typeInfo ? typeInfo.encoding : "utf8"),
tiddlers = $tw.wiki.deserializeTiddlers(ext,data,fields),
metadata;
if(ext !== ".json" && tiddlers.length === 1) {
metadata = $tw.loadMetadataForFile(filepath);
if(metadata) {
if(type === "application/json") {
tiddlers = [{text: data, type: "application/json"}];
}
tiddlers = [$tw.utils.extend({},tiddlers[0],metadata)];
}
return {filepath: filepath, type: type, tiddlers: tiddlers, hasMetaFile: !!metadata};
@@ -1953,10 +1734,8 @@ $tw.loadPlugin = function(name,paths) {
var pluginFields = $tw.loadPluginFolder(pluginPath);
if(pluginFields) {
$tw.wiki.addTiddler(pluginFields);
return;
}
}
console.log("Warning: Cannot find plugin '" + name + "'");
};
/*
@@ -1970,7 +1749,7 @@ $tw.getLibraryItemSearchPaths = function(libraryPath,envVar) {
if(env) {
env.split(path.delimiter).map(function(item) {
if(item) {
pluginPaths.push(item);
pluginPaths.push(item)
}
});
}
@@ -2106,21 +1885,6 @@ $tw.loadTiddlersNode = function() {
});
// Load the core tiddlers
$tw.wiki.addTiddler($tw.loadPluginFolder($tw.boot.corePath));
// Load any extra plugins
$tw.utils.each($tw.boot.extraPlugins,function(name) {
if(name.charAt(0) === "+") { // Relative path to plugin
var pluginFields = $tw.loadPluginFolder(name.substring(1));;
if(pluginFields) {
$tw.wiki.addTiddler(pluginFields);
}
} else {
var parts = name.split("/"),
type = parts[0];
if(parts.length === 3 && ["plugins","themes","languages"].indexOf(type) !== -1) {
$tw.loadPlugins([parts[1] + "/" + parts[2]],$tw.config[type + "Path"],$tw.config[type + "EnvVar"]);
}
}
});
// Load the tiddlers from the wiki directory
if($tw.boot.wikiPath) {
$tw.boot.wikiInfo = $tw.loadWikiTiddlers($tw.boot.wikiPath);
@@ -2184,12 +1948,6 @@ $tw.boot.startup = function(options) {
if($tw.boot.argv.length === 0) {
$tw.boot.argv = ["--help"];
}
// Parse any extra plugin references
$tw.boot.extraPlugins = $tw.boot.extraPlugins || [];
while($tw.boot.argv[0] && $tw.boot.argv[0].indexOf("+") === 0) {
$tw.boot.extraPlugins.push($tw.boot.argv[0].substring(1));
$tw.boot.argv.splice(0,1);
}
// If the first command line argument doesn't start with `--` then we
// interpret it as the path to the wiki folder, which will otherwise default
// to the current folder
@@ -2223,9 +1981,6 @@ $tw.boot.startup = function(options) {
$tw.utils.registerFileType("image/jpeg","base64",[".jpg",".jpeg"],{flags:["image"]});
$tw.utils.registerFileType("image/png","base64",".png",{flags:["image"]});
$tw.utils.registerFileType("image/gif","base64",".gif",{flags:["image"]});
$tw.utils.registerFileType("image/webp","base64",".webp",{flags:["image"]});
$tw.utils.registerFileType("image/heic","base64",".heic",{flags:["image"]});
$tw.utils.registerFileType("image/heif","base64",".heif",{flags:["image"]});
$tw.utils.registerFileType("image/svg+xml","utf8",".svg",{flags:["image"]});
$tw.utils.registerFileType("image/x-icon","base64",".ico",{flags:["image"]});
$tw.utils.registerFileType("application/font-woff","base64",".woff");
@@ -2240,10 +1995,8 @@ $tw.boot.startup = function(options) {
$tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.wordprocessingml.document","base64",".docx");
$tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","base64",".xlsx");
$tw.utils.registerFileType("application/vnd.openxmlformats-officedocument.presentationml.presentation","base64",".pptx");
$tw.utils.registerFileType("text/x-bibtex","utf8",".bib",{deserializerType:"application/x-bibtex"});
$tw.utils.registerFileType("application/x-bibtex","utf8",".bib");
$tw.utils.registerFileType("application/epub+zip","base64",".epub");
$tw.utils.registerFileType("application/octet-stream","base64",".octet-stream");
// Create the wiki store for the app
$tw.wiki = new $tw.Wiki();
// Install built in tiddler fields modules
@@ -2275,8 +2028,6 @@ $tw.boot.startup = function(options) {
if($tw.preloadTiddlers) {
$tw.wiki.addTiddlers($tw.preloadTiddlers);
}
// Give hooks a chance to modify the store
$tw.hooks.invokeHook("th-boot-tiddlers-loaded");
// Unpack plugin tiddlers
$tw.wiki.readPluginInfo();
$tw.wiki.registerPluginTiddlers("plugin",$tw.safeMode ? ["$:/core"] : undefined);

View File

@@ -1,3 +1,4 @@
<h1 class="">Contributing to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a></h1><p>We welcome contributions to the code and documentation of <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> in several ways:</p><ul><li><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/ReportingBugs.html">ReportingBugs</a></li><li>Helping to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Improving%2520TiddlyWiki%2520Documentation.html">improve our documentation</a></li><li>Contributing to the code via <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5" rel="noopener noreferrer" target="_blank">GitHub</a><ul><li>See <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/dev" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/dev</a> for more details</li></ul></li></ul><p>There are other ways to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/HelpingTiddlyWiki.html">help TiddlyWiki</a> too.</p><h1 class="">Contributor License Agreement</h1><p>Like other <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/OpenSource.html">OpenSource</a> projects, <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a> needs a signed contributor license agreement from individual contributors. This is a legal agreement that allows contributors to assert that they own the copyright of their contribution, and that they agree to license it to the <a class="tc-tiddlylink tc-tiddlylink-missing" href="https://tiddlywiki.com/static/UnaMesa.html">UnaMesa</a> Association (the legal entity that owns <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> on behalf of the community).</p><ul><li>For individuals use: <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-individual.md" rel="noopener noreferrer" target="_blank">licenses/CLA-individual</a></li><li>For entities use: <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-entity.md" rel="noopener noreferrer" target="_blank">licenses/CLA-entity</a></li></ul><h1 class="">How to sign the CLA</h1><p>Create a <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/GitHub.html">GitHub</a> pull request to add your name to <code>cla-individual.md</code> or <code>cla-entity.md</code>, with the date in the format (YYYY/MM/DD).</p><p><strong>step by step</strong></p><ol><li>Navigate to <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-individual.md" rel="noopener noreferrer" target="_blank">licenses/CLA-individual</a> or <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-entity.md" rel="noopener noreferrer" target="_blank">licenses/CLA-entity</a> according to whether you are signing as an individual or representative of an organisation</li><li>Ensure that the &quot;branch&quot; dropdown at the top left is set to <code>tiddlywiki-com</code></li><li>Click the &quot;edit&quot; button at the top-right corner (clicking this button will fork the project so you can edit the file)</li><li>Add your name at the bottom<ul><li>eg: <code>Jeremy Ruston, @Jermolene, 2011/11/22</code></li></ul></li><li>Below the edit box for the CLA text you should see a box labelled <strong>Propose file change</strong></li><li>Enter a brief title to explain the change (eg, &quot;Signing the CLA&quot;)</li><li>Click the green button labelled <strong>Propose file change</strong></li><li>On the following screen, click the green button labelled <strong>Create pull request</strong></li></ol><hr><p><em>The CLA documents used for this project were created using <a class="tc-tiddlylink-external" href="http://www.harmonyagreements.org" rel="noopener noreferrer" target="_blank">Harmony Project Templates</a>. &quot;HA-CLA-I-LIST Version 1.0&quot; for &quot;CLA-individual&quot; and &quot;HA-CLA-E-LIST Version 1.0&quot; for &quot;CLA-entity&quot;.</em></p><h2 class="">Remarks</h2><p><strong>If you do not own the copyright in the entire work of authorship</strong>:</p><p>In this case, please clearly state so and provide links and any additional information that clarify under which license the rest of the code is distributed.
<h1 class="">Contributing to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a></h1><p>We welcome contributions to the code and documentation of <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> in several ways:</p><ul><li><a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/ReportingBugs.html">ReportingBugs</a></li><li>Helping to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/Improving%2520TiddlyWiki%2520Documentation.html">improve our documentation</a></li><li>Contributing to the code via <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5" rel="noopener noreferrer" target="_blank">GitHub</a><ul><li>See <a class="tc-tiddlylink-external" href="https://tiddlywiki.com/dev" rel="noopener noreferrer" target="_blank">https://tiddlywiki.com/dev</a> for more details</li></ul></li></ul><p>There are other ways to <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/HelpingTiddlyWiki.html">help TiddlyWiki</a> too.</p><h1 class="">Contributor License Agreement</h1><p>Like other <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/OpenSource.html">OpenSource</a> projects, <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a> needs a signed contributor license agreement from individual contributors. This is a legal agreement that allows contributors to assert that they own the copyright of their contribution, and that they agree to license it to the <a class="tc-tiddlylink tc-tiddlylink-missing" href="https://tiddlywiki.com/static/UnaMesa.html">UnaMesa</a> Association (the legal entity that owns <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki.html">TiddlyWiki</a> on behalf of the community).</p><ul><li>For individuals use: <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-individual.md" rel="noopener noreferrer" target="_blank">licenses/CLA-individual</a></li><li>For entities use: <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-entity.md" rel="noopener noreferrer" target="_blank">licenses/CLA-entity</a></li></ul><h1 class="">How to sign the CLA</h1><p>Create a <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/GitHub.html">GitHub</a> pull request to add your name to <code>cla-individual.md</code> or <code>cla-entity.md</code>, with the date in the format (YYYY/MM/DD).</p><p><strong>step by step</strong></p><ol><li>Navigate to <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-individual.md" rel="noopener noreferrer" target="_blank">licenses/CLA-individual</a> or <a class="tc-tiddlylink-external" href="https://github.com/Jermolene/TiddlyWiki5/tree/master/licenses/cla-entity.md" rel="noopener noreferrer" target="_blank">licenses/CLA-entity</a> according to whether you are signing as an individual or representative of an organisation</li><li>Click the &quot;edit&quot; button at the top-right corner (clicking this button will fork the project so you can edit the file)</li><li>Add your name at the bottom<ul><li>eg: <code>Jeremy Ruston, @Jermolene, 2011/11/22</code></li></ul></li><li>Below the edit box for the CLA text you should see a box labelled <strong>Propose file change</strong></li><li>Enter a brief title to explain the change (eg, &quot;Signing the CLA&quot;)</li><li>Click the green button labelled <strong>Propose file change</strong></li><li>On the following screen, click the green button labelled <strong>Create pull request</strong></li></ol><hr><p><em>The CLA documents used for this project were created using <a class="tc-tiddlylink-external" href="http://www.harmonyagreements.org" rel="noopener noreferrer" target="_blank">Harmony Project Templates</a>. &quot;HA-CLA-I-LIST Version 1.0&quot; for &quot;CLA-individual&quot; and &quot;HA-CLA-E-LIST Version 1.0&quot; for &quot;CLA-entity&quot;.</em></p><p>Remarks
----—</p><ul><li><ul><li>When not owning the copyright in the entire work of authorship**</li></ul></li></ul><p>In this case, please clearly state so, since otherwise we assume that you are the legal copyright holder of the contributed work! Please provide links and additional information that clarify under which license the rest of the code is distributed.
</p><p><em>This file was automatically generated by <a class="tc-tiddlylink tc-tiddlylink-resolves" href="https://tiddlywiki.com/static/TiddlyWiki5.html">TiddlyWiki5</a></em>
</p>

View File

@@ -1,4 +0,0 @@
title: $:/core/images/add-comment
tags: $:/tags/Image
<svg class="tc-image-add-comment tc-image-button" width="22pt" height="22pt" viewBox="0 0 128 128"><path d="M56 56H36a8 8 0 1 0 0 16h20v20a8 8 0 1 0 16 0V72h20a8 8 0 1 0 0-16H72V36a8 8 0 1 0-16 0v20zm-12.595 58.362c-6.683 7.659-20.297 12.903-36.006 12.903-2.196 0-4.35-.102-6.451-.3 9.652-3.836 17.356-12.24 21.01-22.874C8.516 94.28 0 79.734 0 63.5 0 33.953 28.206 10 63 10s63 23.953 63 53.5S97.794 117 63 117c-6.841 0-13.428-.926-19.595-2.638z" fill-rule="evenodd"/></svg>

View File

@@ -1,9 +0,0 @@
title: $:/core/images/gitter
tags: $:/tags/Image
<svg class="tc-image-gitter tc-image-button" width="22pt" height="22pt" viewBox="0 0 18 25">
<rect x="15" y="5" width="2" height="10"></rect>
<rect x="10" y="5" width="2" height="20"></rect>
<rect x="5" y="5" width="2" height="20"></rect>
<rect width="2" height="15"></rect>
</svg>

View File

@@ -32,7 +32,6 @@ ExportTiddler/Caption: export tiddler
ExportTiddler/Hint: Export tiddler
ExportTiddlers/Caption: export tiddlers
ExportTiddlers/Hint: Export tiddlers
SidebarSearch/Hint: Select the sidebar search field
Fold/Caption: fold tiddler
Fold/Hint: Fold the body of this tiddler
Fold/FoldBar/Caption: fold-bar
@@ -182,7 +181,6 @@ Subscript/Caption: subscript
Subscript/Hint: Apply subscript formatting to selection
Superscript/Caption: superscript
Superscript/Hint: Apply superscript formatting to selection
ToggleSidebar/Hint: Toggle the sidebar visibility
Transcludify/Caption: transclusion
Transcludify/Hint: Wrap selection in curly brackets
Underline/Caption: underline

View File

@@ -47,8 +47,6 @@ LoadedModules/Hint: These are the currently loaded tiddler modules linked to the
Palette/Caption: Palette
Palette/Editor/Clone/Caption: clone
Palette/Editor/Clone/Prompt: It is recommended that you clone this shadow palette before editing it
Palette/Editor/Delete/Hint: delete this entry from the current palette
Palette/Editor/Names/External/Show: Show color names that are not part of the current palette
Palette/Editor/Prompt/Modified: This shadow palette has been modified
Palette/Editor/Prompt: Editing
Palette/Editor/Reset/Caption: reset
@@ -91,18 +89,6 @@ Saving/DownloadSaver/Hint: These settings apply to the HTML5-compatible download
Saving/General/Caption: General
Saving/General/Hint: These settings apply to all the loaded savers
Saving/Hint: Settings used for saving the entire TiddlyWiki as a single file via a saver module
Saving/GitService/Branch: Target branch for saving
Saving/GitService/CommitMessage: Saved by TiddlyWiki
Saving/GitService/Description: These settings are only used when saving to <<service-name>>
Saving/GitService/Filename: Filename of target file (e.g. `index.html`)
Saving/GitService/Path: Path to target file (e.g. `/wiki/`)
Saving/GitService/Repo: Target repository (e.g. `Jermolene/TiddlyWiki5`)
Saving/GitService/ServerURL: Server API URL
Saving/GitService/UserName: Username
Saving/GitService/GitHub/Caption: ~GitHub Saver
Saving/GitService/GitHub/Password: Password, OAUTH token, or personal access token (see [[GitHub help page|https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line]] for details)
Saving/GitService/GitLab/Caption: ~GitLab Saver
Saving/GitService/GitLab/Password: Personal access token for API (see [[GitLab help page|https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html]] for details)
Saving/TiddlySpot/Advanced/Heading: Advanced Settings
Saving/TiddlySpot/BackupDir: Backup Directory
Saving/TiddlySpot/Backups: Backups
@@ -140,10 +126,6 @@ Settings/NavigationHistory/Caption: Navigation History
Settings/NavigationHistory/Hint: Update browser history when navigating to a tiddler:
Settings/NavigationHistory/No/Description: Do not update history
Settings/NavigationHistory/Yes/Description: Update history
Settings/NavigationPermalinkviewMode/Caption: Permalink/permaview Mode
Settings/NavigationPermalinkviewMode/Hint: Choose how permalink/permaview is handled:
Settings/NavigationPermalinkviewMode/CopyToClipboard/Description: Copy permalink/permaview URL to clipboard
Settings/NavigationPermalinkviewMode/UpdateAddressBar/Description: Update address bar with permalink/permaview URL
Settings/PerformanceInstrumentation/Caption: Performance Instrumentation
Settings/PerformanceInstrumentation/Hint: Displays performance statistics in the browser developer console. Requires reload to take effect
Settings/PerformanceInstrumentation/Description: Enable performance instrumentation

View File

@@ -2,7 +2,6 @@ title: $:/language/Docs/ModuleTypes/
allfilteroperator: A sub-operator for the ''all'' filter operator.
animation: Animations that may be used with the RevealWidget.
authenticator: Defines how requests are authenticated by the built-in HTTP server.
bitmapeditoroperation: A bitmap editor toolbar operation.
command: Commands that can be executed under Node.js.
config: Data to be inserted into `$tw.config`.
@@ -13,7 +12,6 @@ isfilteroperator: Operands for the ''is'' filter operator.
library: Generic module type for general purpose JavaScript modules.
macro: JavaScript macro definitions.
parser: Parsers for different content types.
route: Defines how individual URL patterns are handled by the built-in HTTP server.
saver: Savers handle different methods for saving files from the browser.
startup: Startup functions.
storyview: Story views customise the animation and behaviour of list widgets.

View File

@@ -45,8 +45,6 @@ page-background: Page background
pre-background: Preformatted code background
pre-border: Preformatted code border
primary: General primary
select-tag-background: `<select>` element background
select-tag-foreground: `<select>` element text
sidebar-button-foreground: Sidebar button foreground
sidebar-controls-foreground-hover: Sidebar controls foreground hover
sidebar-controls-foreground: Sidebar controls foreground

View File

@@ -22,7 +22,6 @@ Tags/Dropdown/Hint: Show tag list
Title/BadCharacterWarning: Warning: avoid using any of the characters <<bad-chars>> in tiddler titles
Title/Exists/Prompt: Target tiddler already exists
Title/Relink/Prompt: Update ''<$text text=<<fromTitle>>/>'' to ''<$text text=<<toTitle>>/>'' in the //tags// and //list// fields of other tiddlers
Title/References/Prompt: The following references to this tiddler will not be automatically updated:
Type/Dropdown/Caption: content type list
Type/Dropdown/Hint: Show content type list
Type/Delete/Caption: delete content type

View File

@@ -14,9 +14,8 @@ draft.of: For draft tiddlers, contains the title of the tiddler of which this is
draft.title: For draft tiddlers, contains the proposed new title of the tiddler
footer: The footer text for a wizard
hack-to-give-us-something-to-compare-against: A temporary storage field used in [[$:/core/templates/static.content]]
hide-body: The view template will hide bodies of tiddlers if set to: ''yes''
icon: The title of the tiddler containing the icon associated with a tiddler
library: Indicates that a tiddler should be saved as a JavaScript library if set to: ''yes''
library: If set to "yes" indicates that a tiddler should be saved as a JavaScript library
list: An ordered list of tiddler titles associated with a tiddler
list-before: If set, the title of a tiddler before which this tiddler should be added to the ordered list of tiddler titles, or at the start of the list if this field is present but empty
list-after: If set, the title of the tiddler after which this tiddler should be added to the ordered list of tiddler titles, or at the end of the list if this field is present but empty
@@ -32,6 +31,5 @@ subtitle: The subtitle text for a wizard
tags: A list of tags associated with a tiddler
text: The body text of a tiddler
title: The unique name of a tiddler
toc-link: Suppresses the tiddler's link in a Table of Contents tree if set to: ''no''
type: The content type of a tiddler
version: Version information for a plugin

View File

@@ -10,7 +10,6 @@ Orphans: Orphan tiddlers
SystemTiddlers: System tiddlers
ShadowTiddlers: Shadow tiddlers
OverriddenShadowTiddlers: Overridden shadow tiddlers
SessionTiddlers: Tiddlers modified since the wiki was loaded
SystemTags: System tags
StoryList: Tiddlers in the story river, excluding <$text text="$:/AdvancedSearch"/>
TypedTiddlers: Non wiki-text tiddlers

View File

@@ -1,8 +0,0 @@
title: $:/language/Help/deletetiddlers
description: Deletes a group of tiddlers
<<.from-version "5.1.20">> Deletes a group of tiddlers identified by a filter.
```
--deletetiddlers <filter>
```

View File

@@ -1,34 +0,0 @@
title: $:/language/Help/listen
description: Provides an HTTP server interface to TiddlyWiki
Serves a wiki over HTTP.
The listen command uses NamedCommandParameters:
```
--listen [<name>=<value>]...
```
All parameters are optional with safe defaults, and can be specified in any order. The recognised parameters are:
* ''host'' - optional hostname to serve from (defaults to "127.0.0.1" aka "localhost")
* ''path-prefix'' - optional prefix for paths
* ''port'' - port number on which to listen; non-numeric values are interpreted as a system environment variable from which the port number is extracted (defaults to "8080")
* ''credentials'' - pathname of credentials CSV file (relative to wiki folder)
* ''anon-username'' - the username for signing edits for anonymous users
* ''username'' - optional username for basic authentication
* ''password'' - optional password for basic authentication
* ''authenticated-user-header'' - optional name of header to be used for trusted authentication
* ''readers'' - comma separated list of principals allowed to read from this wiki
* ''writers'' - comma separated list of principals allowed to write to this wiki
* ''csrf-disable'' - set to "yes" to disable CSRF checks (defaults to "no")
* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
* ''root-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html")
* ''tls-cert'' - pathname of TLS certificate file (relative to wiki folder)
* ''tls-key'' - pathname of TLS key file (relative to wiki folder)
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
* ''gzip'' - set to "yes" to enable gzip compression for some http endpoints (defaults to "no")
For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.

View File

@@ -4,12 +4,10 @@ description: Load tiddlers from a file
Load tiddlers from TiddlyWiki (`.html`), `.tiddler`, `.tid`, `.json` or other local files. The processing applied to incoming files is determined by the file extension. Use the alternative `import` command if you need to specify the deserializer and encoding explicitly.
```
--load <filepath> [noerror]
--load <dirpath> [noerror]
--load <filepath>
--load <dirpath>
```
By default, the load command raises an error if no tiddlers are found. The error can be suppressed by providing the optional "noerror" parameter.
To load tiddlers from an encrypted TiddlyWiki file you should first specify the password with the PasswordCommand. For example:
```

View File

@@ -13,8 +13,8 @@ A name and value for an additional variable may optionally also be specified.
* ''tiddler-filter'': A filter identifying the tiddler(s) to be rendered
* ''filename-filter'': Optional filter transforming tiddler titles into pathnames. If omitted, defaults to `[is[tiddler]addsuffix[.html]]`, which uses the unchanged tiddler title as the filename
* ''render-type'': Optional render type: `text/html` (the default) returns the full HTML text and `text/plain` just returns the text content (ie it ignores HTML tags and other unprintable material)
* ''template'': Optional template through which each tiddler is rendered
* ''render-type'': Optional render type: `text/html` (the default) returns the full HTML text and `text/plain` just returns the text content (ie it ignores HTML tags and other unprintable material)
* ''name'': Name of optional variable
* ''value'': Value of optional variable

View File

@@ -1,19 +0,0 @@
title: $:/language/Help/savewikifolder
description: Saves a wiki to a new wiki folder
<<.from-version "5.1.20">> Saves the current wiki as a wiki folder, including tiddlers, plugins and configuration:
```
--savewikifolder <wikifolderpath> [<filter>]
```
* The target wiki folder must be empty or non-existent
* The filter specifies which tiddlers should be included. It is optional, defaulting to `[all[tiddlers]]`
* Plugins from the official plugin library are replaced with references to those plugins in the `tiddlywiki.info` file
* Custom plugins are unpacked into their own folder
A common usage is to convert a TiddlyWiki HTML file into a wiki folder:
```
tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder
```

View File

@@ -1,25 +1,26 @@
title: $:/language/Help/server
description: Provides an HTTP server interface to TiddlyWiki (deprecated in favour of the new listen command)
description: Provides an HTTP server interface to TiddlyWiki
Legacy command to serve a wiki over HTTP.
The server built in to TiddlyWiki5 is very simple. Although compatible with TiddlyWeb it doesn't support many of the features needed for robust Internet-facing usage.
At the root, it serves a rendering of a specified tiddler. Away from the root, it serves individual tiddlers encoded in JSON, and supports the basic HTTP operations for `GET`, `PUT` and `DELETE`.
```
--server <port> <root-tiddler> <root-render-type> <root-serve-type> <username> <password> <host> <path-prefix> <debug-level>
--server <port> <roottiddler> <rendertype> <servetype> <username> <password> <host> <pathprefix>
```
The parameters are:
* ''port'' - port number on which to listen; non-numeric values are interpreted as a system environment variable from which the port number is extracted (defaults to "8080")
* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
* ''root-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html")
* ''roottiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all")
* ''rendertype'' - the content type to which the root tiddler should be rendered (defaults to "text/plain")
* ''servetype'' - the content type with which the root tiddler should be served (defaults to "text/html")
* ''username'' - the default username for signing edits
* ''password'' - optional password for basic authentication
* ''host'' - optional hostname to serve from (defaults to "127.0.0.1" aka "localhost")
* ''path-prefix'' - optional prefix for paths
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
* ''pathprefix'' - optional prefix for paths
If the password parameter is specified then the browser will prompt the user for the username and password. Note that the password is transmitted in plain text so this implementation should only be used on a trusted network or over HTTPS.
If the password parameter is specified then the browser will prompt the user for the username and password. Note that the password is transmitted in plain text so this implementation isn't suitable for general use.
For example:
@@ -27,17 +28,15 @@ For example:
--server 8080 $:/core/save/all text/plain text/html MyUserName passw0rd
```
The username and password can be specified as empty strings if you need to set the hostname or pathprefix and don't want to require a password.
The username and password can be specified as empty strings if you need to set the hostname or pathprefix and don't want to require a password:
```
--server 8080 $:/core/save/all text/plain text/html "" "" 192.168.0.245
```
Using an address like this exposes your system to the local network. For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.
To run multiple TiddlyWiki servers at the same time you'll need to put each one on a different port. It can be useful to use an environment variable to pass the port number to the Node.js process. This example references an environment variable called "MY_PORT_NUMBER":
```
--server MY_PORT_NUMBER $:/core/save/all text/plain text/html MyUserName passw0rd
```

View File

@@ -18,6 +18,4 @@ Upgrader/Plugins/Suppressed/Version: Blocked plugin (due to incoming <<incoming>
Upgrader/Plugins/Upgraded: Upgraded plugin from <<incoming>> to <<upgraded>>
Upgrader/State/Suppressed: Blocked temporary state tiddler
Upgrader/System/Suppressed: Blocked system tiddler
Upgrader/System/Warning: Core module tiddler
Upgrader/System/Alert: You are about to import a tiddler that will overwrite a core module tiddler. This is not recommended as it may make the system unstable
Upgrader/ThemeTweaks/Created: Migrated theme tweak from <$text text=<<from>>/>

View File

@@ -2,5 +2,5 @@ title: $:/language/Notifications/
Save/Done: Saved wiki
Save/Starting: Starting to save wiki
CopiedToClipboard/Succeeded: Copied to clipboard!
CopiedToClipboard/Succeeded: Copied!
CopiedToClipboard/Failed: Failed to copy to clipboard!

View File

@@ -7,7 +7,7 @@ Options/SidebarLayout: Sidebar layout
Options/SidebarLayout/Fixed-Fluid: Fixed story, fluid sidebar
Options/SidebarLayout/Fluid-Fixed: Fluid story, fixed sidebar
Options/StickyTitles: Sticky titles
Options/StickyTitles/Hint: Causes tiddler titles to "stick" to the top of the browser window
Options/StickyTitles/Hint: Causes tiddler titles to "stick" to the top of the browser window. Caution: Does not work at all with Chrome, and causes some layout issues in Firefox
Options/CodeWrapping: Wrap long lines in code blocks
Settings: Settings
Settings/FontFamily: Font family

View File

@@ -94,13 +94,6 @@ Commander.prototype.executeNextCommand = function() {
if(this.verbose) {
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = this.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return this.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,this);
@@ -129,35 +122,6 @@ Commander.prototype.executeNextCommand = function() {
}
};
/*
Given an array of parameter strings `params` in name:value format, and an array of mandatory parameter names in `mandatoryParameters`, returns a hashmap of values or a string if error
*/
Commander.prototype.extractNamedParameters = function(params,mandatoryParameters) {
mandatoryParameters = mandatoryParameters || [];
var errors = [],
paramsByName = Object.create(null);
// Extract the parameters
$tw.utils.each(params,function(param) {
var index = param.indexOf("=");
if(index < 1) {
errors.push("malformed named parameter: '" + param + "'");
}
paramsByName[param.slice(0,index)] = $tw.utils.trim(param.slice(index+1));
});
// Check the mandatory parameters are present
$tw.utils.each(mandatoryParameters,function(mandatoryParameter) {
if(!$tw.utils.hop(paramsByName,mandatoryParameter)) {
errors.push("missing mandatory parameter: '" + mandatoryParameter + "'");
}
});
// Return any errors
if(errors.length > 0) {
return errors.join(" and\n");
} else {
return paramsByName;
}
};
Commander.initCommands = function(moduleType) {
moduleType = moduleType || "command";
$tw.commands = {};

View File

@@ -1,42 +0,0 @@
/*\
title: $:/core/modules/commands/deletetiddlers.js
type: application/javascript
module-type: command
Command to delete tiddlers
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "deletetiddlers",
synchronous: true
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
if(this.params.length < 1) {
return "Missing filter";
}
var self = this,
wiki = this.commander.wiki,
filter = this.params[0],
tiddlers = wiki.filterTiddlers(filter);
$tw.utils.each(tiddlers,function(title) {
wiki.deleteTiddler(title);
});
return null;
};
exports.Command = Command;
})();

View File

@@ -1,48 +0,0 @@
/*\
title: $:/core/modules/commands/listen.js
type: application/javascript
module-type: command
Listen for HTTP requests and serve tiddlers
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Server = require("$:/core/modules/server/server.js").Server;
exports.info = {
name: "listen",
synchronous: true,
namedParameterMode: true,
mandatoryParameters: [],
};
var Command = function(params,commander,callback) {
var self = this;
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
var self = this;
if(!$tw.boot.wikiTiddlersPath) {
$tw.utils.warning("Warning: Wiki folder '" + $tw.boot.wikiPath + "' does not exist or is missing a tiddlywiki.info file");
}
// Set up server
this.server = new Server({
wiki: this.commander.wiki,
variables: self.params
});
var nodeServer = this.server.listen();
$tw.hooks.invokeHook("th-server-command-post-start",this.server,nodeServer,"tiddlywiki");
return null;
};
exports.Command = Command;
})();

View File

@@ -38,7 +38,7 @@ Command.prototype.execute = function() {
count++;
});
});
if(!count && self.params[1] !== "noerror") {
if(!count) {
self.callback("No tiddlers found in file \"" + self.params[0] + "\"");
} else {
self.callback(null);

View File

@@ -1,184 +0,0 @@
/*\
title: $:/core/modules/commands/savewikifolder.js
type: application/javascript
module-type: command
Command to save the current wiki as a wiki folder
--savewikifolder <wikifolderpath> [<filter>]
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.info = {
name: "savewikifolder",
synchronous: true
};
var fs,path;
if($tw.node) {
fs = require("fs");
path = require("path");
}
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
};
Command.prototype.execute = function() {
if(this.params.length < 1) {
return "Missing wiki folder path";
}
var wikifoldermaker = new WikiFolderMaker(this.params[0],this.params[1],this.commander);
return wikifoldermaker.save();
};
function WikiFolderMaker(wikiFolderPath,wikiFilter,commander) {
this.wikiFolderPath = wikiFolderPath;
this.wikiFilter = wikiFilter || "[all[tiddlers]]";
this.commander = commander;
this.wiki = commander.wiki;
this.savedPaths = []; // So that we can detect filename clashes
}
WikiFolderMaker.prototype.log = function(str) {
if(this.commander.verbose) {
console.log(str);
}
};
WikiFolderMaker.prototype.tiddlersToIgnore = [
"$:/boot/boot.css",
"$:/boot/boot.js",
"$:/boot/bootprefix.js",
"$:/core",
"$:/library/sjcl.js",
"$:/temp/info-plugin"
];
/*
Returns null if successful, or an error string if there was an error
*/
WikiFolderMaker.prototype.save = function() {
var self = this;
// Check that the output directory doesn't exist
if(fs.existsSync(this.wikiFolderPath) && !$tw.utils.isDirectoryEmpty(this.wikiFolderPath)) {
return "The unpackwiki command requires that the output wiki folder be empty";
}
// Get the tiddlers from the source wiki
var tiddlerTitles = this.wiki.filterTiddlers(this.wikiFilter);
// Initialise a new tiddlwiki.info file
var newWikiInfo = {};
// Process each incoming tiddler in turn
$tw.utils.each(tiddlerTitles,function(title) {
var tiddler = self.wiki.getTiddler(title);
if(tiddler) {
if(self.tiddlersToIgnore.indexOf(title) !== -1) {
// Ignore the core plugin and the ephemeral info plugin
self.log("Ignoring tiddler: " + title);
} else {
var type = tiddler.fields.type,
pluginType = tiddler.fields["plugin-type"];
if(type === "application/json" && pluginType) {
// Plugin tiddler
var libraryDetails = self.findPluginInLibrary(title);
if(libraryDetails) {
// A plugin from the core library
self.log("Adding built-in plugin: " + libraryDetails.name);
newWikiInfo[libraryDetails.type] = newWikiInfo[libraryDetails.type] || [];
$tw.utils.pushTop(newWikiInfo[libraryDetails.type],libraryDetails.name);
} else {
// A custom plugin
self.log("Processing custom plugin: " + title);
self.saveCustomPlugin(tiddler);
}
} else {
// Ordinary tiddler
self.saveTiddler("tiddlers",tiddler);
}
}
}
});
// Save the tiddlywiki.info file
this.saveJSONFile("tiddlywiki.info",newWikiInfo);
self.log("Writing tiddlywiki.info: " + JSON.stringify(newWikiInfo,null,$tw.config.preferences.jsonSpaces));
return null;
};
/*
Test whether the specified tiddler is a plugin in the plugin library
*/
WikiFolderMaker.prototype.findPluginInLibrary = function(title) {
var parts = title.split("/"),
pluginPath, type, name;
if(parts[0] === "$:") {
if(parts[1] === "languages" && parts.length === 3) {
pluginPath = "languages" + path.sep + parts[2];
type = parts[1];
name = parts[2];
} else if(parts[1] === "plugins" || parts[1] === "themes" && parts.length === 4) {
pluginPath = parts[1] + path.sep + parts[2] + path.sep + parts[3];
type = parts[1];
name = parts[2] + "/" + parts[3];
}
}
if(pluginPath && type && name) {
pluginPath = path.resolve($tw.boot.bootPath,"..",pluginPath);
if(fs.existsSync(pluginPath)) {
return {
pluginPath: pluginPath,
type: type,
name: name
};
}
}
return false;
};
WikiFolderMaker.prototype.saveCustomPlugin = function(pluginTiddler) {
var self = this,
pluginTitle = pluginTiddler.fields.title,
titleParts = pluginTitle.split("/"),
directory = $tw.utils.generateTiddlerFilepath(titleParts[titleParts.length - 1],{
directory: path.resolve(this.wikiFolderPath,pluginTiddler.fields["plugin-type"] + "s")
}),
pluginInfo = pluginTiddler.getFieldStrings({exclude: ["text","type"]});
this.saveJSONFile(directory + path.sep + "plugin.info",pluginInfo);
self.log("Writing " + directory + path.sep + "plugin.info: " + JSON.stringify(pluginInfo,null,$tw.config.preferences.jsonSpaces));
var pluginTiddlers = JSON.parse(pluginTiddler.fields.text).tiddlers; // A hashmap of tiddlers in the plugin
$tw.utils.each(pluginTiddlers,function(tiddler) {
self.saveTiddler(directory,new $tw.Tiddler(tiddler));
});
};
WikiFolderMaker.prototype.saveTiddler = function(directory,tiddler) {
var fileInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{
directory: path.resolve(this.wikiFolderPath,directory),
wiki: this.wiki
});
$tw.utils.saveTiddlerToFileSync(tiddler,fileInfo);
};
WikiFolderMaker.prototype.saveJSONFile = function(filename,json) {
this.saveTextFile(filename,JSON.stringify(json,null,$tw.config.preferences.jsonSpaces));
};
WikiFolderMaker.prototype.saveTextFile = function(filename,data) {
this.saveFile(filename,"utf8",data);
};
WikiFolderMaker.prototype.saveFile = function(filename,encoding,data) {
var filepath = path.resolve(this.wikiFolderPath,filename);
$tw.utils.createFileDirectories(filepath);
fs.writeFileSync(filepath,data,encoding);
};
exports.Command = Command;
})();

View File

@@ -3,7 +3,7 @@ title: $:/core/modules/commands/server.js
type: application/javascript
module-type: command
Deprecated legacy command for serving tiddlers
Serve tiddlers over http
\*/
(function(){
@@ -12,41 +12,304 @@ Deprecated legacy command for serving tiddlers
/*global $tw: false */
"use strict";
var Server = require("$:/core/modules/server/server.js").Server;
if($tw.node) {
var util = require("util"),
fs = require("fs"),
url = require("url"),
path = require("path"),
http = require("http");
}
exports.info = {
name: "server",
synchronous: true
};
var Command = function(params,commander,callback) {
/*
A simple HTTP server with regexp-based routes
*/
function SimpleServer(options) {
this.routes = options.routes || [];
this.wiki = options.wiki;
this.variables = options.variables || {};
}
SimpleServer.prototype.set = function(obj) {
var self = this;
$tw.utils.each(obj,function(value,name) {
self.variables[name] = value;
});
};
SimpleServer.prototype.get = function(name) {
return this.variables[name];
};
SimpleServer.prototype.addRoute = function(route) {
this.routes.push(route);
};
SimpleServer.prototype.findMatchingRoute = function(request,state) {
var pathprefix = this.get("pathprefix") || "";
for(var t=0; t<this.routes.length; t++) {
var potentialRoute = this.routes[t],
pathRegExp = potentialRoute.path,
pathname = state.urlInfo.pathname,
match;
if(pathprefix) {
if(pathname.substr(0,pathprefix.length) === pathprefix) {
pathname = pathname.substr(pathprefix.length);
match = potentialRoute.path.exec(pathname);
} else {
match = false;
}
} else {
match = potentialRoute.path.exec(pathname);
}
if(match && request.method === potentialRoute.method) {
state.params = [];
for(var p=1; p<match.length; p++) {
state.params.push(match[p]);
}
return potentialRoute;
}
}
return null;
};
SimpleServer.prototype.checkCredentials = function(request,incomingUsername,incomingPassword) {
var header = request.headers.authorization || "",
token = header.split(/\s+/).pop() || "",
auth = $tw.utils.base64Decode(token),
parts = auth.split(/:/),
username = parts[0],
password = parts[1];
if(incomingUsername === username && incomingPassword === password) {
return "ALLOWED";
} else {
return "DENIED";
}
};
SimpleServer.prototype.requestHandler = function(request,response) {
// Compose the state object
var self = this;
var state = {};
state.wiki = self.wiki;
state.server = self;
state.urlInfo = url.parse(request.url);
// Find the route that matches this path
var route = self.findMatchingRoute(request,state);
// Check for the username and password if we've got one
var username = self.get("username"),
password = self.get("password");
if(username && password) {
// Check they match
if(self.checkCredentials(request,username,password) !== "ALLOWED") {
var servername = state.wiki.getTiddlerText("$:/SiteTitle") || "TiddlyWiki5";
response.writeHead(401,"Authentication required",{
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + servername + '"'
});
response.end();
return;
}
}
// Return a 404 if we didn't find a route
if(!route) {
response.writeHead(404);
response.end();
return;
}
// Set the encoding for the incoming request
// TODO: Presumably this would need tweaking if we supported PUTting binary tiddlers
request.setEncoding("utf8");
// Dispatch the appropriate method
switch(request.method) {
case "GET": // Intentional fall-through
case "DELETE":
route.handler(request,response,state);
break;
case "PUT":
var data = "";
request.on("data",function(chunk) {
data += chunk.toString();
});
request.on("end",function() {
state.data = data;
route.handler(request,response,state);
});
break;
}
};
SimpleServer.prototype.listen = function(port,host) {
return http.createServer(this.requestHandler.bind(this)).listen(port,host);
};
var Command = function(params,commander,callback) {
this.params = params;
this.commander = commander;
this.callback = callback;
// Set up server
this.server = new SimpleServer({
wiki: this.commander.wiki
});
// Add route handlers
this.server.addRoute({
method: "PUT",
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
handler: function(request,response,state) {
var title = decodeURIComponent(state.params[0]),
fields = JSON.parse(state.data);
// Pull up any subfields in the `fields` object
if(fields.fields) {
$tw.utils.each(fields.fields,function(field,name) {
fields[name] = field;
});
delete fields.fields;
}
// Remove any revision field
if(fields.revision) {
delete fields.revision;
}
state.wiki.addTiddler(new $tw.Tiddler(state.wiki.getCreationFields(),fields,{title: title},state.wiki.getModificationFields()));
var changeCount = state.wiki.getChangeCount(title).toString();
response.writeHead(204, "OK",{
Etag: "\"default/" + encodeURIComponent(title) + "/" + changeCount + ":\"",
"Content-Type": "text/plain"
});
response.end();
}
});
this.server.addRoute({
method: "DELETE",
path: /^\/bags\/default\/tiddlers\/(.+)$/,
handler: function(request,response,state) {
var title = decodeURIComponent(state.params[0]);
state.wiki.deleteTiddler(title);
response.writeHead(204, "OK", {
"Content-Type": "text/plain"
});
response.end();
}
});
this.server.addRoute({
method: "GET",
path: /^\/$/,
handler: function(request,response,state) {
response.writeHead(200, {"Content-Type": state.server.get("serveType")});
var text = state.wiki.renderTiddler(state.server.get("renderType"),state.server.get("rootTiddler"));
response.end(text,"utf8");
}
});
this.server.addRoute({
method: "GET",
path: /^\/status$/,
handler: function(request,response,state) {
response.writeHead(200, {"Content-Type": "application/json"});
var text = JSON.stringify({
username: state.server.get("username"),
space: {
recipe: "default"
},
tiddlywiki_version: $tw.version
});
response.end(text,"utf8");
}
});
this.server.addRoute({
method: "GET",
path: /^\/favicon.ico$/,
handler: function(request,response,state) {
response.writeHead(200, {"Content-Type": "image/x-icon"});
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
response.end(buffer,"base64");
}
});
this.server.addRoute({
method: "GET",
path: /^\/recipes\/default\/tiddlers.json$/,
handler: function(request,response,state) {
response.writeHead(200, {"Content-Type": "application/json"});
var tiddlers = [];
state.wiki.forEachTiddler({sortField: "title"},function(title,tiddler) {
var tiddlerFields = {};
$tw.utils.each(tiddler.fields,function(field,name) {
if(name !== "text") {
tiddlerFields[name] = tiddler.getFieldString(name);
}
});
tiddlerFields.revision = state.wiki.getChangeCount(title);
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
tiddlers.push(tiddlerFields);
});
var text = JSON.stringify(tiddlers);
response.end(text,"utf8");
}
});
this.server.addRoute({
method: "GET",
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
handler: function(request,response,state) {
var title = decodeURIComponent(state.params[0]),
tiddler = state.wiki.getTiddler(title),
tiddlerFields = {},
knownFields = [
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
];
if(tiddler) {
$tw.utils.each(tiddler.fields,function(field,name) {
var value = tiddler.getFieldString(name);
if(knownFields.indexOf(name) !== -1) {
tiddlerFields[name] = value;
} else {
tiddlerFields.fields = tiddlerFields.fields || {};
tiddlerFields.fields[name] = value;
}
});
tiddlerFields.revision = state.wiki.getChangeCount(title);
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
response.writeHead(200, {"Content-Type": "application/json"});
response.end(JSON.stringify(tiddlerFields),"utf8");
} else {
response.writeHead(404);
response.end();
}
}
});
};
Command.prototype.execute = function() {
if(!$tw.boot.wikiTiddlersPath) {
$tw.utils.warning("Warning: Wiki folder '" + $tw.boot.wikiPath + "' does not exist or is missing a tiddlywiki.info file");
}
// Set up server
this.server = new Server({
wiki: this.commander.wiki,
variables: {
port: this.params[0],
host: this.params[6],
"root-tiddler": this.params[1],
"root-render-type": this.params[2],
"root-serve-type": this.params[3],
username: this.params[4],
password: this.params[5],
"path-prefix": this.params[7],
"debug-level": this.params[8]
}
var port = this.params[0] || "8080",
rootTiddler = this.params[1] || "$:/core/save/all",
renderType = this.params[2] || "text/plain",
serveType = this.params[3] || "text/html",
username = this.params[4],
password = this.params[5],
host = this.params[6] || "127.0.0.1",
pathprefix = this.params[7];
if(parseInt(port,10).toString() !== port) {
port = process.env[port] || 8080;
}
this.server.set({
rootTiddler: rootTiddler,
renderType: renderType,
serveType: serveType,
username: username,
password: password,
pathprefix: pathprefix
});
var nodeServer = this.server.listen();
$tw.hooks.invokeHook("th-server-command-post-start",this.server,nodeServer,"tiddlywiki");
var nodeServer = this.server.listen(port,host);
$tw.utils.log("Serving on " + host + ":" + port,"brown/orange");
$tw.utils.log("(press ctrl-C to exit)","red");
// Warn if required plugins are missing
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) {
$tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file");
}
$tw.hooks.invokeHook('th-server-command-post-start', this.server, nodeServer);
return null;
};

View File

@@ -42,7 +42,6 @@ function FramedEngine(options) {
this.iframeNode.style.border = "none";
this.iframeNode.style.padding = "0";
this.iframeNode.style.resize = "none";
this.iframeNode.style["background-color"] = this.widget.wiki.extractTiddlerDataItem(this.widget.wiki.getTiddlerText("$:/palette"),"tiddler-editor-background");
this.iframeDoc.body.style.margin = "0";
this.iframeDoc.body.style.padding = "0";
this.widget.domNodes.push(this.iframeNode);
@@ -71,15 +70,11 @@ function FramedEngine(options) {
if(this.widget.editRows) {
this.domNode.setAttribute("rows",this.widget.editRows);
}
if(this.widget.editTabIndex) {
this.iframeNode.setAttribute("tabindex",this.widget.editTabIndex);
}
// Copy the styles from the dummy textarea
this.copyStyles();
// Add event listeners
$tw.utils.addEventListeners(this.domNode,[
{name: "click",handlerObject: this,handlerMethod: "handleClickEvent"},
{name: "focus",handlerObject: this,handlerMethod: "handleFocusEvent"},
{name: "input",handlerObject: this,handlerMethod: "handleInputEvent"},
{name: "keydown",handlerObject: this.widget,handlerMethod: "handleKeydownEvent"}
]);
@@ -97,7 +92,6 @@ FramedEngine.prototype.copyStyles = function() {
this.domNode.style.display = "block";
this.domNode.style.width = "100%";
this.domNode.style.margin = "0";
this.domNode.style["background-color"] = this.widget.wiki.extractTiddlerDataItem(this.widget.wiki.getTiddlerText("$:/palette"),"tiddler-editor-background");
// In Chrome setting -webkit-text-fill-color overrides the placeholder text colour
this.domNode.style["-webkit-text-fill-color"] = "currentcolor";
};
@@ -153,14 +147,6 @@ FramedEngine.prototype.focus = function() {
this.domNode.select();
}
};
/*
Handle the focus event
*/
FramedEngine.prototype.handleFocusEvent = function(event) {
this.widget.cancelPopups();
return true;
};
/*
Handle a click

View File

@@ -49,9 +49,6 @@ function SimpleEngine(options) {
if(this.widget.editClass) {
this.domNode.className = this.widget.editClass;
}
if(this.widget.editTabIndex) {
this.domNode.setAttribute("tabindex",this.widget.editTabIndex);
}
// Add an input event handler
$tw.utils.addEventListeners(this.domNode,[
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
@@ -122,7 +119,6 @@ SimpleEngine.prototype.handleInputEvent = function(event) {
Handle a dom "focus" event
*/
SimpleEngine.prototype.handleFocusEvent = function(event) {
this.widget.cancelPopups();
if(this.widget.editFocusPopup) {
$tw.popup.triggerPopup({
domNode: this.domNode,

View File

@@ -176,7 +176,6 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
this.editMinHeight = this.getAttribute("minHeight",DEFAULT_MIN_TEXT_AREA_HEIGHT);
this.editFocusPopup = this.getAttribute("focusPopup");
this.editFocus = this.getAttribute("focus");
this.editTabIndex = this.getAttribute("tabindex");
// Get the default editor element tag and type
var tag,type;
if(this.editField === "text") {
@@ -193,7 +192,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
type = type || "text";
}
// Get the rest of our parameters
this.editTag = this.getAttribute("tag",tag) || "input";
this.editTag = this.getAttribute("tag",tag);
this.editType = this.getAttribute("type",type);
// Make the child widgets
this.makeChildWidgets();
@@ -208,7 +207,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
EditTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Completely rerender if any of our attributes have changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE]) {
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE]) {
this.refreshSelf();
return true;
} else if(changedTiddlers[this.editTitle]) {
@@ -217,7 +216,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
}
this.engine.fixHeight();
if(this.editShowToolbar) {
return this.refreshChildren(changedTiddlers);
return this.refreshChildren(changedTiddlers);
} else {
return false;
}
@@ -248,13 +247,6 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
}
};
/*
Cancel Popups
*/
EditTextWidget.prototype.cancelPopups = function() {
$tw.popup.cancel(0,this.engine.domNode);
};
/*
Handle a dom "keydown" event, which we'll bubble up to our container for the keyboard widgets benefit
*/
@@ -274,7 +266,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
el.dispatchEvent(clickEvent);
event.preventDefault();
event.stopPropagation();
return true;
return true;
}
}
}

View File

@@ -1,23 +0,0 @@
/*\
title: $:/core/modules/editor/operations/text/save-selection.js
type: application/javascript
module-type: texteditoroperation
Text editor operation to save the current selection in a specified tiddler
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports["save-selection"] = function(event,operation) {
var tiddler = event.paramObject.tiddler,
field = event.paramObject.field || "text";
if(tiddler && field) {
this.wiki.setText(tiddler,field,null,operation.text.substring(operation.selStart,operation.selEnd));
}
};
})();

View File

@@ -16,12 +16,14 @@ exports["wrap-selection"] = function(event,operation) {
if(operation.selStart === operation.selEnd) {
// No selection; check if we're within the prefix/suffix
if(operation.text.substring(operation.selStart - event.paramObject.prefix.length,operation.selStart + event.paramObject.suffix.length) === event.paramObject.prefix + event.paramObject.suffix) {
// Remove the prefix and suffix
operation.cutStart = operation.selStart - event.paramObject.prefix.length;
operation.cutEnd = operation.selEnd + event.paramObject.suffix.length;
operation.replacement = "";
operation.newSelStart = operation.cutStart;
operation.newSelEnd = operation.newSelStart;
// Remove the prefix and suffix unless they comprise the entire text
if(operation.selStart > event.paramObject.prefix.length || (operation.selEnd + event.paramObject.suffix.length) < operation.text.length ) {
operation.cutStart = operation.selStart - event.paramObject.prefix.length;
operation.cutEnd = operation.selEnd + event.paramObject.suffix.length;
operation.replacement = "";
operation.newSelStart = operation.cutStart;
operation.newSelEnd = operation.newSelStart;
}
} else {
// Wrap the cursor instead
operation.cutStart = operation.selStart;

View File

@@ -40,23 +40,12 @@ function parseFilterOperation(operators,filterString,p) {
nextBracketPos += p;
var bracket = filterString.charAt(nextBracketPos);
operator.operator = filterString.substring(p,nextBracketPos);
// Any suffix?
var colon = operator.operator.indexOf(':');
if(colon > -1) {
// The raw suffix for older filters
operator.suffix = operator.operator.substring(colon + 1);
operator.operator = operator.operator.substring(0,colon) || "field";
// The processed suffix for newer filters
operator.suffixes = [];
$tw.utils.each(operator.suffix.split(":"),function(subsuffix) {
operator.suffixes.push([]);
$tw.utils.each(subsuffix.split(","),function(entry) {
entry = $tw.utils.trim(entry);
if(entry) {
operator.suffixes[operator.suffixes.length - 1].push(entry);
}
});
});
}
// Empty operator means: title
else if(operator.operator === "") {
@@ -119,7 +108,7 @@ exports.parseFilter = function(filterString) {
p = 0, // Current position in the filter string
match;
var whitespaceRegExp = /(\s+)/mg,
operandRegExp = /((?:\+|\-|~|=)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
operandRegExp = /((?:\+|\-)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
while(p < filterString.length) {
// Skip any whitespace
whitespaceRegExp.lastIndex = p;
@@ -219,7 +208,6 @@ exports.compileFilter = function(filterString) {
operand: operand,
prefix: operator.prefix,
suffix: operator.suffix,
suffixes: operator.suffixes,
regexp: operator.regexp
},{
wiki: self,
@@ -248,10 +236,6 @@ exports.compileFilter = function(filterString) {
return function(results,source,widget) {
$tw.utils.pushTop(results,operationSubFunction(source,widget));
};
case "=": // The results of the operation are pushed into the result without deduplication
return function(results,source,widget) {
Array.prototype.push.apply(results,operationSubFunction(source,widget));
};
case "-": // The results of this operation are removed from the main result
return function(results,source,widget) {
$tw.utils.removeArrayEntries(results,operationSubFunction(source,widget));
@@ -263,18 +247,11 @@ exports.compileFilter = function(filterString) {
results.splice(0,results.length);
$tw.utils.pushTop(results,operationSubFunction(source,widget));
};
case "~": // This operation is unioned into the result only if the main result so far is empty
return function(results,source,widget) {
if(results.length === 0) {
// Main result so far is empty
$tw.utils.pushTop(results,operationSubFunction(source,widget));
}
};
}
})());
});
// Return a function that applies the operations to a source iterator of tiddler titles
return $tw.perf.measure("filter: " + filterString,function filterFunction(source,widget) {
return $tw.perf.measure("filter",function filterFunction(source,widget) {
if(!source) {
source = self.each;
} else if(typeof source === "object") { // Array or hashmap

View File

@@ -1,45 +0,0 @@
/*\
title: $:/core/modules/filters/contains.js
type: application/javascript
module-type: filteroperator
Filter operator for finding values in array fields
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.contains = function(source,operator,options) {
var results = [],
fieldname = (operator.suffix || "list").toLowerCase();
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(tiddler) {
var list = tiddler.getFieldList(fieldname);
if(list.indexOf(operator.operand) === -1) {
results.push(title);
}
} else {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(tiddler) {
var list = tiddler.getFieldList(fieldname);
if(list.indexOf(operator.operand) !== -1) {
results.push(title);
}
}
});
}
return results;
};
})();

View File

@@ -1,30 +0,0 @@
/*\
title: $:/core/modules/filters/else.js
type: application/javascript
module-type: filteroperator
Filter operator for replacing an empty input list with a constant, passing a non-empty input list straight through
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.else = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
results.push(title);
});
if(results.length === 0) {
return [operator.operand];
} else {
return results;
}
};
})();

View File

@@ -19,12 +19,7 @@ Export our filter functions
exports.decodeuricomponent = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var value = title;
try {
value = decodeURIComponent(title);
} catch(e) {
}
results.push(value);
results.push(decodeURIComponent(title));
});
return results;
};
@@ -40,12 +35,7 @@ exports.encodeuricomponent = function(source,operator,options) {
exports.decodeuri = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
var value = title;
try {
value = decodeURI(title);
} catch(e) {
}
results.push(value);
results.push(decodeURI(title));
});
return results;
};
@@ -98,13 +88,4 @@ exports.escaperegexp = function(source,operator,options) {
return results;
};
exports.escapecss = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
// escape any character with a special meaning in CSS using CSS.escape()
results.push(CSS.escape(title));
});
return results;
};
})();

View File

@@ -16,16 +16,7 @@ Filter operator returning its operand parsed as a list
Export our filter function
*/
exports.enlist = function(source,operator,options) {
var allowDuplicates = false;
switch(operator.suffix) {
case "raw":
allowDuplicates = true;
break;
case "dedupe":
allowDuplicates = false;
break;
}
var list = $tw.utils.parseStringArray(operator.operand,allowDuplicates);
var list = $tw.utils.parseStringArray(operator.operand);
if(operator.prefix === "!") {
var results = [];
source(function(tiddler,title) {

View File

@@ -16,7 +16,7 @@ Filter operator for comparing fields for equality
Export our filter function
*/
exports.field = function(source,operator,options) {
var results = [],indexedResults,
var results = [],
fieldname = (operator.suffix || operator.operator || "title").toLowerCase();
if(operator.prefix === "!") {
if(operator.regexp) {
@@ -53,12 +53,6 @@ exports.field = function(source,operator,options) {
}
});
} else {
if(source.byField) {
indexedResults = source.byField(fieldname,operator.operand);
if(indexedResults) {
return indexedResults
}
}
source(function(tiddler,title) {
if(tiddler) {
var text = tiddler.getFieldString(fieldname);

View File

@@ -1,26 +0,0 @@
/*\
title: $:/core/modules/filters/getvariable.js
type: application/javascript
module-type: filteroperator
Filter operator for replacing input values by the value of the variable with the same name, or blank if the variable is missing
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.getvariable = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
results.push(options.widget.getVariable(title) || "");
});
return results;
};
})();

View File

@@ -45,7 +45,7 @@ exports.has = function(source,operator,options) {
if(tiddler && $tw.utils.hop(tiddler.fields,operator.operand) && !(tiddler.fields[operator.operand] === "" || tiddler.fields[operator.operand].length === 0)) {
results.push(title);
}
});
});
}
}
return results;

View File

@@ -26,16 +26,9 @@ function getIsFilterOperators() {
Export our filter function
*/
exports.is = function(source,operator,options) {
// Dispatch to the correct isfilteroperator
var isFilterOperators = getIsFilterOperators();
if(operator.operand) {
var isFilterOperator = isFilterOperators[operator.operand];
if(isFilterOperator) {
return isFilterOperator(source,operator.prefix,options);
} else {
return [$tw.language.getString("Error/IsFilterOperator")];
}
} else {
if( !operator.operand) {
// Return all tiddlers if the operand is missing
var results = [];
source(function(tiddler,title) {
@@ -43,6 +36,31 @@ exports.is = function(source,operator,options) {
});
return results;
}
// Get our isfilteroperators
var isFilterOperators = getIsFilterOperators(),
subops = operator.operand.split("+"),
filteredResults = {},
results = [];
for (var t=0; t<subops.length; t++) {
var subop = isFilterOperators[subops[t]];
if(subop) {
filteredResults[subops[t]] = subop(source,operator.prefix,options);
} else {
return [$tw.language.getString("Error/IsFilterOperator")];
}
}
source(function(tiddler,title) {
for (var t=0; t<subops.length; t++) {
if (filteredResults[subops[t]].indexOf(title) != -1){
results.push(title);
break;
}
}
});
return results;
};
})();
})();

View File

@@ -1,36 +0,0 @@
/*\
title: $:/core/modules/filters/is/blank.js
type: application/javascript
module-type: isfilteroperator
Filter function for [is[blank]]
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.blank = function(source,prefix,options) {
var results = [];
if(prefix === "!") {
source(function(tiddler,title) {
if(title) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(!title) {
results.push(title);
}
});
}
return results;
};
})();

View File

@@ -1,36 +0,0 @@
/*\
title: $:/core/modules/filters/is/variable.js
type: application/javascript
module-type: isfilteroperator
Filter function for [is[variable]]
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.variable = function(source,prefix,options) {
var results = [];
if(prefix === "!") {
source(function(tiddler,title) {
if(!(title in options.widget.variables)) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title in options.widget.variables) {
results.push(title);
}
});
}
return results;
};
})();

View File

@@ -1,53 +0,0 @@
/*\
title: $:/core/modules/filters/match.js
type: application/javascript
module-type: filteroperator
Filter operator for checking if a title matches a string
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.match = function(source,operator,options) {
var results = [],
suffixes = (operator.suffixes || [])[0] || [];
if(suffixes.indexOf("caseinsensitive") !== -1) {
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(title.toLowerCase() !== (operator.operand || "").toLowerCase()) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title.toLowerCase() === (operator.operand || "").toLowerCase()) {
results.push(title);
}
});
}
} else {
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(title !== operator.operand) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title === operator.operand) {
results.push(title);
}
});
}
}
return results;
};
})();

View File

@@ -1,146 +0,0 @@
/*\
title: $:/core/modules/filters/math.js
type: application/javascript
module-type: filteroperator
Filter operators for math. Unary/binary operators work on each item in turn, and return a new item list.
Sum/product/maxall/minall operate on the entire list, returning a single item.
Note that strings are converted to numbers automatically. Trailing non-digits are ignored.
* "" converts to 0
* "12kk" converts to 12
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.negate = makeNumericBinaryOperator(
function(a) {return -a}
);
exports.abs = makeNumericBinaryOperator(
function(a) {return Math.abs(a)}
);
exports.ceil = makeNumericBinaryOperator(
function(a) {return Math.ceil(a)}
);
exports.floor = makeNumericBinaryOperator(
function(a) {return Math.floor(a)}
);
exports.round = makeNumericBinaryOperator(
function(a) {return Math.round(a)}
);
exports.trunc = makeNumericBinaryOperator(
function(a) {return Math.trunc(a)}
);
exports.untrunc = makeNumericBinaryOperator(
function(a) {return Math.ceil(Math.abs(a)) * Math.sign(a)}
);
exports.sign = makeNumericBinaryOperator(
function(a) {return Math.sign(a)}
);
exports.add = makeNumericBinaryOperator(
function(a,b) {return a + b;}
);
exports.subtract = makeNumericBinaryOperator(
function(a,b) {return a - b;}
);
exports.multiply = makeNumericBinaryOperator(
function(a,b) {return a * b;}
);
exports.divide = makeNumericBinaryOperator(
function(a,b) {return a / b;}
);
exports.remainder = makeNumericBinaryOperator(
function(a,b) {return a % b;}
);
exports.max = makeNumericBinaryOperator(
function(a,b) {return Math.max(a,b);}
);
exports.min = makeNumericBinaryOperator(
function(a,b) {return Math.min(a,b);}
);
exports.fixed = makeNumericBinaryOperator(
function(a,b) {return Number.prototype.toFixed.call(a,Math.min(Math.max(b,0),100));}
);
exports.precision = makeNumericBinaryOperator(
function(a,b) {return Number.prototype.toPrecision.call(a,Math.min(Math.max(b,1),100));}
);
exports.exponential = makeNumericBinaryOperator(
function(a,b) {return Number.prototype.toExponential.call(a,Math.min(Math.max(b,0),100));}
);
exports.sum = makeNumericReducingOperator(
function(accumulator,value) {return accumulator + value},
0 // Initial value
);
exports.product = makeNumericReducingOperator(
function(accumulator,value) {return accumulator * value},
1 // Initial value
);
exports.maxall = makeNumericReducingOperator(
function(accumulator,value) {return Math.max(accumulator,value)},
-Infinity // Initial value
);
exports.minall = makeNumericReducingOperator(
function(accumulator,value) {return Math.min(accumulator,value)},
Infinity // Initial value
);
function makeNumericBinaryOperator(fnCalc) {
return function(source,operator,options) {
var result = [],
numOperand = parseNumber(operator.operand);
source(function(tiddler,title) {
result.push(stringifyNumber(fnCalc(parseNumber(title),numOperand)));
});
return result;
};
}
function makeNumericReducingOperator(fnCalc,initialValue) {
initialValue = initialValue || 0;
return function(source,operator,options) {
var result = [];
source(function(tiddler,title) {
result.push(title);
});
return [stringifyNumber(result.reduce(function(accumulator,currentValue) {
return fnCalc(accumulator,parseNumber(currentValue));
},initialValue))];
};
}
function parseNumber(str) {
return parseFloat(str) || 0;
}
function stringifyNumber(num) {
return num + "";
}
})();

View File

@@ -1,99 +0,0 @@
/*\
title: $:/core/modules/filters/range.js
type: application/javascript
module-type: filteroperator
Filter operator for generating a numeric range.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.range = function(source,operator,options) {
var results = [];
// Split the operand into numbers delimited by these symbols
var parts = operator.operand.split(/[,:;]/g),
beg, end, inc, i, fixed = 0;
for (i=0; i<parts.length; i++) {
// Validate real number
if(!/^\s*[+-]?((\d+(\.\d*)?)|(\.\d+))\s*$/.test(parts[i])) {
return ["range: bad number \"" + parts[i] + "\""];
}
// Count digits; the most precise number determines decimal places in output.
var frac = /\.\d+/.exec(parts[i]);
if(frac) {
fixed = Math.max(fixed,frac[0].length-1);
}
parts[i] = parseFloat(parts[i]);
}
switch(parts.length) {
case 1:
end = parts[0];
if (end >= 1) {
beg = 1;
}
else if (end <= -1) {
beg = -1;
}
else {
return [];
}
inc = 1;
break;
case 2:
beg = parts[0];
end = parts[1];
inc = 1;
break;
case 3:
beg = parts[0];
end = parts[1];
inc = Math.abs(parts[2]);
break;
}
if(inc === 0) {
return ["range: increment 0 causes infinite loop"];
}
// May need to count backwards
var direction = ((end < beg) ? -1 : 1);
inc *= direction;
// Estimate number of resulting elements
if((end - beg) / inc > 10000) {
return ["range: too many steps (over 10K)"];
}
// Avoid rounding error on last step
end += direction * 0.5 * Math.pow(0.1,fixed);
var safety = 10010;
// Enumerate the range
if (end<beg) {
for(i=beg; i>end; i+=inc) {
results.push(i.toFixed(fixed));
if(--safety<0) {
break;
}
}
} else {
for(i=beg; i<end; i+=inc) {
results.push(i.toFixed(fixed));
if(--safety<0) {
break;
}
}
}
if(safety<0) {
return ["range: unexpectedly large output"];
}
// Reverse?
if(operator.prefix === "!") {
results.reverse();
}
return results;
};
})();

View File

@@ -18,7 +18,7 @@ Export our filter function
exports.removesuffix = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
if(title && title.substr(-operator.operand.length) === operator.operand) {
if(title.substr(-operator.operand.length) === operator.operand) {
results.push(title.substr(0,title.length - operator.operand.length));
}
});

View File

@@ -17,35 +17,11 @@ Export our filter function
*/
exports.search = function(source,operator,options) {
var invert = operator.prefix === "!";
if(operator.suffixes) {
var hasFlag = function(flag) {
return (operator.suffixes[1] || []).indexOf(flag) !== -1;
},
excludeFields = false,
fieldList = operator.suffixes[0] || [],
firstField = fieldList[0] || "",
firstChar = firstField.charAt(0),
fields;
if(firstChar === "-") {
fields = [firstField.slice(1)].concat(fieldList.slice(1));
excludeFields = true;
} else if(fieldList[0] === "*"){
fields = [];
excludeFields = true;
} else {
fields = fieldList.slice(0);
}
if(operator.suffix) {
return options.wiki.search(operator.operand,{
source: source,
invert: invert,
field: fields,
excludeField: excludeFields,
caseSensitive: hasFlag("casesensitive"),
literal: hasFlag("literal"),
whitespace: hasFlag("whitespace"),
anchored: hasFlag("anchored"),
regexp: hasFlag("regexp"),
words: hasFlag("words")
field: operator.suffix
});
} else {
return options.wiki.search(operator.operand,{

View File

@@ -1,93 +0,0 @@
/*\
title: $:/core/modules/filters/strings.js
type: application/javascript
module-type: filteroperator
Filter operators for strings. Unary/binary operators work on each item in turn, and return a new item list.
Sum/product/maxall/minall operate on the entire list, returning a single item.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.length = makeStringBinaryOperator(
function(a) {return ["" + ("" + a).length];}
);
exports.uppercase = makeStringBinaryOperator(
function(a) {return [("" + a).toUpperCase()];}
);
exports.lowercase = makeStringBinaryOperator(
function(a) {return [("" + a).toLowerCase()];}
);
exports.sentencecase = makeStringBinaryOperator(
function(a) {return [$tw.utils.toSentenceCase(a)];}
);
exports.titlecase = makeStringBinaryOperator(
function(a) {return [$tw.utils.toTitleCase(a)];}
);
exports.trim = makeStringBinaryOperator(
function(a) {return [$tw.utils.trim(a)];}
);
exports.split = makeStringBinaryOperator(
function(a,b) {return ("" + a).split(b);}
);
exports.join = makeStringReducingOperator(
function(accumulator,value,operand) {
if(accumulator === null) {
return value;
} else {
return accumulator + operand + value;
}
},null
);
function makeStringBinaryOperator(fnCalc) {
return function(source,operator,options) {
var result = [];
source(function(tiddler,title) {
Array.prototype.push.apply(result,fnCalc(title,operator.operand || ""));
});
return result;
};
}
function makeStringReducingOperator(fnCalc,initialValue) {
return function(source,operator,options) {
var result = [];
source(function(tiddler,title) {
result.push(title);
});
return [result.reduce(function(accumulator,currentValue) {
return fnCalc(accumulator,currentValue,operator.operand || "");
},initialValue)];
};
}
exports.splitregexp = function(source,operator,options) {
var result = [],
suffix = operator.suffix || "",
flags = (suffix.indexOf("m") !== -1 ? "m" : "") + (suffix.indexOf("i") !== -1 ? "i" : ""),
regExp;
try {
regExp = new RegExp(operator.operand || "",flags);
} catch(ex) {
return ["RegExp error: " + ex];
}
source(function(tiddler,title) {
Array.prototype.push.apply(result,title.split(regExp));
});
return result;
};
})();

View File

@@ -1,33 +0,0 @@
/*\
title: $:/core/modules/filters/subfilter.js
type: application/javascript
module-type: filteroperator
Filter operator returning its operand evaluated as a filter
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.subfilter = function(source,operator,options) {
var list = options.wiki.filterTiddlers(operator.operand,options.widget,source);
if(operator.prefix === "!") {
var results = [];
source(function(tiddler,title) {
if(list.indexOf(title) === -1) {
results.push(title);
}
});
return results;
} else {
return list;
}
};
})();

View File

@@ -16,7 +16,7 @@ Filter operator for checking for the presence of a tag
Export our filter function
*/
exports.tag = function(source,operator,options) {
var results = [],indexedResults;
var results = [];
if((operator.suffix || "").toLowerCase() === "strict" && !operator.operand) {
// New semantics:
// Always return copy of input if operator.operand is missing
@@ -25,10 +25,9 @@ exports.tag = function(source,operator,options) {
});
} else {
// Old semantics:
var tiddlers;
var tiddlers = options.wiki.getTiddlersWithTag(operator.operand);
if(operator.prefix === "!") {
// Returns a copy of the input if operator.operand is missing
tiddlers = options.wiki.getTiddlersWithTag(operator.operand);
source(function(tiddler,title) {
if(tiddlers.indexOf(title) === -1) {
results.push(title);
@@ -36,20 +35,12 @@ exports.tag = function(source,operator,options) {
});
} else {
// Returns empty results if operator.operand is missing
if(source.byTag) {
indexedResults = source.byTag(operator.operand);
if(indexedResults) {
return indexedResults;
source(function(tiddler,title) {
if(tiddlers.indexOf(title) !== -1) {
results.push(title);
}
} else {
tiddlers = options.wiki.getTiddlersWithTag(operator.operand);
source(function(tiddler,title) {
if(tiddlers.indexOf(title) !== -1) {
results.push(title);
}
});
results = options.wiki.sortByList(results,operator.operand);
}
});
results = options.wiki.sortByList(results,operator.operand);
}
}
return results;

View File

@@ -1,26 +0,0 @@
/*\
title: $:/core/modules/filters/then.js
type: application/javascript
module-type: filteroperator
Filter operator for replacing any titles with a constant
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.then = function(source,operator,options) {
var results = [];
source(function(tiddler,title) {
results.push(operator.operand);
});
return results;
};
})();

View File

@@ -1,26 +0,0 @@
/*\
title: $:/core/modules/filters/variables.js
type: application/javascript
module-type: filteroperator
Filter operator for returning the names of the active variables
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Export our filter function
*/
exports.variables = function(source,operator,options) {
var names = [];
for(var variable in options.widget.variables) {
names.push(variable);
}
return names.sort();
};
})();

View File

@@ -95,7 +95,7 @@ Extended filter operators to manipulate the current list.
exports.allafter = function (source, operator) {
var results = prepare_results(source),
index = results.indexOf(operator.operand);
return (index === -1) ? [] :
return (index === -1 || index > (results.length - 2)) ? [] :
(operator.suffix) ? results.slice(index) :
results.slice(index + 1);
};
@@ -106,7 +106,7 @@ Extended filter operators to manipulate the current list.
exports.allbefore = function (source, operator) {
var results = prepare_results(source),
index = results.indexOf(operator.operand);
return (index === -1) ? [] :
return (index < 0) ? [] :
(operator.suffix) ? results.slice(0, index + 1) :
results.slice(0, index);
};

View File

@@ -1,143 +0,0 @@
/*\
title: $:/core/modules/indexers/field-indexer.js
type: application/javascript
module-type: indexer
Indexes the tiddlers with each field value
\*/
(function(){
/*jslint node: true, browser: true */
/*global modules: false */
"use strict";
var DEFAULT_MAXIMUM_INDEXED_VALUE_LENGTH = 128;
function FieldIndexer(wiki) {
this.wiki = wiki;
}
FieldIndexer.prototype.init = function() {
this.index = null;
this.maxIndexedValueLength = DEFAULT_MAXIMUM_INDEXED_VALUE_LENGTH;
this.addIndexMethods();
}
// Provided for testing
FieldIndexer.prototype.setMaxIndexedValueLength = function(length) {
this.index = null;
this.maxIndexedValueLength = length;
};
FieldIndexer.prototype.addIndexMethods = function() {
var self = this;
this.wiki.each.byField = function(name,value) {
var titles = self.wiki.allTitles(),
lookup = self.lookup(name,value);
return lookup && lookup.filter(function(title) {
return titles.indexOf(title) !== -1;
});
};
this.wiki.eachShadow.byField = function(name,value) {
var titles = self.wiki.allShadowTitles(),
lookup = self.lookup(name,value);
return lookup && lookup.filter(function(title) {
return titles.indexOf(title) !== -1;
});
};
this.wiki.eachTiddlerPlusShadows.byField = function(name,value) {
var lookup = self.lookup(name,value);
return lookup ? lookup.slice(0) : null;
};
this.wiki.eachShadowPlusTiddlers.byField = function(name,value) {
var lookup = self.lookup(name,value);
return lookup ? lookup.slice(0) : null;
};
};
/*
Tear down and then rebuild the index as if all tiddlers have changed
*/
FieldIndexer.prototype.rebuild = function() {
// Invalidate the index so that it will be rebuilt when it is next used
this.index = null;
};
/*
Build the index for a particular field
*/
FieldIndexer.prototype.buildIndexForField = function(name) {
var self = this;
// Hashmap by field name of hashmap by field value of array of tiddler titles
this.index = this.index || Object.create(null);
this.index[name] = Object.create(null);
var baseIndex = this.index[name];
// Update the index for each tiddler
this.wiki.eachTiddlerPlusShadows(function(tiddler,title) {
if(name in tiddler.fields) {
var value = tiddler.getFieldString(name);
// Skip any values above the maximum length
if(value.length < self.maxIndexedValueLength) {
baseIndex[value] = baseIndex[value] || [];
baseIndex[value].push(title);
}
}
});
};
/*
Update the index in the light of a tiddler value changing; note that the title must be identical. (Renames are handled as a separate delete and create)
updateDescriptor: {old: {tiddler: <tiddler>, shadow: <boolean>, exists: <boolean>},new: {tiddler: <tiddler>, shadow: <boolean>, exists: <boolean>}}
*/
FieldIndexer.prototype.update = function(updateDescriptor) {
var self = this;
// Don't do anything if the index hasn't been built yet
if(this.index === null) {
return;
}
// Remove the old tiddler from the index
if(updateDescriptor.old.tiddler) {
$tw.utils.each(this.index,function(indexEntry,name) {
if(name in updateDescriptor.old.tiddler.fields) {
var value = updateDescriptor.old.tiddler.getFieldString(name),
tiddlerList = indexEntry[value];
if(tiddlerList) {
var index = tiddlerList.indexOf(updateDescriptor.old.tiddler.fields.title);
if(index !== -1) {
tiddlerList.splice(index,1);
}
}
}
});
}
// Add the new tiddler to the index
if(updateDescriptor["new"].tiddler) {
$tw.utils.each(this.index,function(indexEntry,name) {
if(name in updateDescriptor["new"].tiddler.fields) {
var value = updateDescriptor["new"].tiddler.getFieldString(name);
if(value.length < self.maxIndexedValueLength) {
indexEntry[value] = indexEntry[value] || [];
indexEntry[value].push(updateDescriptor["new"].tiddler.fields.title);
}
}
});
}
};
// Lookup the given field returning a list of tiddler titles
FieldIndexer.prototype.lookup = function(name,value) {
// Fail the lookup if the value is too long
if(value.length >= this.maxIndexedValueLength) {
return null;
}
// Update the index if it has yet to be built
if(this.index === null || !this.index[name]) {
this.buildIndexForField(name);
}
return this.index[name][value] || [];
};
exports.FieldIndexer = FieldIndexer;
})();

View File

@@ -1,98 +0,0 @@
/*\
title: $:/core/modules/indexers/tag-indexer.js
type: application/javascript
module-type: indexer
Indexes the tiddlers with each tag
\*/
(function(){
/*jslint node: true, browser: true */
/*global modules: false */
"use strict";
function TagIndexer(wiki) {
this.wiki = wiki;
}
TagIndexer.prototype.init = function() {
this.subIndexers = [
new TagSubIndexer(this,"each"),
new TagSubIndexer(this,"eachShadow"),
new TagSubIndexer(this,"eachTiddlerPlusShadows"),
new TagSubIndexer(this,"eachShadowPlusTiddlers")
];
$tw.utils.each(this.subIndexers,function(subIndexer) {
subIndexer.addIndexMethod();
});
};
TagIndexer.prototype.rebuild = function() {
$tw.utils.each(this.subIndexers,function(subIndexer) {
subIndexer.rebuild();
});
};
TagIndexer.prototype.update = function(updateDescriptor) {
$tw.utils.each(this.subIndexers,function(subIndexer) {
subIndexer.update(updateDescriptor);
});
};
function TagSubIndexer(indexer,iteratorMethod) {
this.indexer = indexer;
this.iteratorMethod = iteratorMethod;
this.index = null; // Hashmap of tag title to {isSorted: bool, titles: [array]} or null if not yet initialised
}
TagSubIndexer.prototype.addIndexMethod = function() {
var self = this;
this.indexer.wiki[this.iteratorMethod].byTag = function(tag) {
return self.lookup(tag).slice(0);
};
};
TagSubIndexer.prototype.rebuild = function() {
var self = this;
// Hashmap by tag of array of {isSorted:, titles:[]}
this.index = Object.create(null);
// Add all the tags
this.indexer.wiki[this.iteratorMethod](function(tiddler,title) {
$tw.utils.each(tiddler.fields.tags,function(tag) {
if(!self.index[tag]) {
self.index[tag] = {isSorted: false, titles: [title]};
} else {
self.index[tag].titles.push(title);
}
});
});
};
TagSubIndexer.prototype.update = function(updateDescriptor) {
this.index = null;
};
TagSubIndexer.prototype.lookup = function(tag) {
// Update the index if it has yet to be built
if(this.index === null) {
this.rebuild();
}
var indexRecord = this.index[tag];
if(indexRecord) {
if(!indexRecord.isSorted) {
if(this.indexer.wiki.sortByList) {
indexRecord.titles = this.indexer.wiki.sortByList(indexRecord.titles,tag);
}
indexRecord.isSorted = true;
}
return indexRecord.titles;
} else {
return [];
}
};
exports.TagIndexer = TagIndexer;
})();

View File

@@ -35,8 +35,6 @@ exports.getInfoTiddlerFields = function() {
// Screen size
infoTiddlerFields.push({title: "$:/info/browser/screen/width", text: window.screen.width.toString()});
infoTiddlerFields.push({title: "$:/info/browser/screen/height", text: window.screen.height.toString()});
// Language
infoTiddlerFields.push({title: "$:/info/browser/language", text: navigator.language || ""});
}
return infoTiddlerFields;
};

View File

@@ -138,17 +138,6 @@ function KeyboardManager(options) {
});
// Save the platform-specific name of the "meta" key
this.metaKeyName = $tw.platform.isMac ? "cmd-" : "win-";
this.shortcutKeysList = [], // Stores the shortcut-key descriptors
this.shortcutActionList = [], // Stores the corresponding action strings
this.shortcutParsedList = []; // Stores the parsed key descriptors
this.lookupNames = ["shortcuts"];
this.lookupNames.push($tw.platform.isMac ? "shortcuts-mac" : "shortcuts-not-mac")
this.lookupNames.push($tw.platform.isWindows ? "shortcuts-windows" : "shortcuts-not-windows");
this.lookupNames.push($tw.platform.isLinux ? "shortcuts-linux" : "shortcuts-not-linux");
this.updateShortcutLists(this.getShortcutTiddlerList());
$tw.wiki.addEventListener("change",function(changes) {
self.handleShortcutChanges(changes);
});
}
/*
@@ -240,9 +229,10 @@ KeyboardManager.prototype.parseKeyDescriptors = function(keyDescriptors,options)
result.push.apply(result,self.parseKeyDescriptors(keyDescriptors,options));
}
};
$tw.utils.each(self.lookupNames,function(platformDescriptor) {
lookupName(platformDescriptor);
});
lookupName("shortcuts");
lookupName($tw.platform.isMac ? "shortcuts-mac" : "shortcuts-not-mac");
lookupName($tw.platform.isWindows ? "shortcuts-windows" : "shortcuts-not-windows");
lookupName($tw.platform.isLinux ? "shortcuts-linux" : "shortcuts-not-linux");
}
} else {
result.push(self.parseKeyDescriptor(keyDescriptor));
@@ -284,70 +274,6 @@ KeyboardManager.prototype.checkKeyDescriptors = function(event,keyInfoArray) {
return false;
};
KeyboardManager.prototype.getShortcutTiddlerList = function() {
return $tw.wiki.getTiddlersWithTag("$:/tags/KeyboardShortcut");
};
KeyboardManager.prototype.updateShortcutLists = function(tiddlerList) {
this.shortcutTiddlers = tiddlerList;
for(var i=0; i<tiddlerList.length; i++) {
var title = tiddlerList[i],
tiddlerFields = $tw.wiki.getTiddler(title).fields;
this.shortcutKeysList[i] = tiddlerFields.key !== undefined ? tiddlerFields.key : undefined;
this.shortcutActionList[i] = tiddlerFields.text;
this.shortcutParsedList[i] = this.shortcutKeysList[i] !== undefined ? this.parseKeyDescriptors(this.shortcutKeysList[i]) : undefined;
}
};
KeyboardManager.prototype.handleKeydownEvent = function(event) {
var key, action;
for(var i=0; i<this.shortcutTiddlers.length; i++) {
if(this.shortcutParsedList[i] !== undefined && this.checkKeyDescriptors(event,this.shortcutParsedList[i])) {
key = this.shortcutParsedList[i];
action = this.shortcutActionList[i];
}
}
if(key !== undefined) {
event.preventDefault();
event.stopPropagation();
$tw.rootWidget.invokeActionString(action,$tw.rootWidget);
return true;
}
return false;
};
KeyboardManager.prototype.detectNewShortcuts = function(changedTiddlers) {
var shortcutConfigTiddlers = [],
handled = false;
$tw.utils.each(this.lookupNames,function(platformDescriptor) {
var descriptorString = "$:/config/" + platformDescriptor + "/";
Object.keys(changedTiddlers).forEach(function(configTiddler) {
var configString = configTiddler.substr(0, configTiddler.lastIndexOf("/") + 1);
if(configString === descriptorString) {
shortcutConfigTiddlers.push(configTiddler);
handled = true;
}
});
});
if(handled) {
return $tw.utils.hopArray(changedTiddlers,shortcutConfigTiddlers);
} else {
return false;
}
};
KeyboardManager.prototype.handleShortcutChanges = function(changedTiddlers) {
var newList = this.getShortcutTiddlerList();
var hasChanged = $tw.utils.hopArray(changedTiddlers,this.shortcutTiddlers) ? true :
($tw.utils.hopArray(changedTiddlers,newList) ? true :
(this.detectNewShortcuts(changedTiddlers))
);
// Re-cache shortcuts if something changed
if(hasChanged) {
this.updateShortcutLists(newList);
}
};
exports.KeyboardManager = KeyboardManager;
})();

View File

@@ -0,0 +1,41 @@
/*\
title: $:/core/modules/macros/dumpvariables.js
type: application/javascript
module-type: macro
Macro to dump all active variable values
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
*/
exports.name = "dumpvariables";
exports.params = [
];
/*
Run the macro
*/
exports.run = function() {
var output = ["|!Variable |!Value |"],
variables = [], variable;
for(variable in this.variables) {
variables.push(variable);
}
variables.sort();
for(var index=0; index<variables.length; index++) {
var variable = variables[index];
output.push("|" + variable + " |<input size=50 value=<<" + variable + ">>/> |")
}
return output.join("\n");
};
})();

View File

@@ -26,7 +26,19 @@ exports.params = [
Run the macro
*/
exports.run = function(filter) {
return this.wiki.getTiddlersAsJson(filter);
var tiddlers = this.wiki.filterTiddlers(filter),
data = [];
for(var t=0;t<tiddlers.length; t++) {
var tiddler = this.wiki.getTiddler(tiddlers[t]);
if(tiddler) {
var fields = new Object();
for(var field in tiddler.fields) {
fields[field] = tiddler.getFieldString(field);
}
data.push(fields);
}
}
return JSON.stringify(data,null,$tw.config.preferences.jsonSpaces);
};
})();

View File

@@ -1,34 +0,0 @@
/*\
title: $:/core/modules/macros/unusedtitle.js
type: application/javascript
module-type: macro
Macro to return a new title that is unused in the wiki. It can be given a name as a base.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
*/
exports.name = "unusedtitle";
exports.params = [
{name: "baseName"},
{name: "options"}
];
/*
Run the macro
*/
exports.run = function(baseName, options) {
if(!baseName) {
baseName = $tw.language.getString("DefaultNewTiddlerTitle");
}
return this.wiki.generateNewTitle(baseName, options);
};
})();

View File

@@ -1,29 +0,0 @@
/*\
title: $:/core/modules/parsers/binaryparser.js
type: application/javascript
module-type: parser
The video parser parses a video tiddler into an embeddable HTML element
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var BINARY_WARNING_MESSAGE = "$:/core/ui/BinaryWarning";
var BinaryParser = function(type,text,options) {
this.tree = [{
type: "transclude",
attributes: {
tiddler: {type: "string", value: BINARY_WARNING_MESSAGE}
}
}];
};
exports["application/octet-stream"] = BinaryParser;
})();

View File

@@ -35,9 +35,6 @@ exports["image/jpg"] = ImageParser;
exports["image/jpeg"] = ImageParser;
exports["image/png"] = ImageParser;
exports["image/gif"] = ImageParser;
exports["image/webp"] = ImageParser;
exports["image/heic"] = ImageParser;
exports["image/heif"] = ImageParser;
exports["image/x-icon"] = ImageParser;
})();

View File

@@ -1,53 +0,0 @@
/*\
title: $:/core/modules/parsers/wikiparser/rules/import.js
type: application/javascript
module-type: wikirule
Wiki pragma rule for importing variable definitions
```
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "import";
exports.types = {pragma: true};
/*
Instantiate parse rule
*/
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /^\\import[^\S\n]/mg;
};
/*
Parse the most recent match
*/
exports.parse = function() {
var self = this;
// Move past the pragma invocation
this.parser.pos = this.matchRegExp.lastIndex;
// Parse the filter terminated by a line break
var reMatch = /(.*)(\r?\n)|$/mg;
reMatch.lastIndex = this.parser.pos;
var match = reMatch.exec(this.parser.source);
this.parser.pos = reMatch.lastIndex;
// Parse tree nodes to return
return [{
type: "importvariables",
attributes: {
filter: {type: "string", value: match[1]}
},
children: []
}];
};
})();

View File

@@ -84,8 +84,7 @@ exports.parse = function() {
value: {type: "string", value: text}
},
children: [],
params: params,
isMacroDefinition: true
params: params
}];
};

View File

@@ -21,7 +21,6 @@ function SaverHandler(options) {
var self = this;
this.wiki = options.wiki;
this.dirtyTracking = options.dirtyTracking;
this.preloadDirty = options.preloadDirty || [];
this.pendingAutoSave = false;
// Make a logger
this.logger = new $tw.utils.Logger("saver-handler");
@@ -34,13 +33,7 @@ function SaverHandler(options) {
// Compile the dirty tiddler filter
this.filterFn = this.wiki.compileFilter(this.wiki.getTiddlerText(this.titleSyncFilter));
// Count of changes that have not yet been saved
var filteredChanges = self.filterFn.call(self.wiki,function(iterator) {
$tw.utils.each(self.preloadDirty,function(title) {
var tiddler = self.wiki.getTiddler(title);
iterator(tiddler,title);
});
});
this.numChanges = filteredChanges.length;
this.numChanges = 0;
// Listen out for changes to tiddlers
this.wiki.addEventListener("change",function(changes) {
// Filter the changes so that we only count changes to tiddlers that we care about
@@ -151,12 +144,8 @@ Save the wiki contents. Options are:
SaverHandler.prototype.saveWiki = function(options) {
options = options || {};
var self = this,
method = options.method || "save";
// Ignore autosave if disabled
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
return false;
}
var variables = options.variables || {},
method = options.method || "save",
variables = options.variables || {},
template = options.template || "$:/core/save/all",
downloadType = options.downloadType || "text/plain",
text = this.wiki.renderTiddler(downloadType,template,options),
@@ -175,6 +164,10 @@ SaverHandler.prototype.saveWiki = function(options) {
}
}
};
// Ignore autosave if disabled
if(method === "autosave" && this.wiki.getTiddlerText(this.titleAutoSave,"yes") !== "yes") {
return false;
}
// Call the highest priority saver that supports this method
for(var t=this.savers.length-1; t>=0; t--) {
var saver = this.savers[t];

View File

@@ -51,7 +51,7 @@ BeakerSaver.prototype.info = {
Static method that returns true if this saver is capable of working
*/
exports.canSave = function(wiki) {
return !!window.DatArchive && location.protocol==="dat:";
return !!window.DatArchive;
};
/*

View File

@@ -1,118 +0,0 @@
/*\
title: $:/core/modules/savers/github.js
type: application/javascript
module-type: saver
Saves wiki by pushing a commit to the GitHub v3 REST API
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Select the appropriate saver module and set it up
*/
var GitHubSaver = function(wiki) {
this.wiki = wiki;
};
GitHubSaver.prototype.save = function(text,method,callback) {
var self = this,
username = this.wiki.getTiddlerText("$:/GitHub/Username"),
password = $tw.utils.getPassword("github"),
repo = this.wiki.getTiddlerText("$:/GitHub/Repo"),
path = this.wiki.getTiddlerText("$:/GitHub/Path"),
filename = this.wiki.getTiddlerText("$:/GitHub/Filename"),
branch = this.wiki.getTiddlerText("$:/GitHub/Branch") || "master",
endpoint = this.wiki.getTiddlerText("$:/GitHub/ServerURL") || "https://api.github.com",
headers = {
"Accept": "application/vnd.github.v3+json",
"Content-Type": "application/json;charset=UTF-8",
"Authorization": "Basic " + window.btoa(username + ":" + password)
};
// Bail if we don't have everything we need
if(!username || !password || !repo || !path || !filename) {
return false;
}
// Make sure the path start and ends with a slash
if(path.substring(0,1) !== "/") {
path = "/" + path;
}
if(path.substring(path.length - 1) !== "/") {
path = path + "/";
}
// Compose the base URI
var uri = endpoint + "/repos/" + repo + "/contents" + path;
// Perform a get request to get the details (inc shas) of files in the same path as our file
$tw.utils.httpRequest({
url: uri,
type: "GET",
headers: headers,
data: {
ref: branch
},
callback: function(err,getResponseDataJson,xhr) {
var getResponseData,sha = "";
if(err && xhr.status !== 404) {
return callback(err);
}
if(xhr.status !== 404) {
getResponseData = JSON.parse(getResponseDataJson);
$tw.utils.each(getResponseData,function(details) {
if(details.name === filename) {
sha = details.sha;
}
});
}
var data = {
message: $tw.language.getRawString("ControlPanel/Saving/GitService/CommitMessage"),
content: $tw.utils.base64Encode(text),
branch: branch,
sha: sha
};
// Perform a PUT request to save the file
$tw.utils.httpRequest({
url: uri + filename,
type: "PUT",
headers: headers,
data: JSON.stringify(data),
callback: function(err,putResponseDataJson,xhr) {
if(err) {
return callback(err);
}
var putResponseData = JSON.parse(putResponseDataJson);
callback(null);
}
});
}
});
return true;
};
/*
Information about this saver
*/
GitHubSaver.prototype.info = {
name: "github",
priority: 2000,
capabilities: ["save", "autosave"]
};
/*
Static method that returns true if this saver is capable of working
*/
exports.canSave = function(wiki) {
return true;
};
/*
Create an instance of this saver
*/
exports.create = function(wiki) {
return new GitHubSaver(wiki);
};
})();

View File

@@ -1,120 +0,0 @@
/*\
title: $:/core/modules/savers/gitlab.js
type: application/javascript
module-type: saver
Saves wiki by pushing a commit to the GitLab REST API
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: true */
"use strict";
/*
Select the appropriate saver module and set it up
*/
var GitLabSaver = function(wiki) {
this.wiki = wiki;
};
GitLabSaver.prototype.save = function(text,method,callback) {
/* See https://docs.gitlab.com/ee/api/repository_files.html */
var self = this,
username = this.wiki.getTiddlerText("$:/GitLab/Username"),
password = $tw.utils.getPassword("gitlab"),
repo = this.wiki.getTiddlerText("$:/GitLab/Repo"),
path = this.wiki.getTiddlerText("$:/GitLab/Path"),
filename = this.wiki.getTiddlerText("$:/GitLab/Filename"),
branch = this.wiki.getTiddlerText("$:/GitLab/Branch") || "master",
endpoint = this.wiki.getTiddlerText("$:/GitLab/ServerURL") || "https://gitlab.com/api/v4",
headers = {
"Content-Type": "application/json;charset=UTF-8",
"Private-Token": password
};
// Bail if we don't have everything we need
if(!username || !password || !repo || !path || !filename) {
return false;
}
// Make sure the path start and ends with a slash
if(path.substring(0,1) !== "/") {
path = "/" + path;
}
if(path.substring(path.length - 1) !== "/") {
path = path + "/";
}
// Compose the base URI
var uri = endpoint + "/projects/" + encodeURIComponent(repo) + "/repository/";
// Perform a get request to get the details (inc shas) of files in the same path as our file
$tw.utils.httpRequest({
url: uri + "tree/" + encodeURIComponent(path.replace(/^\/+|\/$/g, '')),
type: "GET",
headers: headers,
data: {
ref: branch
},
callback: function(err,getResponseDataJson,xhr) {
var getResponseData,sha = "";
if(err && xhr.status !== 404) {
return callback(err);
}
var requestType = "POST";
if(xhr.status !== 404) {
getResponseData = JSON.parse(getResponseDataJson);
$tw.utils.each(getResponseData,function(details) {
if(details.name === filename) {
requestType = "PUT";
sha = details.sha;
}
});
}
var data = {
commit_message: $tw.language.getRawString("ControlPanel/Saving/GitService/CommitMessage"),
content: $tw.utils.base64Encode(text),
branch: branch,
sha: sha
};
// Perform a request to save the file
$tw.utils.httpRequest({
url: uri + "files/" + encodeURIComponent(path.replace(/^\/+/, '') + filename),
type: requestType,
headers: headers,
data: JSON.stringify(data),
callback: function(err,putResponseDataJson,xhr) {
if(err) {
return callback(err);
}
var putResponseData = JSON.parse(putResponseDataJson);
callback(null);
}
});
}
});
return true;
};
/*
Information about this saver
*/
GitLabSaver.prototype.info = {
name: "gitlab",
priority: 2000,
capabilities: ["save", "autosave"]
};
/*
Static method that returns true if this saver is capable of working
*/
exports.canSave = function(wiki) {
return true;
};
/*
Create an instance of this saver
*/
exports.create = function(wiki) {
return new GitLabSaver(wiki);
};
})();

View File

@@ -15,31 +15,6 @@ to the current URL, such as a WebDAV server.
/*global $tw: false */
"use strict";
/*
Retrieve ETag if available
*/
var retrieveETag = function(self) {
var headers = {
Accept: "*/*;charset=UTF-8"
};
$tw.utils.httpRequest({
url: self.uri(),
type: "HEAD",
headers: headers,
callback: function(err,data,xhr) {
if(err) {
return;
}
var etag = xhr.getResponseHeader("ETag");
if(!etag) {
return;
}
self.etag = etag.replace(/^W\//,"");
}
});
};
/*
Select the appropriate saver module and set it up
*/
@@ -52,14 +27,23 @@ var PutSaver = function(wiki) {
$tw.utils.httpRequest({
url: uri,
type: "OPTIONS",
callback: function(err,data,xhr) {
callback: function(err, data, xhr) {
// Check DAV header http://www.webdav.org/specs/rfc2518.html#rfc.section.9.1
if(!err) {
self.serverAcceptsPuts = xhr.status === 200 && !!xhr.getResponseHeader("dav");
}
}
});
retrieveETag(this);
// Retrieve ETag if available
$tw.utils.httpRequest({
url: uri,
type: "HEAD",
callback: function(err, data, xhr) {
if(!err) {
self.etag = xhr.getResponseHeader("ETag");
}
}
});
};
PutSaver.prototype.uri = function() {
@@ -69,14 +53,12 @@ PutSaver.prototype.uri = function() {
// TODO: in case of edit conflict
// Prompt: Do you want to save over this? Y/N
// Merging would be ideal, and may be possible using future generic merge flow
PutSaver.prototype.save = function(text,method,callback) {
PutSaver.prototype.save = function(text, method, callback) {
if(!this.serverAcceptsPuts) {
return false;
}
var self = this;
var headers = {
"Content-Type": "text/html;charset=UTF-8"
};
var headers = { "Content-Type": "text/html;charset=UTF-8" };
if(this.etag) {
headers["If-Match"] = this.etag;
}
@@ -85,22 +67,17 @@ PutSaver.prototype.save = function(text,method,callback) {
type: "PUT",
headers: headers,
data: text,
callback: function(err,data,xhr) {
callback: function(err, data, xhr) {
if(err) {
// response is textual: "XMLHttpRequest error code: 412"
var status = Number(err.substring(err.indexOf(':') + 2, err.length))
if(status === 412) { // edit conflict
var message = $tw.language.getString("Error/EditConflict");
callback(message);
} else {
callback(err); // fail
}
} else {
callback(err);
} else if(xhr.status === 200 || xhr.status === 201) {
self.etag = xhr.getResponseHeader("ETag");
if(self.etag == null) {
retrieveETag(self);
}
callback(null); // success
} else if(xhr.status === 412) { // edit conflict
var message = $tw.language.getString("Error/EditConflict");
callback(message);
} else {
callback(xhr.responseText); // fail
}
}
});
@@ -113,7 +90,7 @@ Information about this saver
PutSaver.prototype.info = {
name: "put",
priority: 2000,
capabilities: ["save","autosave"]
capabilities: ["save", "autosave"]
};
/*

View File

@@ -1,94 +0,0 @@
/*\
title: $:/core/modules/server/authenticators/basic.js
type: application/javascript
module-type: authenticator
Authenticator for WWW basic authentication
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
if($tw.node) {
var util = require("util"),
fs = require("fs"),
url = require("url"),
path = require("path");
}
function BasicAuthenticator(server) {
this.server = server;
this.credentialsData = [];
}
/*
Returns true if the authenticator is active, false if it is inactive, or a string if there is an error
*/
BasicAuthenticator.prototype.init = function() {
// Read the credentials data
this.credentialsFilepath = this.server.get("credentials");
if(this.credentialsFilepath) {
var resolveCredentialsFilepath = path.resolve($tw.boot.wikiPath,this.credentialsFilepath);
if(fs.existsSync(resolveCredentialsFilepath) && !fs.statSync(resolveCredentialsFilepath).isDirectory()) {
var credentialsText = fs.readFileSync(resolveCredentialsFilepath,"utf8"),
credentialsData = $tw.utils.parseCsvStringWithHeader(credentialsText);
if(typeof credentialsData === "string") {
return "Error: " + credentialsData + " reading credentials from '" + resolveCredentialsFilepath + "'";
} else {
this.credentialsData = credentialsData;
}
} else {
return "Error: Unable to load user credentials from '" + resolveCredentialsFilepath + "'";
}
}
// Add the hardcoded username and password if specified
if(this.server.get("username") && this.server.get("password")) {
this.credentialsData = this.credentialsData || [];
this.credentialsData.push({
username: this.server.get("username"),
password: this.server.get("password")
});
}
return this.credentialsData.length > 0;
};
/*
Returns true if the request is authenticated and assigns the "authenticatedUsername" state variable.
Returns false if the request couldn't be authenticated having sent an appropriate response to the browser
*/
BasicAuthenticator.prototype.authenticateRequest = function(request,response,state) {
// Extract the incoming username and password from the request
var header = request.headers.authorization || "";
if(!header && state.allowAnon) {
// If there's no header and anonymous access is allowed then we don't set authenticatedUsername
return true;
}
var token = header.split(/\s+/).pop() || "",
auth = $tw.utils.base64Decode(token),
parts = auth.split(/:/),
incomingUsername = parts[0],
incomingPassword = parts[1];
// Check that at least one of the credentials matches
var matchingCredentials = this.credentialsData.find(function(credential) {
return credential.username === incomingUsername && credential.password === incomingPassword;
});
if(matchingCredentials) {
// If so, add the authenticated username to the request state
state.authenticatedUsername = incomingUsername;
return true;
} else {
// If not, return an authentication challenge
response.writeHead(401,"Authentication required",{
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"'
});
response.end();
return false;
}
};
exports.AuthenticatorClass = BasicAuthenticator;
})();

View File

@@ -1,47 +0,0 @@
/*\
title: $:/core/modules/server/authenticators/header.js
type: application/javascript
module-type: authenticator
Authenticator for trusted header authentication
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
function HeaderAuthenticator(server) {
this.server = server;
this.header = server.get("authenticated-user-header");
}
/*
Returns true if the authenticator is active, false if it is inactive, or a string if there is an error
*/
HeaderAuthenticator.prototype.init = function() {
return !!this.header;
};
/*
Returns true if the request is authenticated and assigns the "authenticatedUsername" state variable.
Returns false if the request couldn't be authenticated having sent an appropriate response to the browser
*/
HeaderAuthenticator.prototype.authenticateRequest = function(request,response,state) {
// Otherwise, authenticate as the username in the specified header
var username = request.headers[this.header];
if(!username && !state.allowAnon) {
response.writeHead(401,"Authorization header required to login to '" + state.server.servername + "'");
response.end();
return false;
} else {
// authenticatedUsername will be undefined for anonymous users
state.authenticatedUsername = username;
return true;
}
};
exports.AuthenticatorClass = HeaderAuthenticator;
})();

View File

@@ -1,28 +0,0 @@
/*\
title: $:/core/modules/server/routes/delete-tiddler.js
type: application/javascript
module-type: route
DELETE /recipes/default/tiddlers/:title
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "DELETE";
exports.path = /^\/bags\/default\/tiddlers\/(.+)$/;
exports.handler = function(request,response,state) {
var title = decodeURIComponent(state.params[0]);
state.wiki.deleteTiddler(title);
response.writeHead(204, "OK", {
"Content-Type": "text/plain"
});
response.end();
};
}());

View File

@@ -1,25 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-favicon.js
type: application/javascript
module-type: route
GET /favicon.ico
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/favicon.ico$/;
exports.handler = function(request,response,state) {
response.writeHead(200, {"Content-Type": "image/x-icon"});
var buffer = state.wiki.getTiddlerText("$:/favicon.ico","");
response.end(buffer,"base64");
};
}());

View File

@@ -1,50 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-file.js
type: application/javascript
module-type: route
GET /files/:filepath
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/files\/(.+)$/;
exports.handler = function(request,response,state) {
var path = require("path"),
fs = require("fs"),
util = require("util");
var filename = path.resolve($tw.boot.wikiPath,"files",decodeURIComponent(state.params[0])),
extension = path.extname(filename);
fs.readFile(filename,function(err,content) {
var status,content,type = "text/plain";
if(err) {
if(err.code === "ENOENT") {
status = 404;
content = "File '" + filename + "' not found";
} else if(err.code === "EACCES") {
status = 403;
content = "You do not have permission to access the file '" + filename + "'";
} else {
status = 500;
content = err.toString();
}
} else {
status = 200;
content = content;
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
}
response.writeHead(status,{
"Content-Type": type
});
response.end(content);
});
};
}());

View File

@@ -1,51 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-index.js
type: application/javascript
module-type: route
GET /
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var zlib = require('zlib');
exports.method = "GET";
exports.path = /^\/$/;
exports.handler = function(request,response,state) {
var acceptEncoding = request.headers['accept-encoding'];
if (!acceptEncoding) { acceptEncoding = ''; }
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler"));
var responseHeaders = {
"Content-Type": state.server.get("root-serve-type")
};
/*
If the gzip=yes flag for `listen` is set, check if the user agent permits
compression. If so, compress our response. Note that we use the synchronous
functions from zlib to stay in the imperative style. The current `Server`
doesn't depend on this, and we may just as well use the async versions.
*/
if(state.server.enableGzip) {
if (/\bdeflate\b/.test(acceptEncoding)) {
responseHeaders['Content-Encoding'] = 'deflate';
text = zlib.deflateSync(text);
} else if (/\bgzip\b/.test(acceptEncoding)) {
responseHeaders['Content-Encoding'] = 'gzip';
text = zlib.gzipSync(text);
}
}
response.writeHead(200, responseHeaders);
response.end(text);
};
}());

View File

@@ -1,35 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-login-basic.js
type: application/javascript
module-type: route
GET /login-basic -- force a Basic Authentication challenge
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/login-basic$/;
exports.handler = function(request,response,state) {
if(!state.authenticatedUsername) {
// Challenge if there's no username
response.writeHead(401,{
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"'
});
response.end();
} else {
// Redirect to the root wiki if login worked
response.writeHead(302,{
Location: "/"
});
response.end();
}
};
}());

View File

@@ -1,33 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-status.js
type: application/javascript
module-type: route
GET /status
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/status$/;
exports.handler = function(request,response,state) {
response.writeHead(200, {"Content-Type": "application/json"});
var text = JSON.stringify({
username: state.authenticatedUsername || state.server.get("anon-username") || "",
anonymous: !state.authenticatedUsername,
read_only: !state.server.isAuthorized("writers",state.authenticatedUsername),
space: {
recipe: "default"
},
tiddlywiki_version: $tw.version
});
response.end(text,"utf8");
};
}());

View File

@@ -1,44 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-tiddler-html.js
type: application/javascript
module-type: route
GET /:title
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/([^\/]+)$/;
exports.handler = function(request,response,state) {
var title = decodeURIComponent(state.params[0]),
tiddler = state.wiki.getTiddler(title);
if(tiddler) {
var renderType = tiddler.getFieldString("_render_type"),
renderTemplate = tiddler.getFieldString("_render_template");
// Tiddler fields '_render_type' and '_render_template' overwrite
// system wide settings for render type and template
if(state.wiki.isSystemTiddler(title)) {
renderType = renderType || state.server.get("system-tiddler-render-type");
renderTemplate = renderTemplate || state.server.get("system-tiddler-render-template");
} else {
renderType = renderType || state.server.get("tiddler-render-type");
renderTemplate = renderTemplate || state.server.get("tiddler-render-template");
}
var text = state.wiki.renderTiddler(renderType,renderTemplate,{parseAsInline: true, variables: {currentTiddler: title}});
// Naughty not to set a content-type, but it's the easiest way to ensure the browser will see HTML pages as HTML, and accept plain text tiddlers as CSS or JS
response.writeHead(200);
response.end(text,"utf8");
} else {
response.writeHead(404);
response.end();
}
};
}());

View File

@@ -1,46 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-tiddler.js
type: application/javascript
module-type: route
GET /recipes/default/tiddlers/:title
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.handler = function(request,response,state) {
var title = decodeURIComponent(state.params[0]),
tiddler = state.wiki.getTiddler(title),
tiddlerFields = {},
knownFields = [
"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri"
];
if(tiddler) {
$tw.utils.each(tiddler.fields,function(field,name) {
var value = tiddler.getFieldString(name);
if(knownFields.indexOf(name) !== -1) {
tiddlerFields[name] = value;
} else {
tiddlerFields.fields = tiddlerFields.fields || {};
tiddlerFields.fields[name] = value;
}
});
tiddlerFields.revision = state.wiki.getChangeCount(title);
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
response.writeHead(200, {"Content-Type": "application/json"});
response.end(JSON.stringify(tiddlerFields),"utf8");
} else {
response.writeHead(404);
response.end();
}
};
}());

View File

@@ -1,37 +0,0 @@
/*\
title: $:/core/modules/server/routes/get-tiddlers-json.js
type: application/javascript
module-type: route
GET /recipes/default/tiddlers/tiddlers.json
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/recipes\/default\/tiddlers.json$/;
exports.handler = function(request,response,state) {
response.writeHead(200, {"Content-Type": "application/json"});
var tiddlers = [];
state.wiki.forEachTiddler({sortField: "title"},function(title,tiddler) {
var tiddlerFields = {};
$tw.utils.each(tiddler.fields,function(field,name) {
if(name !== "text") {
tiddlerFields[name] = tiddler.getFieldString(name);
}
});
tiddlerFields.revision = state.wiki.getChangeCount(title);
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
tiddlers.push(tiddlerFields);
});
var text = JSON.stringify(tiddlers);
response.end(text,"utf8");
};
}());

View File

@@ -1,42 +0,0 @@
/*\
title: $:/core/modules/server/routes/put-tiddler.js
type: application/javascript
module-type: route
PUT /recipes/default/tiddlers/:title
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "PUT";
exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.handler = function(request,response,state) {
var title = decodeURIComponent(state.params[0]),
fields = JSON.parse(state.data);
// Pull up any subfields in the `fields` object
if(fields.fields) {
$tw.utils.each(fields.fields,function(field,name) {
fields[name] = field;
});
delete fields.fields;
}
// Remove any revision field
if(fields.revision) {
delete fields.revision;
}
state.wiki.addTiddler(new $tw.Tiddler(state.wiki.getCreationFields(),fields,{title: title},state.wiki.getModificationFields()));
var changeCount = state.wiki.getChangeCount(title).toString();
response.writeHead(204, "OK",{
Etag: "\"default/" + encodeURIComponent(title) + "/" + changeCount + ":\"",
"Content-Type": "text/plain"
});
response.end();
};
}());

View File

@@ -1,265 +0,0 @@
/*\
title: $:/core/modules/server/server.js
type: application/javascript
module-type: library
Serve tiddlers over http
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
if($tw.node) {
var util = require("util"),
fs = require("fs"),
url = require("url"),
path = require("path");
}
/*
A simple HTTP server with regexp-based routes
options: variables - optional hashmap of variables to set (a misnomer - they are really constant parameters)
routes - optional array of routes to use
wiki - reference to wiki object
*/
function Server(options) {
var self = this;
this.routes = options.routes || [];
this.authenticators = options.authenticators || [];
this.wiki = options.wiki;
this.servername = $tw.utils.transliterateToSafeASCII(this.wiki.getTiddlerText("$:/SiteTitle") || "TiddlyWiki5");
// Initialise the variables
this.variables = $tw.utils.extend({},this.defaultVariables);
if(options.variables) {
for(var variable in options.variables) {
if(options.variables[variable]) {
this.variables[variable] = options.variables[variable];
}
}
}
$tw.utils.extend({},this.defaultVariables,options.variables);
// Initialise CSRF
this.csrfDisable = this.get("csrf-disable") === "yes";
// Initialize Gzip compression
this.enableGzip = this.get("gzip") === "yes";
// Initialise authorization
var authorizedUserName = (this.get("username") && this.get("password")) ? this.get("username") : "(anon)";
this.authorizationPrincipals = {
readers: (this.get("readers") || authorizedUserName).split(",").map($tw.utils.trim),
writers: (this.get("writers") || authorizedUserName).split(",").map($tw.utils.trim)
}
// Load and initialise authenticators
$tw.modules.forEachModuleOfType("authenticator", function(title,authenticatorDefinition) {
// console.log("Loading server route " + title);
self.addAuthenticator(authenticatorDefinition.AuthenticatorClass);
});
// Load route handlers
$tw.modules.forEachModuleOfType("route", function(title,routeDefinition) {
// console.log("Loading server route " + title);
self.addRoute(routeDefinition);
});
// Initialise the http vs https
this.listenOptions = null;
this.protocol = "http";
var tlsKeyFilepath = this.get("tls-key"),
tlsCertFilepath = this.get("tls-cert");
if(tlsCertFilepath && tlsKeyFilepath) {
this.listenOptions = {
key: fs.readFileSync(path.resolve($tw.boot.wikiPath,tlsKeyFilepath),"utf8"),
cert: fs.readFileSync(path.resolve($tw.boot.wikiPath,tlsCertFilepath),"utf8")
};
this.protocol = "https";
}
this.transport = require(this.protocol);
}
Server.prototype.defaultVariables = {
port: "8080",
host: "127.0.0.1",
"root-tiddler": "$:/core/save/all",
"root-render-type": "text/plain",
"root-serve-type": "text/html",
"tiddler-render-type": "text/html",
"tiddler-render-template": "$:/core/templates/server/static.tiddler.html",
"system-tiddler-render-type": "text/plain",
"system-tiddler-render-template": "$:/core/templates/wikified-tiddler",
"debug-level": "none",
"gzip": "no"
};
Server.prototype.get = function(name) {
return this.variables[name];
};
Server.prototype.addRoute = function(route) {
this.routes.push(route);
};
Server.prototype.addAuthenticator = function(AuthenticatorClass) {
// Instantiate and initialise the authenticator
var authenticator = new AuthenticatorClass(this),
result = authenticator.init();
if(typeof result === "string") {
$tw.utils.error("Error: " + result);
} else if(result) {
// Only use the authenticator if it initialised successfully
this.authenticators.push(authenticator);
}
};
Server.prototype.findMatchingRoute = function(request,state) {
var pathprefix = this.get("path-prefix") || "";
for(var t=0; t<this.routes.length; t++) {
var potentialRoute = this.routes[t],
pathRegExp = potentialRoute.path,
pathname = state.urlInfo.pathname,
match;
if(pathprefix) {
if(pathname.substr(0,pathprefix.length) === pathprefix) {
pathname = pathname.substr(pathprefix.length) || "/";
match = potentialRoute.path.exec(pathname);
} else {
match = false;
}
} else {
match = potentialRoute.path.exec(pathname);
}
if(match && request.method === potentialRoute.method) {
state.params = [];
for(var p=1; p<match.length; p++) {
state.params.push(match[p]);
}
return potentialRoute;
}
}
return null;
};
Server.prototype.methodMappings = {
"GET": "readers",
"OPTIONS": "readers",
"HEAD": "readers",
"PUT": "writers",
"POST": "writers",
"DELETE": "writers"
};
/*
Check whether a given user is authorized for the specified authorizationType ("readers" or "writers"). Pass null or undefined as the username to check for anonymous access
*/
Server.prototype.isAuthorized = function(authorizationType,username) {
var principals = this.authorizationPrincipals[authorizationType] || [];
return principals.indexOf("(anon)") !== -1 || (username && (principals.indexOf("(authenticated)") !== -1 || principals.indexOf(username) !== -1));
}
Server.prototype.requestHandler = function(request,response) {
// Compose the state object
var self = this;
var state = {};
state.wiki = self.wiki;
state.server = self;
state.urlInfo = url.parse(request.url);
// Get the principals authorized to access this resource
var authorizationType = this.methodMappings[request.method] || "readers";
// Check for the CSRF header if this is a write
if(!this.csrfDisable && authorizationType === "writers" && request.headers["x-requested-with"] !== "TiddlyWiki") {
response.writeHead(403,"'X-Requested-With' header required to login to '" + this.servername + "'");
response.end();
return;
}
// Check whether anonymous access is granted
state.allowAnon = this.isAuthorized(authorizationType,null);
// Authenticate with the first active authenticator
if(this.authenticators.length > 0) {
if(!this.authenticators[0].authenticateRequest(request,response,state)) {
// Bail if we failed (the authenticator will have sent the response)
return;
}
}
// Authorize with the authenticated username
if(!this.isAuthorized(authorizationType,state.authenticatedUsername)) {
response.writeHead(401,"'" + state.authenticatedUsername + "' is not authorized to access '" + this.servername + "'");
response.end();
return;
}
// Find the route that matches this path
var route = self.findMatchingRoute(request,state);
// Optionally output debug info
if(self.get("debug-level") !== "none") {
console.log("Request path:",JSON.stringify(state.urlInfo));
console.log("Request headers:",JSON.stringify(request.headers));
console.log("authenticatedUsername:",state.authenticatedUsername);
}
// Return a 404 if we didn't find a route
if(!route) {
response.writeHead(404);
response.end();
return;
}
// Receive the request body if necessary and hand off to the route handler
if(route.bodyFormat === "stream" || request.method === "GET" || request.method === "HEAD") {
// Let the route handle the request stream itself
route.handler(request,response,state);
} else if(route.bodyFormat === "string" || !route.bodyFormat) {
// Set the encoding for the incoming request
request.setEncoding("utf8");
var data = "";
request.on("data",function(chunk) {
data += chunk.toString();
});
request.on("end",function() {
state.data = data;
route.handler(request,response,state);
});
} else if(route.bodyFormat === "buffer") {
var data = [];
request.on("data",function(chunk) {
data.push(chunk);
});
request.on("end",function() {
state.data = Buffer.concat(data);
route.handler(request,response,state);
})
} else {
response.writeHead(400,"Invalid bodyFormat " + route.bodyFormat + " in route " + route.method + " " + route.path.source);
response.end();
}
};
/*
Listen for requests
port: optional port number (falls back to value of "port" variable)
host: optional host address (falls back to value of "host" variable)
prefix: optional prefix (falls back to value of "path-prefix" variable)
*/
Server.prototype.listen = function(port,host,prefix) {
// Handle defaults for port and host
port = port || this.get("port");
host = host || this.get("host");
prefix = prefix || this.get("path-prefix") || "";
// Check for the port being a string and look it up as an environment variable
if(parseInt(port,10).toString() !== port) {
port = process.env[port] || 8080;
}
$tw.utils.log("Serving on " + this.protocol + "://" + host + ":" + port + prefix,"brown/orange");
$tw.utils.log("(press ctrl-C to exit)","red");
// Warn if required plugins are missing
if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) {
$tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file");
}
// Listen
var server;
if(this.listenOptions) {
server = this.transport.createServer(this.listenOptions,this.requestHandler.bind(this));
} else {
server = this.transport.createServer(this.requestHandler.bind(this));
}
return server.listen(port,host);
};
exports.Server = Server;
})();

View File

@@ -144,9 +144,9 @@ exports.startup = function() {
});
// Listen for window messages from other windows
window.addEventListener("message",function listener(event){
// console.log("browser-messaging: ",document.location.toString())
// console.log("browser-messaging: Received message from",event.origin);
// console.log("browser-messaging: Message content",event.data);
console.log("browser-messaging: ",document.location.toString())
console.log("browser-messaging: Received message from",event.origin);
console.log("browser-messaging: Message content",event.data);
switch(event.data.verb) {
case "GET-RESPONSE":
if(event.data.status.charAt(0) === "2") {

View File

@@ -1,114 +0,0 @@
/*\
title: $:/core/modules/startup/CSSescape.js
type: application/javascript
module-type: startup
Polyfill for CSS.escape()
\*/
(function(root,factory){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
// Export name and synchronous status
exports.name = "css-escape";
exports.platforms = ["browser"];
exports.after = ["startup"];
exports.synchronous = true;
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
// https://github.com/umdjs/umd/blob/master/returnExports.js
exports.startup = factory(root);
}(typeof global != 'undefined' ? global : this, function(root) {
if (root.CSS && root.CSS.escape) {
return;
}
// https://drafts.csswg.org/cssom/#serialize-an-identifier
var cssEscape = function(value) {
if (arguments.length == 0) {
throw new TypeError('`CSS.escape` requires an argument.');
}
var string = String(value);
var length = string.length;
var index = -1;
var codeUnit;
var result = '';
var firstCodeUnit = string.charCodeAt(0);
while (++index < length) {
codeUnit = string.charCodeAt(index);
// Note: theres no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.
// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
// (U+FFFD).
if (codeUnit == 0x0000) {
result += '\uFFFD';
continue;
}
if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(
index == 1 &&
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
firstCodeUnit == 0x002D
)
) {
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' ';
continue;
}
if (
// If the character is the first character and is a `-` (U+002D), and
// there is no second character, […]
index == 0 &&
length == 1 &&
codeUnit == 0x002D
) {
result += '\\' + string.charAt(index);
continue;
}
// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit == 0x002D ||
codeUnit == 0x005F ||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
codeUnit >= 0x0061 && codeUnit <= 0x007A
) {
// the character itself
result += string.charAt(index);
continue;
}
// Otherwise, the escaped character.
// https://drafts.csswg.org/cssom/#escape-a-character
result += '\\' + string.charAt(index);
}
return result;
};
if (!root.CSS) {
root.CSS = {};
}
root.CSS.escape = cssEscape;
}));

View File

@@ -27,7 +27,6 @@ exports.startup = function() {
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
$tw.modules.applyMethods("tiddlermethod",$tw.Tiddler.prototype);
$tw.modules.applyMethods("wikimethod",$tw.Wiki.prototype);
$tw.wiki.addIndexersToWiki();
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
$tw.macros = $tw.modules.getModulesByTypeAsHashmap("macro");
$tw.wiki.initParsers();

View File

@@ -59,22 +59,14 @@ exports.startup = function() {
$tw.pageWidgetNode.render($tw.pageContainer,null);
$tw.hooks.invokeHook("th-page-refreshed");
})();
// Remove any splash screen elements
var removeList = document.querySelectorAll(".tc-remove-when-wiki-loaded");
$tw.utils.each(removeList,function(removeItem) {
if(removeItem.parentNode) {
removeItem.parentNode.removeChild(removeItem);
}
});
// Prepare refresh mechanism
var deferredChanges = Object.create(null),
timerId;
function refresh() {
// Process the refresh
$tw.hooks.invokeHook("th-page-refreshing");
$tw.pageWidgetNode.refresh(deferredChanges);
deferredChanges = Object.create(null);
$tw.hooks.invokeHook("th-page-refreshed");
$tw.hooks.invokeHook("th-page-refreshed");
}
// Add the change event handler
$tw.wiki.addEventListener("change",$tw.perf.report("mainRefresh",function(changes) {

Some files were not shown because too many files have changed in this diff Show More