1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-25 04:14:40 +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
553 changed files with 1988 additions and 19333 deletions

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

@@ -135,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) {
@@ -268,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 = {},
@@ -277,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;
}
@@ -1281,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;
@@ -1289,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) {
@@ -1985,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");
@@ -2002,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

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

@@ -126,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

@@ -1,33 +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")
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

@@ -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

@@ -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,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

@@ -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

@@ -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

@@ -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,
@@ -259,13 +247,6 @@ 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));
}
};
}
})());
});

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

@@ -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;
};

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,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

@@ -17,34 +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"),
regexp: hasFlag("regexp"),
words: hasFlag("words")
field: operator.suffix
});
} else {
return options.wiki.search(operator.operand,{

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

@@ -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

@@ -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,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

@@ -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

@@ -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 '" + credentialsFilepath + "'";
}
}
// 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,25 +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";
exports.method = "GET";
exports.path = /^\/$/;
exports.handler = function(request,response,state) {
response.writeHead(200, {"Content-Type": state.server.get("root-serve-type")});
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler"));
response.end(text,"utf8");
};
}());

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,260 +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";
// 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"
};
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 "hist" variable)
*/
Server.prototype.listen = function(port,host) {
// Handle defaults for port and host
port = port || this.get("port");
host = host || this.get("host");
// 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,"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

@@ -59,13 +59,6 @@ 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;

View File

@@ -23,7 +23,7 @@ exports.startup = function() {
// Install the modal message mechanism
$tw.modal = new $tw.utils.Modal($tw.wiki);
$tw.rootWidget.addEventListener("tm-modal",function(event) {
$tw.modal.display(event.param,{variables: event.paramObject, event: event});
$tw.modal.display(event.param,{variables: event.paramObject});
});
// Install the notification mechanism
$tw.notifier = new $tw.utils.Notifier($tw.wiki);
@@ -42,16 +42,10 @@ exports.startup = function() {
var fullscreen = $tw.utils.getFullScreenApis();
if(fullscreen) {
$tw.rootWidget.addEventListener("tm-full-screen",function(event) {
if(event.param === "enter") {
event.event.target.ownerDocument.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
} else if(event.param === "exit") {
event.event.target.ownerDocument[fullscreen._exitFullscreen]();
if(document[fullscreen._fullscreenElement]) {
document[fullscreen._exitFullscreen]();
} else {
if(event.event.target.ownerDocument[fullscreen._fullscreenElement]) {
event.event.target.ownerDocument[fullscreen._exitFullscreen]();
} else {
event.event.target.ownerDocument.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
}
document.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
}
});
}

View File

@@ -34,7 +34,7 @@ exports.startup = function() {
if($tw.browser) {
$tw.platform.isMac = /Mac/.test(navigator.platform);
$tw.platform.isWindows = /win/i.test(navigator.platform);
$tw.platform.isLinux = /Linux/i.test(navigator.platform);
$tw.platform.isLinux = /Linux/i.test(navigator.appVersion);
} else {
switch(require("os").platform()) {
case "darwin":
@@ -87,14 +87,6 @@ exports.startup = function() {
});
// Kick off the keyboard manager
$tw.keyboardManager = new $tw.KeyboardManager();
// Listen for shortcuts
if($tw.browser) {
$tw.utils.addEventListeners(document,[{
name: "keydown",
handlerObject: $tw.keyboardManager,
handlerMethod: "handleKeydownEvent"
}]);
}
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
$tw.rootWidget = new widget.widget({
type: "widget",

View File

@@ -27,12 +27,6 @@ var DEFAULT_TIDDLERS_TITLE = "$:/DefaultTiddlers";
// Config
var CONFIG_UPDATE_ADDRESS_BAR = "$:/config/Navigation/UpdateAddressBar"; // Can be "no", "permalink", "permaview"
var CONFIG_UPDATE_HISTORY = "$:/config/Navigation/UpdateHistory"; // Can be "yes" or "no"
var CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD = "$:/config/Navigation/Permalinkview/CopyToClipboard"; // Can be "yes" (default) or "no"
var CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR = "$:/config/Navigation/Permalinkview/UpdateAddressBar"; // Can be "yes" (default) or "no"
// Links to help, if there is no param
var HELP_OPEN_EXTERNAL_WINDOW = "http://tiddlywiki.com/#WidgetMessage%3A%20tm-open-external-window";
exports.startup = function() {
// Open startup tiddlers
@@ -59,14 +53,6 @@ exports.startup = function() {
$tw.rootWidget.addEventListener("tm-browser-refresh",function(event) {
window.location.reload(true);
});
// Listen for tm-open-external-window message
$tw.rootWidget.addEventListener("tm-open-external-window",function(event) {
var paramObject = event.paramObject || {},
strUrl = event.param || HELP_OPEN_EXTERNAL_WINDOW,
strWindowName = paramObject.windowName,
strWindowFeatures = paramObject.windowFeatures;
window.open(strUrl, strWindowName, strWindowFeatures);
});
// Listen for the tm-print message
$tw.rootWidget.addEventListener("tm-print",function(event) {
(event.event.view || window).print();
@@ -80,26 +66,24 @@ exports.startup = function() {
storyList = $tw.hooks.invokeHook("th-opening-default-tiddlers-list",storyList);
$tw.wiki.addTiddler({title: DEFAULT_STORY_TITLE, text: "", list: storyList},$tw.wiki.getModificationFields());
if(storyList[0]) {
$tw.wiki.addToHistory(storyList[0]);
$tw.wiki.addToHistory(storyList[0]);
}
});
// Listen for the tm-permalink message
$tw.rootWidget.addEventListener("tm-permalink",function(event) {
updateLocationHash({
updateAddressBar: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR,"yes").trim() === "yes" ? "permalink" : "none",
updateAddressBar: "permalink",
updateHistory: $tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim(),
targetTiddler: event.param || event.tiddlerTitle,
copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permalink" : "none"
targetTiddler: event.param || event.tiddlerTitle
});
});
// Listen for the tm-permaview message
$tw.rootWidget.addEventListener("tm-permaview",function(event) {
updateLocationHash({
updateAddressBar: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_UPDATE_ADDRESS_BAR,"yes").trim() === "yes" ? "permaview" : "none",
updateAddressBar: "permaview",
updateHistory: $tw.wiki.getTiddlerText(CONFIG_UPDATE_HISTORY,"no").trim(),
targetTiddler: event.param || event.tiddlerTitle,
copyToClipboard: $tw.wiki.getTiddlerText(CONFIG_PERMALINKVIEW_COPY_TO_CLIPBOARD,"yes").trim() === "yes" ? "permaview" : "none"
});
targetTiddler: event.param || event.tiddlerTitle
});
});
}
};
@@ -162,52 +146,41 @@ function openStartupTiddlers(options) {
options: See below
options.updateAddressBar: "permalink", "permaview" or "no" (defaults to "permaview")
options.updateHistory: "yes" or "no" (defaults to "no")
options.copyToClipboard: "permalink", "permaview" or "no" (defaults to "no")
options.targetTiddler: optional title of target tiddler for permalink
*/
function updateLocationHash(options) {
// Get the story and the history stack
var storyList = $tw.wiki.getTiddlerList(DEFAULT_STORY_TITLE),
historyList = $tw.wiki.getTiddlerData(DEFAULT_HISTORY_TITLE,[]),
targetTiddler = "";
if(options.targetTiddler) {
targetTiddler = options.targetTiddler;
} else {
// The target tiddler is the one at the top of the stack
if(historyList.length > 0) {
targetTiddler = historyList[historyList.length-1].title;
}
// Blank the target tiddler if it isn't present in the story
if(storyList.indexOf(targetTiddler) === -1) {
if(options.updateAddressBar !== "no") {
// Get the story and the history stack
var storyList = $tw.wiki.getTiddlerList(DEFAULT_STORY_TITLE),
historyList = $tw.wiki.getTiddlerData(DEFAULT_HISTORY_TITLE,[]),
targetTiddler = "";
}
}
// Assemble the location hash
switch(options.updateAddressBar) {
case "permalink":
$tw.locationHash = "#" + encodeURIComponent(targetTiddler);
break;
case "permaview":
$tw.locationHash = "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList));
break;
}
// Copy URL to the clipboard
switch(options.copyToClipboard) {
case "permalink":
$tw.utils.copyToClipboard($tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler));
break;
case "permaview":
$tw.utils.copyToClipboard($tw.utils.getLocationPath() + "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList)));
break;
}
// Only change the location hash if we must, thus avoiding unnecessary onhashchange events
if($tw.utils.getLocationHash() !== $tw.locationHash) {
if(options.updateHistory === "yes") {
// Assign the location hash so that history is updated
window.location.hash = $tw.locationHash;
if(options.targetTiddler) {
targetTiddler = options.targetTiddler;
} else {
// We use replace so that browser history isn't affected
window.location.replace(window.location.toString().split("#")[0] + $tw.locationHash);
// The target tiddler is the one at the top of the stack
if(historyList.length > 0) {
targetTiddler = historyList[historyList.length-1].title;
}
// Blank the target tiddler if it isn't present in the story
if(storyList.indexOf(targetTiddler) === -1) {
targetTiddler = "";
}
}
// Assemble the location hash
if(options.updateAddressBar === "permalink") {
$tw.locationHash = "#" + encodeURIComponent(targetTiddler);
} else {
$tw.locationHash = "#" + encodeURIComponent(targetTiddler) + ":" + encodeURIComponent($tw.utils.stringifyList(storyList));
}
// Only change the location hash if we must, thus avoiding unnecessary onhashchange events
if($tw.utils.getLocationHash() !== $tw.locationHash) {
if(options.updateHistory === "yes") {
// Assign the location hash so that history is updated
window.location.hash = $tw.locationHash;
} else {
// We use replace so that browser history isn't affected
window.location.replace(window.location.toString().split("#")[0] + $tw.locationHash);
}
}
}
}

View File

@@ -33,16 +33,8 @@ exports.startup = function() {
height = paramObject.height || "600",
variables = $tw.utils.extend({},paramObject,{currentTiddler: title});
// Open the window
var srcWindow,
srcDocument;
// In case that popup blockers deny opening a new window
try {
srcWindow = window.open("","external-" + title,"scrollbars,width=" + width + ",height=" + height),
var srcWindow = window.open("","external-" + title,"scrollbars,width=" + width + ",height=" + height),
srcDocument = srcWindow.document;
}
catch(e) {
return;
}
windows[title] = srcWindow;
// Check for reopening the same window
if(srcWindow.haveInitialisedWindow) {
@@ -78,16 +70,6 @@ exports.startup = function() {
widgetNode.refresh(changes);
};
$tw.wiki.addEventListener("change",refreshHandler);
// Listen for keyboard shortcuts
$tw.utils.addEventListeners(srcDocument,[{
name: "keydown",
handlerObject: $tw.keyboardManager,
handlerMethod: "handleKeydownEvent"
},{
name: "click",
handlerObject: $tw.popup,
handlerMethod: "handleEvent"
}]);
srcWindow.haveInitialisedWindow = true;
});
// Close open windows when unloading main window

View File

@@ -16,11 +16,8 @@ The syncer tracks changes to the store. If a syncadaptor is used then individual
Defaults
*/
Syncer.prototype.titleIsLoggedIn = "$:/status/IsLoggedIn";
Syncer.prototype.titleIsAnonymous = "$:/status/IsAnonymous";
Syncer.prototype.titleIsReadOnly = "$:/status/IsReadOnly";
Syncer.prototype.titleUserName = "$:/status/UserName";
Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter";
Syncer.prototype.titleSyncPollingInterval = "$:/config/SyncPollingInterval";
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
Syncer.prototype.taskTimerInterval = 1 * 1000; // Interval for sync timer
Syncer.prototype.throttleInterval = 1 * 1000; // Defer saving tiddlers if they've changed in the last 1s...
@@ -44,7 +41,7 @@ function Syncer(options) {
this.taskTimerInterval = options.taskTimerInterval || this.taskTimerInterval;
this.throttleInterval = options.throttleInterval || this.throttleInterval;
this.fallbackInterval = options.fallbackInterval || this.fallbackInterval;
this.pollTimerInterval = options.pollTimerInterval || parseInt(this.wiki.getTiddlerText(this.titleSyncPollingInterval,""),10) || this.pollTimerInterval;
this.pollTimerInterval = options.pollTimerInterval || this.pollTimerInterval;
this.logging = "logging" in options ? options.logging : true;
// Make a logger
this.logger = new $tw.utils.Logger("syncer" + ($tw.browser ? "-browser" : "") + ($tw.node ? "-server" : "") + (this.syncadaptor.name ? ("-" + this.syncadaptor.name) : ""),{
@@ -154,7 +151,7 @@ Save an incoming tiddler in the store, and updates the associated tiddlerInfo
*/
Syncer.prototype.storeTiddler = function(tiddlerFields,hasBeenLazyLoaded) {
// Save the tiddler
var tiddler = new $tw.Tiddler(tiddlerFields);
var tiddler = new $tw.Tiddler(this.wiki.getTiddler(tiddlerFields.title),tiddlerFields);
this.wiki.addTiddler(tiddler);
// Save the tiddler revision and changeCount details
this.tiddlerInfo[tiddlerFields.title] = {
@@ -172,17 +169,17 @@ Syncer.prototype.getStatus = function(callback) {
// Mark us as not logged in
this.wiki.addTiddler({title: this.titleIsLoggedIn,text: "no"});
// Get login status
this.syncadaptor.getStatus(function(err,isLoggedIn,username,isReadOnly,isAnonymous) {
this.syncadaptor.getStatus(function(err,isLoggedIn,username) {
if(err) {
self.logger.alert(err);
return;
}
// Set the various status tiddlers
self.wiki.addTiddler({title: self.titleIsReadOnly,text: isReadOnly ? "yes" : "no"});
self.wiki.addTiddler({title: self.titleIsAnonymous,text: isAnonymous ? "yes" : "no"});
self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"});
if(isLoggedIn) {
self.wiki.addTiddler({title: self.titleUserName,text: username || ""});
} else {
self.wiki.deleteTiddler(self.titleUserName);
}
// Invoke the callback
if(callback) {
@@ -274,16 +271,13 @@ Syncer.prototype.handleLazyLoadEvent = function(title) {
// Don't lazy load the same tiddler twice
var info = this.tiddlerInfo[title];
if(!info || !info.hasBeenLazyLoaded) {
// Don't lazy load if the tiddler isn't included in the sync filter
if(this.filterFn.call(this.wiki).indexOf(title) !== -1) {
this.createTiddlerInfo(title);
this.tiddlerInfo[title].hasBeenLazyLoaded = true;
// Queue up a sync task to load this tiddler
this.enqueueSyncTask({
type: "load",
title: title
});
}
this.createTiddlerInfo(title);
this.tiddlerInfo[title].hasBeenLazyLoaded = true;
// Queue up a sync task to load this tiddler
this.enqueueSyncTask({
type: "load",
title: title
});
}
};

View File

@@ -39,18 +39,6 @@ exports.getFieldString = function(field) {
}
};
/*
Get the value of a field as a list
*/
exports.getFieldList = function(field) {
var value = this.fields[field];
// Check for a missing field
if(value === undefined || value === null) {
return [];
}
return $tw.utils.parseStringArray(value);
};
/*
Get all the fields as a hashmap of strings. Options:
exclude: an array of field names to exclude

View File

@@ -1,46 +0,0 @@
/*\
title: $:/core/modules/utils/csv.js
type: application/javascript
module-type: utils
A barebones CSV parser
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Parse a CSV string with a header row and return an array of hashmaps.
*/
exports.parseCsvStringWithHeader = function(text,options) {
options = options || {};
var separator = options.separator || ",",
rows = text.split(/\r?\n/mg).map(function(row) {
return $tw.utils.trim(row);
}).filter(function(row) {
return row !== "";
});
if(rows.length < 1) {
return "Missing header row";
}
var headings = rows[0].split(separator),
results = [];
for(var row=1; row<rows.length; row++) {
var columns = rows[row].split(separator),
columnResult = Object.create(null);
if(columns.length !== headings.length) {
return "Malformed CSV row '" + rows[row] + "'";
}
for(var column=0; column<columns.length; column++) {
var columnName = headings[column];
columnResult[columnName] = $tw.utils.trim(columns[column] || "");
}
results.push(columnResult);
}
return results;
}
})();

View File

@@ -82,12 +82,11 @@ Returns:
y: vertical scroll position in pixels
}
*/
exports.getScrollPosition = function(srcWindow) {
var scrollWindow = srcWindow || window;
if("scrollX" in scrollWindow) {
return {x: scrollWindow.scrollX, y: scrollWindow.scrollY};
exports.getScrollPosition = function() {
if("scrollX" in window) {
return {x: window.scrollX, y: window.scrollY};
} else {
return {x: scrollWindow.document.documentElement.scrollLeft, y: scrollWindow.document.documentElement.scrollTop};
return {x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop};
}
};
@@ -120,7 +119,7 @@ exports.resizeTextAreaToFit = function(domNode,minHeight) {
Gets the bounding rectangle of an element in absolute page coordinates
*/
exports.getBoundingPageRect = function(element) {
var scrollPos = $tw.utils.getScrollPosition(element.ownerDocument.defaultView),
var scrollPos = $tw.utils.getScrollPosition(),
clientRect = element.getBoundingClientRect();
return {
left: clientRect.left + scrollPos.x,
@@ -264,9 +263,4 @@ exports.copyToClipboard = function(text,options) {
document.body.removeChild(textArea);
};
exports.getLocationPath = function() {
return window.location.toString().split("#")[0];
};
})();

View File

@@ -134,12 +134,6 @@ exports.makeDraggable = function(options) {
exports.importDataTransfer = function(dataTransfer,fallbackTitle,callback) {
// Try each provided data type in turn
if($tw.log.IMPORT) {
console.log("Available data types:");
for(var type=0; type<dataTransfer.types.length; type++) {
console.log("type",dataTransfer.types[type],dataTransfer.getData(dataTransfer.types[type]))
}
}
for(var t=0; t<importDataTypes.length; t++) {
if(!$tw.browser.isIE || importDataTypes[t].IECompatible) {
// Get the data

View File

@@ -15,7 +15,6 @@ Browser HTTP support
/*
A quick and dirty HTTP function; to be refactored later. Options are:
url: URL to retrieve
headers: hashmap of headers to send
type: GET, PUT, POST etc
callback: function invoked with (err,data)
returnProp: string name of the property to return as first argument of callback
@@ -61,9 +60,6 @@ exports.httpRequest = function(options) {
if(data && !$tw.utils.hop(headers,"Content-type")) {
request.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8");
}
if(!$tw.utils.hop(headers,"X-Requested-With")) {
request.setRequestHeader("X-Requested-With","TiddlyWiki");
}
try {
request.send(data);
} catch(e) {

View File

@@ -28,10 +28,6 @@ Options include:
*/
Modal.prototype.display = function(title,options) {
options = options || {};
this.srcDocument = options.variables && (options.variables.rootwindow === "true" ||
options.variables.rootwindow === "yes") ? document :
(options.event.event ? options.event.event.target.ownerDocument : document);
this.srcWindow = this.srcDocument.defaultView;
var self = this,
refreshHandler,
duration = $tw.utils.getAnimationDuration(),
@@ -43,16 +39,16 @@ Modal.prototype.display = function(title,options) {
// Create the variables
var variables = $tw.utils.extend({currentTiddler: title},options.variables);
// Create the wrapper divs
var wrapper = this.srcDocument.createElement("div"),
modalBackdrop = this.srcDocument.createElement("div"),
modalWrapper = this.srcDocument.createElement("div"),
modalHeader = this.srcDocument.createElement("div"),
headerTitle = this.srcDocument.createElement("h3"),
modalBody = this.srcDocument.createElement("div"),
modalLink = this.srcDocument.createElement("a"),
modalFooter = this.srcDocument.createElement("div"),
modalFooterHelp = this.srcDocument.createElement("span"),
modalFooterButtons = this.srcDocument.createElement("span");
var wrapper = document.createElement("div"),
modalBackdrop = document.createElement("div"),
modalWrapper = document.createElement("div"),
modalHeader = document.createElement("div"),
headerTitle = document.createElement("h3"),
modalBody = document.createElement("div"),
modalLink = document.createElement("a"),
modalFooter = document.createElement("div"),
modalFooterHelp = document.createElement("span"),
modalFooterButtons = document.createElement("span");
// Up the modal count and adjust the body class
this.modalCount++;
this.adjustPageClass();
@@ -84,7 +80,7 @@ Modal.prototype.display = function(title,options) {
value: title
}}}],
parentWidget: $tw.rootWidget,
document: this.srcDocument,
document: document,
variables: variables,
importPageMacros: true
});
@@ -92,7 +88,7 @@ Modal.prototype.display = function(title,options) {
// Render the body of the message
var bodyWidgetNode = this.wiki.makeTranscludeWidget(title,{
parentWidget: $tw.rootWidget,
document: this.srcDocument,
document: document,
variables: variables,
importPageMacros: true
});
@@ -100,16 +96,16 @@ Modal.prototype.display = function(title,options) {
// Setup the link if present
if(options.downloadLink) {
modalLink.href = options.downloadLink;
modalLink.appendChild(this.srcDocument.createTextNode("Right-click to save changes"));
modalLink.appendChild(document.createTextNode("Right-click to save changes"));
modalBody.appendChild(modalLink);
}
// Render the footer of the message
if(tiddler && tiddler.fields && tiddler.fields.help) {
var link = this.srcDocument.createElement("a");
var link = document.createElement("a");
link.setAttribute("href",tiddler.fields.help);
link.setAttribute("target","_blank");
link.setAttribute("rel","noopener noreferrer");
link.appendChild(this.srcDocument.createTextNode("Help"));
link.appendChild(document.createTextNode("Help"));
modalFooterHelp.appendChild(link);
modalFooterHelp.style.float = "left";
}
@@ -133,7 +129,7 @@ Modal.prototype.display = function(title,options) {
}}}
]}],
parentWidget: $tw.rootWidget,
document: this.srcDocument,
document: document,
variables: variables,
importPageMacros: true
});
@@ -159,13 +155,13 @@ Modal.prototype.display = function(title,options) {
{opacity: "0"}
]);
$tw.utils.setStyle(modalWrapper,[
{transform: "translateY(" + self.srcWindow.innerHeight + "px)"}
{transform: "translateY(" + window.innerHeight + "px)"}
]);
// Set up an event for the transition end
self.srcWindow.setTimeout(function() {
window.setTimeout(function() {
if(wrapper.parentNode) {
// Remove the modal message from the DOM
self.srcDocument.body.removeChild(wrapper);
document.body.removeChild(wrapper);
}
},duration);
// Don't let anyone else handle the tm-close-tiddler message
@@ -180,10 +176,10 @@ Modal.prototype.display = function(title,options) {
]);
$tw.utils.setStyle(modalWrapper,[
{transformOrigin: "0% 0%"},
{transform: "translateY(" + (-this.srcWindow.innerHeight) + "px)"}
{transform: "translateY(" + (-window.innerHeight) + "px)"}
]);
// Put the message into the document
this.srcDocument.body.appendChild(wrapper);
document.body.appendChild(wrapper);
// Set up animation for the styles
$tw.utils.setStyle(modalBackdrop,[
{transition: "opacity " + duration + "ms ease-out"}
@@ -204,9 +200,8 @@ Modal.prototype.display = function(title,options) {
};
Modal.prototype.adjustPageClass = function() {
var windowContainer = $tw.pageContainer ? ($tw.pageContainer === this.srcDocument.body.firstChild ? $tw.pageContainer : this.srcDocument.body.firstChild) : null;
if(windowContainer) {
$tw.utils.toggleClass(windowContainer,"tc-modal-displayed",this.modalCount > 0);
if($tw.pageContainer) {
$tw.utils.toggleClass($tw.pageContainer,"tc-modal-displayed",this.modalCount > 0);
}
};

View File

@@ -25,11 +25,9 @@ var Popup = function(options) {
/*
Trigger a popup open or closed. Parameters are in a hashmap:
title: title of the tiddler where the popup details are stored
domNode: dom node to which the popup will be positioned (one of domNode or domNodeRect is required)
domNodeRect: rectangle to which the popup will be positioned
domNode: dom node to which the popup will be positioned
wiki: wiki
force: if specified, forces the popup state to true or false (instead of toggling it)
floating: if true, skips registering the popup, meaning that it will need manually clearing
*/
Popup.prototype.triggerPopup = function(options) {
// Check if this popup is already active
@@ -114,9 +112,8 @@ Popup.prototype.show = function(options) {
var info = this.popupInfo(options.domNode);
// Cancel any higher level popups
this.cancel(info.popupLevel);
// Store the popup details if not already there
if(!options.floating && this.findPopup(options.title) === -1) {
if(this.findPopup(options.title) === -1) {
this.popups.push({
title: options.title,
wiki: options.wiki,
@@ -124,24 +121,9 @@ Popup.prototype.show = function(options) {
});
}
// Set the state tiddler
var rect;
if(options.domNodeRect) {
rect = options.domNodeRect;
} else {
rect = {
left: options.domNode.offsetLeft,
top: options.domNode.offsetTop,
width: options.domNode.offsetWidth,
height: options.domNode.offsetHeight
};
}
var popupRect = "(" + rect.left + "," + rect.top + "," +
rect.width + "," + rect.height + ")";
if(options.noStateReference) {
options.wiki.setText(options.title,"text",undefined,popupRect);
} else {
options.wiki.setTextReference(options.title,popupRect);
}
options.wiki.setTextReference(options.title,
"(" + options.domNode.offsetLeft + "," + options.domNode.offsetTop + "," +
options.domNode.offsetWidth + "," + options.domNode.offsetHeight + ")");
// Add the click handler if we have any popups
if(this.popups.length > 0) {
this.rootElement.addEventListener("click",this,true);

View File

@@ -33,9 +33,9 @@ var PageScroller = function() {
};
};
PageScroller.prototype.cancelScroll = function(srcWindow) {
PageScroller.prototype.cancelScroll = function() {
if(this.idRequestFrame) {
this.cancelAnimationFrame.call(srcWindow,this.idRequestFrame);
this.cancelAnimationFrame.call(window,this.idRequestFrame);
this.idRequestFrame = null;
}
};
@@ -53,26 +53,19 @@ PageScroller.prototype.handleEvent = function(event) {
/*
Handle a scroll event hitting the page document
*/
PageScroller.prototype.scrollIntoView = function(element,callback) {
PageScroller.prototype.scrollIntoView = function(element) {
var self = this,
duration = $tw.utils.getAnimationDuration(),
srcWindow = element ? element.ownerDocument.defaultView : window;
duration = $tw.utils.getAnimationDuration();
// Now get ready to scroll the body
this.cancelScroll(srcWindow);
this.cancelScroll();
this.startTime = Date.now();
// Get the height of any position:fixed toolbars
var toolbar = srcWindow.document.querySelector(".tc-adjust-top-of-scroll"),
offset = 0;
if(toolbar) {
offset = toolbar.offsetHeight;
}
// Get the client bounds of the element and adjust by the scroll position
var getBounds = function() {
var clientBounds = typeof callback === 'function' ? callback() : element.getBoundingClientRect(),
scrollPosition = $tw.utils.getScrollPosition(srcWindow);
var clientBounds = element.getBoundingClientRect(),
scrollPosition = $tw.utils.getScrollPosition();
return {
left: clientBounds.left + scrollPosition.x,
top: clientBounds.top + scrollPosition.y - offset,
top: clientBounds.top + scrollPosition.y,
width: clientBounds.width,
height: clientBounds.height
};
@@ -97,17 +90,17 @@ PageScroller.prototype.scrollIntoView = function(element,callback) {
t = ((Date.now()) - self.startTime) / duration;
}
if(t >= 1) {
self.cancelScroll(srcWindow);
self.cancelScroll();
t = 1;
}
t = $tw.utils.slowInSlowOut(t);
var scrollPosition = $tw.utils.getScrollPosition(srcWindow),
var scrollPosition = $tw.utils.getScrollPosition(),
bounds = getBounds(),
endX = getEndPos(bounds.left,bounds.width,scrollPosition.x,srcWindow.innerWidth),
endY = getEndPos(bounds.top,bounds.height,scrollPosition.y,srcWindow.innerHeight);
srcWindow.scrollTo(scrollPosition.x + (endX - scrollPosition.x) * t,scrollPosition.y + (endY - scrollPosition.y) * t);
endX = getEndPos(bounds.left,bounds.width,scrollPosition.x,window.innerWidth),
endY = getEndPos(bounds.top,bounds.height,scrollPosition.y,window.innerHeight);
window.scrollTo(scrollPosition.x + (endX - scrollPosition.x) * t,scrollPosition.y + (endY - scrollPosition.y) * t);
if(t < 1) {
self.idRequestFrame = self.requestAnimationFrame.call(srcWindow,drawFrame);
self.idRequestFrame = self.requestAnimationFrame.call(window,drawFrame);
}
};
drawFrame();

View File

@@ -57,7 +57,7 @@ var FILE_BUFFER_LENGTH = 64 * 1024,
exports.copyFile = function(srcPath,dstPath) {
// Create buffer if required
if(!fileBuffer) {
fileBuffer = Buffer.alloc(FILE_BUFFER_LENGTH);
fileBuffer = new Buffer(FILE_BUFFER_LENGTH);
}
// Create any directories in the destination
$tw.utils.createDirectory(path.dirname(dstPath));

View File

@@ -916,10 +916,4 @@ exports.transliterate = function(str) {
});
};
exports.transliterateToSafeASCII = function(str) {
return str.replace(/[^\x00-\x7F]/g,function(ch) {
return exports.transliterationPairs[ch] || ""
});
};
})();

View File

@@ -149,18 +149,6 @@ exports.isArrayEqual = function(array1,array2) {
});
};
/*
Determine whether an array-item is an object-property
*/
exports.hopArray = function(object,array) {
for(var i=0; i<array.length; i++) {
if($tw.utils.hop(object,array[i])) {
return true;
}
}
return false;
};
/*
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)
@@ -509,21 +497,15 @@ exports.htmlEncode = function(s) {
// Converts all HTML entities to their character equivalents
exports.entityDecode = function(s) {
var converter = String.fromCodePoint || String.fromCharCode,
e = s.substr(1,s.length-2), // Strip the & and the ;
c;
e = s.substr(1,s.length-2); // Strip the & and the ;
if(e.charAt(0) === "#") {
if(e.charAt(1) === "x" || e.charAt(1) === "X") {
c = parseInt(e.substr(2),16);
return converter(parseInt(e.substr(2),16));
} else {
c = parseInt(e.substr(1),10);
}
if(isNaN(c)) {
return s;
} else {
return converter(c);
return converter(parseInt(e.substr(1),10));
}
} else {
c = $tw.config.htmlEntities[e];
var c = $tw.config.htmlEntities[e];
if(c) {
return converter(c);
} else {
@@ -730,7 +712,7 @@ exports.base64Decode = function(string64) {
// TODO
throw "$tw.utils.base64Decode() doesn't work in the browser";
} else {
return Buffer.from(string64,"base64").toString();
return (new Buffer(string64,"base64")).toString();
}
};

View File

@@ -55,7 +55,6 @@ NavigateWidget.prototype.refresh = function(changedTiddlers) {
Invoke the action associated with this widget
*/
NavigateWidget.prototype.invokeAction = function(triggeringWidget,event) {
event = event || {};
var bounds = triggeringWidget && triggeringWidget.getBoundingClientRect && triggeringWidget.getBoundingClientRect(),
suppressNavigation = event.metaKey || event.ctrlKey || (event.button === 1);
if(this.actionScroll === "yes") {

View File

@@ -41,9 +41,9 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
var domNode = this.document.createElement(tag);
// Assign classes
var classes = this["class"].split(" ") || [],
isPoppedUp = (this.popup || this.popupTitle) && this.isPoppedUp();
isPoppedUp = this.popup && this.isPoppedUp();
if(this.selectedClass) {
if((this.set || this.setTitle) && this.setTo && this.isSelected()) {
if(this.set && this.setTo && this.isSelected()) {
$tw.utils.pushTop(classes,this.selectedClass.split(" "));
}
if(isPoppedUp) {
@@ -78,11 +78,11 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
self.dispatchMessage(event);
handled = true;
}
if(self.popup || self.popupTitle) {
if(self.popup) {
self.triggerPopup(event);
handled = true;
}
if(self.set || self.setTitle) {
if(self.set) {
self.setTiddler();
handled = true;
}
@@ -122,14 +122,11 @@ ButtonWidget.prototype.getBoundingClientRect = function() {
};
ButtonWidget.prototype.isSelected = function() {
return this.setTitle ? (this.setField ? this.wiki.getTiddler(this.setTitle).getFieldString(this.setField) === this.setTo :
(this.setIndex ? this.wiki.extractTiddlerDataItem(this.setTitle,this.setIndex) === this.setTo :
this.wiki.getTiddlerText(this.setTitle))) || this.defaultSetValue || this.getVariable("currentTiddler") :
this.wiki.getTextReference(this.set,this.defaultSetValue,this.getVariable("currentTiddler")) === this.setTo;
return this.wiki.getTextReference(this.set,this.defaultSetValue,this.getVariable("currentTiddler")) === this.setTo;
};
ButtonWidget.prototype.isPoppedUp = function() {
var tiddler = this.popupTitle ? this.wiki.getTiddler(this.popupTitle) : this.wiki.getTiddler(this.popup);
var tiddler = this.wiki.getTiddler(this.popup);
var result = tiddler && tiddler.fields.text ? $tw.popup.readPopupState(tiddler.fields.text) : false;
return result;
};
@@ -153,30 +150,15 @@ ButtonWidget.prototype.dispatchMessage = function(event) {
};
ButtonWidget.prototype.triggerPopup = function(event) {
if(this.popupTitle) {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
title: this.popupTitle,
wiki: this.wiki,
noStateReference: true
});
} else {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
title: this.popup,
wiki: this.wiki
});
}
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
title: this.popup,
wiki: this.wiki
});
};
ButtonWidget.prototype.setTiddler = function() {
if(this.setTitle) {
this.setField ? this.wiki.setText(this.setTitle,this.setField,undefined,this.setTo) :
(this.setIndex ? this.wiki.setText(this.setTitle,undefined,this.setIndex,this.setTo) :
this.wiki.setText(this.setTitle,"text",undefined,this.setTo));
} else {
this.wiki.setTextReference(this.set,this.setTo,this.getVariable("currentTiddler"));
}
this.wiki.setTextReference(this.set,this.setTo,this.getVariable("currentTiddler"));
};
/*
@@ -201,10 +183,6 @@ ButtonWidget.prototype.execute = function() {
this.buttonTag = this.getAttribute("tag");
this.dragTiddler = this.getAttribute("dragTiddler");
this.dragFilter = this.getAttribute("dragFilter");
this.setTitle = this.getAttribute("setTitle");
this.setField = this.getAttribute("setField");
this.setIndex = this.getAttribute("setIndex");
this.popupTitle = this.getAttribute("popupTitle");
// Make child widgets
this.makeChildWidgets();
};
@@ -214,7 +192,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
ButtonWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes["class"] || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedAttributes[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle) {
if(changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes["class"] || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup])) {
this.refreshSelf();
return true;
}

View File

@@ -145,7 +145,7 @@ DropZoneWidget.prototype.handlePasteEvent = function(event) {
self.dispatchEvent({type: "tm-import-tiddlers", param: JSON.stringify(tiddlerFieldsArray)});
};
// Let the browser handle it if we're in a textarea or input box
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1 && !event.target.isContentEditable) {
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1) {
var self = this,
items = event.clipboardData.items;
// Enumerate the clipboard items

View File

@@ -35,14 +35,6 @@ ElementWidget.prototype.render = function(parent,nextSibling) {
if($tw.config.htmlUnsafeElements.indexOf(tag) !== -1) {
tag = "safe-" + tag;
}
// Adjust headings by the current base level
var headingLevel = ["h1","h2","h3","h4","h5","h6"].indexOf(tag);
if(headingLevel !== -1) {
var baseLevel = parseInt(this.getVariable("tv-adjust-heading-level","0"),10) || 0;
headingLevel = Math.min(Math.max(headingLevel + 1 + baseLevel,1),6);
tag = "h" + headingLevel;
}
// Create the DOM node
var domNode = this.document.createElementNS(this.namespace,tag);
this.assignAttributes(domNode,{excludeEventAttributes: true});
parent.insertBefore(domNode,nextSibling);

View File

@@ -63,8 +63,7 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
addWidgetNode({
type: "set",
attributes: parseTreeNode.attributes,
params: parseTreeNode.params,
isMacroDefinition: parseTreeNode.isMacroDefinition
params: parseTreeNode.params
});
parseTreeNode = parseTreeNode.children[0];
}

View File

@@ -71,7 +71,6 @@ KeyboardWidget.prototype.dispatchMessage = function(event) {
Compute the internal state of the widget
*/
KeyboardWidget.prototype.execute = function() {
var self = this;
// Get attributes
this.actions = this.getAttribute("actions");
this.message = this.getAttribute("message");
@@ -80,13 +79,6 @@ KeyboardWidget.prototype.execute = function() {
this.tag = this.getAttribute("tag");
this.keyInfoArray = $tw.keyboardManager.parseKeyDescriptors(this.key);
this["class"] = this.getAttribute("class");
if(this.key.substr(0,2) === "((" && this.key.substr(-2,2) === "))") {
this.shortcutTiddlers = [];
var name = this.key.substring(2,this.key.length -2);
$tw.utils.each($tw.keyboardManager.lookupNames,function(platformDescriptor) {
self.shortcutTiddlers.push("$:/config/" + platformDescriptor + "/" + name);
});
}
// Make child widgets
this.makeChildWidgets();
};
@@ -100,10 +92,6 @@ KeyboardWidget.prototype.refresh = function(changedTiddlers) {
this.refreshSelf();
return true;
}
// Update the keyInfoArray if one of its shortcut-config-tiddlers has changed
if(this.shortcutTiddlers && $tw.utils.hopArray(changedTiddlers,this.shortcutTiddlers)) {
this.keyInfoArray = $tw.keyboardManager.parseKeyDescriptors(this.key);
}
return this.refreshChildren(changedTiddlers);
};

View File

@@ -13,6 +13,7 @@ Link widget
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var MISSING_LINK_CONFIG_TITLE = "$:/config/MissingLinks";
var LinkWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
@@ -181,7 +182,7 @@ LinkWidget.prototype.execute = function() {
// Determine the link characteristics
this.isMissing = !this.wiki.tiddlerExists(this.to);
this.isShadow = this.wiki.isShadowTiddler(this.to);
this.hideMissingLinks = (this.getVariable("tv-show-missing-links") || "yes") === "no";
this.hideMissingLinks = ($tw.wiki.getTiddlerText(MISSING_LINK_CONFIG_TITLE,"yes") === "no");
// Make the child widgets
this.makeChildWidgets();
};
@@ -191,7 +192,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
LinkWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.to || changedTiddlers[this.to] || changedAttributes["aria-label"] || changedAttributes.tooltip) {
if(changedAttributes.to || changedTiddlers[this.to] || changedAttributes["aria-label"] || changedAttributes.tooltip || changedTiddlers[MISSING_LINK_CONFIG_TITLE]) {
this.refreshSelf();
return true;
}

View File

@@ -116,7 +116,51 @@ NavigatorWidget.prototype.replaceFirstTitleInStory = function(storyList,oldTitle
};
NavigatorWidget.prototype.addToStory = function(title,fromTitle) {
this.wiki.addToStory(title,fromTitle,this.storyTitle,{openLinkFromInsideRiver: this.getAttribute("openLinkFromInsideRiver","top"),openLinkFromOutsideRiver: this.getAttribute("openLinkFromOutsideRiver","top")});
var storyList = this.getStoryList();
// Quit if we cannot get hold of the story list
if(!storyList) {
return;
}
// See if the tiddler is already there
var slot = storyList.indexOf(title);
// Quit if it already exists in the story river
if(slot >= 0) {
return;
}
// First we try to find the position of the story element we navigated from
var fromIndex = storyList.indexOf(fromTitle);
if(fromIndex >= 0) {
// The tiddler is added from inside the river
// Determine where to insert the tiddler; Fallback is "below"
switch(this.getAttribute("openLinkFromInsideRiver","below")) {
case "top":
slot = 0;
break;
case "bottom":
slot = storyList.length;
break;
case "above":
slot = fromIndex;
break;
case "below": // Intentional fall-through
default:
slot = fromIndex + 1;
break;
}
} else {
// The tiddler is opened from outside the river. Determine where to insert the tiddler; default is "top"
if(this.getAttribute("openLinkFromOutsideRiver","top") === "bottom") {
// Insert at bottom
slot = storyList.length;
} else {
// Insert at top
slot = 0;
}
}
// Add the tiddler
storyList.splice(slot,0,title);
// Save the story
this.saveStoryList(storyList);
};
/*
@@ -280,11 +324,9 @@ Generate a title for the draft of a given tiddler
*/
NavigatorWidget.prototype.generateDraftTitle = function(title) {
var c = 0,
draftTitle,
username = this.wiki.getTiddlerText("$:/status/UserName"),
attribution = username ? " by " + username : "";
draftTitle;
do {
draftTitle = "Draft " + (c ? (c + 1) + " " : "") + "of '" + title + "'" + attribution;
draftTitle = "Draft " + (c ? (c + 1) + " " : "") + "of '" + title + "'";
c++;
} while(this.wiki.tiddlerExists(draftTitle));
return draftTitle;
@@ -456,9 +498,9 @@ NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
},
templateTiddler,
additionalFields,
this.wiki.getCreationFields(),
existingTiddler,
filteredAdditionalFields,
this.wiki.getCreationFields(),
{
title: draftTitle,
"draft.of": title,
@@ -468,9 +510,6 @@ NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
// Update the story to insert the new draft at the top and remove any existing tiddler
if(storyList.indexOf(draftTitle) === -1) {
var slot = storyList.indexOf(event.navigateFromTitle);
if(slot === -1) {
slot = this.getAttribute("openLinkFromOutsideRiver","top") === "bottom" ? storyList.length - 1 : slot;
}
storyList.splice(slot + 1,0,draftTitle);
}
if(storyList.indexOf(title) !== -1) {

View File

@@ -1,66 +0,0 @@
/*\
title: $:/core/modules/widgets/qualify.js
type: application/javascript
module-type: widget
Qualify text to a variable
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var QualifyWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
QualifyWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
QualifyWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
QualifyWidget.prototype.execute = function() {
// Get our parameters
this.qualifyName = this.getAttribute("name");
this.qualifyTitle = this.getAttribute("title");
// Set context variable
if(this.qualifyName) {
this.setVariable(this.qualifyName,this.qualifyTitle + "-" + this.getStateQualifier());
}
// Construct the child widgets
this.makeChildWidgets();
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
QualifyWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.name || changedAttributes.title) {
this.refreshSelf();
return true;
} else {
return this.refreshChildren(changedTiddlers);
}
};
exports.qualify = QualifyWidget;
})();

View File

@@ -58,23 +58,23 @@ RevealWidget.prototype.positionPopup = function(domNode) {
domNode.style.zIndex = "1000";
switch(this.position) {
case "left":
domNode.style.left = Math.max(0, this.popup.left - domNode.offsetWidth) + "px";
domNode.style.left = (this.popup.left - domNode.offsetWidth) + "px";
domNode.style.top = this.popup.top + "px";
break;
case "above":
domNode.style.left = this.popup.left + "px";
domNode.style.top = Math.max(0, this.popup.top - domNode.offsetHeight) + "px";
domNode.style.top = (this.popup.top - domNode.offsetHeight) + "px";
break;
case "aboveright":
domNode.style.left = (this.popup.left + this.popup.width) + "px";
domNode.style.top = Math.max(0, this.popup.top + this.popup.height - domNode.offsetHeight) + "px";
domNode.style.top = (this.popup.top + this.popup.height - domNode.offsetHeight) + "px";
break;
case "right":
domNode.style.left = (this.popup.left + this.popup.width) + "px";
domNode.style.top = this.popup.top + "px";
break;
case "belowleft":
domNode.style.left = Math.max(0, this.popup.left + this.popup.width - domNode.offsetWidth) + "px";
domNode.style.left = (this.popup.left + this.popup.width - domNode.offsetWidth) + "px";
domNode.style.top = (this.popup.top + this.popup.height) + "px";
break;
default: // Below
@@ -102,10 +102,7 @@ RevealWidget.prototype.execute = function() {
this.openAnimation = this.animate === "no" ? undefined : "open";
this.closeAnimation = this.animate === "no" ? undefined : "close";
// Compute the title of the state tiddler and read it
this.stateTiddlerTitle = this.state;
this.stateTitle = this.getAttribute("stateTitle");
this.stateField = this.getAttribute("stateField");
this.stateIndex = this.getAttribute("stateIndex");
this.stateTitle = this.state;
this.readState();
// Construct the child widgets
var childNodes = this.isOpen ? this.parseTreeNode.children : [];
@@ -118,13 +115,7 @@ Read the state tiddler
*/
RevealWidget.prototype.readState = function() {
// Read the information from the state tiddler
var state = this.stateTitle ? (this.stateField ? this.wiki.getTiddler(this.stateTitle).getFieldString(this.stateField) :
(this.stateIndex ? this.wiki.extractTiddlerDataItem(this.stateTitle,this.stateIndex) :
this.wiki.getTiddlerText(this.stateTitle))) || this["default"] || this.getVariable("currentTiddler") :
(this.stateTiddlerTitle ? this.wiki.getTextReference(this.state,this["default"],this.getVariable("currentTiddler")) : this["default"]);
if(state === null) {
state = this["default"];
}
var state = this.stateTitle ? this.wiki.getTextReference(this.stateTitle,this["default"],this.getVariable("currentTiddler")) : this["default"];
switch(this.type) {
case "popup":
this.readPopupState(state);
@@ -179,21 +170,22 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
RevealWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.animate || changedAttributes.stateTitle || changedAttributes.stateField || changedAttributes.stateIndex) {
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.animate) {
this.refreshSelf();
return true;
} else {
var currentlyOpen = this.isOpen;
var refreshed = false,
currentlyOpen = this.isOpen;
this.readState();
if(this.isOpen !== currentlyOpen || (this.stateTiddlerTitle && changedTiddlers[this.stateTiddlerTitle])) {
if(this.isOpen !== currentlyOpen) {
if(this.retain === "yes") {
this.updateState();
} else {
this.refreshSelf();
return true;
refreshed = true;
}
}
return this.refreshChildren(changedTiddlers);
return this.refreshChildren(changedTiddlers) || refreshed;
}
};
@@ -201,7 +193,6 @@ RevealWidget.prototype.refresh = function(changedTiddlers) {
Called by refresh() to dynamically show or hide the content
*/
RevealWidget.prototype.updateState = function() {
var self = this;
// Read the current state
this.readState();
// Construct the child nodes if needed
@@ -222,12 +213,8 @@ RevealWidget.prototype.updateState = function() {
$tw.anim.perform(this.openAnimation,domNode);
} else {
$tw.anim.perform(this.closeAnimation,domNode,{callback: function() {
//make sure that the state hasn't changed during the close animation
self.readState()
if(!self.isOpen) {
domNode.setAttribute("hidden","true");
}
}});
domNode.setAttribute("hidden","true");
}});
}
};

View File

@@ -48,7 +48,7 @@ SetWidget.prototype.execute = function() {
this.setValue = this.getAttribute("value");
this.setEmptyValue = this.getAttribute("emptyValue");
// Set context variable
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,!!this.parseTreeNode.isMacroDefinition);
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params);
// Construct the child widgets
this.makeChildWidgets();
};

View File

@@ -71,10 +71,9 @@ Set the value of a context variable
name: name of the variable
value: value of the variable
params: array of {name:, default:} for each parameter
isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
*/
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition) {
this.variables[name] = {value: value, params: params, isMacroDefinition: !!isMacroDefinition};
Widget.prototype.setVariable = function(name,value,params) {
this.variables[name] = {value: value, params: params};
};
/*
@@ -103,10 +102,7 @@ Widget.prototype.getVariableInfo = function(name,options) {
$tw.utils.each(params,function(param) {
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
});
// Only substitute variable references if this variable was defined with the \define pragma
if(variable.isMacroDefinition) {
value = this.substituteVariableReferences(value);
}
value = this.substituteVariableReferences(value);
return {
text: value,
params: params

View File

@@ -538,45 +538,6 @@ exports.findListingsOfTiddler = function(targetTitle,fieldName) {
Sorts an array of tiddler titles according to an ordered list
*/
exports.sortByList = function(array,listTitle) {
var self = this,
replacedTitles = Object.create(null);
function replaceItem(title) {
if(!$tw.utils.hop(replacedTitles, title)) {
replacedTitles[title] = true;
var newPos = -1,
tiddler = self.getTiddler(title);
if(tiddler) {
var beforeTitle = tiddler.fields["list-before"],
afterTitle = tiddler.fields["list-after"];
if(beforeTitle === "") {
newPos = 0;
} else if(afterTitle === "") {
newPos = titles.length;
} else if(beforeTitle) {
replaceItem(beforeTitle);
newPos = titles.indexOf(beforeTitle);
} else if(afterTitle) {
replaceItem(afterTitle);
newPos = titles.indexOf(afterTitle);
if(newPos >= 0) {
++newPos;
}
}
// We get the currPos //after// figuring out the newPos, because recursive replaceItem calls might alter title's currPos
var currPos = titles.indexOf(title);
if(newPos === -1) {
newPos = currPos;
}
if(currPos >= 0 && newPos !== currPos) {
titles.splice(currPos,1);
if(newPos >= currPos) {
newPos--;
}
titles.splice(newPos,0,title);
}
}
}
}
var list = this.getTiddlerList(listTitle);
if(!array || array.length === 0) {
return [];
@@ -600,7 +561,36 @@ exports.sortByList = function(array,listTitle) {
var sortedTitles = titles.slice(0);
for(t=0; t<sortedTitles.length; t++) {
title = sortedTitles[t];
replaceItem(title);
var currPos = titles.indexOf(title),
newPos = -1,
tiddler = this.getTiddler(title);
if(tiddler) {
var beforeTitle = tiddler.fields["list-before"],
afterTitle = tiddler.fields["list-after"];
if(beforeTitle === "") {
newPos = 0;
} else if(afterTitle === "") {
newPos = titles.length;
} else if(beforeTitle) {
newPos = titles.indexOf(beforeTitle);
} else if(afterTitle) {
newPos = titles.indexOf(afterTitle);
if(newPos >= 0) {
++newPos;
}
}
if(newPos === -1) {
newPos = currPos;
}
if(newPos !== currPos) {
titles.splice(currPos,1);
if(newPos >= currPos) {
newPos--;
}
titles.splice(newPos,0,title);
}
}
}
return titles;
}
@@ -633,22 +623,6 @@ exports.getTiddlerAsJson = function(title) {
}
};
exports.getTiddlersAsJson = function(filter) {
var tiddlers = this.filterTiddlers(filter),
data = [];
for(var t=0;t<tiddlers.length; t++) {
var tiddler = this.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);
};
/*
Get the content of a tiddler as a JavaScript object. How this is done depends on the type of the tiddler:
@@ -811,14 +785,6 @@ exports.initParsers = function(moduleType) {
}
}
});
// Use the generic binary parser for any binary types not registered so far
if($tw.Wiki.parsers["application/octet-stream"]) {
Object.keys($tw.config.contentTypeInfo).forEach(function(type) {
if(!$tw.utils.hop($tw.Wiki.parsers,type) && $tw.config.contentTypeInfo[type].encoding === "base64") {
$tw.Wiki.parsers[type] = $tw.Wiki.parsers["application/octet-stream"];
}
});
}
};
/*
@@ -881,7 +847,7 @@ exports.parseTextReference = function(title,field,index,options) {
}
if(field === "text" || (!field && !index)) {
if(tiddler && tiddler.fields) {
return this.parseText(tiddler.fields.type,tiddler.fields.text,options);
return this.parseText(tiddler.fields.type || "text/vnd.tiddlywiki",tiddler.fields.text,options);
} else {
return null;
}
@@ -1047,13 +1013,8 @@ Options available:
exclude: An array of tiddler titles to exclude from the search
invert: If true returns tiddlers that do not contain the specified string
caseSensitive: If true forces a case sensitive search
field: If specified, restricts the search to the specified field, or an array of field names
excludeField: If true, the field options are inverted to specify the fields that are not to be searched
The search mode is determined by the first of these boolean flags to be true
literal: searches for literal string
whitespace: same as literal except runs of whitespace are treated as a single space
regexp: treats the search term as a regular expression
words: (default) treats search string as a list of tokens, and matches if all tokens are found, regardless of adjacency or ordering
literal: If true, searches for literal string, rather than separate search terms
field: If specified, restricts the search to the specified field
*/
exports.search = function(text,options) {
options = options || {};
@@ -1069,21 +1030,6 @@ exports.search = function(text,options) {
} else {
searchTermsRegExps = [new RegExp("(" + $tw.utils.escapeRegExp(text) + ")",flags)];
}
} else if(options.whitespace) {
terms = [];
$tw.utils.each(text.split(/\s+/g),function(term) {
if(term) {
terms.push($tw.utils.escapeRegExp(term));
}
});
searchTermsRegExps = [new RegExp("(" + terms.join("\\s+") + ")",flags)];
} else if(options.regexp) {
try {
searchTermsRegExps = [new RegExp("(" + text + ")",flags)];
} catch(e) {
searchTermsRegExps = null;
console.log("Regexp error parsing /(" + text + ")/" + flags + ": ",e);
}
} else {
terms = text.split(/ +/);
if(terms.length === 1 && terms[0] === "") {
@@ -1095,25 +1041,6 @@ exports.search = function(text,options) {
}
}
}
// Accumulate the array of fields to be searched or excluded from the search
var fields = [];
if(options.field) {
if($tw.utils.isArray(options.field)) {
$tw.utils.each(options.field,function(fieldName) {
if(fieldName) {
fields.push(fieldName);
}
});
} else {
fields.push(options.field);
}
}
// Use default fields if none specified and we're not excluding fields (excluding fields with an empty field array is the same as searching all fields)
if(fields.length === 0 && !options.excludeField) {
fields.push("title");
fields.push("tags");
fields.push("text");
}
// Function to check a given tiddler for the search term
var searchTiddler = function(title) {
if(!searchTermsRegExps) {
@@ -1124,63 +1051,24 @@ exports.search = function(text,options) {
tiddler = new $tw.Tiddler({title: title, text: "", type: "text/vnd.tiddlywiki"});
}
var contentTypeInfo = $tw.config.contentTypeInfo[tiddler.fields.type] || $tw.config.contentTypeInfo["text/vnd.tiddlywiki"],
searchFields;
// Get the list of fields we're searching
if(options.excludeField) {
searchFields = Object.keys(tiddler.fields);
$tw.utils.each(fields,function(fieldName) {
var p = searchFields.indexOf(fieldName);
if(p !== -1) {
searchFields.splice(p,1);
}
});
} else {
searchFields = fields;
}
for(var fieldIndex=0; fieldIndex<searchFields.length; fieldIndex++) {
// Don't search the text field if the content type is binary
var fieldName = searchFields[fieldIndex];
if(fieldName === "text" && contentTypeInfo.encoding !== "utf8") {
break;
}
var matches = true,
str = tiddler.fields[fieldName],
t;
if(str) {
if($tw.utils.isArray(str)) {
// If the field value is an array, test each regexp against each field array entry and fail if each regexp doesn't match at least one field array entry
for(t=0; t<searchTermsRegExps.length; t++) {
var thisRegExpMatches = false
for(var s=0; s<str.length; s++) {
if(searchTermsRegExps[t].test(str[s])) {
thisRegExpMatches = true;
break;
}
}
// Bail if the current search expression doesn't match any entry in the current field array
if(!thisRegExpMatches) {
matches = false;
break;
}
}
} else {
// If the field isn't an array, force it to a string and test each regexp against it and fail if any do not match
str = tiddler.getFieldString(fieldName);
for(t=0; t<searchTermsRegExps.length; t++) {
if(!searchTermsRegExps[t].test(str)) {
matches = false;
break;
}
}
}
match;
for(var t=0; t<searchTermsRegExps.length; t++) {
match = false;
if(options.field) {
match = searchTermsRegExps[t].test(tiddler.getFieldString(options.field));
} else {
matches = false;
// Search title, tags and body
if(contentTypeInfo.encoding === "utf8") {
match = match || searchTermsRegExps[t].test(tiddler.fields.text);
}
var tags = tiddler.fields.tags ? tiddler.fields.tags.join("\0") : "";
match = match || searchTermsRegExps[t].test(tags) || searchTermsRegExps[t].test(tiddler.fields.title);
}
if(matches) {
return true;
if(!match) {
return false;
}
};
return false;
}
return true;
};
// Loop through all the tiddlers doing the search
var results = [],
@@ -1258,7 +1146,7 @@ exports.readFiles = function(files,options) {
}
};
for(var f=0; f<files.length; f++) {
this.readFile(files[f],$tw.utils.extend({},options,{callback: readFileCallback}));
this.readFile(files[f],Object.assign({},options,{callback: readFileCallback}));
}
return files.length;
};
@@ -1381,18 +1269,6 @@ exports.addToHistory = function(title,fromPageRect,historyTitle) {
story.addToHistory(title,fromPageRect);
};
/*
Add a new tiddler to the story river
title: a title string or an array of title strings
fromTitle: the title of the tiddler from which the navigation originated
storyTitle: title of story tiddler (defaults to $:/StoryList)
options: see story.js
*/
exports.addToStory = function(title,fromTitle,storyTitle,options) {
var story = new $tw.Story({wiki: this, storyTitle: storyTitle});
story.addToStory(title,fromTitle,options);
};
/*
Invoke the available upgrader modules
titles: array of tiddler titles to be processed

View File

@@ -49,8 +49,6 @@ page-background: #ffffff
pre-background: #f5f5f5
pre-border: #cccccc
primary: #7897f3
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #ccc

View File

@@ -49,8 +49,6 @@ page-background: #ddddff
pre-background: #f5f5f5
pre-border: #cccccc
primary: #5778d8
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #ffffff

View File

@@ -49,8 +49,6 @@ page-background: #6f6f70
pre-background: #f5f5f5
pre-border: #cccccc
primary: #29a6ee
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #c2c1c2

View File

@@ -49,8 +49,6 @@ page-background: <<colour background>>
pre-background: <<colour background>>
pre-border: <<colour foreground>>
primary: #00f
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: <<colour background>>
sidebar-controls-foreground: <<colour foreground>>

View File

@@ -49,8 +49,6 @@ page-background: <<colour background>>
pre-background: <<colour background>>
pre-border: <<colour foreground>>
primary: #00f
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: <<colour background>>
sidebar-controls-foreground: <<colour foreground>>

View File

@@ -51,8 +51,6 @@ page-background: #336438
pre-background: #f5f5f5
pre-border: #cccccc
primary: #5778d8
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #ccf
sidebar-controls-foreground: #fff

View File

@@ -49,8 +49,6 @@ page-background: #000
pre-background: #f5f5f5
pre-border: #cccccc
primary: #cc0000
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #ffffff

View File

@@ -132,8 +132,6 @@ external-link-background: inherit
external-link-foreground-hover: inherit
message-border: #cfd6e6
modal-border: #999999
select-tag-background:
select-tag-foreground:
sidebar-controls-foreground-hover:
sidebar-muted-foreground-hover:
sidebar-tab-background: #ded8c5

View File

@@ -57,8 +57,6 @@ page-background: #f4f4f4
pre-background: #f5f5f5
pre-border: #cccccc
primary: #5778d8
select-tag-background:
select-tag-foreground:
sidebar-button-foreground: <<colour foreground>>
sidebar-controls-foreground-hover: #000000
sidebar-controls-foreground: #aaaaaa

View File

@@ -3,5 +3,6 @@ title: $:/core/templates/exporters/StaticRiver/Content
\define renderContent()
{{{ $(exportFilter)$ ||$:/core/templates/static-tiddler}}}
\end
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]">
<<renderContent>>
</$importvariables>

View File

@@ -6,5 +6,4 @@ extension: .tid
\define renderContent()
{{{ $(exportFilter)$ +[limit[1]] ||$:/core/templates/tid-tiddler}}}
\end
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
<<renderContent>>
<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"><<renderContent>></$importvariables>

View File

@@ -1,6 +0,0 @@
title: $:/core/save/all-external-js
\define saveTiddlerFilter()
[is[tiddler]] -[prefix[$:/state/popup/]] -[[$:/HistoryList]] -[[$:/core]] -[[$:/boot/boot.css]] -[type[application/javascript]library[yes]] -[[$:/boot/boot.js]] -[[$:/boot/bootprefix.js]] +[sort[title]] $(publishFilter)$
\end
{{$:/core/templates/tiddlywiki5-external-js.html}}

View File

@@ -1,15 +0,0 @@
title: $:/core/templates/tiddlywiki5.js
\rules only filteredtranscludeinline transcludeinline codeinline
/*
{{ $:/core/copyright.txt ||$:/core/templates/plain-text-tiddler}}
`*/
`<!--~~ Library modules ~~-->
{{{ [is[system]type[application/javascript]library[yes]] ||$:/core/templates/plain-text-tiddler}}}
<!--~~ Boot prefix ~~-->
{{ $:/boot/bootprefix.js ||$:/core/templates/plain-text-tiddler}}
<!--~~ Core plugin ~~-->
{{$:/core/templates/tiddlywiki5.js/tiddlers}}
<!--~~ Boot kernel ~~-->
{{ $:/boot/boot.js ||$:/core/templates/plain-text-tiddler}}

View File

@@ -1,9 +0,0 @@
title: $:/core/templates/tiddlywiki5.js/tiddlers
`
$tw.preloadTiddlerArray(`<$text text=<<jsontiddlers "[[$:/core]]">>/>`);
$tw.preloadTiddlerArray([{
title: "$:/config/SaveWikiButton/Template",
text: "$:/core/save/all-external-js"
}]);
`

View File

@@ -1,41 +0,0 @@
title: $:/core/templates/tiddlywiki5-external-js.html
\rules only filteredtranscludeinline transcludeinline
<!doctype html>
{{$:/core/templates/MOTW.html}}<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="application-name" content="TiddlyWiki" />
<meta name="generator" content="TiddlyWiki" />
<meta name="tiddlywiki-version" content="{{$:/core/templates/version}}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="mobile-web-app-capable" content="yes"/>
<meta name="format-detection" content="telephone=no" />
<meta name="copyright" content="{{$:/core/copyright.txt}}" />
<link id="faviconLink" rel="shortcut icon" href="favicon.ico">
<title>{{$:/core/wiki/title}}</title>
<!--~~ This is a Tiddlywiki file. The points of interest in the file are marked with this pattern ~~-->
<!--~~ Raw markup ~~-->
{{{ [all[shadows+tiddlers]tag[$:/core/wiki/rawmarkup]] [all[shadows+tiddlers]tag[$:/tags/RawMarkup]] ||$:/core/templates/plain-text-tiddler}}}
{{{ [all[shadows+tiddlers]tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}
</head>
<body class="tc-body">
<!--~~ Static styles ~~-->
<div id="styleArea">
{{$:/boot/boot.css||$:/core/templates/css-tiddler}}
</div>
<!--~~ Static content for Google and browsers without JavaScript ~~-->
<noscript>
<div id="splashArea">
{{$:/core/templates/static.area}}
</div>
</noscript>
<!--~~ Ordinary tiddlers ~~-->
{{$:/core/templates/store.area.template.html}}
</body>
<script src="%24%3A%2Fcore%2Ftemplates%2Ftiddlywiki5.js" onerror="alert('Error: Cannot load tiddlywiki.js');"></script>
</html>

View File

@@ -1,30 +0,0 @@
title: $:/core/templates/server/static.sidebar.wikitext
\whitespace trim
<div class="tc-sidebar-scrollable" style="overflow: auto;">
<div class="tc-sidebar-header">
<h1 class="tc-site-title">
<$transclude tiddler="$:/SiteTitle"/>
</h1>
<div class="tc-site-subtitle">
<$transclude tiddler="$:/SiteSubtitle"/>
</div>
<h2>
</h2>
<div class="tc-sidebar-lists">
<$list filter={{$:/DefaultTiddlers}}>
<div class="tc-menu-list-subitem">
<$link><$text text=<<currentTiddler>>/></$link>
</div>
</$list>
</div>
<!-- Currently disabled the recent list as it is unweildy when the responsive narrow view kicks in
<h2>
{{$:/language/SideBar/Recent/Caption}}
</h2>
<div class="tc-sidebar-lists">
<$macrocall $name="timeline" format={{$:/language/RecentChanges/DateFormat}}/>
</div>
</div>
</div>
-->

View File

@@ -1,28 +0,0 @@
title: $:/core/templates/server/static.tiddler.html
\whitespace trim
\define tv-wikilink-template() $uri_encoded$
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="generator" content="TiddlyWiki" />
<meta name="tiddlywiki-version" content={{$:/core/templates/version}} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="mobile-web-app-capable" content="yes"/>
<meta name="format-detection" content="telephone=no">
<link id="faviconLink" rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="%24%3A%2Fcore%2Ftemplates%2Fstatic.template.css">
<title><$view field="caption" format="plainwikified"><$view field="title"/></$view>: <$view tiddler="$:/core/wiki/title" format="plainwikified"/></title>
</head>
<body class="tc-body">
<$transclude tiddler="$:/core/templates/server/static.sidebar.wikitext" mode="inline"/>
<section class="tc-story-river">
<div class="tc-tiddler-frame">
<$transclude tiddler="$:/core/templates/server/static.tiddler.wikitext" mode="inline"/>
</div>
</section>
</body>
</html>

View File

@@ -1,23 +0,0 @@
title: $:/core/templates/server/static.tiddler.wikitext
\whitespace trim
<div class="tc-tiddler-title">
<div class="tc-titlebar">
<h2><$text text=<<currentTiddler>>/></h2>
</div>
</div>
<div class="tc-subtitle">
<$link to={{!!modifier}}>
<$view field="modifier"/>
</$link> <$view field="modified" format="date" template={{$:/language/Tiddler/DateFormat}}/>
</div>
<div class="tc-tags-wrapper">
<$list filter="[all[current]tags[]sort[title]]">
<a href={{{ [<currentTiddler>encodeuricomponent[]] }}}>
<$macrocall $name="tag-pill" tag=<<currentTiddler>>/>
</a>
</$list>
</div>
<div class="tc-tiddler-body">
<$transclude mode="block"/>
</div>

View File

@@ -4,7 +4,6 @@ title: $:/core/templates/static.tiddler.html
\define tv-config-toolbar-icons() no
\define tv-config-toolbar-text() no
\define tv-config-toolbar-class() tc-btn-invisible
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
`<!doctype html>
<html>
<head>
@@ -23,7 +22,9 @@ title: $:/core/templates/static.tiddler.html
<body class="tc-body">
`{{$:/StaticBanner||$:/core/templates/html-tiddler}}`
<section class="tc-story-river">
`<$view tiddler="$:/core/ui/ViewTemplate" format="htmlwikified"/>`
`<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]">
<$view tiddler="$:/core/ui/ViewTemplate" format="htmlwikified"/>
</$importvariables>`
</section>
</body>
</html>

View File

@@ -4,10 +4,8 @@ title: $:/core/templates/tiddlywiki5.html
<!doctype html>
{{$:/core/templates/MOTW.html}}<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!--~~ Raw markup for the top of the head section ~~-->
{{{ [all[shadows+tiddlers]tag[$:/tags/RawMarkupWikified/TopHead]] ||$:/core/templates/raw-static-tiddler}}}
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta name="application-name" content="TiddlyWiki" />
<meta name="generator" content="TiddlyWiki" />
<meta name="tiddlywiki-version" content="{{$:/core/templates/version}}" />
@@ -26,8 +24,6 @@ title: $:/core/templates/tiddlywiki5.html
{{{ [all[shadows+tiddlers]tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}
</head>
<body class="tc-body">
<!--~~ Raw markup for the top of the body section ~~-->
{{{ [all[shadows+tiddlers]tag[$:/tags/RawMarkupWikified/TopBody]] ||$:/core/templates/raw-static-tiddler}}}
<!--~~ Static styles ~~-->
<div id="styleArea">
{{$:/boot/boot.css||$:/core/templates/css-tiddler}}
@@ -52,7 +48,5 @@ title: $:/core/templates/tiddlywiki5.html
<div id="bootKernel" style="display:none;">
{{ $:/boot/boot.js ||$:/core/templates/javascript-tiddler}}
</div>
<!--~~ Raw markup for the bottom of the body section ~~-->
{{{ [all[shadows+tiddlers]tag[$:/tags/RawMarkupWikified/BottomBody]] ||$:/core/templates/raw-static-tiddler}}}
</body>
</html>

View File

@@ -1,10 +0,0 @@
title: $:/core/ui/Actions/new-image
tags: $:/tags/Actions
description: create a new image tiddler
\define get-type()
image/$(imageType)$
\end
<$vars imageType={{$:/config/NewImageType}}>
<$action-sendmessage $message="tm-new-tiddler" type=<<get-type>>/>
</$vars>

View File

@@ -1,14 +0,0 @@
title: $:/core/ui/Actions/new-journal
tags: $:/tags/Actions
description: create a new journal tiddler
<$vars journalTitleTemplate={{$:/config/NewJournal/Title}} journalTags={{$:/config/NewJournal/Tags}} journalText={{$:/config/NewJournal/Text}}>
<$wikify name="journalTitle" text="""<$macrocall $name="now" format=<<journalTitleTemplate>>/>""">
<$reveal type="nomatch" state=<<journalTitle>> text="">
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<journalTags>> text={{{ [<journalTitle>get[]] }}}/>
</$reveal>
<$reveal type="match" state=<<journalTitle>> text="">
<$action-sendmessage $message="tm-new-tiddler" title=<<journalTitle>> tags=<<journalTags>> text=<<journalText>>/>
</$reveal>
</$wikify>
</$vars>

View File

@@ -1,5 +0,0 @@
title: $:/core/ui/Actions/new-tiddler
tags: $:/tags/Actions
description: create a new empty tiddler
<$action-sendmessage $message="tm-new-tiddler"/>

View File

@@ -8,7 +8,6 @@ tags: $:/tags/AdvancedSearch/FilterButton
</span>
<$reveal state=<<qualify "$:/state/filterDropdown">> type="popup" position="belowleft" animate="yes">
<$set name="tv-show-missing-links" value="yes">
<$linkcatcher to="$:/temp/advancedsearch">
<div class="tc-block-dropdown-wrapper">
<div class="tc-block-dropdown tc-edit-type-dropdown">
@@ -17,5 +16,4 @@ tags: $:/tags/AdvancedSearch/FilterButton
</div>
</div>
</$linkcatcher>
</$set>
</$reveal>

View File

@@ -66,7 +66,7 @@ caption: {{$:/language/ControlPanel/KeyboardShortcuts/Caption}}
\define shortcut-list(caption,prefix)
<tr>
<$list filter="[[$prefix$$(shortcutName)$]]" variable="shortcutTitle">
<$list filter="[all[tiddlers+shadows][$prefix$$(shortcutName)$]]" variable="shortcutTitle">
<<shortcut-list-item "$caption$">>
</$list>
</tr>

View File

@@ -119,8 +119,10 @@ $:/state/add-plugin-info/$(connectionTiddler)$/$(assetInfo)$
</$list>
\end
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]">
<div>
<<plugin-library-listing>>
</div>
</$importvariables>

View File

@@ -1,10 +0,0 @@
title: $:/core/ui/ControlPanel/Settings/NavigationPermalinkviewMode
tags: $:/tags/ControlPanel/Settings
caption: {{$:/language/ControlPanel/Settings/NavigationPermalinkviewMode/Caption}}
\define lingo-base() $:/language/ControlPanel/Settings/NavigationPermalinkviewMode/
<<lingo Hint>>
<$checkbox tiddler="$:/config/Navigation/Permalinkview/CopyToClipboard" field="text" checked="yes" unchecked="no" default="yes"> <$link to="$:/config/Navigation/Permalinkview/CopyToClipboard"><<lingo CopyToClipboard/Description>></$link> </$checkbox>
<$checkbox tiddler="$:/config/Navigation/Permalinkview/UpdateAddressBar" field="text" checked="yes" unchecked="no" default="yes"> <$link to="$:/config/Navigation/Permalinkview/UpdateAddressBar"><<lingo UpdateAddressBar/Description>></$link> </$checkbox>

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