mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-02-17 07:29:49 +00:00
Compare commits
200 Commits
v5.1.23
...
word-games
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e26d0175eb | ||
|
|
652e8b1262 | ||
|
|
82ec63e711 | ||
|
|
51fd02d9b6 | ||
|
|
1a6d3e686b | ||
|
|
e694145eec | ||
|
|
3a740b23bb | ||
|
|
082aeb92ac | ||
|
|
903cfd98a6 | ||
|
|
eaf1da66b6 | ||
|
|
c6ed4aa84e | ||
|
|
123666c240 | ||
|
|
68930ceb1b | ||
|
|
fcea51bb95 | ||
|
|
8d7930f660 | ||
|
|
7a41283c6b | ||
|
|
c30ce544d1 | ||
|
|
dcba17fc5f | ||
|
|
2ab0474e14 | ||
|
|
61714cbda3 | ||
|
|
8fbf52e419 | ||
|
|
3fe5b77770 | ||
|
|
485779f5b2 | ||
|
|
3fc7895af2 | ||
|
|
bf25c4d34a | ||
|
|
10b20657cc | ||
|
|
0003d70132 | ||
|
|
81b5fe944a | ||
|
|
270ead4701 | ||
|
|
1ddc3ab037 | ||
|
|
05d38054c8 | ||
|
|
c7f6cedc43 | ||
|
|
ec1df7edf0 | ||
|
|
dbd3f835bf | ||
|
|
fe12a4adbf | ||
|
|
b267a71f2d | ||
|
|
e7b3f69162 | ||
|
|
f4d7b2c7f7 | ||
|
|
e699cf1fe8 | ||
|
|
d8ac00a108 | ||
|
|
cf56a17f28 | ||
|
|
3f98686153 | ||
|
|
cb44cc0f2b | ||
|
|
44df6fe52f | ||
|
|
cb34c695b5 | ||
|
|
07caa16e87 | ||
|
|
30d23196b6 | ||
|
|
bf773eb39a | ||
|
|
4a99e0cc7d | ||
|
|
e2379b599e | ||
|
|
8203ee06c3 | ||
|
|
ac15334bb0 | ||
|
|
8849ed0d46 | ||
|
|
ca1cf7bb41 | ||
|
|
80133895ba | ||
|
|
5e236d35a5 | ||
|
|
85ba7ac041 | ||
|
|
a725da2b39 | ||
|
|
7878e77e96 | ||
|
|
c325380231 | ||
|
|
a6a2535c3a | ||
|
|
caec6bc3fe | ||
|
|
ac022ec79f | ||
|
|
89546b3357 | ||
|
|
55173c17a3 | ||
|
|
7f3fed2f50 | ||
|
|
eced60853f | ||
|
|
b9647b2c48 | ||
|
|
953fb9f237 | ||
|
|
b90aad9cea | ||
|
|
e2d35751e2 | ||
|
|
fdf89f83c2 | ||
|
|
dce425ecb8 | ||
|
|
55735d7552 | ||
|
|
a8fe653e3c | ||
|
|
013218b852 | ||
|
|
c976aad5e0 | ||
|
|
28521d82f3 | ||
|
|
54d3782167 | ||
|
|
ef6307a64e | ||
|
|
3b35411aba | ||
|
|
d6ea369f5e | ||
|
|
55e44a9554 | ||
|
|
860568136f | ||
|
|
85835ebe42 | ||
|
|
3d608892bd | ||
|
|
5be647b610 | ||
|
|
a2e7cc51b5 | ||
|
|
c3955c3cf9 | ||
|
|
226df2ad7d | ||
|
|
8d763f7682 | ||
|
|
f5887d9e25 | ||
|
|
7f202f35b4 | ||
|
|
9c31ff1fb1 | ||
|
|
0beac47243 | ||
|
|
99bef2614c | ||
|
|
a44a8c31f0 | ||
|
|
8aad7b00ab | ||
|
|
a38dc17300 | ||
|
|
9af68297cd | ||
|
|
80ee5adb14 | ||
|
|
743d9c56c0 | ||
|
|
427eb6d085 | ||
|
|
715ce6b603 | ||
|
|
a6958bfe85 | ||
|
|
7b1a0c6e6a | ||
|
|
e157d16b72 | ||
|
|
05acf3dce4 | ||
|
|
37fd52e6c9 | ||
|
|
85646e5db3 | ||
|
|
9eda02868f | ||
|
|
8980927b54 | ||
|
|
c69a3e827a | ||
|
|
3c9ee052a3 | ||
|
|
81546c5bf4 | ||
|
|
5e4430dbf9 | ||
|
|
93f4b5dac9 | ||
|
|
b58e4236b7 | ||
|
|
60e40b5af9 | ||
|
|
6e93770459 | ||
|
|
d56e8764a1 | ||
|
|
e84c87ef37 | ||
|
|
ef76349c37 | ||
|
|
010fa140c7 | ||
|
|
625ea364c4 | ||
|
|
5ad1193eb6 | ||
|
|
0ed32fded9 | ||
|
|
50d0b6ee50 | ||
|
|
c0dc2669c0 | ||
|
|
40d21f607a | ||
|
|
bfa062f23d | ||
|
|
9f9ce6595b | ||
|
|
12f1847475 | ||
|
|
f2aba29d94 | ||
|
|
6a55069609 | ||
|
|
3eefb3cce6 | ||
|
|
2b41661721 | ||
|
|
a360adbba9 | ||
|
|
4c7dcb83d1 | ||
|
|
010158db81 | ||
|
|
45355a7fcf | ||
|
|
f77015ea18 | ||
|
|
8bab081c9e | ||
|
|
4667139864 | ||
|
|
2e47f277ac | ||
|
|
ea12994f47 | ||
|
|
b4605e3573 | ||
|
|
674d55db06 | ||
|
|
678ec7b3dd | ||
|
|
53ebfffedf | ||
|
|
1ec532ea50 | ||
|
|
3c3f1b60c6 | ||
|
|
ae273a08f1 | ||
|
|
36de5f65ff | ||
|
|
7be1e7e5f8 | ||
|
|
b205da2007 | ||
|
|
ffc8feea0c | ||
|
|
ce8c03250c | ||
|
|
82b7167d55 | ||
|
|
afa490a0c1 | ||
|
|
8344d13efb | ||
|
|
17b4f53ba2 | ||
|
|
a8457f7f9e | ||
|
|
ca95f1069f | ||
|
|
65ffe96cc2 | ||
|
|
b8a9826f23 | ||
|
|
65932a9b21 | ||
|
|
af897361c7 | ||
|
|
4858b24cfe | ||
|
|
5125b91b3f | ||
|
|
b632cea6b7 | ||
|
|
eadbd62e6d | ||
|
|
e280f89ca5 | ||
|
|
87b9dbcda1 | ||
|
|
5832002feb | ||
|
|
6a98106679 | ||
|
|
d5175e4fdc | ||
|
|
58010e089f | ||
|
|
6edcbfd0cd | ||
|
|
be70e5851d | ||
|
|
6f62c4fc7f | ||
|
|
4fe411be80 | ||
|
|
ac40ee4246 | ||
|
|
bbe94f3544 | ||
|
|
03626bc142 | ||
|
|
69e595abf9 | ||
|
|
e96a54c753 | ||
|
|
a8639c3129 | ||
|
|
3ae27cab9e | ||
|
|
9434e95396 | ||
|
|
fecf622616 | ||
|
|
d25e540dd2 | ||
|
|
4ee3ded04a | ||
|
|
87704b1770 | ||
|
|
493b45706f | ||
|
|
0ea89970d1 | ||
|
|
c2c8892aa5 | ||
|
|
7ec6d37031 | ||
|
|
24646e1993 | ||
|
|
1e1b52088f |
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -23,6 +23,11 @@ A clear and concise description of what you expected to happen.
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**TiddlyWiki Configuration (please complete the following information):**
|
||||
- Version [e.g. v5.1.24]
|
||||
- Saving mechanism [e.g. Node.js, TiddlyDesktop, TiddlyHost etc]
|
||||
- Plugins installed [e.g. Freelinks, TiddlyMap]
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Default to the current version number for building the plugin library
|
||||
|
||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||
TW5_BUILD_VERSION=v5.1.23
|
||||
TW5_BUILD_VERSION=v5.1.24
|
||||
fi
|
||||
|
||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||
@@ -107,7 +107,7 @@ node $TW5_BUILD_TIDDLYWIKI \
|
||||
# /empty.html Empty
|
||||
# /empty.hta For Internet Explorer
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
./editions/empty \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
--output $TW5_BUILD_OUTPUT \
|
||||
--build empty \
|
||||
|
||||
@@ -5,52 +5,52 @@ Optimise the SVGs in ./core/images using SVGO from https://github.com/svg/svgo
|
||||
|
||||
Install SVGO with the following command in the root of the repo:
|
||||
|
||||
npm install svgo
|
||||
npm install svgo@2.3.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var fs = require("fs"),
|
||||
path = require("path"),
|
||||
SVGO = require("svgo"),
|
||||
svgo = new SVGO({
|
||||
{ optimize } = require("svgo"),
|
||||
config = {
|
||||
plugins: [
|
||||
{cleanupAttrs: true},
|
||||
{removeDoctype: true},
|
||||
{removeXMLProcInst: true},
|
||||
{removeComments: true},
|
||||
{removeMetadata: true},
|
||||
{removeTitle: true},
|
||||
{removeDesc: true},
|
||||
{removeUselessDefs: true},
|
||||
{removeEditorsNSData: true},
|
||||
{removeEmptyAttrs: true},
|
||||
{removeHiddenElems: true},
|
||||
{removeEmptyText: true},
|
||||
{removeEmptyContainers: true},
|
||||
{removeViewBox: false},
|
||||
{cleanupEnableBackground: true},
|
||||
{convertStyleToAttrs: true},
|
||||
{convertColors: true},
|
||||
{convertPathData: true},
|
||||
{convertTransform: true},
|
||||
{removeUnknownsAndDefaults: true},
|
||||
{removeNonInheritableGroupAttrs: true},
|
||||
{removeUselessStrokeAndFill: true},
|
||||
{removeUnusedNS: true},
|
||||
{cleanupIDs: true},
|
||||
{cleanupNumericValues: true},
|
||||
{moveElemsAttrsToGroup: true},
|
||||
{moveGroupAttrsToElems: true},
|
||||
{collapseGroups: true},
|
||||
{removeRasterImages: false},
|
||||
{mergePaths: true},
|
||||
{convertShapeToPath: true},
|
||||
{sortAttrs: true},
|
||||
{removeDimensions: false},
|
||||
{removeAttrs: {attrs: "(stroke|fill)"}}
|
||||
'cleanupAttrs',
|
||||
'removeDoctype',
|
||||
'removeXMLProcInst',
|
||||
'removeComments',
|
||||
'removeMetadata',
|
||||
'removeTitle',
|
||||
'removeDesc',
|
||||
'removeUselessDefs',
|
||||
'removeEditorsNSData',
|
||||
'removeEmptyAttrs',
|
||||
'removeHiddenElems',
|
||||
'removeEmptyText',
|
||||
'removeEmptyContainers',
|
||||
// 'removeViewBox',
|
||||
'cleanupEnableBackground',
|
||||
'convertStyleToAttrs',
|
||||
'convertColors',
|
||||
'convertPathData',
|
||||
'convertTransform',
|
||||
'removeUnknownsAndDefaults',
|
||||
'removeNonInheritableGroupAttrs',
|
||||
'removeUselessStrokeAndFill',
|
||||
'removeUnusedNS',
|
||||
'cleanupIDs',
|
||||
'cleanupNumericValues',
|
||||
'moveElemsAttrsToGroup',
|
||||
'moveGroupAttrsToElems',
|
||||
'collapseGroups',
|
||||
// 'removeRasterImages',
|
||||
'mergePaths',
|
||||
'convertShapeToPath',
|
||||
'sortAttrs',
|
||||
//'removeDimensions',
|
||||
{name: 'removeAttrs', params: { attrs: '(stroke|fill)' } }
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
var basepath = "./core/images/",
|
||||
files = fs.readdirSync(basepath).sort();
|
||||
@@ -66,12 +66,14 @@ files.forEach(function(filename) {
|
||||
fakeSVG = body.join("\n");
|
||||
// A hack to make the new-journal-button work
|
||||
fakeSVG = fakeSVG.replace("<<now \"DD\">>","<<now "DD">>");
|
||||
svgo.optimize(fakeSVG, {path: filepath}).then(function(result) {
|
||||
config.path = filepath;
|
||||
var result = optimize(fakeSVG,config);
|
||||
if(result) {
|
||||
var newSVG = header.join("\n") + "\n\n" + result.data.replace("<<now "DD">>","<<now \"DD\">>");
|
||||
fs.writeFileSync(filepath,newSVG);
|
||||
},function(err) {
|
||||
} else {
|
||||
console.log("Error " + err + " with " + filename)
|
||||
process.exit();
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
50
boot/boot.js
50
boot/boot.js
@@ -892,6 +892,19 @@ $tw.modules.applyMethods = function(moduleType,targetObject) {
|
||||
return targetObject;
|
||||
};
|
||||
|
||||
/*
|
||||
Return a class created from a modules. The module should export the properties to be added to those of the optional base class
|
||||
*/
|
||||
$tw.modules.createClassFromModule = function(moduleExports,baseClass) {
|
||||
var newClass = function() {};
|
||||
if(baseClass) {
|
||||
newClass.prototype = new baseClass();
|
||||
newClass.prototype.constructor = baseClass;
|
||||
}
|
||||
$tw.utils.extend(newClass.prototype,moduleExports);
|
||||
return newClass;
|
||||
};
|
||||
|
||||
/*
|
||||
Return an array of classes created from the modules of a specified type. Each module should export the properties to be added to those of the optional base class
|
||||
*/
|
||||
@@ -899,13 +912,7 @@ $tw.modules.createClassesFromModules = function(moduleType,subType,baseClass) {
|
||||
var classes = Object.create(null);
|
||||
$tw.modules.forEachModuleOfType(moduleType,function(title,moduleExports) {
|
||||
if(!subType || moduleExports.types[subType]) {
|
||||
var newClass = function() {};
|
||||
if(baseClass) {
|
||||
newClass.prototype = new baseClass();
|
||||
newClass.prototype.constructor = baseClass;
|
||||
}
|
||||
$tw.utils.extend(newClass.prototype,moduleExports);
|
||||
classes[moduleExports.name] = newClass;
|
||||
classes[moduleExports.name] = $tw.modules.createClassFromModule(moduleExports,baseClass);
|
||||
}
|
||||
});
|
||||
return classes;
|
||||
@@ -1147,7 +1154,7 @@ $tw.Wiki = function(options) {
|
||||
var index = tiddlerTitles.indexOf(title);
|
||||
if(index !== -1) {
|
||||
tiddlerTitles.splice(index,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Record the new tiddler state
|
||||
updateDescriptor["new"] = {
|
||||
@@ -1294,7 +1301,7 @@ $tw.Wiki = function(options) {
|
||||
}
|
||||
} else {
|
||||
if(pluginInfo[title]) {
|
||||
delete pluginInfo[title];
|
||||
delete pluginInfo[title];
|
||||
results.deletedPlugins.push(title);
|
||||
}
|
||||
}
|
||||
@@ -1933,7 +1940,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
}
|
||||
} else {
|
||||
console.log("Warning: a directory in a tiddlywiki.files file does not exist.");
|
||||
console.log("dirPath: " + dirPath);
|
||||
console.log("dirPath: " + dirPath);
|
||||
console.log("tiddlywiki.files location: " + filepath);
|
||||
}
|
||||
}
|
||||
@@ -2126,6 +2133,7 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
|
||||
fileInfo = $tw.boot.files[title];
|
||||
if(fileInfo.isEditableFile) {
|
||||
relativePath = path.relative($tw.boot.wikiTiddlersPath,fileInfo.filepath);
|
||||
fileInfo.originalpath = relativePath;
|
||||
output[title] =
|
||||
path.sep === "/" ?
|
||||
relativePath :
|
||||
@@ -2191,7 +2199,7 @@ $tw.loadTiddlersNode = function() {
|
||||
type = parts[0];
|
||||
if(parts.length === 3 && ["plugins","themes","languages"].indexOf(type) !== -1) {
|
||||
$tw.loadPlugins([parts[1] + "/" + parts[2]],$tw.config[type + "Path"],$tw.config[type + "EnvVar"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Load the tiddlers from the wiki directory
|
||||
@@ -2301,6 +2309,7 @@ $tw.boot.initStartup = function(options) {
|
||||
$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/vnd.microsoft.icon","base64",".ico",{flags:["image"]});
|
||||
$tw.utils.registerFileType("image/x-icon","base64",".ico",{flags:["image"]});
|
||||
$tw.utils.registerFileType("application/font-woff","base64",".woff");
|
||||
$tw.utils.registerFileType("application/x-font-ttf","base64",".woff");
|
||||
@@ -2458,16 +2467,29 @@ $tw.boot.executeNextStartupTask = function(callback) {
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if we are running on one platforms specified in a task modules `platforms` array
|
||||
Returns true if we are running on one of the platforms specified in taskModule's
|
||||
`platforms` array; or if `platforms` property is not defined.
|
||||
*/
|
||||
$tw.boot.doesTaskMatchPlatform = function(taskModule) {
|
||||
var platforms = taskModule.platforms;
|
||||
if(platforms) {
|
||||
for(var t=0; t<platforms.length; t++) {
|
||||
if((platforms[t] === "browser" && !$tw.browser) || (platforms[t] === "node" && !$tw.node)) {
|
||||
return false;
|
||||
switch (platforms[t]) {
|
||||
case "browser":
|
||||
if ($tw.browser) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "node":
|
||||
if ($tw.node) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$tw.utils.error("Module " + taskModule.name + ": '" + platforms[t] + "' in export.platforms invalid");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@ type: text/plain
|
||||
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
|
||||
|
||||
Copyright (c) 2004-2007, Jeremy Ruston
|
||||
Copyright (c) 2007-2020, UnaMesa Association
|
||||
Copyright (c) 2007-2021, UnaMesa Association
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
4
core/images/minus-button.tid
Normal file
4
core/images/minus-button.tid
Normal file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/minus-button
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" class="tc-image-minus-button tc-image-button" viewBox="0 0 128 128"><path d="M64 0c35.346 0 64 28.654 64 64 0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64C0 28.654 28.654 0 64 0zm.332 16c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48z"/><rect width="80" height="16" x="24" y="56" rx="8"/></svg>
|
||||
@@ -1,4 +1,4 @@
|
||||
title: $:/core/images/plugin-generic-language
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 128 128"><path fill-rule="evenodd" d="M61.207 68.137c-4.324 2.795-6.999 6.656-6.999 10.921 0 7.906 9.19 14.424 21.042 15.336 2.162 3.902 8.598 6.785 16.318 7.01-5.126-1.125-9.117-3.742-10.62-7.01C92.805 93.487 102 86.967 102 79.059c0-8.53-10.699-15.445-23.896-15.445-6.599 0-12.572 1.729-16.897 4.524zm12.794-14.158c-4.324 2.795-10.298 4.524-16.897 4.524-2.619 0-5.14-.272-7.497-.775-3.312 2.25-8.383 3.69-14.067 3.69l-.255-.002c4.119-.892 7.511-2.747 9.478-5.13-6.925-2.704-11.555-7.617-11.555-13.228 0-8.53 10.699-15.445 23.896-15.445C70.301 27.613 81 34.528 81 43.058c0 4.265-2.675 8.126-6.999 10.921zM64 0l54.56 32v64L64 128 9.44 96V32L64 0z"/></svg>
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 128 128" class="tc-image-plugin-generic-language tc-image-button"><path fill-rule="evenodd" d="M61.207 68.137c-4.324 2.795-6.999 6.656-6.999 10.921 0 7.906 9.19 14.424 21.042 15.336 2.162 3.902 8.598 6.785 16.318 7.01-5.126-1.125-9.117-3.742-10.62-7.01C92.805 93.487 102 86.967 102 79.059c0-8.53-10.699-15.445-23.896-15.445-6.599 0-12.572 1.729-16.897 4.524zm12.794-14.158c-4.324 2.795-10.298 4.524-16.897 4.524-2.619 0-5.14-.272-7.497-.775-3.312 2.25-8.383 3.69-14.067 3.69l-.255-.002c4.119-.892 7.511-2.747 9.478-5.13-6.925-2.704-11.555-7.617-11.555-13.228 0-8.53 10.699-15.445 23.896-15.445C70.301 27.613 81 34.528 81 43.058c0 4.265-2.675 8.126-6.999 10.921zM64 0l54.56 32v64L64 128 9.44 96V32L64 0z"/></svg>
|
||||
@@ -1,4 +1,4 @@
|
||||
title: $:/core/images/plugin-generic-plugin
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 128 128"><path fill-rule="evenodd" d="M40.397 76.446V95.34h14.12l-.001-.005a6.912 6.912 0 005.364-11.593l.046-.023a6.912 6.912 0 119.979.526l.086.055a6.914 6.914 0 004.408 10.948l-.023.092h21.32V75.568l-.15.038a6.912 6.912 0 00-11.593-5.364l-.022-.046a6.912 6.912 0 11.526-9.979l.055-.086a6.914 6.914 0 0010.948-4.408c.079.018.158.038.236.059v-15.74h-21.32l.023-.094a6.914 6.914 0 01-4.408-10.947 10.23 10.23 0 00-.086-.055 6.912 6.912 0 10-9.979-.526l-.046.023a6.912 6.912 0 01-5.364 11.593l.001.005h-14.12v12.847A6.912 6.912 0 0129.5 59.843l-.054.086a6.912 6.912 0 10-.526 9.979l.023.046a6.912 6.912 0 0111.455 6.492zM64 0l54.56 32v64L64 128 9.44 96V32L64 0z"/></svg>
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 128 128" class="tc-image-plugin-generic-plugin tc-image-button"><path fill-rule="evenodd" d="M40.397 76.446V95.34h14.12l-.001-.005a6.912 6.912 0 005.364-11.593l.046-.023a6.912 6.912 0 119.979.526l.086.055a6.914 6.914 0 004.408 10.948l-.023.092h21.32V75.568l-.15.038a6.912 6.912 0 00-11.593-5.364l-.022-.046a6.912 6.912 0 11.526-9.979l.055-.086a6.914 6.914 0 0010.948-4.408c.079.018.158.038.236.059v-15.74h-21.32l.023-.094a6.914 6.914 0 01-4.408-10.947 10.23 10.23 0 00-.086-.055 6.912 6.912 0 10-9.979-.526l-.046.023a6.912 6.912 0 01-5.364 11.593l.001.005h-14.12v12.847A6.912 6.912 0 0129.5 59.843l-.054.086a6.912 6.912 0 10-.526 9.979l.023.046a6.912 6.912 0 0111.455 6.492zM64 0l54.56 32v64L64 128 9.44 96V32L64 0z"/></svg>
|
||||
@@ -1,4 +1,4 @@
|
||||
title: $:/core/images/plugin-generic-theme
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 128 128"><path fill-rule="evenodd" d="M29.408 91.472L51.469 69.41l-.004-.005a2.22 2.22 0 01.004-3.146c.87-.87 2.281-.872 3.147-.005l9.465 9.464a2.22 2.22 0 01-.005 3.147c-.87.87-2.28.871-3.147.005l-.005-.005-22.061 22.062a6.686 6.686 0 11-9.455-9.455zM60.802 66.38c-2.436-2.704-4.465-5.091-5.817-6.869-6.855-9.014-10.313-4.268-14.226 0-3.913 4.268 1.03 7.726-2.683 10.741-3.713 3.015-3.484 4.06-9.752-1.455-6.267-5.516-6.7-7.034-3.823-10.181 2.877-3.147 5.281 1.808 11.159-3.785 5.877-5.593.94-10.55.94-10.55s12.237-25.014 28.588-23.167c16.351 1.848-6.186-2.392-11.792 17.226-2.4 8.4.447 6.42 4.998 9.968 1.394 1.086 6.03 4.401 11.794 8.685l20.677-20.676 1.615-4.766 7.84-4.689 3.151 3.152-4.688 7.84-4.766 1.615-20.224 20.223c12.663 9.547 28.312 22.146 28.312 26.709 0 7.217-3.071 11.526-9.535 9.164-4.693-1.715-18.768-15.192-28.753-25.897l-2.893 2.893-3.151-3.152 3.029-3.029zM63.953 0l54.56 32v64l-54.56 32-54.56-32V32l54.56-32z"/></svg>
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 128 128" class="tc-image-plugin-generic-theme tc-image-button"><path fill-rule="evenodd" d="M29.408 91.472L51.469 69.41l-.004-.005a2.22 2.22 0 01.004-3.146c.87-.87 2.281-.872 3.147-.005l9.465 9.464a2.22 2.22 0 01-.005 3.147c-.87.87-2.28.871-3.147.005l-.005-.005-22.061 22.062a6.686 6.686 0 11-9.455-9.455zM60.802 66.38c-2.436-2.704-4.465-5.091-5.817-6.869-6.855-9.014-10.313-4.268-14.226 0-3.913 4.268 1.03 7.726-2.683 10.741-3.713 3.015-3.484 4.06-9.752-1.455-6.267-5.516-6.7-7.034-3.823-10.181 2.877-3.147 5.281 1.808 11.159-3.785 5.877-5.593.94-10.55.94-10.55s12.237-25.014 28.588-23.167c16.351 1.848-6.186-2.392-11.792 17.226-2.4 8.4.447 6.42 4.998 9.968 1.394 1.086 6.03 4.401 11.794 8.685l20.677-20.676 1.615-4.766 7.84-4.689 3.151 3.152-4.688 7.84-4.766 1.615-20.224 20.223c12.663 9.547 28.312 22.146 28.312 26.709 0 7.217-3.071 11.526-9.535 9.164-4.693-1.715-18.768-15.192-28.753-25.897l-2.893 2.893-3.151-3.152 3.029-3.029zM63.953 0l54.56 32v64l-54.56 32-54.56-32V32l54.56-32z"/></svg>
|
||||
4
core/images/plus-button.tid
Normal file
4
core/images/plus-button.tid
Normal file
@@ -0,0 +1,4 @@
|
||||
title: $:/core/images/plus-button
|
||||
tags: $:/tags/Image
|
||||
|
||||
<svg width="22pt" height="22pt" class="tc-image-plus-button tc-image-button" viewBox="0 0 128 128"><path d="M64-.333c35.346 0 64 28.654 64 64 0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64 0-35.346 28.654-64 64-64zM64 16c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48z"/><rect width="80" height="16" x="24" y="56" rx="8"/><rect width="16" height="80" x="56" y="24" rx="8"/></svg>
|
||||
@@ -123,12 +123,12 @@ Saving/TiddlySpot/BackupDir: Backup Directory
|
||||
Saving/TiddlySpot/ControlPanel: ~TiddlySpot Control Panel
|
||||
Saving/TiddlySpot/Backups: Backups
|
||||
Saving/TiddlySpot/Caption: ~TiddlySpot Saver
|
||||
Saving/TiddlySpot/Description: These settings are only used when saving to http://tiddlyspot.com or a compatible remote server
|
||||
Saving/TiddlySpot/Description: These settings are only used when saving to [[TiddlySpot|http://tiddlyspot.com]], [[TiddlyHost|https://tiddlyhost.com]], or a compatible remote server. See [[here|https://github.com/simonbaird/tiddlyhost/wiki/TiddlySpot-Saver-configuration-for-Tiddlyhost-and-Tiddlyspot]] for information on ~TiddlySpot and ~TiddlyHost saving configuration.
|
||||
Saving/TiddlySpot/Filename: Upload Filename
|
||||
Saving/TiddlySpot/Heading: ~TiddlySpot
|
||||
Saving/TiddlySpot/Hint: //The server URL defaults to `http://<wikiname>.tiddlyspot.com/store.cgi` and can be changed to use a custom server address, e.g. `http://example.com/store.php`.//
|
||||
Saving/TiddlySpot/Password: Password
|
||||
Saving/TiddlySpot/ReadOnly: The ~TiddlySpot service is currently only available in read-only form. Please see http://tiddlyspot.com/ for the latest details. The ~TiddlySpot saver can still be used to save to compatible servers.
|
||||
Saving/TiddlySpot/ReadOnly: Note that [[TiddlySpot|http://tiddlyspot.com]] no longer allows the creation of new sites. For new sites, you can use [[TiddlyHost|https://tiddlyhost.com]], a new hosting service that replaces ~TiddlySpot.
|
||||
Saving/TiddlySpot/ServerURL: Server URL
|
||||
Saving/TiddlySpot/UploadDir: Upload Directory
|
||||
Saving/TiddlySpot/UserName: Wiki Name
|
||||
|
||||
@@ -23,6 +23,7 @@ tiddlerfield: Defines the behaviour of an individual tiddler field.
|
||||
tiddlermethod: Adds methods to the `$tw.Tiddler` prototype.
|
||||
upgrader: Applies upgrade processing to tiddlers during an upgrade/import.
|
||||
utils: Adds methods to `$tw.utils`.
|
||||
utils-browser: Adds browser-specific methods to `$tw.utils`.
|
||||
utils-node: Adds Node.js-specific methods to `$tw.utils`.
|
||||
widget: Widgets encapsulate DOM rendering and refreshing.
|
||||
wikimethod: Adds methods to `$tw.Wiki`.
|
||||
|
||||
@@ -3,6 +3,8 @@ title: $:/language/EditTemplate/
|
||||
Body/External/Hint: This tiddler shows content stored outside of the main TiddlyWiki file. You can edit the tags and fields but cannot directly edit the content itself
|
||||
Body/Placeholder: Type the text for this tiddler
|
||||
Body/Preview/Type/Output: output
|
||||
Body/Preview/Type/DiffShadow: differences from shadow (if any)
|
||||
Body/Preview/Type/DiffCurrent: differences from current
|
||||
Field/Remove/Caption: remove field
|
||||
Field/Remove/Hint: Remove field
|
||||
Field/Dropdown/Caption: field list
|
||||
|
||||
@@ -19,9 +19,10 @@ All parameters are optional with safe defaults, and can be specified in any orde
|
||||
* ''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
|
||||
* ''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")
|
||||
* ''sse-enabled'' - set to "yes" to enable Server-sent events (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")
|
||||
@@ -29,6 +30,7 @@ All parameters are optional with safe defaults, and can be specified in any orde
|
||||
* ''tls-key'' - pathname of TLS key file (relative to wiki folder)
|
||||
* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none")
|
||||
* ''gzip'' - set to "yes" to enable gzip compression for some http endpoints (defaults to "no")
|
||||
* ''use-browser-cache'' - set to "yes" to allow the browser to cache responses to save bandwidth (defaults to "no")
|
||||
|
||||
For information on opening up your instance to the entire local network, and possible security concerns, see the WebServer tiddler at TiddlyWiki.com.
|
||||
|
||||
|
||||
@@ -8,15 +8,15 @@ Optionally, the title of a template tiddler can be specified. In this case, inst
|
||||
A name and value for an additional variable may optionally also be specified.
|
||||
|
||||
```
|
||||
--render <tiddler-filter> [<filename-filter>] [<render-type>] [<template>] [<name>] [<value>]
|
||||
--render <tiddler-filter> [<filename-filter>] [<render-type>] [<template>] [ [<name>] [<value>] ]*
|
||||
```
|
||||
|
||||
* ''tiddler-filter'': A filter identifying the tiddler(s) to be rendered
|
||||
* ''filename-filter'': Optional filter transforming tiddler titles into pathnames. If omitted, defaults to `[is[tiddler]addsuffix[.html]]`, which uses the unchanged tiddler title as the filename
|
||||
* ''render-type'': Optional render type: `text/html` (the default) returns the full HTML text and `text/plain` just returns the text content (ie it ignores HTML tags and other unprintable material)
|
||||
* ''template'': Optional template through which each tiddler is rendered
|
||||
* ''name'': Name of optional variable
|
||||
* ''value'': Value of optional variable
|
||||
* ''name'': Name of optional variables
|
||||
* ''value'': Value of optional variables
|
||||
|
||||
By default, the filename is resolved relative to the `output` subdirectory of the edition directory. The `--output` command can be used to direct output to a different directory.
|
||||
|
||||
@@ -24,8 +24,9 @@ Notes:
|
||||
|
||||
* The output directory is not cleared of any existing files
|
||||
* Any missing directories in the path to the filename are automatically created.
|
||||
* When referring to a tiddler with spaces in its title, take care to use both the quotes required by your shell and also TiddlyWiki's double square brackets : `--render "[[Motovun Jack.jpg]]"`
|
||||
* When referring to a tiddler with spaces in its title, take care to use both the quotes required by your shell and also TiddlyWiki's double square brackets: `--render "[[Motovun Jack.jpg]]"`
|
||||
* The filename filter is evaluated with the selected items being set to the title of the tiddler currently being rendered, allowing the title to be used as the basis for computing the filename. For example `[encodeuricomponent[]addprefix[static/]]` applies URI encoding to each title, and then adds the prefix `static/`
|
||||
* Multiple ''name''/''value'' pairs can be used to pass more than one variable
|
||||
* The `--render` command is a more flexible replacement for both the `--rendertiddler` and `--rendertiddlers` commands, which are deprecated
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -16,7 +16,7 @@ Notes:
|
||||
|
||||
* The output directory is not cleared of any existing files
|
||||
* Any missing directories in the path to the filename are automatically created.
|
||||
* When saving a tiddler with spaces in its title, take care to use both the quotes required by your shell and also TiddlyWiki's double square brackets : `--save "[[Motovun Jack.jpg]]"`
|
||||
* When saving a tiddler with spaces in its title, take care to use both the quotes required by your shell and also TiddlyWiki's double square brackets: `--save "[[Motovun Jack.jpg]]"`
|
||||
* The filename filter is evaluated with the selected items being set to the title of the tiddler currently being saved, allowing the title to be used as the basis for computing the filename. For example `[encodeuricomponent[]addprefix[static/]]` applies URI encoding to each title, and then adds the prefix `static/`
|
||||
* The `--save` command is a more flexible replacement for both the `--savetiddler` and `--savetiddlers` commands, which are deprecated
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
title: $:/language/Import/
|
||||
|
||||
Editor/Import/Heading: Import images and insert them into the editor.
|
||||
Imported/Hint: The following tiddlers were imported:
|
||||
Listing/Cancel/Caption: Cancel
|
||||
Listing/Hint: These tiddlers are ready to import:
|
||||
@@ -22,7 +23,11 @@ Upgrader/Plugins/Suppressed/Incompatible: Blocked incompatible or obsolete plugi
|
||||
Upgrader/Plugins/Suppressed/Version: Blocked plugin (due to incoming <<incoming>> not being newer than existing <<existing>>).
|
||||
Upgrader/Plugins/Upgraded: Upgraded plugin from <<incoming>> to <<upgraded>>.
|
||||
Upgrader/State/Suppressed: Blocked temporary state tiddler.
|
||||
Upgrader/System/Disabled: Disabled system tiddler.
|
||||
Upgrader/System/Suppressed: Blocked system tiddler.
|
||||
Upgrader/System/Warning: Core module tiddler.
|
||||
Upgrader/System/Alert: You are about to import a tiddler that will overwrite a core module tiddler. This is not recommended as it may make the system unstable.
|
||||
Upgrader/ThemeTweaks/Created: Migrated theme tweak from <$text text=<<from>>/>.
|
||||
Upgrader/Tiddler/Disabled: Disabled tiddler.
|
||||
Upgrader/Tiddler/Selected: User selected.
|
||||
Upgrader/Tiddler/Unselected: Unselected tiddler.
|
||||
|
||||
@@ -24,7 +24,6 @@ Encryption/RepeatPassword: Repeat password
|
||||
Encryption/PasswordNoMatch: Passwords do not match
|
||||
Encryption/SetPassword: Set password
|
||||
Error/Caption: Error
|
||||
Error/EditConflict: File changed on server
|
||||
Error/Filter: Filter error
|
||||
Error/FilterSyntax: Syntax error in filter expression
|
||||
Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run
|
||||
@@ -32,6 +31,9 @@ Error/IsFilterOperator: Filter Error: Unknown operand for the 'is' filter operat
|
||||
Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator
|
||||
Error/LoadingPluginLibrary: Error loading plugin library
|
||||
Error/NetworkErrorAlert: `<h2>''Network Error''</h2>It looks like the connection to the server has been lost. This may indicate a problem with your network connection. Please attempt to restore network connectivity before continuing.<br><br>''Any unsaved changes will be automatically synchronised when connectivity is restored''.`
|
||||
Error/PutEditConflict: File changed on server
|
||||
Error/PutForbidden: Permission denied
|
||||
Error/PutUnauthorized: Authentication required
|
||||
Error/RecursiveTransclusion: Recursive transclusion error in transclude widget
|
||||
Error/RetrievingSkinny: Error retrieving skinny tiddler list
|
||||
Error/SavingToTWEdit: Error saving to TWEdit
|
||||
|
||||
@@ -154,7 +154,7 @@ Commander.prototype.extractNamedParameters = function(params,mandatoryParameters
|
||||
if(errors.length > 0) {
|
||||
return errors.join(" and\n");
|
||||
} else {
|
||||
return paramsByName;
|
||||
return paramsByName;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ Command.prototype.fetchFile = function(url,options,callback,redirectCount) {
|
||||
if(response.statusCode === 302 || response.statusCode === 303 || response.statusCode === 307) {
|
||||
return self.fetchFile(response.headers.location,options,callback,redirectCount + 1);
|
||||
} else {
|
||||
return callback("Error " + response.statusCode + " retrieving " + url)
|
||||
return callback("Error " + response.statusCode + " retrieving " + url)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -37,16 +37,16 @@ Command.prototype.execute = function() {
|
||||
filenameFilter = this.params[1] || "[is[tiddler]addsuffix[.html]]",
|
||||
type = this.params[2] || "text/html",
|
||||
template = this.params[3],
|
||||
varName = this.params[4],
|
||||
varValue = this.params[5],
|
||||
tiddlers = wiki.filterTiddlers(tiddlerFilter);
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var parser = wiki.parseTiddler(template || title),
|
||||
variables = {currentTiddler: title};
|
||||
if(varName) {
|
||||
variables[varName] = varValue || "";
|
||||
variableList = this.params.slice(4),
|
||||
tiddlers = wiki.filterTiddlers(tiddlerFilter),
|
||||
variables = Object.create(null);
|
||||
while(variableList.length >= 2) {
|
||||
variables[variableList[0]] = variableList[1];
|
||||
variableList = variableList.slice(2);
|
||||
}
|
||||
var widgetNode = wiki.makeWidget(parser,{variables: variables}),
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
var parser = wiki.parseTiddler(template || title);
|
||||
var widgetNode = wiki.makeWidget(parser,{variables: $tw.utils.extend({},variables,{currentTiddler: title})}),
|
||||
container = $tw.fakeDocument.createElement("div");
|
||||
widgetNode.render(container,null);
|
||||
var text = type === "text/html" ? container.innerHTML : container.textContent,
|
||||
|
||||
@@ -36,7 +36,7 @@ Command.prototype.execute = function() {
|
||||
filter = this.params[0],
|
||||
template = this.params[1],
|
||||
outputPath = this.commander.outputPath,
|
||||
pathname = path.resolve(outputPath,this.params[2]),
|
||||
pathname = path.resolve(outputPath,this.params[2]),
|
||||
type = this.params[3] || "text/html",
|
||||
extension = this.params[4] || ".html",
|
||||
deleteDirectory = (this.params[5] || "").toLowerCase() !== "noclean",
|
||||
|
||||
@@ -97,7 +97,7 @@ WikiFolderMaker.prototype.save = function() {
|
||||
// A custom plugin
|
||||
self.log("Processing custom plugin: " + title);
|
||||
self.saveCustomPlugin(tiddler);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ordinary tiddler
|
||||
self.saveTiddler("tiddlers",tiddler);
|
||||
@@ -158,11 +158,25 @@ WikiFolderMaker.prototype.saveCustomPlugin = function(pluginTiddler) {
|
||||
};
|
||||
|
||||
WikiFolderMaker.prototype.saveTiddler = function(directory,tiddler) {
|
||||
var title = tiddler.fields.title, fileInfo, pathFilters, extFilters;
|
||||
if(this.wiki.tiddlerExists("$:/config/FileSystemPaths")) {
|
||||
pathFilters = this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n");
|
||||
}
|
||||
if(this.wiki.tiddlerExists("$:/config/FileSystemExtensions")) {
|
||||
extFilters = this.wiki.getTiddlerText("$:/config/FileSystemExtensions","").split("\n");
|
||||
}
|
||||
var fileInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{
|
||||
directory: path.resolve(this.wikiFolderPath,directory),
|
||||
wiki: this.wiki
|
||||
pathFilters: pathFilters,
|
||||
extFilters: extFilters,
|
||||
wiki: this.wiki,
|
||||
fileInfo: {}
|
||||
});
|
||||
$tw.utils.saveTiddlerToFileSync(tiddler,fileInfo);
|
||||
try {
|
||||
$tw.utils.saveTiddlerToFileSync(tiddler,fileInfo);
|
||||
} catch (err) {
|
||||
console.log("SaveWikiFolder: Error saving file '" + fileInfo.filepath + "', tiddler: '" + tiddler.fields.title);
|
||||
}
|
||||
};
|
||||
|
||||
WikiFolderMaker.prototype.saveJSONFile = function(filename,json) {
|
||||
|
||||
@@ -34,7 +34,7 @@ exports.htmlEntities = {quot:34, amp:38, apos:39, lt:60, gt:62, nbsp:160, iexcl:
|
||||
|
||||
exports.htmlVoidElements = "area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",");
|
||||
|
||||
exports.htmlBlockElements = "address,article,aside,audio,blockquote,canvas,dd,div,dl,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,li,noscript,ol,output,p,pre,section,table,tfoot,ul,video".split(",");
|
||||
exports.htmlBlockElements = "address,article,aside,audio,blockquote,canvas,dd,details,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,li,nav,ol,p,pre,section,summary,table,tfoot,ul,video".split(",");
|
||||
|
||||
exports.htmlUnsafeElements = "script".split(",");
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ var parseTiddlerDiv = function(text /* [,fields] */) {
|
||||
for(var f=1; f<arguments.length; f++) {
|
||||
var fields = arguments[f];
|
||||
for(var t in fields) {
|
||||
result[t] = fields[t];
|
||||
result[t] = fields[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ function FramedEngine(options) {
|
||||
}
|
||||
if(this.widget.isDisabled === "yes") {
|
||||
this.domNode.setAttribute("disabled",true);
|
||||
}
|
||||
}
|
||||
// Copy the styles from the dummy textarea
|
||||
this.copyStyles();
|
||||
// Add event listeners
|
||||
@@ -88,6 +88,18 @@ function FramedEngine(options) {
|
||||
{name: "keydown",handlerObject: this.widget,handlerMethod: "handleKeydownEvent"},
|
||||
{name: "focus",handlerObject: this,handlerMethod: "handleFocusEvent"}
|
||||
]);
|
||||
// Add drag and drop event listeners if fileDrop is enabled
|
||||
if(this.widget.isFileDropEnabled) {
|
||||
$tw.utils.addEventListeners(this.domNode,[
|
||||
{name: "dragenter",handlerObject: this.widget,handlerMethod: "handleDragEnterEvent"},
|
||||
{name: "dragover",handlerObject: this.widget,handlerMethod: "handleDragOverEvent"},
|
||||
{name: "dragleave",handlerObject: this.widget,handlerMethod: "handleDragLeaveEvent"},
|
||||
{name: "dragend",handlerObject: this.widget,handlerMethod: "handleDragEndEvent"},
|
||||
{name: "drop", handlerObject: this.widget,handlerMethod: "handleDropEvent"},
|
||||
{name: "paste", handlerObject: this.widget,handlerMethod: "handlePasteEvent"},
|
||||
{name: "click",handlerObject: this.widget,handlerMethod: "handleClickEvent"}
|
||||
]);
|
||||
}
|
||||
// Insert the element into the DOM
|
||||
this.iframeDoc.body.appendChild(this.domNode);
|
||||
}
|
||||
@@ -170,7 +182,7 @@ Handle a focus event
|
||||
*/
|
||||
FramedEngine.prototype.handleFocusEvent = function(event) {
|
||||
if(this.widget.editCancelPopups) {
|
||||
$tw.popup.cancel(0);
|
||||
$tw.popup.cancel(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -103,7 +103,11 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
var tiddler = this.wiki.getTiddler(this.editTitle);
|
||||
if(tiddler) {
|
||||
// If we've got a tiddler, the value to display is the field string value
|
||||
value = tiddler.getFieldString(this.editField);
|
||||
if(tiddler.hasField(this.editField)) {
|
||||
value = tiddler.getFieldString(this.editField);
|
||||
} else {
|
||||
value = this.editDefault || "";
|
||||
}
|
||||
if(this.editField === "text") {
|
||||
type = tiddler.fields.type || "text/vnd.tiddlywiki";
|
||||
}
|
||||
@@ -182,6 +186,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
this.editRefreshTitle = this.getAttribute("refreshTitle");
|
||||
this.editAutoComplete = this.getAttribute("autocomplete");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
this.isFileDropEnabled = this.getAttribute("fileDrop","no") === "yes";
|
||||
// Get the default editor element tag and type
|
||||
var tag,type;
|
||||
if(this.editField === "text") {
|
||||
@@ -213,7 +218,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
EditTextWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Completely rerender if any of our attributes have changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedAttributes.disabled) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedAttributes.disabled || changedAttributes.fileDrop) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if (changedTiddlers[this.editRefreshTitle]) {
|
||||
@@ -293,21 +298,89 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
Propogate keydown events to our container for the keyboard widgets benefit
|
||||
*/
|
||||
EditTextWidget.prototype.propogateKeydownEvent = function(event) {
|
||||
var newEvent = this.document.createEventObject ? this.document.createEventObject() : this.document.createEvent("Events");
|
||||
if(newEvent.initEvent) {
|
||||
newEvent.initEvent("keydown", true, true);
|
||||
}
|
||||
newEvent.keyCode = event.keyCode;
|
||||
newEvent.which = event.which;
|
||||
newEvent.metaKey = event.metaKey;
|
||||
newEvent.ctrlKey = event.ctrlKey;
|
||||
newEvent.altKey = event.altKey;
|
||||
newEvent.shiftKey = event.shiftKey;
|
||||
var newEvent = this.cloneEvent(event,["keyCode","which","metaKey","ctrlKey","altKey","shiftKey"]);
|
||||
return !this.parentDomNode.dispatchEvent(newEvent);
|
||||
};
|
||||
|
||||
return EditTextWidget;
|
||||
EditTextWidget.prototype.cloneEvent = function(event,propertiesToCopy) {
|
||||
var propertiesToCopy = propertiesToCopy || [],
|
||||
newEvent = this.document.createEventObject ? this.document.createEventObject() : this.document.createEvent("Events");
|
||||
if(newEvent.initEvent) {
|
||||
newEvent.initEvent(event.type, true, true);
|
||||
}
|
||||
$tw.utils.each(propertiesToCopy,function(prop){
|
||||
newEvent[prop] = event[prop];
|
||||
});
|
||||
return newEvent;
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.dispatchDOMEvent = function(newEvent) {
|
||||
var dispatchNode = this.engine.iframeNode || this.engine.parentNode;
|
||||
return dispatchNode.dispatchEvent(newEvent);
|
||||
};
|
||||
|
||||
/*
|
||||
Propogate drag and drop events with File data to our container for the dropzone widgets benefit.
|
||||
If there are no Files, let the browser handle it.
|
||||
*/
|
||||
EditTextWidget.prototype.handleDropEvent = function(event) {
|
||||
if(event.dataTransfer.files.length) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.dispatchDOMEvent(this.cloneEvent(event,["dataTransfer"]));
|
||||
}
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handlePasteEvent = function(event) {
|
||||
if(event.clipboardData.files.length) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.dispatchDOMEvent(this.cloneEvent(event,["clipboardData"]));
|
||||
}
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handleDragEnterEvent = function(event) {
|
||||
if($tw.utils.dragEventContainsFiles(event)) {
|
||||
// Ignore excessive events fired by FF when entering and leaving text nodes in a text area.
|
||||
if( event.relatedTarget && (event.relatedTarget.nodeType === 3 || event.target === event.relatedTarget)) {
|
||||
return true;
|
||||
}
|
||||
event.preventDefault();
|
||||
return this.dispatchDOMEvent(this.cloneEvent(event,["dataTransfer"]));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handleDragOverEvent = function(event) {
|
||||
if($tw.utils.dragEventContainsFiles(event)) {
|
||||
// Call preventDefault() in browsers that default to not allowing drop events on textarea
|
||||
if($tw.browser.isFirefox || $tw.browser.isIE) {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
return this.dispatchDOMEvent(this.cloneEvent(event,["dataTransfer"]));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handleDragLeaveEvent = function(event) {
|
||||
// Ignore excessive events fired by FF when entering and leaving text nodes in a text area.
|
||||
if(event.relatedTarget && ((event.relatedTarget.nodeType === 3) || (event.target === event.relatedTarget))) {
|
||||
return true;
|
||||
}
|
||||
event.preventDefault();
|
||||
this.dispatchDOMEvent(this.cloneEvent(event,["dataTransfer"]));
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handleDragEndEvent = function(event) {
|
||||
this.dispatchDOMEvent(this.cloneEvent(event));
|
||||
};
|
||||
|
||||
EditTextWidget.prototype.handleClickEvent = function(event) {
|
||||
return !this.dispatchDOMEvent(this.cloneEvent(event));
|
||||
};
|
||||
|
||||
return EditTextWidget;
|
||||
}
|
||||
|
||||
exports.editTextWidgetFactory = editTextWidgetFactory;
|
||||
|
||||
23
core/modules/editor/operations/text/insert-text.js
Normal file
23
core/modules/editor/operations/text/insert-text.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/*\
|
||||
title: $:/core/modules/editor/operations/text/insert-text.js
|
||||
type: application/javascript
|
||||
module-type: texteditoroperation
|
||||
|
||||
Text editor operation insert text at the caret position. If there is a selection it is replaced.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports["insert-text"] = function(event,operation) {
|
||||
operation.replacement = event.paramObject.text;
|
||||
operation.cutStart = operation.selStart;
|
||||
operation.cutEnd = operation.selEnd;
|
||||
operation.newSelStart = operation.selStart + operation.replacement.length;
|
||||
operation.newSelEnd = operation.newSelStart;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -17,10 +17,21 @@ exports.filter = function(operationSubFunction,options) {
|
||||
return function(results,source,widget) {
|
||||
if(results.length > 0) {
|
||||
var resultsToRemove = [];
|
||||
results.each(function(result) {
|
||||
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([result]),widget);
|
||||
results.each(function(title) {
|
||||
var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
|
||||
getVariable: function(name) {
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "..currentTiddler":
|
||||
return widget.getVariable("currentTiddler");
|
||||
default:
|
||||
return widget.getVariable(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
if(filtered.length === 0) {
|
||||
resultsToRemove.push(result);
|
||||
resultsToRemove.push(title);
|
||||
}
|
||||
});
|
||||
results.remove(resultsToRemove);
|
||||
|
||||
@@ -19,30 +19,32 @@ exports.reduce = function(operationSubFunction,options) {
|
||||
var index = 0;
|
||||
results.each(function(title) {
|
||||
var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
|
||||
getVariable: function(name) {
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "accumulator":
|
||||
return "" + accumulator;
|
||||
case "index":
|
||||
return "" + index;
|
||||
case "revIndex":
|
||||
return "" + (results.length - 1 - index);
|
||||
case "length":
|
||||
return "" + results.length;
|
||||
default:
|
||||
return widget.getVariable(name);
|
||||
}
|
||||
getVariable: function(name) {
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "..currentTiddler":
|
||||
return widget.getVariable("currentTiddler");
|
||||
case "accumulator":
|
||||
return "" + accumulator;
|
||||
case "index":
|
||||
return "" + index;
|
||||
case "revIndex":
|
||||
return "" + (results.length - 1 - index);
|
||||
case "length":
|
||||
return "" + results.length;
|
||||
default:
|
||||
return widget.getVariable(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if(list.length > 0) {
|
||||
accumulator = "" + list[0];
|
||||
}
|
||||
++index;
|
||||
});
|
||||
results.clear();
|
||||
results.push(accumulator);
|
||||
results.push(accumulator);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
60
core/modules/filterrunprefixes/sort.js
Normal file
60
core/modules/filterrunprefixes/sort.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/sort.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.sort = function(operationSubFunction,options) {
|
||||
return function(results,source,widget) {
|
||||
if(results.length > 0) {
|
||||
var suffixes = options.suffixes,
|
||||
sortType = (suffixes[0] && suffixes[0][0]) ? suffixes[0][0] : "string",
|
||||
invert = suffixes[1] ? (suffixes[1].indexOf("reverse") !== -1) : false,
|
||||
isCaseSensitive = suffixes[1] ? (suffixes[1].indexOf("casesensitive") !== -1) : false,
|
||||
inputTitles = results.toArray(),
|
||||
sortKeys = [],
|
||||
indexes = new Array(inputTitles.length),
|
||||
compareFn;
|
||||
results.each(function(title) {
|
||||
var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{
|
||||
getVariable: function(name) {
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "..currentTiddler":
|
||||
return widget.getVariable("currentTiddler");
|
||||
default:
|
||||
return widget.getVariable(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
sortKeys.push(key[0] || "");
|
||||
});
|
||||
results.clear();
|
||||
// Prepare an array of indexes to sort
|
||||
for(var t=0; t<inputTitles.length; t++) {
|
||||
indexes[t] = t;
|
||||
}
|
||||
// Sort the indexes
|
||||
compareFn = $tw.utils.makeCompareFunction(sortType,{defaultType: "string", invert:invert, isCaseSensitive:isCaseSensitive});
|
||||
indexes = indexes.sort(function(a,b) {
|
||||
return compareFn(sortKeys[a],sortKeys[b]);
|
||||
});
|
||||
// Add to results in correct order
|
||||
$tw.utils.each(indexes,function(index) {
|
||||
results.push(inputTitles[index]);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -63,7 +63,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
operator.operator = "title";
|
||||
}
|
||||
operator.operands = [];
|
||||
function parseOperand(bracketType) {
|
||||
var parseOperand = function(bracketType) {
|
||||
var operand = {};
|
||||
switch (bracketType) {
|
||||
case "{": // Curly brackets
|
||||
@@ -78,7 +78,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
nextBracketPos = filterString.indexOf(">",p);
|
||||
break;
|
||||
case "/": // regexp brackets
|
||||
var rex = /^((?:[^\\\/]*|\\.)*)\/(?:\(([mygi]+)\))?/g,
|
||||
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
|
||||
rexMatch = rex.exec(filterString.substring(p));
|
||||
if(rexMatch) {
|
||||
operator.regexp = new RegExp(rexMatch[1], rexMatch[2]);
|
||||
@@ -101,10 +101,10 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
}
|
||||
p = nextBracketPos + 1;
|
||||
}
|
||||
|
||||
|
||||
p = nextBracketPos + 1;
|
||||
parseOperand(bracket);
|
||||
|
||||
|
||||
// Check for multiple operands
|
||||
while(filterString.charAt(p) === ",") {
|
||||
p++;
|
||||
@@ -116,7 +116,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
throw "Missing [ in filter expression";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Push this operator
|
||||
operators.push(operator);
|
||||
} while(filterString.charAt(p) !== "]");
|
||||
@@ -137,7 +137,7 @@ exports.parseFilter = function(filterString) {
|
||||
p = 0, // Current position in the filter string
|
||||
match;
|
||||
var whitespaceRegExp = /(\s+)/mg,
|
||||
operandRegExp = /((?:\+|\-|~|=|\:(\w+))?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
while(p < filterString.length) {
|
||||
// Skip any whitespace
|
||||
whitespaceRegExp.lastIndex = p;
|
||||
@@ -162,15 +162,27 @@ exports.parseFilter = function(filterString) {
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
if(match[3]) { // Opening square bracket
|
||||
if(match[4]) { // Opening square bracket
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
}
|
||||
if(match[4] || match[5] || match[6]) { // Double quoted string, single quoted string or unquoted title
|
||||
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
|
||||
operation.operators.push(
|
||||
{operator: "title", operands: [{text: match[4] || match[5] || match[6]}]}
|
||||
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
|
||||
);
|
||||
}
|
||||
results.push(operation);
|
||||
@@ -236,7 +248,7 @@ exports.compileFilter = function(filterString) {
|
||||
} else {
|
||||
operatorFunction = filterOperators[operator.operator];
|
||||
}
|
||||
|
||||
|
||||
$tw.utils.each(operator.operands,function(operand) {
|
||||
if(operand.indirect) {
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
@@ -280,7 +292,7 @@ exports.compileFilter = function(filterString) {
|
||||
var filterRunPrefixes = self.getFilterRunPrefixes();
|
||||
// Wrap the operator functions in a wrapper function that depends on the prefix
|
||||
operationFunctions.push((function() {
|
||||
var options = {wiki: self};
|
||||
var options = {wiki: self, suffixes: operation.suffixes || []};
|
||||
switch(operation.prefix || "") {
|
||||
case "": // No prefix means that the operation is unioned into the result
|
||||
return filterRunPrefixes["or"](operationSubFunction, options);
|
||||
@@ -311,6 +323,9 @@ exports.compileFilter = function(filterString) {
|
||||
} else if(typeof source === "object") { // Array or hashmap
|
||||
source = self.makeTiddlerIterator(source);
|
||||
}
|
||||
if(!widget) {
|
||||
widget = $tw.rootWidget;
|
||||
}
|
||||
var results = new $tw.utils.LinkedList();
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
|
||||
@@ -31,7 +31,7 @@ exports.all = function(source,operator,options) {
|
||||
// Get our suboperators
|
||||
var allFilterOperators = getAllFilterOperators();
|
||||
// Cycle through the suboperators accumulating their results
|
||||
var results = [],
|
||||
var results = new $tw.utils.LinkedList(),
|
||||
subops = operator.operand.split("+");
|
||||
// Check for common optimisations
|
||||
if(subops.length === 1 && subops[0] === "") {
|
||||
@@ -49,10 +49,10 @@ exports.all = function(source,operator,options) {
|
||||
for(var t=0; t<subops.length; t++) {
|
||||
var subop = allFilterOperators[subops[t]];
|
||||
if(subop) {
|
||||
$tw.utils.pushTop(results,subop(source,operator.prefix,options));
|
||||
results.pushTop(subop(source,operator.prefix,options));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
return results.toArray();
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
27
core/modules/filters/deserializers.js
Normal file
27
core/modules/filters/deserializers.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/deserializers.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for returning the names of the deserializers in this wiki
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.deserializers = function(source,operator,options) {
|
||||
var results = [];
|
||||
$tw.utils.each($tw.Wiki.tiddlerDeserializerModules,function(deserializer,type) {
|
||||
results.push(type);
|
||||
});
|
||||
results.sort();
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -22,7 +22,7 @@ exports.editiondescription = function(source,operator,options) {
|
||||
if(editionInfo) {
|
||||
source(function(tiddler,title) {
|
||||
if($tw.utils.hop(editionInfo,title)) {
|
||||
results.push(editionInfo[title].description || "");
|
||||
results.push(editionInfo[title].description || "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ exports.escapecss = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
// escape any character with a special meaning in CSS using CSS.escape()
|
||||
results.push(CSS.escape(title));
|
||||
results.push($tw.utils.escapeCSS(title));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ exports.fields = function(source,operator,options) {
|
||||
fieldName,
|
||||
suffixes = (operator.suffixes || [])[0] || [],
|
||||
operand = $tw.utils.parseStringArray(operator.operand);
|
||||
|
||||
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler) {
|
||||
if(suffixes.indexOf("include") !== -1) {
|
||||
|
||||
@@ -20,7 +20,18 @@ exports.filter = function(source,operator,options) {
|
||||
results = [],
|
||||
target = operator.prefix !== "!";
|
||||
source(function(tiddler,title) {
|
||||
var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]));
|
||||
var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{
|
||||
getVariable: function(name) {
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "..currentTiddler":
|
||||
return options.widget.getVariable("currentTiddler");
|
||||
default:
|
||||
return options.widget.getVariable(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
if((list.length > 0) === target) {
|
||||
results.push(title);
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ module-type: formatfilteroperator
|
||||
Export our filter function
|
||||
*/
|
||||
exports.date = function(source,operand,options) {
|
||||
var results = [];
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var value = $tw.utils.parseDate(title);
|
||||
if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") {
|
||||
results.push($tw.utils.formatDateString(value,operand || "YYYY MM DD 0hh:0mm"));
|
||||
}
|
||||
});
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ module-type: formatfilteroperator
|
||||
Export our filter function
|
||||
*/
|
||||
exports.relativedate = function(source,operand,options) {
|
||||
var results = [];
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var value = $tw.utils.parseDate(title);
|
||||
if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") {
|
||||
results.push($tw.utils.getRelativeDate((new Date()) - (new Date(value))).description);
|
||||
}
|
||||
});
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
|
||||
25
core/modules/filters/format/titlelist.js
Normal file
25
core/modules/filters/format/titlelist.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/format/titlelist.js
|
||||
type: application/javascript
|
||||
module-type: formatfilteroperator
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.titlelist = function(source,operand,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
if(title && title.length) {
|
||||
results.push($tw.utils.stringifyList([title]));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -61,7 +61,7 @@ exports.has = function(source,operator,options) {
|
||||
if(tiddler && $tw.utils.hop(tiddler.fields,operator.operand) && (tiddler.fields[operator.operand].length !== 0)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
||||
@@ -28,7 +28,7 @@ exports.draft = function(source,prefix,options) {
|
||||
if(tiddler && $tw.utils.hop(tiddler.fields,"draft.of") && (tiddler.fields["draft.of"].length !== 0)) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -16,11 +16,11 @@ Filter operator for returning all the links from a tiddler
|
||||
Export our filter function
|
||||
*/
|
||||
exports.links = function(source,operator,options) {
|
||||
var results = [];
|
||||
var results = new $tw.utils.LinkedList();
|
||||
source(function(tiddler,title) {
|
||||
$tw.utils.pushTop(results,options.wiki.getTiddlerLinks(title));
|
||||
results.pushTop(options.wiki.getTiddlerLinks(title));
|
||||
});
|
||||
return results;
|
||||
return results.toArray();
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -22,7 +22,7 @@ Export our filter function
|
||||
exports.lookup = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push(options.wiki.getTiddlerText(operator.operand + title) || operator.suffix);
|
||||
results.push(options.wiki.getTiddlerText(operator.operand + title) || operator.suffix || '');
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -125,6 +125,54 @@ exports.minall = makeNumericReducingOperator(
|
||||
Infinity // Initial value
|
||||
);
|
||||
|
||||
exports.median = makeNumericArrayOperator(
|
||||
function(values) {
|
||||
var len = values.length, median;
|
||||
values.sort();
|
||||
if(len % 2) {
|
||||
// Odd, return the middle number
|
||||
median = values[(len - 1) / 2];
|
||||
} else {
|
||||
// Even, return average of two middle numbers
|
||||
median = (values[len / 2 - 1] + values[len / 2]) / 2;
|
||||
}
|
||||
return [median];
|
||||
}
|
||||
);
|
||||
|
||||
exports.average = makeNumericReducingOperator(
|
||||
function(accumulator,value) {return accumulator + value},
|
||||
0, // Initial value
|
||||
function(finalValue,numberOfValues) {
|
||||
return finalValue/numberOfValues;
|
||||
}
|
||||
);
|
||||
|
||||
exports.variance = makeNumericReducingOperator(
|
||||
function(accumulator,value) {return accumulator + value},
|
||||
0,
|
||||
function(finalValue,numberOfValues,originalValues) {
|
||||
return getVarianceFromArray(originalValues,finalValue/numberOfValues);
|
||||
}
|
||||
);
|
||||
|
||||
exports["standard-deviation"] = makeNumericReducingOperator(
|
||||
function(accumulator,value) {return accumulator + value},
|
||||
0,
|
||||
function(finalValue,numberOfValues,originalValues) {
|
||||
var variance = getVarianceFromArray(originalValues,finalValue/numberOfValues);
|
||||
return Math.sqrt(variance);
|
||||
}
|
||||
);
|
||||
|
||||
//Calculate the variance of a population of numbers in an array given its mean
|
||||
function getVarianceFromArray(values,mean) {
|
||||
var deviationTotal = values.reduce(function(accumulator,value) {
|
||||
return accumulator + Math.pow(value - mean, 2);
|
||||
},0);
|
||||
return deviationTotal/values.length;
|
||||
};
|
||||
|
||||
function makeNumericBinaryOperator(fnCalc) {
|
||||
return function(source,operator,options) {
|
||||
var result = [],
|
||||
@@ -134,19 +182,37 @@ function makeNumericBinaryOperator(fnCalc) {
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function makeNumericReducingOperator(fnCalc,initialValue) {
|
||||
function makeNumericReducingOperator(fnCalc,initialValue,fnFinal) {
|
||||
initialValue = initialValue || 0;
|
||||
return function(source,operator,options) {
|
||||
var result = [];
|
||||
source(function(tiddler,title) {
|
||||
result.push(title);
|
||||
result.push($tw.utils.parseNumber(title));
|
||||
});
|
||||
return [$tw.utils.stringifyNumber(result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,$tw.utils.parseNumber(currentValue));
|
||||
},initialValue))];
|
||||
var value = result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,currentValue);
|
||||
},initialValue);
|
||||
if(fnFinal) {
|
||||
value = fnFinal(value,result.length,result);
|
||||
}
|
||||
return [$tw.utils.stringifyNumber(value)];
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function makeNumericArrayOperator(fnCalc) {
|
||||
return function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.parseNumber(title));
|
||||
});
|
||||
results = fnCalc(results);
|
||||
$tw.utils.each(results,function(value,index) {
|
||||
results[index] = $tw.utils.stringifyNumber(value);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -31,6 +31,8 @@ exports.reduce = function(source,operator,options) {
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "..currentTiddler":
|
||||
return options.widget.getVariable("currentTiddler");
|
||||
case "accumulator":
|
||||
return "" + accumulator;
|
||||
case "index":
|
||||
|
||||
@@ -27,10 +27,13 @@ exports.sortsub = function(source,operator,options) {
|
||||
iterator(options.wiki.getTiddler(title),title);
|
||||
},{
|
||||
getVariable: function(name) {
|
||||
if(name === "currentTiddler") {
|
||||
return title;
|
||||
} else {
|
||||
return options.widget.getVariable(name);
|
||||
switch(name) {
|
||||
case "currentTiddler":
|
||||
return "" + title;
|
||||
case "..currentTiddler":
|
||||
return options.widget.getVariable("currentTiddler");
|
||||
default:
|
||||
return options.widget.getVariable(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,13 +105,13 @@ exports.splitregexp = function(source,operator,options) {
|
||||
flags = (suffix.indexOf("m") !== -1 ? "m" : "") + (suffix.indexOf("i") !== -1 ? "i" : ""),
|
||||
regExp;
|
||||
try {
|
||||
regExp = new RegExp(operator.operand || "",flags);
|
||||
regExp = new RegExp(operator.operand || "",flags);
|
||||
} catch(ex) {
|
||||
return ["RegExp error: " + ex];
|
||||
}
|
||||
source(function(tiddler,title) {
|
||||
Array.prototype.push.apply(result,title.split(regExp));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -123,7 +123,7 @@ exports["search-replace"] = function(source,operator,options) {
|
||||
isRegExp = (suffixes[1] && suffixes[1][0] === "regexp") ? true : false,
|
||||
searchTerm,
|
||||
regExp;
|
||||
|
||||
|
||||
source(function(tiddler,title) {
|
||||
if(title && (operator.operands.length > 1)) {
|
||||
//Escape regexp characters if the operand is not a regular expression
|
||||
@@ -156,7 +156,7 @@ exports.pad = function(source,operator,options) {
|
||||
var padString = "",
|
||||
padStringLength = targetLength - title.length;
|
||||
while (padStringLength > padString.length) {
|
||||
padString += fill;
|
||||
padString += fill;
|
||||
}
|
||||
//make sure we do not exceed the specified length
|
||||
padString = padString.slice(0,padStringLength);
|
||||
|
||||
@@ -50,7 +50,7 @@ exports.tag = function(source,operator,options) {
|
||||
});
|
||||
results = options.wiki.sortByList(results,operator.operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -193,7 +193,7 @@ Extended filter operators to manipulate the current list.
|
||||
step = stepSize || 1,
|
||||
i = 0,
|
||||
opLength = operands.length,
|
||||
nextOperandIndex;
|
||||
nextOperandIndex;
|
||||
for(i; i < opLength; i++) {
|
||||
resultsIndex = results.indexOf(operands[i]);
|
||||
if(resultsIndex !== -1) {
|
||||
@@ -211,12 +211,12 @@ Extended filter operators to manipulate the current list.
|
||||
} else {
|
||||
results.push(operands[0]);
|
||||
}
|
||||
return results;
|
||||
return results;
|
||||
}
|
||||
|
||||
/*
|
||||
Toggles an item in the current list.
|
||||
*/
|
||||
*/
|
||||
exports.toggle = function(source,operator) {
|
||||
return cycleValueInArray(prepare_results(source),operator.operands);
|
||||
}
|
||||
@@ -228,8 +228,8 @@ Extended filter operators to manipulate the current list.
|
||||
if(step < 0) {
|
||||
operands.reverse();
|
||||
step = Math.abs(step);
|
||||
}
|
||||
}
|
||||
return cycleValueInArray(results,operands,step);
|
||||
}
|
||||
|
||||
|
||||
})();
|
||||
|
||||
@@ -121,7 +121,7 @@ FieldIndexer.prototype.update = function(updateDescriptor) {
|
||||
indexEntry[value].push(updateDescriptor["new"].tiddler.fields.title);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ TagSubIndexer.prototype.rebuild = function() {
|
||||
} else {
|
||||
self.index[tag].titles.push(title);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ TagSubIndexer.prototype.lookup = function(tag) {
|
||||
if(!indexRecord.isSorted) {
|
||||
if(this.indexer.wiki.sortByList) {
|
||||
indexRecord.titles = this.indexer.wiki.sortByList(indexRecord.titles,tag);
|
||||
}
|
||||
}
|
||||
indexRecord.isSorted = true;
|
||||
}
|
||||
return indexRecord.titles;
|
||||
|
||||
@@ -22,7 +22,7 @@ exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
|
||||
if($tw.browser) {
|
||||
// Document location
|
||||
var setLocationProperty = function(name,value) {
|
||||
infoTiddlerFields.push({title: "$:/info/url/" + name, text: value});
|
||||
infoTiddlerFields.push({title: "$:/info/url/" + name, text: value});
|
||||
},
|
||||
location = document.location;
|
||||
setLocationProperty("full", (location.toString()).split("#")[0]);
|
||||
|
||||
@@ -285,7 +285,7 @@ KeyboardManager.prototype.checkKeyDescriptors = function(event,keyInfoArray) {
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.getEventModifierKeyDescriptor = function(event) {
|
||||
return event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey ? "ctrl" :
|
||||
return event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey ? "ctrl" :
|
||||
event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey ? "shift" :
|
||||
event.ctrlKey && event.shiftKey && !event.altKey && !event.metaKey ? "ctrl-shift" :
|
||||
event.altKey && !event.shiftKey && !event.ctrlKey && !event.metaKey ? "alt" :
|
||||
@@ -295,7 +295,7 @@ KeyboardManager.prototype.getEventModifierKeyDescriptor = function(event) {
|
||||
event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey ? "meta" :
|
||||
event.metaKey && event.ctrlKey && !event.shiftKey && !event.altKey ? "meta-ctrl" :
|
||||
event.metaKey && event.ctrlKey && event.shiftKey && !event.altKey ? "meta-ctrl-shift" :
|
||||
event.metaKey && event.ctrlKey & event.shiftKey && event.altKey ? "meta-ctrl-alt-shift" : "normal";
|
||||
event.metaKey && event.ctrlKey && event.shiftKey && event.altKey ? "meta-ctrl-alt-shift" : "normal";
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.getShortcutTiddlerList = function() {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: $:/core/modules/macros/unusedtitle.js
|
||||
type: application/javascript
|
||||
module-type: macro
|
||||
|
||||
Macro to return a new title that is unused in the wiki. It can be given a name as a base.
|
||||
\*/
|
||||
(function(){
|
||||
@@ -10,25 +11,25 @@ Macro to return a new title that is unused in the wiki. It can be given a name a
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Information about this macro
|
||||
*/
|
||||
|
||||
exports.name = "unusedtitle";
|
||||
|
||||
exports.params = [
|
||||
{name: "baseName"},
|
||||
{name: "options"}
|
||||
{name: "separator"},
|
||||
{name: "template"}
|
||||
];
|
||||
|
||||
/*
|
||||
Run the macro
|
||||
*/
|
||||
exports.run = function(baseName, options) {
|
||||
exports.run = function(baseName,separator,template) {
|
||||
separator = separator || " ";
|
||||
if(!baseName) {
|
||||
baseName = $tw.language.getString("DefaultNewTiddlerTitle");
|
||||
}
|
||||
return this.wiki.generateNewTitle(baseName, options);
|
||||
// $tw.wiki.generateNewTitle = function(baseTitle,options)
|
||||
// options.prefix must be a string!
|
||||
return this.wiki.generateNewTitle(baseName, {"prefix": separator, "template": template});
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -23,10 +23,12 @@ var HtmlParser = function(type,text,options) {
|
||||
type: "element",
|
||||
tag: "iframe",
|
||||
attributes: {
|
||||
src: {type: "string", value: src},
|
||||
sandbox: {type: "string", value: ""}
|
||||
src: {type: "string", value: src}
|
||||
}
|
||||
}];
|
||||
if($tw.wiki.getTiddlerText("$:/config/HtmlParser/DisableSandbox","no") !== "yes") {
|
||||
this.tree[0].attributes.sandbox = {type: "string", value: $tw.wiki.getTiddlerText("$:/config/HtmlParser/SandboxTokens","")};
|
||||
}
|
||||
};
|
||||
|
||||
exports["text/html"] = HtmlParser;
|
||||
|
||||
@@ -39,6 +39,7 @@ exports["image/webp"] = ImageParser;
|
||||
exports["image/heic"] = ImageParser;
|
||||
exports["image/heif"] = ImageParser;
|
||||
exports["image/x-icon"] = ImageParser;
|
||||
exports["image/vnd.microsoft.icon"] = ImageParser;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ exports.parseMacroParameter = function(source,pos) {
|
||||
start: pos
|
||||
};
|
||||
// Define our regexp
|
||||
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|([^\s>"'=]+)))/g;
|
||||
var reMacroParameter = /(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|((?:(?:>(?!>))|[^\s>"'])+)))/g;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the parameter
|
||||
|
||||
@@ -31,7 +31,7 @@ exports.findNextMatch = function(startPos) {
|
||||
this.matchRegExp.lastIndex = startPos;
|
||||
this.match = this.matchRegExp.exec(this.parser.source);
|
||||
if(this.match) {
|
||||
this.endMatchRegExp.lastIndex = startPos + this.match[0].length;
|
||||
this.endMatchRegExp.lastIndex = this.match.index + this.match[0].length;
|
||||
this.endMatch = this.endMatchRegExp.exec(this.parser.source);
|
||||
if(this.endMatch) {
|
||||
return this.match.index;
|
||||
|
||||
@@ -31,7 +31,7 @@ exports.findNextMatch = function(startPos) {
|
||||
this.matchRegExp.lastIndex = startPos;
|
||||
this.match = this.matchRegExp.exec(this.parser.source);
|
||||
if(this.match) {
|
||||
this.endMatchRegExp.lastIndex = startPos + this.match[0].length;
|
||||
this.endMatchRegExp.lastIndex = this.match.index + this.match[0].length;
|
||||
this.endMatch = this.endMatchRegExp.exec(this.parser.source);
|
||||
if(this.endMatch) {
|
||||
return this.match.index;
|
||||
|
||||
@@ -53,17 +53,12 @@ exports.parse = function() {
|
||||
tag.isBlock = this.is.block || hasLineBreak;
|
||||
// Parse the body if we need to
|
||||
if(!tag.isSelfClosing && $tw.config.htmlVoidElements.indexOf(tag.tag) === -1) {
|
||||
var reEndString = "</" + $tw.utils.escapeRegExp(tag.tag) + ">",
|
||||
reEnd = new RegExp("(" + reEndString + ")","mg");
|
||||
var reEndString = "</" + $tw.utils.escapeRegExp(tag.tag) + ">";
|
||||
if(hasLineBreak) {
|
||||
tag.children = this.parser.parseBlocks(reEndString);
|
||||
} else {
|
||||
tag.children = this.parser.parseInlineRun(reEnd);
|
||||
}
|
||||
reEnd.lastIndex = this.parser.pos;
|
||||
var endMatch = reEnd.exec(this.parser.source);
|
||||
if(endMatch && endMatch.index === this.parser.pos) {
|
||||
this.parser.pos = endMatch.index + endMatch[0].length;
|
||||
var reEnd = new RegExp("(" + reEndString + ")","mg");
|
||||
tag.children = this.parser.parseInlineRun(reEnd,{eatTerminator: true});
|
||||
}
|
||||
}
|
||||
// Return the tag
|
||||
|
||||
@@ -36,7 +36,7 @@ exports.parse = function() {
|
||||
// Move past the pragma invocation
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
// Parse the filter terminated by a line break
|
||||
var reMatch = /(.*)(\r?\n)|$/mg;
|
||||
var reMatch = /(.*)(?:$|\r?\n)/mg;
|
||||
reMatch.lastIndex = this.parser.pos;
|
||||
var match = reMatch.exec(this.parser.source);
|
||||
this.parser.pos = reMatch.lastIndex;
|
||||
|
||||
@@ -21,40 +21,36 @@ exports.types = {block: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*?)>>(?:\r?\n|$)/mg;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
var nextStart = startPos;
|
||||
// Try parsing at all possible macrocall openers until we match
|
||||
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
|
||||
var nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart);
|
||||
if(nextCall) {
|
||||
var c = this.parser.source.charAt(nextCall.end);
|
||||
// Ensure EOL after parsed macro
|
||||
// If we didn't need to support IE, we'd just use /(?:\r?\n|$)/ym
|
||||
if ((c === "") || (c === "\n") || ((c === "\r") && this.parser.source.charAt(nextCall.end+1) === "\n")) {
|
||||
this.nextCall = nextCall;
|
||||
return nextStart;
|
||||
}
|
||||
}
|
||||
nextStart += 2;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get all the details of the match
|
||||
var macroName = this.match[1],
|
||||
paramString = this.match[2];
|
||||
// Move past the macro call
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
var params = [],
|
||||
reParam = /\s*(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|([^"'\s]+)))/mg,
|
||||
paramMatch = reParam.exec(paramString);
|
||||
while(paramMatch) {
|
||||
// Process this parameter
|
||||
var paramInfo = {
|
||||
value: paramMatch[2] || paramMatch[3] || paramMatch[4] || paramMatch[5] || paramMatch[6]
|
||||
};
|
||||
if(paramMatch[1]) {
|
||||
paramInfo.name = paramMatch[1];
|
||||
}
|
||||
params.push(paramInfo);
|
||||
// Find the next match
|
||||
paramMatch = reParam.exec(paramString);
|
||||
}
|
||||
return [{
|
||||
type: "macrocall",
|
||||
name: macroName,
|
||||
params: params,
|
||||
isBlock: true
|
||||
}];
|
||||
var call = this.nextCall;
|
||||
call.isBlock = true;
|
||||
this.nextCall = null;
|
||||
this.parser.pos = call.end;
|
||||
return [call];
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -21,39 +21,29 @@ exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
// Regexp to match
|
||||
this.matchRegExp = /<<([^\s>]+)\s*([\s\S]*?)>>/mg;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
var nextStart = startPos;
|
||||
// Try parsing at all possible macrocall openers until we match
|
||||
while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) {
|
||||
this.nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart);
|
||||
if(this.nextCall) {
|
||||
return nextStart;
|
||||
}
|
||||
nextStart += 2;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the most recent match
|
||||
*/
|
||||
exports.parse = function() {
|
||||
// Get all the details of the match
|
||||
var macroName = this.match[1],
|
||||
paramString = this.match[2];
|
||||
// Move past the macro call
|
||||
this.parser.pos = this.matchRegExp.lastIndex;
|
||||
var params = [],
|
||||
reParam = /\s*(?:([A-Za-z0-9\-_]+)\s*:)?(?:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|\[\[([^\]]*)\]\]|([^"'\s]+)))/mg,
|
||||
paramMatch = reParam.exec(paramString);
|
||||
while(paramMatch) {
|
||||
// Process this parameter
|
||||
var paramInfo = {
|
||||
value: paramMatch[2] || paramMatch[3] || paramMatch[4] || paramMatch[5]|| paramMatch[6]
|
||||
};
|
||||
if(paramMatch[1]) {
|
||||
paramInfo.name = paramMatch[1];
|
||||
}
|
||||
params.push(paramInfo);
|
||||
// Find the next match
|
||||
paramMatch = reParam.exec(paramString);
|
||||
}
|
||||
return [{
|
||||
type: "macrocall",
|
||||
name: macroName,
|
||||
params: params
|
||||
}];
|
||||
var call = this.nextCall;
|
||||
this.nextCall = null;
|
||||
this.parser.pos = call.end;
|
||||
return [call];
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -161,7 +161,7 @@ exports.parse = function() {
|
||||
// Move the caption to the first row if it isn't already
|
||||
if(table.children.length !== 1) {
|
||||
table.children.pop(); // Take rowContainer out of the children array
|
||||
table.children.splice(0,0,rowContainer); // Insert it at the bottom
|
||||
table.children.splice(0,0,rowContainer); // Insert it at the bottom
|
||||
}
|
||||
// Set the alignment - TODO: figure out why TW did this
|
||||
// rowContainer.attributes.align = rowCount === 0 ? "top" : "bottom";
|
||||
|
||||
@@ -25,6 +25,14 @@ Attributes are stored as hashmaps of the following objects:
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
type: content type of text
|
||||
text: text to be parsed
|
||||
options: see below:
|
||||
parseAsInline: true to parse text as inline instead of block
|
||||
wiki: reference to wiki to use
|
||||
_canonical_uri: optional URI of content if text is missing or empty
|
||||
*/
|
||||
var WikiParser = function(type,text,options) {
|
||||
this.wiki = options.wiki;
|
||||
var self = this;
|
||||
@@ -33,19 +41,6 @@ var WikiParser = function(type,text,options) {
|
||||
this.loadRemoteTiddler(options._canonical_uri);
|
||||
text = $tw.language.getRawString("LazyLoadingWarning");
|
||||
}
|
||||
// Initialise the classes if we don't have them already
|
||||
if(!this.pragmaRuleClasses) {
|
||||
WikiParser.prototype.pragmaRuleClasses = $tw.modules.createClassesFromModules("wikirule","pragma",$tw.WikiRuleBase);
|
||||
this.setupRules(WikiParser.prototype.pragmaRuleClasses,"$:/config/WikiParserRules/Pragmas/");
|
||||
}
|
||||
if(!this.blockRuleClasses) {
|
||||
WikiParser.prototype.blockRuleClasses = $tw.modules.createClassesFromModules("wikirule","block",$tw.WikiRuleBase);
|
||||
this.setupRules(WikiParser.prototype.blockRuleClasses,"$:/config/WikiParserRules/Block/");
|
||||
}
|
||||
if(!this.inlineRuleClasses) {
|
||||
WikiParser.prototype.inlineRuleClasses = $tw.modules.createClassesFromModules("wikirule","inline",$tw.WikiRuleBase);
|
||||
this.setupRules(WikiParser.prototype.inlineRuleClasses,"$:/config/WikiParserRules/Inline/");
|
||||
}
|
||||
// Save the parse text
|
||||
this.type = type || "text/vnd.tiddlywiki";
|
||||
this.source = text || "";
|
||||
@@ -54,13 +49,38 @@ var WikiParser = function(type,text,options) {
|
||||
this.configTrimWhiteSpace = false;
|
||||
// Set current parse position
|
||||
this.pos = 0;
|
||||
// Instantiate the pragma parse rules
|
||||
this.pragmaRules = this.instantiateRules(this.pragmaRuleClasses,"pragma",0);
|
||||
// Instantiate the parser block and inline rules
|
||||
this.blockRules = this.instantiateRules(this.blockRuleClasses,"block",0);
|
||||
this.inlineRules = this.instantiateRules(this.inlineRuleClasses,"inline",0);
|
||||
// Parse any pragmas
|
||||
// Start with empty output
|
||||
this.tree = [];
|
||||
// Assemble the rule classes we're going to use
|
||||
var pragmaRuleClasses, blockRuleClasses, inlineRuleClasses;
|
||||
if(options.rules) {
|
||||
pragmaRuleClasses = options.rules.pragma;
|
||||
blockRuleClasses = options.rules.block;
|
||||
inlineRuleClasses = options.rules.inline;
|
||||
} else {
|
||||
// Setup the rule classes if we don't have them already
|
||||
if(!this.pragmaRuleClasses) {
|
||||
WikiParser.prototype.pragmaRuleClasses = $tw.modules.createClassesFromModules("wikirule","pragma",$tw.WikiRuleBase);
|
||||
this.setupRules(WikiParser.prototype.pragmaRuleClasses,"$:/config/WikiParserRules/Pragmas/");
|
||||
}
|
||||
pragmaRuleClasses = this.pragmaRuleClasses;
|
||||
if(!this.blockRuleClasses) {
|
||||
WikiParser.prototype.blockRuleClasses = $tw.modules.createClassesFromModules("wikirule","block",$tw.WikiRuleBase);
|
||||
this.setupRules(WikiParser.prototype.blockRuleClasses,"$:/config/WikiParserRules/Block/");
|
||||
}
|
||||
blockRuleClasses = this.blockRuleClasses;
|
||||
if(!this.inlineRuleClasses) {
|
||||
WikiParser.prototype.inlineRuleClasses = $tw.modules.createClassesFromModules("wikirule","inline",$tw.WikiRuleBase);
|
||||
this.setupRules(WikiParser.prototype.inlineRuleClasses,"$:/config/WikiParserRules/Inline/");
|
||||
}
|
||||
inlineRuleClasses = this.inlineRuleClasses;
|
||||
}
|
||||
// Instantiate the pragma parse rules
|
||||
this.pragmaRules = this.instantiateRules(pragmaRuleClasses,"pragma",0);
|
||||
// Instantiate the parser block and inline rules
|
||||
this.blockRules = this.instantiateRules(blockRuleClasses,"block",0);
|
||||
this.inlineRules = this.instantiateRules(inlineRuleClasses,"inline",0);
|
||||
// Parse any pragmas
|
||||
var topBranch = this.parsePragmas();
|
||||
// Parse the text into inline runs or blocks
|
||||
if(options.parseAsInline) {
|
||||
@@ -358,7 +378,7 @@ WikiParser.prototype.pushTextWidget = function(array,text) {
|
||||
text = $tw.utils.trim(text);
|
||||
}
|
||||
if(text) {
|
||||
array.push({type: "text", text: text});
|
||||
array.push({type: "text", text: text});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,9 +89,12 @@ PutSaver.prototype.save = function(text,method,callback) {
|
||||
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);
|
||||
if(status === 412) { // file changed on server
|
||||
callback($tw.language.getString("Error/PutEditConflict"));
|
||||
} else if(status === 401) { // authentication required
|
||||
callback($tw.language.getString("Error/PutUnauthorized"));
|
||||
} else if(status === 403) { // permission denied
|
||||
callback($tw.language.getString("Error/PutForbidden"));
|
||||
} else {
|
||||
callback(err); // fail
|
||||
}
|
||||
|
||||
@@ -28,17 +28,29 @@ UploadSaver.prototype.save = function(text,method,callback) {
|
||||
password = $tw.utils.getPassword("upload"),
|
||||
uploadDir = this.wiki.getTextReference("$:/UploadDir") || ".",
|
||||
uploadFilename = this.wiki.getTextReference("$:/UploadFilename") || "index.html",
|
||||
uploadWithUrlOnly = this.wiki.getTextReference("$:/UploadWithUrlOnly") || "no",
|
||||
url = this.wiki.getTextReference("$:/UploadURL");
|
||||
// Bail out if we don't have the bits we need
|
||||
if(!username || username.toString().trim() === "" || !password || password.toString().trim() === "") {
|
||||
return false;
|
||||
if (uploadWithUrlOnly === "yes") {
|
||||
// The url is good enough. No need for a username and password.
|
||||
// Assume the server uses some other kind of auth mechanism.
|
||||
if(!url || url.toString().trim() === "") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Require username and password to be present.
|
||||
// Assume the server uses the standard UploadPlugin username/password.
|
||||
if(!username || username.toString().trim() === "" || !password || password.toString().trim() === "") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Construct the url if not provided
|
||||
if(!url) {
|
||||
url = "http://" + username + ".tiddlyspot.com/store.cgi";
|
||||
}
|
||||
// Assemble the header
|
||||
var boundary = "---------------------------" + "AaB03x";
|
||||
var boundary = "---------------------------" + "AaB03x";
|
||||
var uploadFormName = "UploadPlugin";
|
||||
var head = [];
|
||||
head.push("--" + boundary + "\r\nContent-disposition: form-data; name=\"UploadPlugin\"\r\n");
|
||||
|
||||
@@ -17,9 +17,8 @@ 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");
|
||||
state.sendResponse(200,{"Content-Type": "image/x-icon"},buffer,"base64");
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
@@ -34,10 +34,7 @@ exports.handler = function(request,response,state) {
|
||||
content = content;
|
||||
type = ($tw.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : "application/octet-stream");
|
||||
}
|
||||
response.writeHead(status,{
|
||||
"Content-Type": type
|
||||
});
|
||||
response.end(content);
|
||||
state.sendResponse(status,{"Content-Type": type},content);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -12,38 +12,16 @@ GET /
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var zlib = require("zlib");
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/$/;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
var acceptEncoding = request.headers["accept-encoding"];
|
||||
if(!acceptEncoding) {
|
||||
acceptEncoding = "";
|
||||
}
|
||||
var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")),
|
||||
responseHeaders = {
|
||||
"Content-Type": state.server.get("root-serve-type")
|
||||
};
|
||||
/*
|
||||
If the gzip=yes flag for `listen` is set, check if the user agent permits
|
||||
compression. If so, compress our response. Note that we use the synchronous
|
||||
functions from zlib to stay in the imperative style. The current `Server`
|
||||
doesn't depend on this, and we may just as well use the async versions.
|
||||
*/
|
||||
if(state.server.enableGzip) {
|
||||
if (/\bdeflate\b/.test(acceptEncoding)) {
|
||||
responseHeaders["Content-Encoding"] = "deflate";
|
||||
text = zlib.deflateSync(text);
|
||||
} else if (/\bgzip\b/.test(acceptEncoding)) {
|
||||
responseHeaders["Content-Encoding"] = "gzip";
|
||||
text = zlib.gzipSync(text);
|
||||
}
|
||||
}
|
||||
response.writeHead(200,responseHeaders);
|
||||
response.end(text);
|
||||
state.sendResponse(200,responseHeaders,text);
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
@@ -22,11 +22,12 @@ exports.handler = function(request,response,state) {
|
||||
response.writeHead(401,{
|
||||
"WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"'
|
||||
});
|
||||
response.end();
|
||||
response.end();
|
||||
} else {
|
||||
// Redirect to the root wiki if login worked
|
||||
var location = ($tw.syncadaptor && $tw.syncadaptor.host)? $tw.syncadaptor.host: "/";
|
||||
response.writeHead(302,{
|
||||
Location: "/"
|
||||
Location: location
|
||||
});
|
||||
response.end();
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ 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),
|
||||
sse_enabled: state.server.get("sse-enabled") === "yes",
|
||||
space: {
|
||||
recipe: "default"
|
||||
},
|
||||
tiddlywiki_version: $tw.version
|
||||
});
|
||||
response.end(text,"utf8");
|
||||
state.sendResponse(200,{"Content-Type": "application/json"},text,"utf8");
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
@@ -32,9 +32,9 @@ exports.handler = function(request,response,state) {
|
||||
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");
|
||||
state.sendResponse(200,{},text,"utf8");
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
|
||||
@@ -36,8 +36,7 @@ exports.handler = function(request,response,state) {
|
||||
tiddlerFields.revision = state.wiki.getChangeCount(title);
|
||||
tiddlerFields.bag = "default";
|
||||
tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki";
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
response.end(JSON.stringify(tiddlerFields),"utf8");
|
||||
state.sendResponse(200,{"Content-Type": "application/json"},JSON.stringify(tiddlerFields),"utf8");
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
|
||||
@@ -33,7 +33,6 @@ exports.handler = function(request,response,state) {
|
||||
}
|
||||
var excludeFields = (state.queryParameters.exclude || "text").split(","),
|
||||
titles = state.wiki.filterTiddlers(filter);
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
var tiddlers = [];
|
||||
$tw.utils.each(titles,function(title) {
|
||||
var tiddler = state.wiki.getTiddler(title);
|
||||
@@ -45,7 +44,7 @@ exports.handler = function(request,response,state) {
|
||||
}
|
||||
});
|
||||
var text = JSON.stringify(tiddlers);
|
||||
response.end(text,"utf8");
|
||||
state.sendResponse(200,{"Content-Type": "application/json"},text,"utf8");
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
70
core/modules/server/server-sent-events.js
Normal file
70
core/modules/server/server-sent-events.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/*\
|
||||
title: $:/core/modules/server/server-sent-events.js
|
||||
type: application/javascript
|
||||
module-type: library
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
parameters:
|
||||
prefix - usually the plugin path, such as `plugins/tiddlywiki/tiddlyweb`. The
|
||||
route will match `/events/${prefix}` exactly.
|
||||
|
||||
handler - a function that will be called each time a request comes in with the
|
||||
request and state from the route and an emit function to call.
|
||||
*/
|
||||
|
||||
var ServerSentEvents = function ServerSentEvents(prefix, handler) {
|
||||
this.handler = handler;
|
||||
this.prefix = prefix;
|
||||
};
|
||||
|
||||
ServerSentEvents.prototype.getExports = function() {
|
||||
return {
|
||||
bodyFormat: "stream",
|
||||
method: "GET",
|
||||
path: new RegExp("^/events/" + this.prefix + "$"),
|
||||
handler: this.handleEventRequest.bind(this)
|
||||
};
|
||||
};
|
||||
|
||||
ServerSentEvents.prototype.handleEventRequest = function(request,response,state) {
|
||||
if(ServerSentEvents.prototype.isEventStreamRequest(request)) {
|
||||
response.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive"
|
||||
});
|
||||
this.handler(request,state,this.emit.bind(this,response),this.end.bind(this,response));
|
||||
} else {
|
||||
response.writeHead(406,"Not Acceptable",{});
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
ServerSentEvents.prototype.isEventStreamRequest = function(request) {
|
||||
return request.headers.accept &&
|
||||
request.headers.accept.match(/^text\/event-stream/);
|
||||
};
|
||||
|
||||
ServerSentEvents.prototype.emit = function(response,event,data) {
|
||||
if(typeof event !== "string" || event.indexOf("\n") !== -1) {
|
||||
throw new Error("Type must be a single-line string");
|
||||
}
|
||||
if(typeof data !== "string" || data.indexOf("\n") !== -1) {
|
||||
throw new Error("Data must be a single-line string");
|
||||
}
|
||||
response.write("event: " + event + "\ndata: " + data + "\n\n", "utf8");
|
||||
};
|
||||
|
||||
ServerSentEvents.prototype.end = function(response) {
|
||||
response.end();
|
||||
};
|
||||
|
||||
exports.ServerSentEvents = ServerSentEvents;
|
||||
|
||||
})();
|
||||
@@ -17,7 +17,9 @@ if($tw.node) {
|
||||
fs = require("fs"),
|
||||
url = require("url"),
|
||||
path = require("path"),
|
||||
querystring = require("querystring");
|
||||
querystring = require("querystring"),
|
||||
crypto = require("crypto"),
|
||||
zlib = require("zlib");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -40,13 +42,15 @@ function Server(options) {
|
||||
if(options.variables[variable]) {
|
||||
this.variables[variable] = options.variables[variable];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$tw.utils.extend({},this.defaultVariables,options.variables);
|
||||
// Initialise CSRF
|
||||
this.csrfDisable = this.get("csrf-disable") === "yes";
|
||||
// Initialize Gzip compression
|
||||
this.enableGzip = this.get("gzip") === "yes";
|
||||
// Initialize browser-caching
|
||||
this.enableBrowserCache = this.get("use-browser-cache") === "yes";
|
||||
// Initialise authorization
|
||||
var authorizedUserName = (this.get("username") && this.get("password")) ? this.get("username") : "(anon)";
|
||||
this.authorizationPrincipals = {
|
||||
@@ -78,6 +82,71 @@ function Server(options) {
|
||||
this.transport = require(this.protocol);
|
||||
}
|
||||
|
||||
/*
|
||||
Send a response to the client. This method checks if the response must be sent
|
||||
or if the client alrady has the data cached. If that's the case only a 304
|
||||
response will be transmitted and the browser will use the cached data.
|
||||
Only requests with status code 200 are considdered for caching.
|
||||
request: request instance passed to the handler
|
||||
response: response instance passed to the handler
|
||||
statusCode: stauts code to send to the browser
|
||||
headers: response headers (they will be augmented with an `Etag` header)
|
||||
data: the data to send (passed to the end method of the response instance)
|
||||
encoding: the encoding of the data to send (passed to the end method of the response instance)
|
||||
*/
|
||||
function sendResponse(request,response,statusCode,headers,data,encoding) {
|
||||
if(this.enableBrowserCache && (statusCode == 200)) {
|
||||
var hash = crypto.createHash('md5');
|
||||
// Put everything into the hash that could change and invalidate the data that
|
||||
// the browser already stored. The headers the data and the encoding.
|
||||
hash.update(data);
|
||||
hash.update(JSON.stringify(headers));
|
||||
if(encoding) {
|
||||
hash.update(encoding);
|
||||
}
|
||||
var contentDigest = hash.digest("hex");
|
||||
// RFC 7232 section 2.3 mandates for the etag to be enclosed in quotes
|
||||
headers["Etag"] = '"' + contentDigest + '"';
|
||||
headers["Cache-Control"] = "max-age=0, must-revalidate";
|
||||
// Check if any of the hashes contained within the if-none-match header
|
||||
// matches the current hash.
|
||||
// If one matches, do not send the data but tell the browser to use the
|
||||
// cached data.
|
||||
// We do not implement "*" as it makes no sense here.
|
||||
var ifNoneMatch = request.headers["if-none-match"];
|
||||
if(ifNoneMatch) {
|
||||
var matchParts = ifNoneMatch.split(",").map(function(etag) {
|
||||
return etag.replace(/^[ "]+|[ "]+$/g, "");
|
||||
});
|
||||
if(matchParts.indexOf(contentDigest) != -1) {
|
||||
response.writeHead(304,headers);
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
If the gzip=yes is set, check if the user agent permits compression. If so,
|
||||
compress our response if the raw data is bigger than 2k. Compressing less
|
||||
data is inefficient. Note that we use the synchronous functions from zlib
|
||||
to stay in the imperative style. The current `Server` doesn't depend on
|
||||
this, and we may just as well use the async versions.
|
||||
*/
|
||||
if(this.enableGzip && (data.length > 2048)) {
|
||||
var acceptEncoding = request.headers["accept-encoding"] || "";
|
||||
if(/\bdeflate\b/.test(acceptEncoding)) {
|
||||
headers["Content-Encoding"] = "deflate";
|
||||
data = zlib.deflateSync(data);
|
||||
} else if(/\bgzip\b/.test(acceptEncoding)) {
|
||||
headers["Content-Encoding"] = "gzip";
|
||||
data = zlib.gzipSync(data);
|
||||
}
|
||||
}
|
||||
|
||||
response.writeHead(statusCode,headers);
|
||||
response.end(data,encoding);
|
||||
}
|
||||
|
||||
Server.prototype.defaultVariables = {
|
||||
port: "8080",
|
||||
host: "127.0.0.1",
|
||||
@@ -89,7 +158,8 @@ Server.prototype.defaultVariables = {
|
||||
"system-tiddler-render-type": "text/plain",
|
||||
"system-tiddler-render-template": "$:/core/templates/wikified-tiddler",
|
||||
"debug-level": "none",
|
||||
"gzip": "no"
|
||||
"gzip": "no",
|
||||
"use-browser-cache": "no"
|
||||
};
|
||||
|
||||
Server.prototype.get = function(name) {
|
||||
@@ -167,13 +237,14 @@ Server.prototype.requestHandler = function(request,response,options) {
|
||||
state.urlInfo = url.parse(request.url);
|
||||
state.queryParameters = querystring.parse(state.urlInfo.query);
|
||||
state.pathPrefix = options.pathPrefix || this.get("path-prefix") || "";
|
||||
state.sendResponse = sendResponse.bind(self,request,response);
|
||||
// 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;
|
||||
return;
|
||||
}
|
||||
// Check whether anonymous access is granted
|
||||
state.allowAnon = this.isAuthorized(authorizationType,null);
|
||||
@@ -182,7 +253,7 @@ Server.prototype.requestHandler = function(request,response,options) {
|
||||
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)) {
|
||||
|
||||
@@ -17,7 +17,7 @@ exports.name = "favicon";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["startup"];
|
||||
exports.synchronous = true;
|
||||
|
||||
|
||||
// Favicon tiddler
|
||||
var FAVICON_TITLE = "$:/favicon.ico";
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ exports.startup = function() {
|
||||
if($tw.node) {
|
||||
$tw.modules.applyMethods("utils-node",$tw.utils);
|
||||
}
|
||||
if($tw.browser) {
|
||||
$tw.modules.applyMethods("utils-browser",$tw.utils);
|
||||
}
|
||||
$tw.modules.applyMethods("global",$tw);
|
||||
$tw.modules.applyMethods("config",$tw.config);
|
||||
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
|
||||
|
||||
@@ -82,7 +82,7 @@ exports.startup = function() {
|
||||
var onlyThrottledTiddlersHaveChanged = true;
|
||||
for(var title in changes) {
|
||||
var tiddler = $tw.wiki.getTiddler(title);
|
||||
if(!tiddler || !(tiddler.hasField("draft.of") || tiddler.hasField("throttle.refresh"))) {
|
||||
if(!$tw.wiki.isVolatileTiddler(title) && (!tiddler || !(tiddler.hasField("draft.of") || tiddler.hasField("throttle.refresh")))) {
|
||||
onlyThrottledTiddlersHaveChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ exports.startup = function() {
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tm-show-switcher",function(event) {
|
||||
$tw.modal.display("$:/core/ui/SwitcherModal",{variables: event.paramObject, event: event});
|
||||
});
|
||||
});
|
||||
// Install the notification mechanism
|
||||
$tw.notifier = new $tw.utils.Notifier($tw.wiki);
|
||||
$tw.rootWidget.addEventListener("tm-notify",function(event) {
|
||||
@@ -68,7 +68,7 @@ exports.startup = function() {
|
||||
fullScreenDocument[fullscreen._exitFullscreen]();
|
||||
} else {
|
||||
fullScreenDocument.documentElement[fullscreen._requestFullscreen](Element.ALLOW_KEYBOARD_INPUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ var PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE = "$:/config/Performance/Instrument
|
||||
var widget = require("$:/core/modules/widgets/widget.js");
|
||||
|
||||
exports.startup = function() {
|
||||
var modules,n,m,f;
|
||||
// Minimal browser detection
|
||||
if($tw.browser) {
|
||||
$tw.browser.isIE = (/msie|trident/i.test(navigator.userAgent));
|
||||
@@ -66,10 +65,10 @@ exports.startup = function() {
|
||||
// Execute any startup actions
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction");
|
||||
if($tw.browser) {
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser");
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser");
|
||||
}
|
||||
if($tw.node) {
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node");
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node");
|
||||
}
|
||||
// Kick off the language manager and switcher
|
||||
$tw.language = new $tw.Language();
|
||||
|
||||
@@ -101,7 +101,7 @@ exports.startup = function() {
|
||||
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"
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -165,7 +165,7 @@ function openStartupTiddlers(options) {
|
||||
story.addToHistory(target);
|
||||
} else if(storyList.length > 0) {
|
||||
story.addToHistory(storyList[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) {
|
||||
}
|
||||
if(duration) {
|
||||
// Scroll the node into view
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement});
|
||||
this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement});
|
||||
} else {
|
||||
targetElement.scrollIntoView();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ Syncer.prototype.titleIsAnonymous = "$:/status/IsAnonymous";
|
||||
Syncer.prototype.titleIsReadOnly = "$:/status/IsReadOnly";
|
||||
Syncer.prototype.titleUserName = "$:/status/UserName";
|
||||
Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter";
|
||||
Syncer.prototype.titleSyncDisablePolling = "$:/config/SyncDisablePolling";
|
||||
Syncer.prototype.titleSyncPollingInterval = "$:/config/SyncPollingInterval";
|
||||
Syncer.prototype.titleSyncDisableLazyLoading = "$:/config/SyncDisableLazyLoading";
|
||||
Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done";
|
||||
@@ -89,7 +90,7 @@ function Syncer(options) {
|
||||
if(filteredChanges.length > 0) {
|
||||
self.processTaskQueue();
|
||||
} else {
|
||||
// Look for deletions of tiddlers we're already syncing
|
||||
// Look for deletions of tiddlers we're already syncing
|
||||
var outstandingDeletion = false
|
||||
$tw.utils.each(changes,function(change,title,object) {
|
||||
if(change.deleted && $tw.utils.hop(self.tiddlerInfo,title)) {
|
||||
@@ -121,7 +122,7 @@ function Syncer(options) {
|
||||
self.login(username,password,function() {});
|
||||
} else {
|
||||
// No username and password, so we display a prompt
|
||||
self.handleLoginEvent();
|
||||
self.handleLoginEvent();
|
||||
}
|
||||
});
|
||||
$tw.rootWidget.addEventListener("tm-logout",function() {
|
||||
@@ -138,7 +139,7 @@ function Syncer(options) {
|
||||
if(!this.disableUI && this.wiki.getTiddlerText(this.titleSyncDisableLazyLoading) !== "yes") {
|
||||
this.wiki.addEventListener("lazyLoad",function(title) {
|
||||
self.handleLazyLoadEvent(title);
|
||||
});
|
||||
});
|
||||
}
|
||||
// Get the login status
|
||||
this.getStatus(function(err,isLoggedIn) {
|
||||
@@ -173,8 +174,8 @@ Syncer.prototype.getTiddlerRevision = function(title) {
|
||||
if(this.syncadaptor && this.syncadaptor.getTiddlerRevision) {
|
||||
return this.syncadaptor.getTiddlerRevision(title);
|
||||
} else {
|
||||
return this.wiki.getTiddler(title).fields.revision;
|
||||
}
|
||||
return this.wiki.getTiddler(title).fields.revision;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -267,7 +268,7 @@ 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,isReadOnly,isAnonymous,isPollingDisabled) {
|
||||
if(err) {
|
||||
self.logger.alert(err);
|
||||
} else {
|
||||
@@ -278,6 +279,9 @@ Syncer.prototype.getStatus = function(callback) {
|
||||
if(isLoggedIn) {
|
||||
self.wiki.addTiddler({title: self.titleUserName,text: username || ""});
|
||||
}
|
||||
if(isPollingDisabled) {
|
||||
self.wiki.addTiddler({title: self.titleSyncDisablePolling, text: "yes"});
|
||||
}
|
||||
}
|
||||
// Invoke the callback
|
||||
if(callback) {
|
||||
@@ -301,12 +305,15 @@ Syncer.prototype.syncFromServer = function() {
|
||||
}
|
||||
},
|
||||
triggerNextSync = function() {
|
||||
self.pollTimerId = setTimeout(function() {
|
||||
self.pollTimerId = null;
|
||||
self.syncFromServer.call(self);
|
||||
},self.pollTimerInterval);
|
||||
if(pollingEnabled) {
|
||||
self.pollTimerId = setTimeout(function() {
|
||||
self.pollTimerId = null;
|
||||
self.syncFromServer.call(self);
|
||||
},self.pollTimerInterval);
|
||||
}
|
||||
},
|
||||
syncSystemFromServer = (self.wiki.getTiddlerText("$:/config/SyncSystemTiddlersFromServer") === "yes" ? true : false);
|
||||
syncSystemFromServer = (self.wiki.getTiddlerText("$:/config/SyncSystemTiddlersFromServer") === "yes"),
|
||||
pollingEnabled = (self.wiki.getTiddlerText(self.titleSyncDisablePolling) !== "yes");
|
||||
if(this.syncadaptor && this.syncadaptor.getUpdatedTiddlers) {
|
||||
this.logger.log("Retrieving updated tiddler list");
|
||||
cancelNextSync();
|
||||
@@ -329,7 +336,7 @@ Syncer.prototype.syncFromServer = function() {
|
||||
});
|
||||
if(updates.modifications.length > 0 || updates.deletions.length > 0) {
|
||||
self.processTaskQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) {
|
||||
@@ -509,7 +516,7 @@ Syncer.prototype.processTaskQueue = function() {
|
||||
} else {
|
||||
self.updateDirtyStatus();
|
||||
// Process the next task
|
||||
self.processTaskQueue.call(self);
|
||||
self.processTaskQueue.call(self);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -517,11 +524,11 @@ Syncer.prototype.processTaskQueue = function() {
|
||||
this.updateDirtyStatus();
|
||||
// And trigger a timeout if there is a pending task
|
||||
if(task === true) {
|
||||
this.triggerTimeout();
|
||||
this.triggerTimeout();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.updateDirtyStatus();
|
||||
this.updateDirtyStatus();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -555,7 +562,7 @@ Syncer.prototype.chooseNextTask = function() {
|
||||
isReadyToSave = !tiddlerInfo || !tiddlerInfo.timestampLastSaved || tiddlerInfo.timestampLastSaved < thresholdLastSaved;
|
||||
if(hasChanged) {
|
||||
if(isReadyToSave) {
|
||||
return new SaveTiddlerTask(this,title);
|
||||
return new SaveTiddlerTask(this,title);
|
||||
} else {
|
||||
havePending = true;
|
||||
}
|
||||
@@ -594,7 +601,10 @@ SaveTiddlerTask.prototype.run = function(callback) {
|
||||
tiddler = this.syncer.wiki.tiddlerExists(this.title) && this.syncer.wiki.getTiddler(this.title);
|
||||
this.syncer.logger.log("Dispatching 'save' task:",this.title);
|
||||
if(tiddler) {
|
||||
this.syncer.syncadaptor.saveTiddler(tiddler,function(err,adaptorInfo,revision) {
|
||||
this.syncer.syncadaptor.saveTiddler(tiddler,{
|
||||
changeCount: changeCount,
|
||||
tiddlerInfo: self.syncer.tiddlerInfo[self.title]
|
||||
},function(err,adaptorInfo,revision) {
|
||||
// If there's an error, exit without changing any internal state
|
||||
if(err) {
|
||||
return callback(err);
|
||||
@@ -608,8 +618,6 @@ SaveTiddlerTask.prototype.run = function(callback) {
|
||||
};
|
||||
// Invoke the callback
|
||||
callback(null);
|
||||
},{
|
||||
tiddlerInfo: self.syncer.tiddlerInfo[self.title]
|
||||
});
|
||||
} else {
|
||||
this.syncer.logger.log(" Not Dispatching 'save' task:",this.title,"tiddler does not exist");
|
||||
@@ -626,21 +634,17 @@ function DeleteTiddlerTask(syncer,title) {
|
||||
DeleteTiddlerTask.prototype.run = function(callback) {
|
||||
var self = this;
|
||||
this.syncer.logger.log("Dispatching 'delete' task:",this.title);
|
||||
this.syncer.syncadaptor.deleteTiddler(this.title,function(err) {
|
||||
this.syncer.syncadaptor.deleteTiddler(this.title,{
|
||||
tiddlerInfo: self.syncer.tiddlerInfo[this.title]
|
||||
},function(err,adaptorInfo) {
|
||||
// If there's an error, exit without changing any internal state
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
// Remove the info stored about this tiddler
|
||||
delete self.syncer.tiddlerInfo[self.title];
|
||||
if($tw.boot.files){
|
||||
// Remove the tiddler from $tw.boot.files
|
||||
delete $tw.boot.files[self.title];
|
||||
}
|
||||
// Invoke the callback
|
||||
callback(null);
|
||||
},{
|
||||
tiddlerInfo: self.syncer.tiddlerInfo[this.title]
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ Upgrader module that suppresses certain system tiddlers that shouldn't be import
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var DONT_IMPORT_LIST = ["$:/StoryList","$:/HistoryList"],
|
||||
DONT_IMPORT_PREFIX_LIST = ["$:/temp/","$:/state/","$:/Import"],
|
||||
var DONT_IMPORT_LIST = ["$:/Import"],
|
||||
UNSELECT_PREFIX_LIST = ["$:/temp/","$:/state/","$:/StoryList","$:/HistoryList"],
|
||||
WARN_IMPORT_PREFIX_LIST = ["$:/core/modules/"];
|
||||
|
||||
exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
@@ -26,11 +26,10 @@ exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
tiddlers[title] = Object.create(null);
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/System/Suppressed");
|
||||
} else {
|
||||
for(var t=0; t<DONT_IMPORT_PREFIX_LIST.length; t++) {
|
||||
var prefix = DONT_IMPORT_PREFIX_LIST[t];
|
||||
for(var t=0; t<UNSELECT_PREFIX_LIST.length; t++) {
|
||||
var prefix = UNSELECT_PREFIX_LIST[t];
|
||||
if(title.substr(0,prefix.length) === prefix) {
|
||||
tiddlers[title] = Object.create(null);
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/State/Suppressed");
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/Tiddler/Unselected");
|
||||
}
|
||||
}
|
||||
for(var t=0; t<WARN_IMPORT_PREFIX_LIST.length; t++) {
|
||||
|
||||
@@ -38,7 +38,7 @@ exports.parseCsvStringWithHeader = function(text,options) {
|
||||
var columnName = headings[column];
|
||||
columnResult[columnName] = $tw.utils.trim(columns[column] || "");
|
||||
}
|
||||
results.push(columnResult);
|
||||
results.push(columnResult);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ exports.domContains = function(a,b) {
|
||||
!!(a.compareDocumentPosition(b) & 16);
|
||||
};
|
||||
|
||||
exports.domMatchesSelector = function(node,selector) {
|
||||
return node.matches ? node.matches(selector) : node.msMatchesSelector(selector);
|
||||
};
|
||||
|
||||
exports.removeChildren = function(node) {
|
||||
while(node.hasChildNodes()) {
|
||||
node.removeChild(node.firstChild);
|
||||
@@ -65,7 +69,7 @@ Get the first parent element that has scrollbars or use the body as fallback.
|
||||
*/
|
||||
exports.getScrollContainer = function(el) {
|
||||
var doc = el.ownerDocument;
|
||||
while(el.parentNode) {
|
||||
while(el.parentNode) {
|
||||
el = el.parentNode;
|
||||
if(el.scrollTop) {
|
||||
return el;
|
||||
@@ -204,7 +208,7 @@ exports.addEventListeners = function(domNode,events) {
|
||||
if(eventInfo.handlerMethod) {
|
||||
handler = function(event) {
|
||||
eventInfo.handlerObject[eventInfo.handlerMethod].call(eventInfo.handlerObject,event);
|
||||
};
|
||||
};
|
||||
} else {
|
||||
handler = eventInfo.handlerObject;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ exports.makeDraggable = function(options) {
|
||||
domNode = options.domNode;
|
||||
// Make the dom node draggable (not necessary for anchor tags)
|
||||
if((domNode.tagName || "").toLowerCase() !== "a") {
|
||||
domNode.setAttribute("draggable","true");
|
||||
domNode.setAttribute("draggable","true");
|
||||
}
|
||||
// Add event handlers
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
@@ -80,7 +80,7 @@ exports.makeDraggable = function(options) {
|
||||
}
|
||||
// Set up the data transfer
|
||||
if(dataTransfer.clearData) {
|
||||
dataTransfer.clearData();
|
||||
dataTransfer.clearData();
|
||||
}
|
||||
var jsonData = [];
|
||||
if(titles.length > 1) {
|
||||
@@ -205,4 +205,16 @@ function parseJSONTiddlers(json,fallbackTitle) {
|
||||
return data;
|
||||
};
|
||||
|
||||
exports.dragEventContainsFiles = function(event) {
|
||||
if(event.dataTransfer.types) {
|
||||
for(var i=0; i<event.dataTransfer.types.length; i++) {
|
||||
if(event.dataTransfer.types[i] === "Files") {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
@@ -31,7 +31,7 @@ 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 ? options.event.event.target.ownerDocument : document);
|
||||
(options.event && options.event.event && options.event.event.target ? options.event.event.target.ownerDocument : document);
|
||||
this.srcWindow = this.srcDocument.defaultView;
|
||||
var self = this,
|
||||
refreshHandler,
|
||||
@@ -105,7 +105,7 @@ Modal.prototype.display = function(title,options) {
|
||||
parentWidget: $tw.rootWidget
|
||||
});
|
||||
navigatorWidgetNode.render(modalBody,null);
|
||||
|
||||
|
||||
// Render the title of the message
|
||||
var headerWidgetNode = this.wiki.makeTranscludeWidget(title,{
|
||||
field: "subtitle",
|
||||
|
||||
@@ -145,7 +145,7 @@ Popup.prototype.show = function(options) {
|
||||
}
|
||||
// Add the click handler if we have any popups
|
||||
if(this.popups.length > 0) {
|
||||
this.rootElement.addEventListener("click",this,true);
|
||||
this.rootElement.addEventListener("click",this,true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ PageScroller.prototype.handleEvent = function(event) {
|
||||
if(event.paramObject && event.paramObject.selector) {
|
||||
this.scrollSelectorIntoView(null,event.paramObject.selector);
|
||||
} else {
|
||||
this.scrollIntoView(event.target);
|
||||
this.scrollIntoView(event.target);
|
||||
}
|
||||
return false; // Event was handled
|
||||
}
|
||||
@@ -103,7 +103,7 @@ PageScroller.prototype.scrollIntoView = function(element,callback) {
|
||||
if(duration <= 0) {
|
||||
t = 1;
|
||||
} else {
|
||||
t = ((Date.now()) - self.startTime) / duration;
|
||||
t = ((Date.now()) - self.startTime) / duration;
|
||||
}
|
||||
if(t >= 1) {
|
||||
self.cancelScroll(srcWindow);
|
||||
@@ -126,7 +126,7 @@ PageScroller.prototype.scrollSelectorIntoView = function(baseElement,selector,ca
|
||||
baseElement = baseElement || document.body;
|
||||
var element = baseElement.querySelector(selector);
|
||||
if(element) {
|
||||
this.scrollIntoView(element,callback);
|
||||
this.scrollIntoView(element,callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
/*\
|
||||
title: $:/core/modules/startup/CSSescape.js
|
||||
title: $:/core/modules/utils/escapecss.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
module-type: utils
|
||||
|
||||
Polyfill for CSS.escape()
|
||||
Provides CSS.escape() functionality.
|
||||
|
||||
\*/
|
||||
(function(root,factory){
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
/*global $tw: false, window: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "css-escape";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["startup"];
|
||||
exports.synchronous = true;
|
||||
|
||||
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
|
||||
// https://github.com/umdjs/umd/blob/master/returnExports.js
|
||||
exports.startup = factory(root);
|
||||
}(typeof global != 'undefined' ? global : this, function(root) {
|
||||
|
||||
if (root.CSS && root.CSS.escape) {
|
||||
return;
|
||||
exports.escapeCSS = (function() {
|
||||
// use browser's native CSS.escape() function if available
|
||||
if ($tw.browser && window.CSS && window.CSS.escape) {
|
||||
return window.CSS.escape;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom/#serialize-an-identifier
|
||||
var cssEscape = function(value) {
|
||||
// otherwise, a utility method is provided
|
||||
// see also https://drafts.csswg.org/cssom/#serialize-an-identifier
|
||||
|
||||
/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */
|
||||
return function(value) {
|
||||
if (arguments.length == 0) {
|
||||
throw new TypeError('`CSS.escape` requires an argument.');
|
||||
}
|
||||
@@ -104,11 +99,6 @@ exports.startup = factory(root);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
|
||||
if (!root.CSS) {
|
||||
root.CSS = {};
|
||||
}
|
||||
|
||||
root.CSS.escape = cssEscape;
|
||||
|
||||
}));
|
||||
})();
|
||||
@@ -213,13 +213,13 @@ Options include:
|
||||
extFilters: optional array of filters to be used to generate the base path
|
||||
wiki: optional wiki for evaluating the pathFilters,
|
||||
fileInfo: an existing fileInfo to check against
|
||||
originalpath: a preferred filepath if no pathFilters match
|
||||
*/
|
||||
exports.generateTiddlerFileInfo = function(tiddler,options) {
|
||||
var fileInfo = {}, metaExt;
|
||||
// Propagate the isEditableFile flag
|
||||
if(options.fileInfo) {
|
||||
fileInfo.isEditableFile = options.fileInfo.isEditableFile || false;
|
||||
if(options.fileInfo && !!options.fileInfo.isEditableFile) {
|
||||
fileInfo.isEditableFile = true;
|
||||
fileInfo.originalpath = options.fileInfo.originalpath;
|
||||
}
|
||||
// Check if the tiddler has any unsafe fields that can't be expressed in a .tid or .meta file: containing control characters, or leading/trailing whitespace
|
||||
var hasUnsafeFields = false;
|
||||
@@ -247,12 +247,12 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
|
||||
fileInfo.hasMetaFile = true;
|
||||
}
|
||||
if(options.extFilters) {
|
||||
// Check for extension override
|
||||
// Check for extension overrides
|
||||
metaExt = $tw.utils.generateTiddlerExtension(tiddler.fields.title,{
|
||||
extFilters: options.extFilters,
|
||||
wiki: options.wiki
|
||||
});
|
||||
if(metaExt){
|
||||
if(metaExt) {
|
||||
if(metaExt === ".tid") {
|
||||
// Overriding to the .tid extension needs special handling
|
||||
fileInfo.type = "application/x-tiddler";
|
||||
@@ -279,8 +279,7 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
|
||||
directory: options.directory,
|
||||
pathFilters: options.pathFilters,
|
||||
wiki: options.wiki,
|
||||
fileInfo: options.fileInfo,
|
||||
originalpath: options.originalpath
|
||||
fileInfo: options.fileInfo
|
||||
});
|
||||
return fileInfo;
|
||||
};
|
||||
@@ -292,8 +291,7 @@ Options include:
|
||||
wiki: optional wiki for evaluating the extFilters
|
||||
*/
|
||||
exports.generateTiddlerExtension = function(title,options) {
|
||||
var self = this,
|
||||
extension;
|
||||
var extension;
|
||||
// Check if any of the extFilters applies
|
||||
if(options.extFilters && options.wiki) {
|
||||
$tw.utils.each(options.extFilters,function(filter) {
|
||||
@@ -319,11 +317,10 @@ Options include:
|
||||
fileInfo: an existing fileInfo object to check against
|
||||
*/
|
||||
exports.generateTiddlerFilepath = function(title,options) {
|
||||
var self = this,
|
||||
directory = options.directory || "",
|
||||
var directory = options.directory || "",
|
||||
extension = options.extension || "",
|
||||
originalpath = options.originalpath || "",
|
||||
filepath;
|
||||
originalpath = (options.fileInfo && options.fileInfo.originalpath) ? options.fileInfo.originalpath : "",
|
||||
filepath;
|
||||
// Check if any of the pathFilters applies
|
||||
if(options.pathFilters && options.wiki) {
|
||||
$tw.utils.each(options.pathFilters,function(filter) {
|
||||
@@ -336,34 +333,46 @@ exports.generateTiddlerFilepath = function(title,options) {
|
||||
}
|
||||
});
|
||||
}
|
||||
if(!filepath && originalpath !== "") {
|
||||
if(!filepath && !!originalpath) {
|
||||
//Use the originalpath without the extension
|
||||
var ext = path.extname(originalpath);
|
||||
filepath = originalpath.substring(0,originalpath.length - ext.length);
|
||||
} else if(!filepath) {
|
||||
filepath = title;
|
||||
// If the filepath already ends in the extension then remove it
|
||||
if(filepath.substring(filepath.length - extension.length) === extension) {
|
||||
filepath = filepath.substring(0,filepath.length - extension.length);
|
||||
}
|
||||
// Remove any forward or backward slashes so we don't create directories
|
||||
filepath = filepath.replace(/\/|\\/g,"_");
|
||||
}
|
||||
//If the path does not start with "." or ".." and a path seperator, then
|
||||
// Replace any Windows control codes
|
||||
filepath = filepath.replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i,"_$1_");
|
||||
// Replace any leading spaces with the same number of underscores
|
||||
filepath = filepath.replace(/^ +/,function (u) { return u.replace(/ /g, "_")});
|
||||
//If the path does not start with "." or ".." && a path seperator, then
|
||||
if(!/^\.{1,2}[/\\]/g.test(filepath)) {
|
||||
// Don't let the filename start with any dots because such files are invisible on *nix
|
||||
filepath = filepath.replace(/^\.+/g,"_");
|
||||
filepath = filepath.replace(/^\.+/g,function (u) { return u.replace(/\./g, "_")});
|
||||
}
|
||||
// Remove any characters that can't be used in cross-platform filenames
|
||||
// Replace any Unicode control codes
|
||||
filepath = filepath.replace(/[\x00-\x1f\x80-\x9f]/g,"_");
|
||||
// Replace any characters that can't be used in cross-platform filenames
|
||||
filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^/g,"_"));
|
||||
// Replace any dots or spaces at the end of the extension with the same number of underscores
|
||||
extension = extension.replace(/[\. ]+$/, function (u) { return u.replace(/[\. ]/g, "_")});
|
||||
// Truncate the extension if it is too long
|
||||
if(extension.length > 32) {
|
||||
extension = extension.substr(0,32);
|
||||
}
|
||||
// If the filepath already ends in the extension then remove it
|
||||
if(filepath.substring(filepath.length - extension.length) === extension) {
|
||||
filepath = filepath.substring(0,filepath.length - extension.length);
|
||||
}
|
||||
// Truncate the filename if it is too long
|
||||
if(filepath.length > 200) {
|
||||
filepath = filepath.substr(0,200);
|
||||
}
|
||||
// If the resulting filename is blank (eg because the title is just punctuation characters)
|
||||
if(!filepath) {
|
||||
// If the resulting filename is blank (eg because the title is just punctuation)
|
||||
if(!filepath || /^_+$/g.test(filepath)) {
|
||||
// ...then just use the character codes of the title
|
||||
filepath = "";
|
||||
filepath = "";
|
||||
$tw.utils.each(title.split(""),function(char) {
|
||||
if(filepath) {
|
||||
filepath += "-";
|
||||
@@ -382,22 +391,21 @@ exports.generateTiddlerFilepath = function(title,options) {
|
||||
count++;
|
||||
} while(fs.existsSync(fullPath));
|
||||
// If the last write failed with an error, or if path does not start with:
|
||||
// the resolved options.directory, the resolved wikiPath directory, or the wikiTiddlersPath directory,
|
||||
// then encodeURIComponent() and resolve to tiddler directory
|
||||
var newPath = fullPath,
|
||||
// the resolved options.directory, the resolved wikiPath directory, the wikiTiddlersPath directory,
|
||||
// or the 'originalpath' directory, then encodeURIComponent() and resolve to tiddler directory.
|
||||
var writePath = $tw.hooks.invokeHook("th-make-tiddler-path",fullPath,fullPath),
|
||||
encode = (options.fileInfo || {writeError: false}).writeError == true;
|
||||
if(!encode){
|
||||
encode = !(fullPath.indexOf(path.resolve(directory)) == 0 ||
|
||||
fullPath.indexOf(path.resolve($tw.boot.wikiPath)) == 0 ||
|
||||
fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0);
|
||||
if(!encode) {
|
||||
encode = !(writePath.indexOf($tw.boot.wikiTiddlersPath) == 0 ||
|
||||
writePath.indexOf(path.resolve(directory)) == 0 ||
|
||||
writePath.indexOf(path.resolve($tw.boot.wikiPath)) == 0 ||
|
||||
writePath.indexOf(path.resolve($tw.boot.wikiTiddlersPath,originalpath)) == 0 );
|
||||
}
|
||||
if(encode){
|
||||
fullPath = path.resolve(directory, encodeURIComponent(fullPath));
|
||||
if(encode) {
|
||||
writePath = path.resolve(directory,encodeURIComponent(fullPath));
|
||||
}
|
||||
// Call hook to allow plugins to modify the final path
|
||||
fullPath = $tw.hooks.invokeHook("th-make-tiddler-path", newPath, fullPath);
|
||||
// Return the full path to the file
|
||||
return fullPath;
|
||||
return writePath;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -411,18 +419,33 @@ exports.saveTiddlerToFile = function(tiddler,fileInfo,callback) {
|
||||
if(fileInfo.hasMetaFile) {
|
||||
// Save the tiddler as a separate body and meta file
|
||||
var typeInfo = $tw.config.contentTypeInfo[tiddler.fields.type || "text/plain"] || {encoding: "utf8"};
|
||||
fs.writeFile(fileInfo.filepath,tiddler.fields.text,typeInfo.encoding,function(err) {
|
||||
fs.writeFile(fileInfo.filepath,tiddler.fields.text || "",typeInfo.encoding,function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
fs.writeFile(fileInfo.filepath + ".meta",tiddler.getFieldStringBlock({exclude: ["text","bag"]}),"utf8",callback);
|
||||
fs.writeFile(fileInfo.filepath + ".meta",tiddler.getFieldStringBlock({exclude: ["text","bag"]}),"utf8",function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Save the tiddler as a self contained templated file
|
||||
if(fileInfo.type === "application/x-tiddler") {
|
||||
fs.writeFile(fileInfo.filepath,tiddler.getFieldStringBlock({exclude: ["text","bag"]}) + (!!tiddler.fields.text ? "\n\n" + tiddler.fields.text : ""),"utf8",callback);
|
||||
fs.writeFile(fileInfo.filepath,tiddler.getFieldStringBlock({exclude: ["text","bag"]}) + (!!tiddler.fields.text ? "\n\n" + tiddler.fields.text : ""),"utf8",function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
} else {
|
||||
fs.writeFile(fileInfo.filepath,JSON.stringify([tiddler.getFieldStrings({exclude: ["bag"]})],null,$tw.config.preferences.jsonSpaces),"utf8",callback);
|
||||
fs.writeFile(fileInfo.filepath,JSON.stringify([tiddler.getFieldStrings({exclude: ["bag"]})],null,$tw.config.preferences.jsonSpaces),"utf8",function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -438,7 +461,7 @@ exports.saveTiddlerToFileSync = function(tiddler,fileInfo) {
|
||||
if(fileInfo.hasMetaFile) {
|
||||
// Save the tiddler as a separate body and meta file
|
||||
var typeInfo = $tw.config.contentTypeInfo[tiddler.fields.type || "text/plain"] || {encoding: "utf8"};
|
||||
fs.writeFileSync(fileInfo.filepath,tiddler.fields.text,typeInfo.encoding);
|
||||
fs.writeFileSync(fileInfo.filepath,tiddler.fields.text || "",typeInfo.encoding);
|
||||
fs.writeFileSync(fileInfo.filepath + ".meta",tiddler.getFieldStringBlock({exclude: ["text","bag"]}),"utf8");
|
||||
} else {
|
||||
// Save the tiddler as a self contained templated file
|
||||
@@ -448,31 +471,44 @@ exports.saveTiddlerToFileSync = function(tiddler,fileInfo) {
|
||||
fs.writeFileSync(fileInfo.filepath,JSON.stringify([tiddler.getFieldStrings({exclude: ["bag"]})],null,$tw.config.preferences.jsonSpaces),"utf8");
|
||||
}
|
||||
}
|
||||
return fileInfo;
|
||||
};
|
||||
|
||||
/*
|
||||
Delete a file described by the fileInfo if it exits
|
||||
*/
|
||||
exports.deleteTiddlerFile = function(fileInfo, callback) {
|
||||
exports.deleteTiddlerFile = function(fileInfo,callback) {
|
||||
//Only attempt to delete files that exist on disk
|
||||
if(!fileInfo.filepath || !fs.existsSync(fileInfo.filepath)) {
|
||||
return callback(null);
|
||||
//For some reason, the tiddler is only in memory or we can't modify the file at this path
|
||||
$tw.syncer.displayError("Server deleteTiddlerFile task failed for filepath: "+fileInfo.filepath);
|
||||
return callback(null,fileInfo);
|
||||
}
|
||||
// Delete the file
|
||||
fs.unlink(fileInfo.filepath,function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
// Delete the metafile if present
|
||||
if(fileInfo.hasMetaFile && fs.existsSync(fileInfo.filepath + ".meta")) {
|
||||
fs.unlink(fileInfo.filepath + ".meta",function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),callback);
|
||||
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),callback);
|
||||
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -482,25 +518,25 @@ Cleanup old files on disk, by comparing the options values:
|
||||
adaptorInfo from $tw.syncer.tiddlerInfo
|
||||
bootInfo from $tw.boot.files
|
||||
*/
|
||||
exports.cleanupTiddlerFiles = function(options, callback) {
|
||||
exports.cleanupTiddlerFiles = function(options,callback) {
|
||||
var adaptorInfo = options.adaptorInfo || {},
|
||||
bootInfo = options.bootInfo || {},
|
||||
title = options.title || "undefined";
|
||||
if(adaptorInfo.filepath && bootInfo.filepath && adaptorInfo.filepath !== bootInfo.filepath) {
|
||||
return $tw.utils.deleteTiddlerFile(adaptorInfo, function(err){
|
||||
$tw.utils.deleteTiddlerFile(adaptorInfo,function(err) {
|
||||
if(err) {
|
||||
if ((err.code == "EPERM" || err.code == "EACCES") && err.syscall == "unlink") {
|
||||
// Error deleting the previous file on disk, should fail gracefully
|
||||
$tw.syncer.displayError("Server desynchronized. Error cleaning up previous file for tiddler: "+title, err);
|
||||
return callback(null);
|
||||
$tw.syncer.displayError("Server desynchronized. Error cleaning up previous file for tiddler: \""+title+"\"",err);
|
||||
return callback(null,bootInfo);
|
||||
} else {
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
return callback(null);
|
||||
return callback(null,bootInfo);
|
||||
});
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null,bootInfo);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,105 +14,182 @@ function LinkedList() {
|
||||
};
|
||||
|
||||
LinkedList.prototype.clear = function() {
|
||||
this.index = Object.create(null);
|
||||
// LinkedList performs the duty of both the head and tail node
|
||||
this.next = this;
|
||||
this.prev = this;
|
||||
this.next = Object.create(null);
|
||||
this.prev = Object.create(null);
|
||||
this.first = undefined;
|
||||
this.last = undefined;
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
LinkedList.prototype.remove = function(value) {
|
||||
if($tw.utils.isArray(value)) {
|
||||
for(var t=0; t<value.length; t++) {
|
||||
_assertString(value[t]);
|
||||
}
|
||||
for(var t=0; t<value.length; t++) {
|
||||
_removeOne(this,value[t]);
|
||||
}
|
||||
} else {
|
||||
_assertString(value);
|
||||
_removeOne(this,value);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Push behaves like array.push and accepts multiple string arguments. But it also
|
||||
accepts a single array argument too, to be consistent with its other methods.
|
||||
*/
|
||||
LinkedList.prototype.push = function(/* values */) {
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
var value = arguments[i];
|
||||
var node = {value: value};
|
||||
var preexistingNode = this.index[value];
|
||||
_linkToEnd(this,node);
|
||||
if(preexistingNode) {
|
||||
// We want to keep pointing to the first instance, but we want
|
||||
// to have that instance (or chain of instances) point to the
|
||||
// new one.
|
||||
while (preexistingNode.copy) {
|
||||
preexistingNode = preexistingNode.copy;
|
||||
}
|
||||
preexistingNode.copy = node;
|
||||
} else {
|
||||
this.index[value] = node;
|
||||
}
|
||||
var values = arguments;
|
||||
if($tw.utils.isArray(values[0])) {
|
||||
values = values[0];
|
||||
}
|
||||
for(var i = 0; i < values.length; i++) {
|
||||
_assertString(values[i]);
|
||||
}
|
||||
for(var i = 0; i < values.length; i++) {
|
||||
_linkToEnd(this,values[i]);
|
||||
}
|
||||
return this.length;
|
||||
};
|
||||
|
||||
LinkedList.prototype.pushTop = function(value) {
|
||||
if($tw.utils.isArray(value)) {
|
||||
for (var t=0; t<value.length; t++) {
|
||||
_assertString(value[t]);
|
||||
}
|
||||
for(var t=0; t<value.length; t++) {
|
||||
_removeOne(this,value[t]);
|
||||
}
|
||||
this.push.apply(this,value);
|
||||
} else {
|
||||
var node = _removeOne(this,value);
|
||||
if(!node) {
|
||||
node = {value: value};
|
||||
this.index[value] = node;
|
||||
} else {
|
||||
// Put this node at the end of the copy chain.
|
||||
var preexistingNode = node;
|
||||
while(preexistingNode.copy) {
|
||||
preexistingNode = preexistingNode.copy;
|
||||
}
|
||||
// The order of these three statements is important,
|
||||
// because sometimes preexistingNode == node.
|
||||
preexistingNode.copy = node;
|
||||
this.index[value] = node.copy;
|
||||
node.copy = undefined;
|
||||
for(var t=0; t<value.length; t++) {
|
||||
_linkToEnd(this,value[t]);
|
||||
}
|
||||
_linkToEnd(this,node);
|
||||
} else {
|
||||
_assertString(value);
|
||||
_removeOne(this,value);
|
||||
_linkToEnd(this,value);
|
||||
}
|
||||
};
|
||||
|
||||
LinkedList.prototype.each = function(callback) {
|
||||
for(var ptr = this.next; ptr !== this; ptr = ptr.next) {
|
||||
callback(ptr.value);
|
||||
var visits = Object.create(null),
|
||||
value = this.first;
|
||||
while(value !== undefined) {
|
||||
callback(value);
|
||||
var next = this.next[value];
|
||||
if(typeof next === "object") {
|
||||
var i = visits[value] || 0;
|
||||
visits[value] = i+1;
|
||||
value = next[i];
|
||||
} else {
|
||||
value = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LinkedList.prototype.toArray = function() {
|
||||
var output = [];
|
||||
for(var ptr = this.next; ptr !== this; ptr = ptr.next) {
|
||||
output.push(ptr.value);
|
||||
}
|
||||
var output = new Array(this.length),
|
||||
index = 0;
|
||||
this.each(function(value) { output[index++] = value; });
|
||||
return output;
|
||||
};
|
||||
|
||||
function _removeOne(list,value) {
|
||||
var node = list.index[value];
|
||||
if(node) {
|
||||
node.prev.next = node.next;
|
||||
node.next.prev = node.prev;
|
||||
list.length -= 1;
|
||||
// Point index to the next instance of the same value, maybe nothing.
|
||||
list.index[value] = node.copy;
|
||||
var prevEntry = list.prev[value],
|
||||
nextEntry = list.next[value],
|
||||
prev = prevEntry,
|
||||
next = nextEntry;
|
||||
if(typeof nextEntry === "object") {
|
||||
next = nextEntry[0];
|
||||
prev = prevEntry[0];
|
||||
}
|
||||
return node;
|
||||
// Relink preceding element.
|
||||
if(list.first === value) {
|
||||
list.first = next
|
||||
} else if(prev !== undefined) {
|
||||
if(typeof list.next[prev] === "object") {
|
||||
if(next === undefined) {
|
||||
// Must have been last, and 'i' would be last element.
|
||||
list.next[prev].pop();
|
||||
} else {
|
||||
var i = list.next[prev].indexOf(value);
|
||||
list.next[prev][i] = next;
|
||||
}
|
||||
} else {
|
||||
list.next[prev] = next;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// Now relink following element
|
||||
// Check "next !== undefined" rather than "list.last === value" because
|
||||
// we need to know if the FIRST value is the last in the list, not the last.
|
||||
if(next !== undefined) {
|
||||
if(typeof list.prev[next] === "object") {
|
||||
if(prev === undefined) {
|
||||
// Must have been first, and 'i' would be 0.
|
||||
list.prev[next].shift();
|
||||
} else {
|
||||
var i = list.prev[next].indexOf(value);
|
||||
list.prev[next][i] = prev;
|
||||
}
|
||||
} else {
|
||||
list.prev[next] = prev;
|
||||
}
|
||||
} else {
|
||||
list.last = prev;
|
||||
}
|
||||
// Delink actual value. If it uses arrays, just remove first entries.
|
||||
if(typeof nextEntry === "object") {
|
||||
nextEntry.shift();
|
||||
prevEntry.shift();
|
||||
} else {
|
||||
list.next[value] = undefined;
|
||||
list.prev[value] = undefined;
|
||||
}
|
||||
list.length -= 1;
|
||||
};
|
||||
|
||||
function _linkToEnd(list,node) {
|
||||
// Sticks the given node onto the end of the list.
|
||||
list.prev.next = node;
|
||||
node.prev = list.prev;
|
||||
list.prev = node;
|
||||
node.next = list;
|
||||
// Sticks the given node onto the end of the list.
|
||||
function _linkToEnd(list,value) {
|
||||
if(list.first === undefined) {
|
||||
list.first = value;
|
||||
} else {
|
||||
// Does it already exists?
|
||||
if(list.first === value || list.prev[value] !== undefined) {
|
||||
if(typeof list.next[value] === "string") {
|
||||
list.next[value] = [list.next[value]];
|
||||
list.prev[value] = [list.prev[value]];
|
||||
} else if(typeof list.next[value] === "undefined") {
|
||||
// list.next[value] must be undefined.
|
||||
// Special case. List already has 1 value. It's at the end.
|
||||
list.next[value] = [];
|
||||
list.prev[value] = [list.prev[value]];
|
||||
}
|
||||
list.prev[value].push(list.last);
|
||||
// We do NOT append a new value onto "next" list. Iteration will
|
||||
// figure out it must point to End-of-List on its own.
|
||||
} else {
|
||||
list.prev[value] = list.last;
|
||||
}
|
||||
// Make the old last point to this new one.
|
||||
if(typeof list.next[list.last] === "object") {
|
||||
list.next[list.last].push(value);
|
||||
} else {
|
||||
list.next[list.last] = value;
|
||||
}
|
||||
}
|
||||
list.last = value;
|
||||
list.length += 1;
|
||||
};
|
||||
|
||||
function _assertString(value) {
|
||||
if(typeof value !== "string") {
|
||||
throw "Linked List only accepts string values, not " + value;
|
||||
}
|
||||
};
|
||||
|
||||
exports.LinkedList = LinkedList;
|
||||
|
||||
})();
|
||||
|
||||
@@ -45,7 +45,7 @@ Logger.prototype.log = function(/* args */) {
|
||||
self.saveBufferLogger.buffer += " " + arg;
|
||||
});
|
||||
this.saveBufferLogger.buffer += "\n";
|
||||
this.saveBufferLogger.buffer = this.saveBufferLogger.buffer.slice(-this.saveBufferLogger.saveLimit);
|
||||
this.saveBufferLogger.buffer = this.saveBufferLogger.buffer.slice(-this.saveBufferLogger.saveLimit);
|
||||
}
|
||||
if(console !== undefined && console.log !== undefined) {
|
||||
return Function.apply.call(console.log, console, [$tw.utils.terminalColour(this.colour),this.componentName + ":"].concat(Array.prototype.slice.call(arguments,0)).concat($tw.utils.terminalColour()));
|
||||
@@ -111,7 +111,7 @@ Logger.prototype.alert = function(/* args */) {
|
||||
} else {
|
||||
// Print an orange message to the console if not in the browser
|
||||
console.error("\x1b[1;33m" + text + "\x1b[0m");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ function Performance(enabled) {
|
||||
|
||||
Performance.prototype.showGreeting = function() {
|
||||
if($tw.browser) {
|
||||
this.logger.log("Execute $tw.perf.log(); to see filter execution timings");
|
||||
this.logger.log("Execute $tw.perf.log(); to see filter execution timings");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user