mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-01-11 18:00:26 +00:00
Merge remote-tracking branch 'origin/master' into parser-ranges
This commit is contained in:
commit
79f5e6b498
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();
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
124
boot/boot.js
124
boot/boot.js
@ -267,8 +267,16 @@ $tw.utils.htmlDecode = function(s) {
|
||||
Get the browser location.hash. We don't use location.hash because of the way that Firefox auto-urldecodes it (see http://stackoverflow.com/questions/1703552/encoding-of-window-location-hash)
|
||||
*/
|
||||
$tw.utils.getLocationHash = function() {
|
||||
var parts = window.location.href.split('#');
|
||||
return "#" + (parts.length > 1 ? parts[1] : "");
|
||||
var href = window.location.href;
|
||||
var idx = href.indexOf('#');
|
||||
if(idx === -1) {
|
||||
return "#";
|
||||
} else if(idx < href.length-1 && href[idx+1] === '#') {
|
||||
// Special case: ignore location hash if it itself starts with a #
|
||||
return "#";
|
||||
} else {
|
||||
return href.substring(idx);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -297,13 +305,21 @@ $tw.utils.stringifyDate = function(value) {
|
||||
// Parse a date from a UTC YYYYMMDDHHMMSSmmm format string
|
||||
$tw.utils.parseDate = function(value) {
|
||||
if(typeof value === "string") {
|
||||
return new Date(Date.UTC(parseInt(value.substr(0,4),10),
|
||||
var negative = 1;
|
||||
if(value.charAt(0) === "-") {
|
||||
negative = -1;
|
||||
value = value.substr(1);
|
||||
}
|
||||
var year = parseInt(value.substr(0,4),10) * negative,
|
||||
d = new Date(Date.UTC(year,
|
||||
parseInt(value.substr(4,2),10)-1,
|
||||
parseInt(value.substr(6,2),10),
|
||||
parseInt(value.substr(8,2)||"00",10),
|
||||
parseInt(value.substr(10,2)||"00",10),
|
||||
parseInt(value.substr(12,2)||"00",10),
|
||||
parseInt(value.substr(14,3)||"000",10)));
|
||||
d.setUTCFullYear(year); // See https://stackoverflow.com/a/5870822
|
||||
return d;
|
||||
} else if($tw.utils.isDate(value)) {
|
||||
return value;
|
||||
} else {
|
||||
@ -876,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
|
||||
*/
|
||||
@ -883,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;
|
||||
@ -1878,7 +1901,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
});
|
||||
});
|
||||
if(isEditableFile) {
|
||||
tiddlers.push({filepath: pathname, hasMetaFile: !!metadata && !isTiddlerFile, tiddlers: fileTiddlers});
|
||||
tiddlers.push({filepath: pathname, hasMetaFile: !!metadata && !isTiddlerFile, isEditableFile: true, tiddlers: fileTiddlers});
|
||||
} else {
|
||||
tiddlers.push({tiddlers: fileTiddlers});
|
||||
}
|
||||
@ -1904,15 +1927,21 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
|
||||
}
|
||||
} else {
|
||||
// Process directory specifier
|
||||
var dirPath = path.resolve(filepath,dirSpec.path),
|
||||
files = fs.readdirSync(dirPath),
|
||||
fileRegExp = new RegExp(dirSpec.filesRegExp || "^.*$"),
|
||||
metaRegExp = /^.*\.meta$/;
|
||||
for(var t=0; t<files.length; t++) {
|
||||
var filename = files[t];
|
||||
if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) {
|
||||
processFile(dirPath + path.sep + filename,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile);
|
||||
var dirPath = path.resolve(filepath,dirSpec.path);
|
||||
if(fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
||||
var files = fs.readdirSync(dirPath),
|
||||
fileRegExp = new RegExp(dirSpec.filesRegExp || "^.*$"),
|
||||
metaRegExp = /^.*\.meta$/;
|
||||
for(var t=0; t<files.length; t++) {
|
||||
var filename = files[t];
|
||||
if(filename !== "tiddlywiki.files" && !metaRegExp.test(filename) && fileRegExp.test(filename)) {
|
||||
processFile(dirPath + path.sep + filename,dirSpec.isTiddlerFile,dirSpec.fields,dirSpec.isEditableFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("Warning: a directory in a tiddlywiki.files file does not exist.");
|
||||
console.log("dirPath: " + dirPath);
|
||||
console.log("tiddlywiki.files location: " + filepath);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -2052,6 +2081,11 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
// Save the path to the tiddlers folder for the filesystemadaptor
|
||||
var config = wikiInfo.config || {};
|
||||
if($tw.boot.wikiPath == wikiPath) {
|
||||
$tw.boot.wikiTiddlersPath = path.resolve($tw.boot.wikiPath,config["default-tiddler-location"] || $tw.config.wikiTiddlersSubDir);
|
||||
}
|
||||
// Load any parent wikis
|
||||
if(wikiInfo.includeWikis) {
|
||||
parentPaths = parentPaths.slice(0);
|
||||
@ -2085,27 +2119,31 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
|
||||
$tw.boot.files[tiddler.title] = {
|
||||
filepath: tiddlerFile.filepath,
|
||||
type: tiddlerFile.type,
|
||||
hasMetaFile: tiddlerFile.hasMetaFile
|
||||
hasMetaFile: tiddlerFile.hasMetaFile,
|
||||
isEditableFile: config["retain-original-tiddler-path"] || tiddlerFile.isEditableFile || tiddlerFile.filepath.indexOf($tw.boot.wikiTiddlersPath) !== 0
|
||||
};
|
||||
});
|
||||
}
|
||||
$tw.wiki.addTiddlers(tiddlerFile.tiddlers);
|
||||
});
|
||||
// Save the original tiddler file locations if requested
|
||||
var config = wikiInfo.config || {};
|
||||
if(config["retain-original-tiddler-path"]) {
|
||||
var output = {}, relativePath;
|
||||
if ($tw.boot.wikiPath == wikiPath) {
|
||||
// Save the original tiddler file locations if requested
|
||||
var output = {}, relativePath, fileInfo;
|
||||
for(var title in $tw.boot.files) {
|
||||
relativePath = path.relative(resolvedWikiPath,$tw.boot.files[title].filepath);
|
||||
output[title] =
|
||||
path.sep === "/" ?
|
||||
relativePath :
|
||||
relativePath.split(path.sep).join("/");
|
||||
fileInfo = $tw.boot.files[title];
|
||||
if(fileInfo.isEditableFile) {
|
||||
relativePath = path.relative($tw.boot.wikiTiddlersPath,fileInfo.filepath);
|
||||
fileInfo.originalpath = relativePath;
|
||||
output[title] =
|
||||
path.sep === "/" ?
|
||||
relativePath :
|
||||
relativePath.split(path.sep).join("/");
|
||||
}
|
||||
}
|
||||
if(Object.keys(output).length > 0){
|
||||
$tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)});
|
||||
}
|
||||
$tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)});
|
||||
}
|
||||
// Save the path to the tiddlers folder for the filesystemadaptor
|
||||
$tw.boot.wikiTiddlersPath = path.resolve($tw.boot.wikiPath,config["default-tiddler-location"] || $tw.config.wikiTiddlersSubDir);
|
||||
// Load any plugins within the wiki folder
|
||||
var wikiPluginsPath = path.resolve(wikiPath,$tw.config.wikiPluginsSubDir);
|
||||
if(fs.existsSync(wikiPluginsPath)) {
|
||||
@ -2152,7 +2190,7 @@ $tw.loadTiddlersNode = function() {
|
||||
// Load any extra plugins
|
||||
$tw.utils.each($tw.boot.extraPlugins,function(name) {
|
||||
if(name.charAt(0) === "+") { // Relative path to plugin
|
||||
var pluginFields = $tw.loadPluginFolder(name.substring(1));;
|
||||
var pluginFields = $tw.loadPluginFolder(name.substring(1));
|
||||
if(pluginFields) {
|
||||
$tw.wiki.addTiddler(pluginFields);
|
||||
}
|
||||
@ -2271,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");
|
||||
@ -2428,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>
|
@ -17,6 +17,8 @@ Basics/NewJournal/Tags/Prompt: Tags for new journal tiddlers
|
||||
Basics/NewTiddler/Title/Prompt: Title of new tiddlers
|
||||
Basics/NewTiddler/Tags/Prompt: Tags for new tiddlers
|
||||
Basics/OverriddenShadowTiddlers/Prompt: Number of overridden shadow tiddlers
|
||||
Basics/RemoveTags: Update to current format
|
||||
Basics/RemoveTags/Hint: Update the tags configuration to the latest format
|
||||
Basics/ShadowTiddlers/Prompt: Number of shadow tiddlers
|
||||
Basics/Subtitle/Prompt: Subtitle
|
||||
Basics/SystemTiddlers/Prompt: Number of system tiddlers
|
||||
@ -44,6 +46,7 @@ KeyboardShortcuts/Platform/Linux: Linux platform only
|
||||
KeyboardShortcuts/Platform/NonLinux: Non-Linux platforms only
|
||||
KeyboardShortcuts/Platform/Windows: Windows platform only
|
||||
KeyboardShortcuts/Platform/NonWindows: Non-Windows platforms only
|
||||
LayoutSwitcher/Caption: Layout
|
||||
LoadedModules/Caption: Loaded Modules
|
||||
LoadedModules/Hint: These are the currently loaded tiddler modules linked to their source tiddlers. Any italicised modules lack a source tiddler, typically because they were setup during the boot process.
|
||||
Palette/Caption: Palette
|
||||
@ -120,11 +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: 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 which 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`.
|
||||
|
@ -19,6 +19,8 @@ Shadow/OverriddenWarning: This is a modified shadow tiddler. You can revert to t
|
||||
Tags/Add/Button: add
|
||||
Tags/Add/Button/Hint: add tag
|
||||
Tags/Add/Placeholder: tag name
|
||||
Tags/ClearInput/Caption: clear input
|
||||
Tags/ClearInput/Hint: Clear tag input
|
||||
Tags/Dropdown/Caption: tag list
|
||||
Tags/Dropdown/Hint: Show tag list
|
||||
Title/BadCharacterWarning: Warning: avoid using any of the characters <<bad-chars>> in tiddler titles
|
||||
|
@ -22,6 +22,7 @@ All parameters are optional with safe defaults, and can be specified in any orde
|
||||
* ''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")
|
||||
|
@ -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.
|
||||
|
||||
@ -26,6 +26,7 @@ Notes:
|
||||
* 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]]"`
|
||||
* 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:
|
||||
|
@ -13,11 +13,16 @@ Listing/Preview/TextRaw: Text (Raw)
|
||||
Listing/Preview/Fields: Fields
|
||||
Listing/Preview/Diff: Diff
|
||||
Listing/Preview/DiffFields: Diff (Fields)
|
||||
Upgrader/Plugins/Suppressed/Incompatible: Blocked incompatible or obsolete plugin
|
||||
Upgrader/Plugins/Suppressed/Version: Blocked plugin (due to incoming <<incoming>> being older than existing <<existing>>)
|
||||
Upgrader/Plugins/Upgraded: Upgraded plugin from <<incoming>> to <<upgraded>>
|
||||
Upgrader/State/Suppressed: Blocked temporary state tiddler
|
||||
Upgrader/System/Suppressed: Blocked system tiddler
|
||||
Upgrader/System/Warning: Core module tiddler
|
||||
Upgrader/System/Alert: You are about to import a tiddler that will overwrite a core module tiddler. This is not recommended as it may make the system unstable
|
||||
Upgrader/ThemeTweaks/Created: Migrated theme tweak from <$text text=<<from>>/>
|
||||
Listing/Rename/Tooltip: Rename tiddler before importing
|
||||
Listing/Rename/Prompt: Rename to:
|
||||
Listing/Rename/ConfirmRename: Rename tiddler
|
||||
Listing/Rename/CancelRename: Cancel
|
||||
Listing/Rename/OverwriteWarning: A tiddler with this title already exists.
|
||||
Upgrader/Plugins/Suppressed/Incompatible: Blocked incompatible or obsolete plugin.
|
||||
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/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>>/>.
|
||||
|
@ -10,6 +10,7 @@ ConfirmCancelTiddler: Do you wish to discard changes to the tiddler "<$text text
|
||||
ConfirmDeleteTiddler: Do you wish to delete the tiddler "<$text text=<<title>>/>"?
|
||||
ConfirmOverwriteTiddler: Do you wish to overwrite the tiddler "<$text text=<<title>>/>"?
|
||||
ConfirmEditShadowTiddler: You are about to edit a ShadowTiddler. Any changes will override the default system making future upgrades non-trivial. Are you sure you want to edit "<$text text=<<title>>/>"?
|
||||
ConfirmAction: Do you wish to proceed?
|
||||
Count: count
|
||||
DefaultNewTiddlerTitle: New Tiddler
|
||||
Diffs/CountMessage: <<diff-count>> differences
|
||||
@ -26,6 +27,7 @@ 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
|
||||
Error/IsFilterOperator: Filter Error: Unknown operand for the 'is' filter operator
|
||||
Error/FormatFilterOperator: Filter Error: Unknown suffix for the 'format' filter operator
|
||||
Error/LoadingPluginLibrary: Error loading plugin library
|
||||
@ -38,6 +40,7 @@ Error/XMLHttpRequest: XMLHttpRequest error code
|
||||
InternalJavaScriptError/Title: Internal JavaScript Error
|
||||
InternalJavaScriptError/Hint: Well, this is embarrassing. It is recommended that you restart TiddlyWiki by refreshing your browser
|
||||
InvalidFieldName: Illegal characters in field name "<$text text=<<fieldName>>/>". Fields can only contain lowercase letters, digits and the characters underscore (`_`), hyphen (`-`) and period (`.`)
|
||||
LayoutSwitcher/Description: Open the layout switcher
|
||||
LazyLoadingWarning: <p>Trying to load external content from ''<$text text={{!!_canonical_uri}}/>''</p><p>If this message doesn't disappear, either the tiddler content type doesn't match the type of the external content, or you may be using a browser that doesn't support external content for wikis loaded as standalone files. See https://tiddlywiki.com/#ExternalText</p>
|
||||
LoginToTiddlySpace: Login to TiddlySpace
|
||||
Manager/Controls/FilterByTag/None: (none)
|
||||
@ -61,8 +64,11 @@ MissingTiddler/Hint: Missing tiddler "<$text text=<<currentTiddler>>/>" -- click
|
||||
No: No
|
||||
OfficialPluginLibrary: Official ~TiddlyWiki Plugin Library
|
||||
OfficialPluginLibrary/Hint: The official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.
|
||||
PageTemplate/Description: the default ~TiddlyWiki layout
|
||||
PageTemplate/Name: Default ~PageTemplate
|
||||
PluginReloadWarning: Please save {{$:/core/ui/Buttons/save-wiki}} and reload {{$:/core/ui/Buttons/refresh}} to allow changes to ~JavaScript plugins to take effect
|
||||
RecentChanges/DateFormat: DDth MMM YYYY
|
||||
Shortcuts/Input/AdvancedSearch/Hint: Open the ~AdvancedSearch panel from within the sidebar search field
|
||||
Shortcuts/Input/Accept/Hint: Accept the selected item
|
||||
Shortcuts/Input/AcceptVariant/Hint: Accept the selected item (variant)
|
||||
Shortcuts/Input/Cancel/Hint: Clear the input field
|
||||
@ -70,6 +76,11 @@ Shortcuts/Input/Down/Hint: Select the next item
|
||||
Shortcuts/Input/Tab-Left/Hint: Select the previous Tab
|
||||
Shortcuts/Input/Tab-Right/Hint: Select the next Tab
|
||||
Shortcuts/Input/Up/Hint: Select the previous item
|
||||
Shortcuts/SidebarLayout/Hint: Change the sidebar layout
|
||||
Switcher/Subtitle/theme: Switch Theme
|
||||
Switcher/Subtitle/layout: Switch Layout
|
||||
Switcher/Subtitle/language: Switch Language
|
||||
Switcher/Subtitle/palette: Switch Palette
|
||||
SystemTiddler/Tooltip: This is a system tiddler
|
||||
SystemTiddlers/Include/Prompt: Include system tiddlers
|
||||
TagManager/Colour/Heading: Colour
|
||||
|
@ -14,7 +14,7 @@ List/Caption: List
|
||||
List/Empty: This tiddler does not have a list
|
||||
Listed/Caption: Listed
|
||||
Listed/Empty: This tiddler is not listed by any others
|
||||
References/Caption: References
|
||||
References/Caption: Backlinks
|
||||
References/Empty: No tiddlers link to this one
|
||||
Tagging/Caption: Tagging
|
||||
Tagging/Empty: No tiddlers are tagged with this one
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -42,7 +42,6 @@ function FramedEngine(options) {
|
||||
this.iframeNode.style.border = "none";
|
||||
this.iframeNode.style.padding = "0";
|
||||
this.iframeNode.style.resize = "none";
|
||||
this.iframeNode.style["background-color"] = this.widget.wiki.extractTiddlerDataItem(this.widget.wiki.getTiddlerText("$:/palette"),"tiddler-editor-background");
|
||||
this.iframeDoc.body.style.margin = "0";
|
||||
this.iframeDoc.body.style.padding = "0";
|
||||
this.widget.domNodes.push(this.iframeNode);
|
||||
@ -74,6 +73,12 @@ function FramedEngine(options) {
|
||||
if(this.widget.editTabIndex) {
|
||||
this.iframeNode.setAttribute("tabindex",this.widget.editTabIndex);
|
||||
}
|
||||
if(this.widget.editAutoComplete) {
|
||||
this.domNode.setAttribute("autocomplete",this.widget.editAutoComplete);
|
||||
}
|
||||
if(this.widget.isDisabled === "yes") {
|
||||
this.domNode.setAttribute("disabled",true);
|
||||
}
|
||||
// Copy the styles from the dummy textarea
|
||||
this.copyStyles();
|
||||
// Add event listeners
|
||||
@ -97,7 +102,6 @@ FramedEngine.prototype.copyStyles = function() {
|
||||
this.domNode.style.display = "block";
|
||||
this.domNode.style.width = "100%";
|
||||
this.domNode.style.margin = "0";
|
||||
this.domNode.style["background-color"] = this.widget.wiki.extractTiddlerDataItem(this.widget.wiki.getTiddlerText("$:/palette"),"tiddler-editor-background");
|
||||
// In Chrome setting -webkit-text-fill-color overrides the placeholder text colour
|
||||
this.domNode.style["-webkit-text-fill-color"] = "currentcolor";
|
||||
};
|
||||
|
@ -52,6 +52,12 @@ function SimpleEngine(options) {
|
||||
if(this.widget.editTabIndex) {
|
||||
this.domNode.setAttribute("tabindex",this.widget.editTabIndex);
|
||||
}
|
||||
if(this.widget.editAutoComplete) {
|
||||
this.domNode.setAttribute("autocomplete",this.widget.editAutoComplete);
|
||||
}
|
||||
if(this.widget.isDisabled === "yes") {
|
||||
this.domNode.setAttribute("disabled",true);
|
||||
}
|
||||
// Add an input event handler
|
||||
$tw.utils.addEventListeners(this.domNode,[
|
||||
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
|
||||
|
@ -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";
|
||||
}
|
||||
@ -180,6 +184,8 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
|
||||
this.editCancelPopups = this.getAttribute("cancelPopups","") === "yes";
|
||||
this.editInputActions = this.getAttribute("inputActions");
|
||||
this.editRefreshTitle = this.getAttribute("refreshTitle");
|
||||
this.editAutoComplete = this.getAttribute("autocomplete");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
// Get the default editor element tag and type
|
||||
var tag,type;
|
||||
if(this.editField === "text") {
|
||||
@ -211,7 +217,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 || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE]) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedAttributes.disabled) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if (changedTiddlers[this.editRefreshTitle]) {
|
||||
|
25
core/modules/filterrunprefixes/all.js
Normal file
25
core/modules/filterrunprefixes/all.js
Normal file
@ -0,0 +1,25 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/all.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Union of sets without de-duplication.
|
||||
Equivalent to = filter run prefix.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.all = function(operationSubFunction) {
|
||||
return function(results,source,widget) {
|
||||
results.push.apply(results, operationSubFunction(source,widget));
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
28
core/modules/filterrunprefixes/and.js
Normal file
28
core/modules/filterrunprefixes/and.js
Normal file
@ -0,0 +1,28 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/and.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Intersection of sets.
|
||||
Equivalent to + filter run prefix.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.and = function(operationSubFunction,options) {
|
||||
return function(results,source,widget) {
|
||||
// This replaces all the elements of the array, but keeps the actual array so that references to it are preserved
|
||||
source = options.wiki.makeTiddlerIterator(results.toArray());
|
||||
results.clear();
|
||||
results.pushTop(operationSubFunction(source,widget));
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
27
core/modules/filterrunprefixes/else.js
Normal file
27
core/modules/filterrunprefixes/else.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/else.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Equivalent to ~ filter run prefix.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.else = function(operationSubFunction) {
|
||||
return function(results,source,widget) {
|
||||
if(results.length === 0) {
|
||||
// Main result so far is empty
|
||||
results.pushTop(operationSubFunction(source,widget));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
25
core/modules/filterrunprefixes/except.js
Normal file
25
core/modules/filterrunprefixes/except.js
Normal file
@ -0,0 +1,25 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/except.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Difference of sets.
|
||||
Equivalent to - filter run prefix.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.except = function(operationSubFunction) {
|
||||
return function(results,source,widget) {
|
||||
results.remove(operationSubFunction(source,widget));
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
31
core/modules/filterrunprefixes/filter.js
Normal file
31
core/modules/filterrunprefixes/filter.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/filter.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
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);
|
||||
if(filtered.length === 0) {
|
||||
resultsToRemove.push(result);
|
||||
}
|
||||
});
|
||||
results.remove(resultsToRemove);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
31
core/modules/filterrunprefixes/intersection.js
Normal file
31
core/modules/filterrunprefixes/intersection.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/intersection.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.intersection = function(operationSubFunction) {
|
||||
return function(results,source,widget) {
|
||||
if(results.length !== 0) {
|
||||
var secondRunResults = operationSubFunction(source,widget);
|
||||
var firstRunResults = results.toArray();
|
||||
results.clear();
|
||||
$tw.utils.each(firstRunResults,function(title) {
|
||||
if(secondRunResults.indexOf(title) !== -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
24
core/modules/filterrunprefixes/or.js
Normal file
24
core/modules/filterrunprefixes/or.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/or.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Equivalent to a filter run with no prefix.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.or = function(operationSubFunction) {
|
||||
return function(results,source,widget) {
|
||||
results.pushTop(operationSubFunction(source,widget));
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
50
core/modules/filterrunprefixes/reduce.js
Normal file
50
core/modules/filterrunprefixes/reduce.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/reduce.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.reduce = function(operationSubFunction,options) {
|
||||
return function(results,source,widget) {
|
||||
if(results.length > 0) {
|
||||
var accumulator = "";
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
if(list.length > 0) {
|
||||
accumulator = "" + list[0];
|
||||
}
|
||||
++index;
|
||||
});
|
||||
results.clear();
|
||||
results.push(accumulator);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
@ -62,42 +62,60 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
else if(operator.operator === "") {
|
||||
operator.operator = "title";
|
||||
}
|
||||
operator.operands = [];
|
||||
var parseOperand = function(bracketType) {
|
||||
var operand = {};
|
||||
switch (bracketType) {
|
||||
case "{": // Curly brackets
|
||||
operand.indirect = true;
|
||||
nextBracketPos = filterString.indexOf("}",p);
|
||||
break;
|
||||
case "[": // Square brackets
|
||||
nextBracketPos = filterString.indexOf("]",p);
|
||||
break;
|
||||
case "<": // Angle brackets
|
||||
operand.variable = true;
|
||||
nextBracketPos = filterString.indexOf(">",p);
|
||||
break;
|
||||
case "/": // regexp brackets
|
||||
var rex = /^((?:[^\\\/]|\\.)*)\/(?:\(([mygi]+)\))?/g,
|
||||
rexMatch = rex.exec(filterString.substring(p));
|
||||
if(rexMatch) {
|
||||
operator.regexp = new RegExp(rexMatch[1], rexMatch[2]);
|
||||
// DEPRECATION WARNING
|
||||
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
|
||||
nextBracketPos = p + rex.lastIndex - 1;
|
||||
}
|
||||
else {
|
||||
throw "Unterminated regular expression in filter expression";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(nextBracketPos === -1) {
|
||||
throw "Missing closing bracket in filter expression";
|
||||
}
|
||||
if(!operator.regexp) {
|
||||
operand.text = filterString.substring(p,nextBracketPos);
|
||||
operator.operands.push(operand);
|
||||
}
|
||||
p = nextBracketPos + 1;
|
||||
}
|
||||
|
||||
p = nextBracketPos + 1;
|
||||
switch (bracket) {
|
||||
case "{": // Curly brackets
|
||||
operator.indirect = true;
|
||||
nextBracketPos = filterString.indexOf("}",p);
|
||||
break;
|
||||
case "[": // Square brackets
|
||||
nextBracketPos = filterString.indexOf("]",p);
|
||||
break;
|
||||
case "<": // Angle brackets
|
||||
operator.variable = true;
|
||||
nextBracketPos = filterString.indexOf(">",p);
|
||||
break;
|
||||
case "/": // regexp brackets
|
||||
var rex = /^((?:[^\\\/]*|\\.)*)\/(?:\(([mygi]+)\))?/g,
|
||||
rexMatch = rex.exec(filterString.substring(p));
|
||||
if(rexMatch) {
|
||||
operator.regexp = new RegExp(rexMatch[1], rexMatch[2]);
|
||||
// DEPRECATION WARNING
|
||||
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
|
||||
nextBracketPos = p + rex.lastIndex - 1;
|
||||
}
|
||||
else {
|
||||
throw "Unterminated regular expression in filter expression";
|
||||
}
|
||||
break;
|
||||
}
|
||||
parseOperand(bracket);
|
||||
|
||||
if(nextBracketPos === -1) {
|
||||
throw "Missing closing bracket in filter expression";
|
||||
// Check for multiple operands
|
||||
while(filterString.charAt(p) === ",") {
|
||||
p++;
|
||||
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
|
||||
nextBracketPos = p;
|
||||
p++;
|
||||
parseOperand(filterString.charAt(nextBracketPos));
|
||||
} else {
|
||||
throw "Missing [ in filter expression";
|
||||
}
|
||||
}
|
||||
if(!operator.regexp) {
|
||||
operator.operand = filterString.substring(p,nextBracketPos);
|
||||
}
|
||||
p = nextBracketPos + 1;
|
||||
|
||||
// Push this operator
|
||||
operators.push(operator);
|
||||
@ -119,7 +137,7 @@ exports.parseFilter = function(filterString) {
|
||||
p = 0, // Current position in the filter string
|
||||
match;
|
||||
var whitespaceRegExp = /(\s+)/mg,
|
||||
operandRegExp = /((?:\+|\-|~|=)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
operandRegExp = /((?:\+|\-|~|=|\:(\w+))?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
while(p < filterString.length) {
|
||||
// Skip any whitespace
|
||||
whitespaceRegExp.lastIndex = p;
|
||||
@ -140,16 +158,19 @@ exports.parseFilter = function(filterString) {
|
||||
};
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p++;
|
||||
p = p + operation.prefix.length;
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
}
|
||||
if(match[2]) { // Opening square bracket
|
||||
if(match[3]) { // Opening square bracket
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
}
|
||||
if(match[3] || match[4] || match[5]) { // Double quoted string, single quoted string or unquoted title
|
||||
if(match[4] || match[5] || match[6]) { // Double quoted string, single quoted string or unquoted title
|
||||
operation.operators.push(
|
||||
{operator: "title", operand: match[3] || match[4] || match[5]}
|
||||
{operator: "title", operands: [{text: match[4] || match[5] || match[6]}]}
|
||||
);
|
||||
}
|
||||
results.push(operation);
|
||||
@ -166,6 +187,14 @@ exports.getFilterOperators = function() {
|
||||
return this.filterOperators;
|
||||
};
|
||||
|
||||
exports.getFilterRunPrefixes = function() {
|
||||
if(!this.filterRunPrefixes) {
|
||||
$tw.Wiki.prototype.filterRunPrefixes = {};
|
||||
$tw.modules.applyMethods("filterrunprefix",this.filterRunPrefixes);
|
||||
}
|
||||
return this.filterRunPrefixes;
|
||||
}
|
||||
|
||||
exports.filterTiddlers = function(filterString,widget,source) {
|
||||
var fn = this.compileFilter(filterString);
|
||||
return fn.call(this,source,widget);
|
||||
@ -198,7 +227,7 @@ exports.compileFilter = function(filterString) {
|
||||
results = [],
|
||||
currTiddlerTitle = widget && widget.getVariable("currentTiddler");
|
||||
$tw.utils.each(operation.operators,function(operator) {
|
||||
var operand = operator.operand,
|
||||
var operands = [],
|
||||
operatorFunction;
|
||||
if(!operator.operator) {
|
||||
operatorFunction = filterOperators.title;
|
||||
@ -207,16 +236,23 @@ exports.compileFilter = function(filterString) {
|
||||
} else {
|
||||
operatorFunction = filterOperators[operator.operator];
|
||||
}
|
||||
if(operator.indirect) {
|
||||
operand = self.getTextReference(operator.operand,"",currTiddlerTitle);
|
||||
}
|
||||
if(operator.variable) {
|
||||
operand = widget.getVariable(operator.operand,{defaultValue: ""});
|
||||
}
|
||||
|
||||
$tw.utils.each(operator.operands,function(operand) {
|
||||
if(operand.indirect) {
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
} else if(operand.variable) {
|
||||
operand.value = widget.getVariable(operand.text,{defaultValue: ""});
|
||||
} else {
|
||||
operand.value = operand.text;
|
||||
}
|
||||
operands.push(operand.value);
|
||||
});
|
||||
|
||||
// Invoke the appropriate filteroperator module
|
||||
results = operatorFunction(accumulator,{
|
||||
operator: operator.operator,
|
||||
operand: operand,
|
||||
operand: operands.length > 0 ? operands[0] : undefined,
|
||||
operands: operands,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
@ -241,35 +277,30 @@ exports.compileFilter = function(filterString) {
|
||||
return resultArray;
|
||||
}
|
||||
};
|
||||
var filterRunPrefixes = self.getFilterRunPrefixes();
|
||||
// Wrap the operator functions in a wrapper function that depends on the prefix
|
||||
operationFunctions.push((function() {
|
||||
var options = {wiki: self};
|
||||
switch(operation.prefix || "") {
|
||||
case "": // No prefix means that the operation is unioned into the result
|
||||
return function(results,source,widget) {
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
};
|
||||
return filterRunPrefixes["or"](operationSubFunction, options);
|
||||
case "=": // The results of the operation are pushed into the result without deduplication
|
||||
return function(results,source,widget) {
|
||||
Array.prototype.push.apply(results,operationSubFunction(source,widget));
|
||||
};
|
||||
return filterRunPrefixes["all"](operationSubFunction, options);
|
||||
case "-": // The results of this operation are removed from the main result
|
||||
return function(results,source,widget) {
|
||||
$tw.utils.removeArrayEntries(results,operationSubFunction(source,widget));
|
||||
};
|
||||
return filterRunPrefixes["except"](operationSubFunction, options);
|
||||
case "+": // This operation is applied to the main results so far
|
||||
return function(results,source,widget) {
|
||||
// This replaces all the elements of the array, but keeps the actual array so that references to it are preserved
|
||||
source = self.makeTiddlerIterator(results);
|
||||
results.splice(0,results.length);
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
};
|
||||
return filterRunPrefixes["and"](operationSubFunction, options);
|
||||
case "~": // This operation is unioned into the result only if the main result so far is empty
|
||||
return function(results,source,widget) {
|
||||
if(results.length === 0) {
|
||||
// Main result so far is empty
|
||||
$tw.utils.pushTop(results,operationSubFunction(source,widget));
|
||||
}
|
||||
};
|
||||
return filterRunPrefixes["else"](operationSubFunction, options);
|
||||
default:
|
||||
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
|
||||
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
|
||||
} else {
|
||||
return function(results,source,widget) {
|
||||
results.clear();
|
||||
results.push($tw.language.getString("Error/FilterRunPrefix"));
|
||||
};
|
||||
}
|
||||
}
|
||||
})());
|
||||
});
|
||||
@ -280,11 +311,11 @@ exports.compileFilter = function(filterString) {
|
||||
} else if(typeof source === "object") { // Array or hashmap
|
||||
source = self.makeTiddlerIterator(source);
|
||||
}
|
||||
var results = [];
|
||||
var results = new $tw.utils.LinkedList();
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
});
|
||||
return results;
|
||||
return results.toArray();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -16,14 +16,16 @@ Filter operator for returning the descriptions of the specified edition names
|
||||
Export our filter function
|
||||
*/
|
||||
exports.editiondescription = function(source,operator,options) {
|
||||
var results = [],
|
||||
editionInfo = $tw.utils.getEditionInfo();
|
||||
if(editionInfo) {
|
||||
source(function(tiddler,title) {
|
||||
if($tw.utils.hop(editionInfo,title)) {
|
||||
results.push(editionInfo[title].description || "");
|
||||
}
|
||||
});
|
||||
var results = [];
|
||||
if($tw.node) {
|
||||
var editionInfo = $tw.utils.getEditionInfo();
|
||||
if(editionInfo) {
|
||||
source(function(tiddler,title) {
|
||||
if($tw.utils.hop(editionInfo,title)) {
|
||||
results.push(editionInfo[title].description || "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
@ -16,14 +16,16 @@ Filter operator for returning the names of the available editions in this wiki
|
||||
Export our filter function
|
||||
*/
|
||||
exports.editions = function(source,operator,options) {
|
||||
var results = [],
|
||||
editionInfo = $tw.utils.getEditionInfo();
|
||||
if(editionInfo) {
|
||||
$tw.utils.each(editionInfo,function(info,name) {
|
||||
results.push(name);
|
||||
});
|
||||
var results = [];
|
||||
if($tw.node) {
|
||||
var editionInfo = $tw.utils.getEditionInfo();
|
||||
if(editionInfo) {
|
||||
$tw.utils.each(editionInfo,function(info,name) {
|
||||
results.push(name);
|
||||
});
|
||||
}
|
||||
results.sort();
|
||||
}
|
||||
results.sort();
|
||||
return results;
|
||||
};
|
||||
|
||||
|
@ -77,7 +77,7 @@ exports.encodehtml = function(source,operator,options) {
|
||||
exports.stringify = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.stringify(title));
|
||||
results.push($tw.utils.stringify(title,(operator.suffix === "rawunicode")));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
@ -85,7 +85,7 @@ exports.stringify = function(source,operator,options) {
|
||||
exports.jsonstringify = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.jsonStringify(title));
|
||||
results.push($tw.utils.jsonStringify(title,(operator.suffix === "rawunicode")));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
|
36
core/modules/filters/is/draft.js
Normal file
36
core/modules/filters/is/draft.js
Normal file
@ -0,0 +1,36 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/is/draft.js
|
||||
type: application/javascript
|
||||
module-type: isfilteroperator
|
||||
|
||||
Filter function for [is[draft]] analagous to [has[draft.of]]
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.draft = function(source,prefix,options) {
|
||||
var results = [];
|
||||
if(prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(!tiddler || !$tw.utils.hop(tiddler.fields,"draft.of")) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
source(function(tiddler,title) {
|
||||
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) || options.wiki.getTiddlerText(operator.operand + operator.suffix));
|
||||
results.push(options.wiki.getTiddlerText(operator.operand + title) || operator.suffix || '');
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
@ -91,6 +91,20 @@ exports.exponential = makeNumericBinaryOperator(
|
||||
function(a,b) {return Number.prototype.toExponential.call(a,Math.min(Math.max(b,0),100));}
|
||||
);
|
||||
|
||||
exports.power = makeNumericBinaryOperator(
|
||||
function(a,b) {return Math.pow(a,b);}
|
||||
);
|
||||
|
||||
exports.log = makeNumericBinaryOperator(
|
||||
function(a,b) {
|
||||
if(b) {
|
||||
return Math.log(a)/Math.log(b);
|
||||
} else {
|
||||
return Math.log(a);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
exports.sum = makeNumericReducingOperator(
|
||||
function(accumulator,value) {return accumulator + value},
|
||||
0 // Initial value
|
||||
|
@ -23,7 +23,7 @@ exports.reduce = function(source,operator,options) {
|
||||
});
|
||||
// Run the filter over each item
|
||||
var filterFn = options.wiki.compileFilter(operator.operand),
|
||||
accumulator = operator.suffix || "";
|
||||
accumulator = operator.operands[1] || "";
|
||||
for(var index=0; index<results.length; index++) {
|
||||
var title = results[index],
|
||||
list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{
|
||||
@ -48,7 +48,11 @@ exports.reduce = function(source,operator,options) {
|
||||
accumulator = "" + list[0];
|
||||
}
|
||||
}
|
||||
return [accumulator];
|
||||
if(results.length > 0) {
|
||||
return [accumulator];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -56,14 +56,14 @@ exports.trim = function(source,operator,options) {
|
||||
return result;
|
||||
};
|
||||
|
||||
// makeStringBinaryOperator(
|
||||
// function(a) {return [$tw.utils.trim(a)];}
|
||||
// );
|
||||
|
||||
exports.split = makeStringBinaryOperator(
|
||||
function(a,b) {return ("" + a).split(b);}
|
||||
);
|
||||
|
||||
exports["enlist-input"] = makeStringBinaryOperator(
|
||||
function(a,o,s) {return $tw.utils.parseStringArray("" + a,(s === "raw"));}
|
||||
);
|
||||
|
||||
exports.join = makeStringReducingOperator(
|
||||
function(accumulator,value,operand) {
|
||||
if(accumulator === null) {
|
||||
@ -78,7 +78,7 @@ function makeStringBinaryOperator(fnCalc) {
|
||||
return function(source,operator,options) {
|
||||
var result = [];
|
||||
source(function(tiddler,title) {
|
||||
Array.prototype.push.apply(result,fnCalc(title,operator.operand || ""));
|
||||
Array.prototype.push.apply(result,fnCalc(title,operator.operand || "",operator.suffix || ""));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
@ -115,4 +115,61 @@ exports.splitregexp = function(source,operator,options) {
|
||||
return result;
|
||||
};
|
||||
|
||||
exports["search-replace"] = function(source,operator,options) {
|
||||
var results = [],
|
||||
suffixes = operator.suffixes || [],
|
||||
flagSuffix = (suffixes[0] ? (suffixes[0][0] || "") : ""),
|
||||
flags = (flagSuffix.indexOf("g") !== -1 ? "g" : "") + (flagSuffix.indexOf("i") !== -1 ? "i" : ""),
|
||||
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
|
||||
searchTerm = isRegExp ? operator.operand : $tw.utils.escapeRegExp(operator.operand);
|
||||
try {
|
||||
regExp = new RegExp(searchTerm,flags);
|
||||
} catch(ex) {
|
||||
return ["RegExp error: " + ex];
|
||||
}
|
||||
results.push(
|
||||
title.replace(regExp,operator.operands[1])
|
||||
);
|
||||
} else {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.pad = function(source,operator,options) {
|
||||
var results = [],
|
||||
targetLength = operator.operand ? parseInt(operator.operand) : 0,
|
||||
fill = operator.operands[1] || "0";
|
||||
|
||||
source(function(tiddler,title) {
|
||||
if(title && title.length) {
|
||||
if(title.length >= targetLength) {
|
||||
results.push(title);
|
||||
} else {
|
||||
var padString = "",
|
||||
padStringLength = targetLength - title.length;
|
||||
while (padStringLength > padString.length) {
|
||||
padString += fill;
|
||||
}
|
||||
//make sure we do not exceed the specified length
|
||||
padString = padString.slice(0,padStringLength);
|
||||
if(operator.suffix && (operator.suffix === "suffix")) {
|
||||
title = title + padString;
|
||||
} else {
|
||||
title = padString + title;
|
||||
}
|
||||
results.push(title);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -8,183 +8,228 @@ Extended filter operators to manipulate the current list.
|
||||
\*/
|
||||
(function () {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Fetch titles from the current list
|
||||
*/
|
||||
var prepare_results = function (source) {
|
||||
var results = [];
|
||||
source(function (tiddler, title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
/*
|
||||
Fetch titles from the current list
|
||||
*/
|
||||
var prepare_results = function (source) {
|
||||
var results = [];
|
||||
source(function (tiddler, title) {
|
||||
results.push(title);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Moves a number of items from the tail of the current list before the item named in the operand
|
||||
*/
|
||||
exports.putbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -1) :
|
||||
results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index, -count));
|
||||
};
|
||||
/*
|
||||
Moves a number of items from the tail of the current list before the item named in the operand
|
||||
*/
|
||||
exports.putbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -1) :
|
||||
results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index, -count));
|
||||
};
|
||||
|
||||
/*
|
||||
Moves a number of items from the tail of the current list after the item named in the operand
|
||||
*/
|
||||
exports.putafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -1) :
|
||||
results.slice(0, index + 1).concat(results.slice(-count)).concat(results.slice(index + 1, -count));
|
||||
};
|
||||
/*
|
||||
Moves a number of items from the tail of the current list after the item named in the operand
|
||||
*/
|
||||
exports.putafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -1) :
|
||||
results.slice(0, index + 1).concat(results.slice(-count)).concat(results.slice(index + 1, -count));
|
||||
};
|
||||
|
||||
/*
|
||||
Replaces the item named in the operand with a number of items from the tail of the current list
|
||||
*/
|
||||
exports.replace = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -count) :
|
||||
results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index + 1, -count));
|
||||
};
|
||||
/*
|
||||
Replaces the item named in the operand with a number of items from the tail of the current list
|
||||
*/
|
||||
exports.replace = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return (index === -1) ?
|
||||
results.slice(0, -count) :
|
||||
results.slice(0, index).concat(results.slice(-count)).concat(results.slice(index + 1, -count));
|
||||
};
|
||||
|
||||
/*
|
||||
Moves a number of items from the tail of the current list to the head of the list
|
||||
*/
|
||||
exports.putfirst = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return results.slice(-count).concat(results.slice(0, -count));
|
||||
};
|
||||
/*
|
||||
Moves a number of items from the tail of the current list to the head of the list
|
||||
*/
|
||||
exports.putfirst = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return results.slice(-count).concat(results.slice(0, -count));
|
||||
};
|
||||
|
||||
/*
|
||||
Moves a number of items from the head of the current list to the tail of the list
|
||||
*/
|
||||
exports.putlast = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return results.slice(count).concat(results.slice(0, count));
|
||||
};
|
||||
/*
|
||||
Moves a number of items from the head of the current list to the tail of the list
|
||||
*/
|
||||
exports.putlast = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,1);
|
||||
return results.slice(count).concat(results.slice(0, count));
|
||||
};
|
||||
|
||||
/*
|
||||
Moves the item named in the operand a number of places forward or backward in the list
|
||||
*/
|
||||
exports.move = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1),
|
||||
marker = results.splice(index, 1),
|
||||
offset = (index + count) > 0 ? index + count : 0;
|
||||
return results.slice(0, offset).concat(marker).concat(results.slice(offset));
|
||||
};
|
||||
/*
|
||||
Moves the item named in the operand a number of places forward or backward in the list
|
||||
*/
|
||||
exports.move = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand),
|
||||
count = $tw.utils.getInt(operator.suffix,1),
|
||||
marker = results.splice(index, 1),
|
||||
offset = (index + count) > 0 ? index + count : 0;
|
||||
return results.slice(0, offset).concat(marker).concat(results.slice(offset));
|
||||
};
|
||||
|
||||
/*
|
||||
Returns the items from the current list that are after the item named in the operand
|
||||
*/
|
||||
exports.allafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand);
|
||||
return (index === -1) ? [] :
|
||||
(operator.suffix) ? results.slice(index) :
|
||||
results.slice(index + 1);
|
||||
};
|
||||
/*
|
||||
Returns the items from the current list that are after the item named in the operand
|
||||
*/
|
||||
exports.allafter = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand);
|
||||
return (index === -1) ? [] :
|
||||
(operator.suffix) ? results.slice(index) :
|
||||
results.slice(index + 1);
|
||||
};
|
||||
|
||||
/*
|
||||
Returns the items from the current list that are before the item named in the operand
|
||||
*/
|
||||
exports.allbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand);
|
||||
return (index === -1) ? [] :
|
||||
(operator.suffix) ? results.slice(0, index + 1) :
|
||||
results.slice(0, index);
|
||||
};
|
||||
/*
|
||||
Returns the items from the current list that are before the item named in the operand
|
||||
*/
|
||||
exports.allbefore = function (source, operator) {
|
||||
var results = prepare_results(source),
|
||||
index = results.indexOf(operator.operand);
|
||||
return (index === -1) ? [] :
|
||||
(operator.suffix) ? results.slice(0, index + 1) :
|
||||
results.slice(0, index);
|
||||
};
|
||||
|
||||
/*
|
||||
Appends the items listed in the operand array to the tail of the current list
|
||||
*/
|
||||
exports.append = function (source, operator) {
|
||||
var append = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || append.length;
|
||||
return (append.length === 0) ? results :
|
||||
(operator.prefix) ? results.concat(append.slice(-count)) :
|
||||
results.concat(append.slice(0, count));
|
||||
};
|
||||
/*
|
||||
Appends the items listed in the operand array to the tail of the current list
|
||||
*/
|
||||
exports.append = function (source, operator) {
|
||||
var append = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || append.length;
|
||||
return (append.length === 0) ? results :
|
||||
(operator.prefix) ? results.concat(append.slice(-count)) :
|
||||
results.concat(append.slice(0, count));
|
||||
};
|
||||
|
||||
/*
|
||||
Prepends the items listed in the operand array to the head of the current list
|
||||
*/
|
||||
exports.prepend = function (source, operator) {
|
||||
var prepend = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,prepend.length);
|
||||
return (prepend.length === 0) ? results :
|
||||
(operator.prefix) ? prepend.slice(-count).concat(results) :
|
||||
prepend.slice(0, count).concat(results);
|
||||
};
|
||||
/*
|
||||
Prepends the items listed in the operand array to the head of the current list
|
||||
*/
|
||||
exports.prepend = function (source, operator) {
|
||||
var prepend = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = $tw.utils.getInt(operator.suffix,prepend.length);
|
||||
return (prepend.length === 0) ? results :
|
||||
(operator.prefix) ? prepend.slice(-count).concat(results) :
|
||||
prepend.slice(0, count).concat(results);
|
||||
};
|
||||
|
||||
/*
|
||||
Returns all items from the current list except the items listed in the operand array
|
||||
*/
|
||||
exports.remove = function (source, operator) {
|
||||
var array = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || array.length,
|
||||
p,
|
||||
len,
|
||||
index;
|
||||
len = array.length - 1;
|
||||
for (p = 0; p < count; ++p) {
|
||||
if (operator.prefix) {
|
||||
index = results.indexOf(array[len - p]);
|
||||
} else {
|
||||
index = results.indexOf(array[p]);
|
||||
}
|
||||
if (index !== -1) {
|
||||
results.splice(index, 1);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
/*
|
||||
Returns all items from the current list except the items listed in the operand array
|
||||
*/
|
||||
exports.remove = function (source, operator) {
|
||||
var array = $tw.utils.parseStringArray(operator.operand, "true"),
|
||||
results = prepare_results(source),
|
||||
count = parseInt(operator.suffix) || array.length,
|
||||
p,
|
||||
len,
|
||||
index;
|
||||
len = array.length - 1;
|
||||
for (p = 0; p < count; ++p) {
|
||||
if (operator.prefix) {
|
||||
index = results.indexOf(array[len - p]);
|
||||
} else {
|
||||
index = results.indexOf(array[p]);
|
||||
}
|
||||
if (index !== -1) {
|
||||
results.splice(index, 1);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Returns all items from the current list sorted in the order of the items in the operand array
|
||||
*/
|
||||
exports.sortby = function (source, operator) {
|
||||
var results = prepare_results(source);
|
||||
if (!results || results.length < 2) {
|
||||
return results;
|
||||
}
|
||||
var lookup = $tw.utils.parseStringArray(operator.operand, "true");
|
||||
results.sort(function (a, b) {
|
||||
return lookup.indexOf(a) - lookup.indexOf(b);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
/*
|
||||
Returns all items from the current list sorted in the order of the items in the operand array
|
||||
*/
|
||||
exports.sortby = function (source, operator) {
|
||||
var results = prepare_results(source);
|
||||
if (!results || results.length < 2) {
|
||||
return results;
|
||||
}
|
||||
var lookup = $tw.utils.parseStringArray(operator.operand, "true");
|
||||
results.sort(function (a, b) {
|
||||
return lookup.indexOf(a) - lookup.indexOf(b);
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Removes all duplicate items from the current list
|
||||
*/
|
||||
exports.unique = function (source, operator) {
|
||||
var results = prepare_results(source);
|
||||
var set = results.reduce(function (a, b) {
|
||||
if (a.indexOf(b) < 0) {
|
||||
a.push(b);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
return set;
|
||||
};
|
||||
|
||||
var cycleValueInArray = function(results,operands,stepSize) {
|
||||
var resultsIndex,
|
||||
step = stepSize || 1,
|
||||
i = 0,
|
||||
opLength = operands.length,
|
||||
nextOperandIndex;
|
||||
for(i; i < opLength; i++) {
|
||||
resultsIndex = results.indexOf(operands[i]);
|
||||
if(resultsIndex !== -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(resultsIndex !== -1) {
|
||||
i = i + step;
|
||||
nextOperandIndex = (i < opLength ? i : i - opLength);
|
||||
if(operands.length > 1) {
|
||||
results.splice(resultsIndex,1,operands[nextOperandIndex]);
|
||||
} else {
|
||||
results.splice(resultsIndex,1);
|
||||
}
|
||||
} else {
|
||||
results.push(operands[0]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/*
|
||||
Toggles an item in the current list.
|
||||
*/
|
||||
exports.toggle = function(source,operator) {
|
||||
return cycleValueInArray(prepare_results(source),operator.operands);
|
||||
}
|
||||
|
||||
exports.cycle = function(source,operator) {
|
||||
var results = prepare_results(source),
|
||||
operands = (operator.operand.length ? $tw.utils.parseStringArray(operator.operand, "true") : [""]),
|
||||
step = $tw.utils.getInt(operator.operands[1]||"",1);
|
||||
if(step < 0) {
|
||||
operands.reverse();
|
||||
step = Math.abs(step);
|
||||
}
|
||||
return cycleValueInArray(results,operands,step);
|
||||
}
|
||||
|
||||
/*
|
||||
Removes all duplicate items from the current list
|
||||
*/
|
||||
exports.unique = function (source, operator) {
|
||||
var results = prepare_results(source);
|
||||
var set = results.reduce(function (a, b) {
|
||||
if (a.indexOf(b) < 0) {
|
||||
a.push(b);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
return set;
|
||||
};
|
||||
})();
|
||||
|
@ -12,7 +12,7 @@ Initialise basic platform $:/info/ tiddlers
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.getInfoTiddlerFields = function() {
|
||||
exports.getInfoTiddlerFields = function(updateInfoTiddlersCallback) {
|
||||
var mapBoolean = function(value) {return value ? "yes" : "no";},
|
||||
infoTiddlerFields = [];
|
||||
// Basics
|
||||
@ -36,6 +36,13 @@ exports.getInfoTiddlerFields = function() {
|
||||
// Screen size
|
||||
infoTiddlerFields.push({title: "$:/info/browser/screen/width", text: window.screen.width.toString()});
|
||||
infoTiddlerFields.push({title: "$:/info/browser/screen/height", text: window.screen.height.toString()});
|
||||
// Dark mode through event listener on MediaQueryList
|
||||
var mqList = window.matchMedia("(prefers-color-scheme: dark)"),
|
||||
getDarkModeTiddler = function() {return {title: "$:/info/darkmode", text: mqList.matches ? "yes" : "no"};};
|
||||
infoTiddlerFields.push(getDarkModeTiddler());
|
||||
mqList.addListener(function(event) {
|
||||
updateInfoTiddlersCallback([getDarkModeTiddler()]);
|
||||
});
|
||||
// Language
|
||||
infoTiddlerFields.push({title: "$:/info/browser/language", text: navigator.language || ""});
|
||||
}
|
||||
|
@ -285,13 +285,17 @@ KeyboardManager.prototype.checkKeyDescriptors = function(event,keyInfoArray) {
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.getEventModifierKeyDescriptor = function(event) {
|
||||
return event.ctrlKey && !event.shiftKey && !event.altKey ? "ctrl" :
|
||||
event.shiftKey && !event.ctrlKey && !event.altKey? "shift" :
|
||||
event.ctrlKey && event.shiftKey && !event.altKey ? "ctrl-shift" :
|
||||
event.altKey && !event.shiftKey && !event.ctrlKey ? "alt" :
|
||||
event.altKey && event.shiftKey && !event.ctrlKey ? "alt-shift" :
|
||||
event.altKey && event.ctrlKey && !event.shiftKey ? "ctrl-alt" :
|
||||
event.altKey && event.shiftKey && event.ctrlKey ? "ctrl-alt-shift" : "normal";
|
||||
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" :
|
||||
event.altKey && event.shiftKey && !event.ctrlKey && !event.metaKey ? "alt-shift" :
|
||||
event.altKey && event.ctrlKey && !event.shiftKey && !event.metaKey ? "ctrl-alt" :
|
||||
event.altKey && event.shiftKey && event.ctrlKey && !event.metaKey ? "ctrl-alt-shift" :
|
||||
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";
|
||||
};
|
||||
|
||||
KeyboardManager.prototype.getShortcutTiddlerList = function() {
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -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) {
|
||||
@ -405,22 +425,18 @@ Amend the rules used by this instance of the parser
|
||||
WikiParser.prototype.amendRules = function(type,names) {
|
||||
names = names || [];
|
||||
// Define the filter function
|
||||
var keepFilter;
|
||||
var target;
|
||||
if(type === "only") {
|
||||
keepFilter = function(name) {
|
||||
return names.indexOf(name) !== -1;
|
||||
};
|
||||
target = true;
|
||||
} else if(type === "except") {
|
||||
keepFilter = function(name) {
|
||||
return names.indexOf(name) === -1;
|
||||
};
|
||||
target = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// Define a function to process each of our rule arrays
|
||||
var processRuleArray = function(ruleArray) {
|
||||
for(var t=ruleArray.length-1; t>=0; t--) {
|
||||
if(!keepFilter(ruleArray[t].rule.name)) {
|
||||
if((names.indexOf(ruleArray[t].rule.name) === -1) === target) {
|
||||
ruleArray.splice(t,1);
|
||||
}
|
||||
}
|
||||
|
@ -197,8 +197,12 @@ SaverHandler.prototype.isDirty = function() {
|
||||
Update the document body with the class "tc-dirty" if the wiki has unsaved/unsynced changes
|
||||
*/
|
||||
SaverHandler.prototype.updateDirtyStatus = function() {
|
||||
var self = this;
|
||||
if($tw.browser) {
|
||||
$tw.utils.toggleClass(document.body,"tc-dirty",this.isDirty());
|
||||
$tw.utils.each($tw.windows,function(win) {
|
||||
$tw.utils.toggleClass(win.document.body,"tc-dirty",self.isDirty());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
60
core/modules/savers/custom.js
Normal file
60
core/modules/savers/custom.js
Normal file
@ -0,0 +1,60 @@
|
||||
/*\
|
||||
title: $:/core/modules/savers/custom.js
|
||||
type: application/javascript
|
||||
module-type: saver
|
||||
|
||||
Looks for `window.$tw.customSaver` first on the current window, then
|
||||
on the parent window (of an iframe). If present, the saver must define
|
||||
save: function(text,method,callback) { ... }
|
||||
and the saver may define
|
||||
priority: number
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var findSaver = function(window) {
|
||||
try {
|
||||
return window && window.$tw && window.$tw.customSaver;
|
||||
} catch (err) {
|
||||
// Catching the exception is the most reliable way to detect cross-origin iframe errors.
|
||||
// For example, instead of saying that `window.parent.$tw` is undefined, Firefox will throw
|
||||
// Uncaught DOMException: Permission denied to access property "$tw" on cross-origin object
|
||||
console.log({ msg: "custom saver is disabled", reason: err });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var saver = findSaver(window) || findSaver(window.parent) || {};
|
||||
|
||||
var CustomSaver = function(wiki) {
|
||||
};
|
||||
|
||||
CustomSaver.prototype.save = function(text,method,callback) {
|
||||
return saver.save(text, method, callback);
|
||||
};
|
||||
|
||||
/*
|
||||
Information about this saver
|
||||
*/
|
||||
CustomSaver.prototype.info = {
|
||||
name: "custom",
|
||||
priority: saver.priority || 4000,
|
||||
capabilities: ["save","autosave"]
|
||||
};
|
||||
|
||||
/*
|
||||
Static method that returns true if this saver is capable of working
|
||||
*/
|
||||
exports.canSave = function(wiki) {
|
||||
return !!(saver.save);
|
||||
};
|
||||
|
||||
/*
|
||||
Create an instance of this saver
|
||||
*/
|
||||
exports.create = function(wiki) {
|
||||
return new CustomSaver(wiki);
|
||||
};
|
||||
})();
|
@ -26,12 +26,13 @@ GitHubSaver.prototype.save = function(text,method,callback) {
|
||||
repo = this.wiki.getTiddlerText("$:/GitHub/Repo"),
|
||||
path = this.wiki.getTiddlerText("$:/GitHub/Path",""),
|
||||
filename = this.wiki.getTiddlerText("$:/GitHub/Filename"),
|
||||
branch = this.wiki.getTiddlerText("$:/GitHub/Branch") || "master",
|
||||
branch = this.wiki.getTiddlerText("$:/GitHub/Branch") || "main",
|
||||
endpoint = this.wiki.getTiddlerText("$:/GitHub/ServerURL") || "https://api.github.com",
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Authorization": "Basic " + window.btoa(username + ":" + password)
|
||||
"Authorization": "Basic " + window.btoa(username + ":" + password),
|
||||
"If-None-Match": ""
|
||||
};
|
||||
// Bail if we don't have everything we need
|
||||
if(!username || !password || !repo || !filename) {
|
||||
|
@ -28,10 +28,22 @@ 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) {
|
||||
|
@ -25,8 +25,9 @@ exports.handler = function(request,response,state) {
|
||||
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();
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ exports.handler = function(request,response,state) {
|
||||
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"
|
||||
},
|
||||
|
@ -28,6 +28,9 @@ exports.handler = function(request,response,state) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(state.wiki.getTiddlerText("$:/config/SyncSystemTiddlersFromServer") === "no") {
|
||||
filter += "+[!is[system]]";
|
||||
}
|
||||
var excludeFields = (state.queryParameters.exclude || "text").split(","),
|
||||
titles = state.wiki.filterTiddlers(filter);
|
||||
response.writeHead(200, {"Content-Type": "application/json"});
|
||||
|
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;
|
||||
|
||||
})();
|
@ -21,29 +21,37 @@ exports.synchronous = true;
|
||||
var TITLE_INFO_PLUGIN = "$:/temp/info-plugin";
|
||||
|
||||
exports.startup = function() {
|
||||
// Function to bake the info plugin with new tiddlers
|
||||
var updateInfoPlugin = function(tiddlerFieldsArray) {
|
||||
// Get the existing tiddlers
|
||||
var json = $tw.wiki.getTiddlerData(TITLE_INFO_PLUGIN,{tiddlers: {}});
|
||||
// Add the new ones
|
||||
$tw.utils.each(tiddlerFieldsArray,function(fields) {
|
||||
if(fields && fields.title) {
|
||||
json.tiddlers[fields.title] = fields;
|
||||
}
|
||||
});
|
||||
// Bake the info tiddlers into a plugin. We use the non-standard plugin-type "info" because ordinary plugins are only registered asynchronously after being loaded dynamically
|
||||
var fields = {
|
||||
title: TITLE_INFO_PLUGIN,
|
||||
type: "application/json",
|
||||
"plugin-type": "info",
|
||||
text: JSON.stringify(json,null,$tw.config.preferences.jsonSpaces)
|
||||
};
|
||||
$tw.wiki.addTiddler(new $tw.Tiddler(fields));
|
||||
|
||||
};
|
||||
// Collect up the info tiddlers
|
||||
var infoTiddlerFields = {};
|
||||
// Give each info module a chance to fill in as many info tiddlers as they want
|
||||
var tiddlerFieldsArray = [];
|
||||
// Give each info module a chance to provide as many info tiddlers as they want as an array, and give them a callback for dynamically updating them
|
||||
$tw.modules.forEachModuleOfType("info",function(title,moduleExports) {
|
||||
if(moduleExports && moduleExports.getInfoTiddlerFields) {
|
||||
var tiddlerFieldsArray = moduleExports.getInfoTiddlerFields(infoTiddlerFields);
|
||||
$tw.utils.each(tiddlerFieldsArray,function(fields) {
|
||||
if(fields) {
|
||||
infoTiddlerFields[fields.title] = fields;
|
||||
}
|
||||
});
|
||||
Array.prototype.push.apply(tiddlerFieldsArray,moduleExports.getInfoTiddlerFields(updateInfoPlugin));
|
||||
}
|
||||
});
|
||||
// Bake the info tiddlers into a plugin. We use the non-standard plugin-type "info" because ordinary plugins are only registered asynchronously after being loaded dynamically
|
||||
var fields = {
|
||||
title: TITLE_INFO_PLUGIN,
|
||||
type: "application/json",
|
||||
"plugin-type": "info",
|
||||
text: JSON.stringify({tiddlers: infoTiddlerFields},null,$tw.config.preferences.jsonSpaces)
|
||||
};
|
||||
$tw.wiki.addTiddler(new $tw.Tiddler(fields));
|
||||
$tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
|
||||
$tw.wiki.registerPluginTiddlers("info");
|
||||
updateInfoPlugin(tiddlerFieldsArray);
|
||||
var changes = $tw.wiki.readPluginInfo([TITLE_INFO_PLUGIN]);
|
||||
$tw.wiki.registerPluginTiddlers("info",[TITLE_INFO_PLUGIN]);
|
||||
$tw.wiki.unpackPluginTiddlers();
|
||||
};
|
||||
|
||||
|
@ -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");
|
||||
|
@ -24,6 +24,7 @@ var PREFIX_CONFIG_REGISTER_PLUGIN_TYPE = "$:/config/RegisterPluginType/";
|
||||
exports.startup = function() {
|
||||
$tw.wiki.addTiddler({title: TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE,text: "no"});
|
||||
$tw.wiki.addEventListener("change",function(changes) {
|
||||
// Work out which of the changed tiddlers are plugins that we need to reregister
|
||||
var changesToProcess = [],
|
||||
requireReloadDueToPluginChange = false;
|
||||
$tw.utils.each(Object.keys(changes),function(title) {
|
||||
@ -38,6 +39,7 @@ exports.startup = function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
// Issue warning if any of the tiddlers require a reload
|
||||
if(requireReloadDueToPluginChange) {
|
||||
$tw.wiki.addTiddler({title: TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE,text: "yes"});
|
||||
}
|
||||
@ -45,12 +47,35 @@ exports.startup = function() {
|
||||
if(changesToProcess.length > 0) {
|
||||
var changes = $tw.wiki.readPluginInfo(changesToProcess);
|
||||
if(changes.modifiedPlugins.length > 0 || changes.deletedPlugins.length > 0) {
|
||||
var changedShadowTiddlers = {};
|
||||
// Collect the shadow tiddlers of any deleted plugins
|
||||
$tw.utils.each(changes.deletedPlugins,function(pluginTitle) {
|
||||
var pluginInfo = $tw.wiki.getPluginInfo(pluginTitle);
|
||||
if(pluginInfo) {
|
||||
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
|
||||
changedShadowTiddlers[title] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
// Collect the shadow tiddlers of any modified plugins
|
||||
$tw.utils.each(changes.modifiedPlugins,function(pluginTitle) {
|
||||
var pluginInfo = $tw.wiki.getPluginInfo(pluginTitle);
|
||||
if(pluginInfo) {
|
||||
$tw.utils.each(Object.keys(pluginInfo.tiddlers),function(title) {
|
||||
changedShadowTiddlers[title] = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
// (Re-)register any modified plugins
|
||||
$tw.wiki.registerPluginTiddlers(null,changes.modifiedPlugins);
|
||||
// Unregister any deleted plugins
|
||||
$tw.wiki.unregisterPluginTiddlers(null,changes.deletedPlugins);
|
||||
// Unpack the shadow tiddlers
|
||||
$tw.wiki.unpackPluginTiddlers();
|
||||
// Queue change events for the changed shadow tiddlers
|
||||
$tw.utils.each(Object.keys(changedShadowTiddlers),function(title) {
|
||||
$tw.wiki.enqueueTiddlerEvent(title,changedShadowTiddlers[title]);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ exports.synchronous = true;
|
||||
// Default story and history lists
|
||||
var PAGE_TITLE_TITLE = "$:/core/wiki/title";
|
||||
var PAGE_STYLESHEET_TITLE = "$:/core/ui/PageStylesheet";
|
||||
var PAGE_TEMPLATE_TITLE = "$:/core/ui/PageTemplate";
|
||||
var PAGE_TEMPLATE_TITLE = "$:/core/ui/RootTemplate";
|
||||
|
||||
// Time (in ms) that we defer refreshing changes to draft tiddlers
|
||||
var DRAFT_TIDDLER_TIMEOUT_TITLE = "$:/config/Drafts/TypingTimeout";
|
||||
@ -52,7 +52,7 @@ exports.startup = function() {
|
||||
}));
|
||||
// Display the $:/core/ui/PageTemplate tiddler to kick off the display
|
||||
$tw.perf.report("mainRender",function() {
|
||||
$tw.pageWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TEMPLATE_TITLE,{document: document, parentWidget: $tw.rootWidget});
|
||||
$tw.pageWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TEMPLATE_TITLE,{document: document, parentWidget: $tw.rootWidget, recursionMarker: "no"});
|
||||
$tw.pageContainer = document.createElement("div");
|
||||
$tw.utils.addClass($tw.pageContainer,"tc-page-container-wrapper");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
@ -107,7 +107,7 @@ exports.startup = function() {
|
||||
$tw.rootWidget.domNodes = [$tw.pageContainer];
|
||||
$tw.rootWidget.children = [$tw.pageWidgetNode];
|
||||
// Run any post-render startup actions
|
||||
$tw.rootWidget.executeStartupTiddlers("$:/tags/StartupAction/PostRender");
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/PostRender");
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -25,6 +25,9 @@ exports.startup = function() {
|
||||
$tw.rootWidget.addEventListener("tm-modal",function(event) {
|
||||
$tw.modal.display(event.param,{variables: event.paramObject, event: event});
|
||||
});
|
||||
$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) {
|
||||
|
@ -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));
|
||||
@ -64,12 +63,12 @@ exports.startup = function() {
|
||||
document: $tw.browser ? document : $tw.fakeDocument
|
||||
});
|
||||
// Execute any startup actions
|
||||
$tw.rootWidget.executeStartupTiddlers("$:/tags/StartupAction");
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction");
|
||||
if($tw.browser) {
|
||||
$tw.rootWidget.executeStartupTiddlers("$:/tags/StartupAction/Browser");
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Browser");
|
||||
}
|
||||
if($tw.node) {
|
||||
$tw.rootWidget.executeStartupTiddlers("$:/tags/StartupAction/Node");
|
||||
$tw.rootWidget.invokeActionsByTag("$:/tags/StartupAction/Node");
|
||||
}
|
||||
// Kick off the language manager and switcher
|
||||
$tw.language = new $tw.Language();
|
||||
|
@ -150,6 +150,11 @@ function openStartupTiddlers(options) {
|
||||
// Save the story list
|
||||
$tw.wiki.addTiddler({title: DEFAULT_STORY_TITLE, text: "", list: storyList},$tw.wiki.getModificationFields());
|
||||
// Update history
|
||||
var story = new $tw.Story({
|
||||
wiki: $tw.wiki,
|
||||
storyTitle: DEFAULT_STORY_TITLE,
|
||||
historyTitle: DEFAULT_HISTORY_TITLE
|
||||
});
|
||||
if(!options.disableHistory) {
|
||||
// If a target tiddler was specified add it to the history stack
|
||||
if(target && target !== "") {
|
||||
@ -157,9 +162,9 @@ function openStartupTiddlers(options) {
|
||||
if(target.indexOf("[[") === 0 && target.substr(-2) === "]]") {
|
||||
target = target.substr(2,target.length - 4);
|
||||
}
|
||||
$tw.wiki.addToHistory(target);
|
||||
story.addToHistory(target);
|
||||
} else if(storyList.length > 0) {
|
||||
$tw.wiki.addToHistory(storyList[0]);
|
||||
story.addToHistory(storyList[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ exports.after = ["startup"];
|
||||
exports.synchronous = true;
|
||||
|
||||
// Global to keep track of open windows (hashmap by title)
|
||||
var windows = {};
|
||||
$tw.windows = {};
|
||||
|
||||
exports.startup = function() {
|
||||
// Handle open window message
|
||||
@ -44,7 +44,7 @@ exports.startup = function() {
|
||||
catch(e) {
|
||||
return;
|
||||
}
|
||||
windows[title] = srcWindow;
|
||||
$tw.windows[title] = srcWindow;
|
||||
// Check for reopening the same window
|
||||
if(srcWindow.haveInitialisedWindow) {
|
||||
return;
|
||||
@ -54,7 +54,7 @@ exports.startup = function() {
|
||||
srcDocument.close();
|
||||
srcDocument.title = windowTitle;
|
||||
srcWindow.addEventListener("beforeunload",function(event) {
|
||||
delete windows[title];
|
||||
delete $tw.windows[title];
|
||||
$tw.wiki.removeEventListener("change",refreshHandler);
|
||||
},false);
|
||||
// Set up the styles
|
||||
@ -84,16 +84,13 @@ exports.startup = function() {
|
||||
name: "keydown",
|
||||
handlerObject: $tw.keyboardManager,
|
||||
handlerMethod: "handleKeydownEvent"
|
||||
},{
|
||||
name: "click",
|
||||
handlerObject: $tw.popup,
|
||||
handlerMethod: "handleEvent"
|
||||
}]);
|
||||
srcWindow.document.documentElement.addEventListener("click",$tw.popup,true);
|
||||
srcWindow.haveInitialisedWindow = true;
|
||||
});
|
||||
// Close open windows when unloading main window
|
||||
$tw.addUnloadTask(function() {
|
||||
$tw.utils.each(windows,function(win) {
|
||||
$tw.utils.each($tw.windows,function(win) {
|
||||
win.close();
|
||||
});
|
||||
});
|
||||
|
@ -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";
|
||||
@ -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,11 +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"),
|
||||
pollingEnabled = (self.wiki.getTiddlerText(self.titleSyncDisablePolling) !== "yes");
|
||||
if(this.syncadaptor && this.syncadaptor.getUpdatedTiddlers) {
|
||||
this.logger.log("Retrieving updated tiddler list");
|
||||
cancelNextSync();
|
||||
@ -320,9 +328,11 @@ Syncer.prototype.syncFromServer = function() {
|
||||
self.titlesToBeLoaded[title] = true;
|
||||
});
|
||||
$tw.utils.each(updates.deletions,function(title) {
|
||||
delete self.tiddlerInfo[title];
|
||||
self.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.wiki.deleteTiddler(title);
|
||||
if(syncSystemFromServer || !self.wiki.isSystemTiddler(title)) {
|
||||
delete self.tiddlerInfo[title];
|
||||
self.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.wiki.deleteTiddler(title);
|
||||
}
|
||||
});
|
||||
if(updates.modifications.length > 0 || updates.deletions.length > 0) {
|
||||
self.processTaskQueue();
|
||||
@ -365,9 +375,11 @@ Syncer.prototype.syncFromServer = function() {
|
||||
}
|
||||
// Delete any tiddlers that were previously reported but missing this time
|
||||
$tw.utils.each(previousTitles,function(title) {
|
||||
delete self.tiddlerInfo[title];
|
||||
self.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.wiki.deleteTiddler(title);
|
||||
if(syncSystemFromServer || !self.wiki.isSystemTiddler(title)) {
|
||||
delete self.tiddlerInfo[title];
|
||||
self.logger.log("Deleting tiddler missing from server:",title);
|
||||
self.wiki.deleteTiddler(title);
|
||||
}
|
||||
});
|
||||
self.processTaskQueue();
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
// Reject the incoming plugin by blanking all its fields
|
||||
if($tw.utils.checkVersions(existingTiddler.fields.version,incomingTiddler.version)) {
|
||||
tiddlers[title] = Object.create(null);
|
||||
messages[title] = requiresReload + $tw.language.getString("Import/Upgrader/Plugins/Suppressed/Version",{variables: {incoming: incomingTiddler.version, existing: existingTiddler.fields.version}});
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/Plugins/Suppressed/Version",{variables: {incoming: incomingTiddler.version, existing: existingTiddler.fields.version}});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ Modal message mechanism
|
||||
"use strict";
|
||||
|
||||
var widget = require("$:/core/modules/widgets/widget.js");
|
||||
var navigator = require("$:/core/modules/widgets/navigator.js");
|
||||
|
||||
var Modal = function(wiki) {
|
||||
this.wiki = wiki;
|
||||
@ -30,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,
|
||||
@ -41,7 +42,12 @@ Modal.prototype.display = function(title,options) {
|
||||
return;
|
||||
}
|
||||
// Create the variables
|
||||
var variables = $tw.utils.extend({currentTiddler: title},options.variables);
|
||||
var variables = $tw.utils.extend({
|
||||
currentTiddler: title,
|
||||
"tv-story-list": (options.event && options.event.widget ? options.event.widget.getVariable("tv-story-list") : ""),
|
||||
"tv-history-list": (options.event && options.event.widget ? options.event.widget.getVariable("tv-history-list") : "")
|
||||
},options.variables);
|
||||
|
||||
// Create the wrapper divs
|
||||
var wrapper = this.srcDocument.createElement("div"),
|
||||
modalBackdrop = this.srcDocument.createElement("div"),
|
||||
@ -75,6 +81,31 @@ Modal.prototype.display = function(title,options) {
|
||||
modalFooter.appendChild(modalFooterHelp);
|
||||
modalFooter.appendChild(modalFooterButtons);
|
||||
modalWrapper.appendChild(modalFooter);
|
||||
var navigatorTree = {
|
||||
"type": "navigator",
|
||||
"attributes": {
|
||||
"story": {
|
||||
"name": "story",
|
||||
"type": "string",
|
||||
"value": variables["tv-story-list"]
|
||||
},
|
||||
"history": {
|
||||
"name": "history",
|
||||
"type": "string",
|
||||
"value": variables["tv-history-list"]
|
||||
}
|
||||
},
|
||||
"tag": "$navigator",
|
||||
"isBlock": true,
|
||||
"children": []
|
||||
};
|
||||
var navigatorWidgetNode = new navigator.navigator(navigatorTree, {
|
||||
wiki: this.wiki,
|
||||
document : this.srcDocument,
|
||||
parentWidget: $tw.rootWidget
|
||||
});
|
||||
navigatorWidgetNode.render(modalBody,null);
|
||||
|
||||
// Render the title of the message
|
||||
var headerWidgetNode = this.wiki.makeTranscludeWidget(title,{
|
||||
field: "subtitle",
|
||||
@ -86,7 +117,7 @@ Modal.prototype.display = function(title,options) {
|
||||
type: "string",
|
||||
value: title
|
||||
}}}],
|
||||
parentWidget: $tw.rootWidget,
|
||||
parentWidget: navigatorWidgetNode,
|
||||
document: this.srcDocument,
|
||||
variables: variables,
|
||||
importPageMacros: true
|
||||
@ -94,11 +125,12 @@ Modal.prototype.display = function(title,options) {
|
||||
headerWidgetNode.render(headerTitle,null);
|
||||
// Render the body of the message
|
||||
var bodyWidgetNode = this.wiki.makeTranscludeWidget(title,{
|
||||
parentWidget: $tw.rootWidget,
|
||||
parentWidget: navigatorWidgetNode,
|
||||
document: this.srcDocument,
|
||||
variables: variables,
|
||||
importPageMacros: true
|
||||
});
|
||||
|
||||
bodyWidgetNode.render(modalBody,null);
|
||||
// Setup the link if present
|
||||
if(options.downloadLink) {
|
||||
@ -135,7 +167,7 @@ Modal.prototype.display = function(title,options) {
|
||||
value: $tw.language.getString("Buttons/Close/Caption")
|
||||
}}}
|
||||
]}],
|
||||
parentWidget: $tw.rootWidget,
|
||||
parentWidget: navigatorWidgetNode,
|
||||
document: this.srcDocument,
|
||||
variables: variables,
|
||||
importPageMacros: true
|
||||
|
@ -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;
|
||||
|
||||
}));
|
||||
})();
|
@ -204,15 +204,23 @@ exports.deleteEmptyDirs = function(dirpath,callback) {
|
||||
/*
|
||||
Create a fileInfo object for saving a tiddler:
|
||||
filepath: the absolute path to the file containing the tiddler
|
||||
type: the type of the tiddler file (NOT the type of the tiddler)
|
||||
type: the type of the tiddler file on disk (NOT the type of the tiddler)
|
||||
hasMetaFile: true if the file also has a companion .meta file
|
||||
isEditableFile: true if the tiddler was loaded via non-standard options & marked editable
|
||||
Options include:
|
||||
directory: absolute path of root directory to which we are saving
|
||||
pathFilters: optional array of filters to be used to generate the base path
|
||||
wiki: optional wiki for evaluating the pathFilters
|
||||
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
|
||||
*/
|
||||
exports.generateTiddlerFileInfo = function(tiddler,options) {
|
||||
var fileInfo = {};
|
||||
var fileInfo = {}, metaExt;
|
||||
// Propagate the isEditableFile flag
|
||||
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;
|
||||
$tw.utils.each(tiddler.getFieldStrings(),function(value,fieldName) {
|
||||
@ -238,19 +246,67 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
|
||||
fileInfo.type = tiddlerType;
|
||||
fileInfo.hasMetaFile = true;
|
||||
}
|
||||
if(options.extFilters) {
|
||||
// Check for extension overrides
|
||||
metaExt = $tw.utils.generateTiddlerExtension(tiddler.fields.title,{
|
||||
extFilters: options.extFilters,
|
||||
wiki: options.wiki
|
||||
});
|
||||
if(metaExt) {
|
||||
if(metaExt === ".tid") {
|
||||
// Overriding to the .tid extension needs special handling
|
||||
fileInfo.type = "application/x-tiddler";
|
||||
fileInfo.hasMetaFile = false;
|
||||
} else if (metaExt === ".json") {
|
||||
// Overriding to the .json extension needs special handling
|
||||
fileInfo.type = "application/json";
|
||||
fileInfo.hasMetaFile = false;
|
||||
} else {
|
||||
//If the new type matches a known extention, use that MIME type's encoding
|
||||
var extInfo = $tw.utils.getFileExtensionInfo(metaExt);
|
||||
fileInfo.type = extInfo ? extInfo.type : null;
|
||||
fileInfo.encoding = $tw.utils.getTypeEncoding(metaExt);
|
||||
fileInfo.hasMetaFile = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Take the file extension from the tiddler content type
|
||||
// Take the file extension from the tiddler content type or metaExt
|
||||
var contentTypeInfo = $tw.config.contentTypeInfo[fileInfo.type] || {extension: ""};
|
||||
// Generate the filepath
|
||||
fileInfo.filepath = $tw.utils.generateTiddlerFilepath(tiddler.fields.title,{
|
||||
extension: contentTypeInfo.extension,
|
||||
extension: metaExt || contentTypeInfo.extension,
|
||||
directory: options.directory,
|
||||
pathFilters: options.pathFilters,
|
||||
wiki: options.wiki
|
||||
wiki: options.wiki,
|
||||
fileInfo: options.fileInfo
|
||||
});
|
||||
return fileInfo;
|
||||
};
|
||||
|
||||
/*
|
||||
Generate the file extension for saving a tiddler
|
||||
Options include:
|
||||
extFilters: optional array of filters to be used to generate the extention
|
||||
wiki: optional wiki for evaluating the extFilters
|
||||
*/
|
||||
exports.generateTiddlerExtension = function(title,options) {
|
||||
var extension;
|
||||
// Check if any of the extFilters applies
|
||||
if(options.extFilters && options.wiki) {
|
||||
$tw.utils.each(options.extFilters,function(filter) {
|
||||
if(!extension) {
|
||||
var source = options.wiki.makeTiddlerIterator([title]),
|
||||
result = options.wiki.filterTiddlers(filter,null,source);
|
||||
if(result.length > 0) {
|
||||
extension = result[0];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return extension;
|
||||
};
|
||||
|
||||
/*
|
||||
Generate the filepath for saving a tiddler
|
||||
Options include:
|
||||
@ -258,11 +314,12 @@ Options include:
|
||||
directory: absolute path of root directory to which we are saving
|
||||
pathFilters: optional array of filters to be used to generate the base path
|
||||
wiki: optional wiki for evaluating the pathFilters
|
||||
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.fileInfo && options.fileInfo.originalpath) ? options.fileInfo.originalpath : "",
|
||||
filepath;
|
||||
// Check if any of the pathFilters applies
|
||||
if(options.pathFilters && options.wiki) {
|
||||
@ -276,26 +333,44 @@ exports.generateTiddlerFilepath = function(title,options) {
|
||||
}
|
||||
});
|
||||
}
|
||||
// If not, generate a base pathname
|
||||
if(!filepath) {
|
||||
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,"_");
|
||||
}
|
||||
// Don't let the filename start with a dot because such files are invisible on *nix
|
||||
filepath = filepath.replace(/^\./g,"_");
|
||||
// Remove any characters that can't be used in cross-platform filenames
|
||||
filepath = $tw.utils.transliterate(filepath.replace(/<|>|\:|\"|\||\?|\*|\^/g,"_"));
|
||||
// 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,function (u) { return u.replace(/\./g, "_")});
|
||||
}
|
||||
// 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 = "";
|
||||
$tw.utils.each(title.split(""),function(char) {
|
||||
@ -306,14 +381,31 @@ exports.generateTiddlerFilepath = function(title,options) {
|
||||
});
|
||||
}
|
||||
// Add a uniquifier if the file already exists
|
||||
var fullPath,
|
||||
var fullPath, oldPath = (options.fileInfo) ? options.fileInfo.filepath : undefined,
|
||||
count = 0;
|
||||
do {
|
||||
fullPath = path.resolve(directory,filepath + (count ? "_" + count : "") + extension);
|
||||
if(oldPath && oldPath == fullPath) {
|
||||
break;
|
||||
}
|
||||
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, 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 = !(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) {
|
||||
writePath = path.resolve(directory,encodeURIComponent(fullPath));
|
||||
}
|
||||
// Return the full path to the file
|
||||
return fullPath;
|
||||
return writePath;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -327,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);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -354,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
|
||||
@ -364,6 +471,73 @@ 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) {
|
||||
//Only attempt to delete files that exist on disk
|
||||
if(!fileInfo.filepath || !fs.existsSync(fileInfo.filepath)) {
|
||||
//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),function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),function(err) {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null,fileInfo);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
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) {
|
||||
var adaptorInfo = options.adaptorInfo || {},
|
||||
bootInfo = options.bootInfo || {},
|
||||
title = options.title || "undefined";
|
||||
if(adaptorInfo.filepath && bootInfo.filepath && adaptorInfo.filepath !== bootInfo.filepath) {
|
||||
$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,bootInfo);
|
||||
} else {
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
return callback(null,bootInfo);
|
||||
});
|
||||
} else {
|
||||
return callback(null,bootInfo);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
195
core/modules/utils/linked-list.js
Normal file
195
core/modules/utils/linked-list.js
Normal file
@ -0,0 +1,195 @@
|
||||
/*\
|
||||
module-type: utils
|
||||
title: $:/core/modules/utils/linkedlist.js
|
||||
type: application/javascript
|
||||
|
||||
This is a doubly-linked indexed list intended for manipulation, particularly
|
||||
pushTop, which it does with significantly better performance than an array.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
function LinkedList() {
|
||||
this.clear();
|
||||
};
|
||||
|
||||
LinkedList.prototype.clear = function() {
|
||||
// LinkedList performs the duty of both the head and tail node
|
||||
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 */) {
|
||||
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]);
|
||||
}
|
||||
for(var t=0; t<value.length; t++) {
|
||||
_linkToEnd(this,value[t]);
|
||||
}
|
||||
} else {
|
||||
_assertString(value);
|
||||
_removeOne(this,value);
|
||||
_linkToEnd(this,value);
|
||||
}
|
||||
};
|
||||
|
||||
LinkedList.prototype.each = function(callback) {
|
||||
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 = new Array(this.length),
|
||||
index = 0;
|
||||
this.each(function(value) { output[index++] = value; });
|
||||
return output;
|
||||
};
|
||||
|
||||
function _removeOne(list,value) {
|
||||
var prevEntry = list.prev[value],
|
||||
nextEntry = list.next[value],
|
||||
prev = prevEntry,
|
||||
next = nextEntry;
|
||||
if(typeof nextEntry === "object") {
|
||||
next = nextEntry[0];
|
||||
prev = prevEntry[0];
|
||||
}
|
||||
// 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;
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
})();
|
@ -53,6 +53,19 @@ exports.warning = function(text) {
|
||||
exports.log(text,"brown/orange");
|
||||
};
|
||||
|
||||
/*
|
||||
Log a table of name: value pairs
|
||||
*/
|
||||
exports.logTable = function(data) {
|
||||
if(console.table) {
|
||||
console.table(data);
|
||||
} else {
|
||||
$tw.utils.each(data,function(value,name) {
|
||||
console.log(name + ": " + value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Return the integer represented by the str (string).
|
||||
Return the dflt (default) parameter if str is not a base-10 number.
|
||||
@ -289,7 +302,7 @@ exports.formatDateString = function(date,template) {
|
||||
return $tw.utils.pad($tw.utils.getHours12(date));
|
||||
}],
|
||||
[/^wYYYY/, function() {
|
||||
return $tw.utils.getYearForWeekNo(date);
|
||||
return $tw.utils.pad($tw.utils.getYearForWeekNo(date),4);
|
||||
}],
|
||||
[/^hh12/, function() {
|
||||
return $tw.utils.getHours12(date);
|
||||
@ -298,7 +311,14 @@ exports.formatDateString = function(date,template) {
|
||||
return date.getDate() + $tw.utils.getDaySuffix(date);
|
||||
}],
|
||||
[/^YYYY/, function() {
|
||||
return date.getFullYear();
|
||||
return $tw.utils.pad(date.getFullYear(),4);
|
||||
}],
|
||||
[/^aYYYY/, function() {
|
||||
return $tw.utils.pad(Math.abs(date.getFullYear()),4);
|
||||
}],
|
||||
[/^\{era:([^,\|}]*)\|([^}\|]*)\|([^}]*)\}/, function(match) {
|
||||
var year = date.getFullYear();
|
||||
return year === 0 ? match[2] : (year < 0 ? match[1] : match[3]);
|
||||
}],
|
||||
[/^0hh/, function() {
|
||||
return $tw.utils.pad(date.getHours());
|
||||
@ -387,7 +407,7 @@ exports.formatDateString = function(date,template) {
|
||||
$tw.utils.each(matches, function(m) {
|
||||
var match = m[0].exec(t);
|
||||
if(match) {
|
||||
matchString = m[1].call();
|
||||
matchString = m[1].call(null,match);
|
||||
t = t.substr(match[0].length);
|
||||
return false;
|
||||
}
|
||||
@ -495,6 +515,15 @@ exports.htmlEncode = function(s) {
|
||||
}
|
||||
};
|
||||
|
||||
// Converts like htmlEncode, but forgets the double quote for brevity
|
||||
exports.htmlTextEncode = function(s) {
|
||||
if(s) {
|
||||
return s.toString().replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">");
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// Converts all HTML entities to their character equivalents
|
||||
exports.entityDecode = function(s) {
|
||||
var converter = String.fromCodePoint || String.fromCharCode,
|
||||
@ -544,7 +573,7 @@ exports.escape = function(ch) {
|
||||
|
||||
// Turns a string into a legal JavaScript string
|
||||
// Copied from peg.js, thanks to David Majda
|
||||
exports.stringify = function(s) {
|
||||
exports.stringify = function(s, rawUnicode) {
|
||||
/*
|
||||
* ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string
|
||||
* literal except for the closing quote character, backslash, carriage return,
|
||||
@ -553,19 +582,21 @@ exports.stringify = function(s) {
|
||||
*
|
||||
* For portability, we also escape all non-ASCII characters.
|
||||
*/
|
||||
var regex = rawUnicode ? /[\x00-\x1f]/g : /[\x00-\x1f\x80-\uFFFF]/g;
|
||||
return (s || "")
|
||||
.replace(/\\/g, '\\\\') // backslash
|
||||
.replace(/"/g, '\\"') // double quote character
|
||||
.replace(/'/g, "\\'") // single quote character
|
||||
.replace(/\r/g, '\\r') // carriage return
|
||||
.replace(/\n/g, '\\n') // line feed
|
||||
.replace(/[\x00-\x1f\x80-\uFFFF]/g, exports.escape); // non-ASCII characters
|
||||
.replace(regex, exports.escape); // non-ASCII characters
|
||||
};
|
||||
|
||||
// Turns a string into a legal JSON string
|
||||
// Derived from peg.js, thanks to David Majda
|
||||
exports.jsonStringify = function(s) {
|
||||
exports.jsonStringify = function(s, rawUnicode) {
|
||||
// See http://www.json.org/
|
||||
var regex = rawUnicode ? /[\x00-\x1f]/g : /[\x00-\x1f\x80-\uFFFF]/g;
|
||||
return (s || "")
|
||||
.replace(/\\/g, '\\\\') // backslash
|
||||
.replace(/"/g, '\\"') // double quote character
|
||||
@ -574,7 +605,7 @@ exports.jsonStringify = function(s) {
|
||||
.replace(/\x08/g, '\\b') // backspace
|
||||
.replace(/\x0c/g, '\\f') // formfeed
|
||||
.replace(/\t/g, '\\t') // tab
|
||||
.replace(/[\x00-\x1f\x80-\uFFFF]/g,function(s) {
|
||||
.replace(regex,function(s) {
|
||||
return '\\u' + $tw.utils.pad(s.charCodeAt(0).toString(16).toUpperCase(),4);
|
||||
}); // non-ASCII characters
|
||||
};
|
||||
@ -596,7 +627,7 @@ exports.nextTick = function(fn) {
|
||||
/*global window: false */
|
||||
if(typeof process === "undefined") {
|
||||
// Apparently it would be faster to use postMessage - http://dbaron.org/log/20100309-faster-timeouts
|
||||
window.setTimeout(fn,4);
|
||||
window.setTimeout(fn,0);
|
||||
} else {
|
||||
process.nextTick(fn);
|
||||
}
|
||||
|
77
core/modules/widgets/action-confirm.js
Normal file
77
core/modules/widgets/action-confirm.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*\
|
||||
|
||||
title: $:/core/modules/widgets/action-confirm.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var ConfirmWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
ConfirmWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ConfirmWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.parentDomNode = parent;
|
||||
this.renderChildren(parent,nextSibling);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ConfirmWidget.prototype.execute = function() {
|
||||
this.message = this.getAttribute("$message",$tw.language.getString("ConfirmAction"));
|
||||
this.prompt = (this.getAttribute("$prompt","yes") == "no" ? false : true);
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Refresh the widget by ensuring our attributes are up to date
|
||||
*/
|
||||
ConfirmWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes["$message"] || changedAttributes["$prompt"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
/*
|
||||
Invoke the action associated with this widget
|
||||
*/
|
||||
ConfirmWidget.prototype.invokeAction = function(triggeringWidget,event) {
|
||||
var invokeActions = true,
|
||||
handled = true;
|
||||
if(this.prompt) {
|
||||
invokeActions = confirm(this.message);
|
||||
}
|
||||
if(invokeActions) {
|
||||
handled = this.invokeActions(triggeringWidget,event);
|
||||
}
|
||||
return handled;
|
||||
};
|
||||
|
||||
ConfirmWidget.prototype.allowActionPropagation = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
exports["action-confirm"] = ConfirmWidget;
|
||||
|
||||
})();
|
93
core/modules/widgets/action-log.js
Normal file
93
core/modules/widgets/action-log.js
Normal file
@ -0,0 +1,93 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/action-log.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Action widget to log debug messages
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var LogWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
LogWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
LogWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
};
|
||||
|
||||
LogWidget.prototype.execute = function(){
|
||||
this.message = this.getAttribute("$$message","debug");
|
||||
this.logAll = this.getAttribute("$$all","no") === "yes" ? true : false;
|
||||
this.filter = this.getAttribute("$$filter");
|
||||
}
|
||||
|
||||
/*
|
||||
Refresh the widget by ensuring our attributes are up to date
|
||||
*/
|
||||
LogWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
Invoke the action associated with this widget
|
||||
*/
|
||||
LogWidget.prototype.invokeAction = function(triggeringWidget,event) {
|
||||
this.log();
|
||||
return true; // Action was invoked
|
||||
};
|
||||
|
||||
LogWidget.prototype.log = function() {
|
||||
var data = {},
|
||||
dataCount,
|
||||
allVars = {},
|
||||
filteredVars;
|
||||
|
||||
$tw.utils.each(this.attributes,function(attribute,name) {
|
||||
if(name.substring(0,2) !== "$$") {
|
||||
data[name] = attribute;
|
||||
}
|
||||
});
|
||||
|
||||
for(var v in this.variables) {
|
||||
allVars[v] = this.getVariable(v,{defaultValue:""});
|
||||
}
|
||||
if(this.filter) {
|
||||
filteredVars = this.wiki.compileFilter(this.filter).call(this.wiki,this.wiki.makeTiddlerIterator(allVars));
|
||||
$tw.utils.each(filteredVars,function(name) {
|
||||
data[name] = allVars[name];
|
||||
});
|
||||
}
|
||||
dataCount = $tw.utils.count(data);
|
||||
|
||||
console.group(this.message);
|
||||
if(dataCount > 0) {
|
||||
$tw.utils.logTable(data);
|
||||
}
|
||||
if(this.logAll || !dataCount) {
|
||||
console.groupCollapsed("All variables");
|
||||
$tw.utils.logTable(allVars);
|
||||
console.groupEnd();
|
||||
}
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
exports["action-log"] = LogWidget;
|
||||
|
||||
})();
|
@ -27,18 +27,20 @@ ButtonWidget.prototype = new Widget();
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ButtonWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
var self = this,
|
||||
tag = "button",
|
||||
domNode;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
var tag = "button";
|
||||
if(this.buttonTag && $tw.config.htmlUnsafeElements.indexOf(this.buttonTag) === -1) {
|
||||
tag = this.buttonTag;
|
||||
}
|
||||
var domNode = this.document.createElement(tag);
|
||||
domNode = this.document.createElement(tag);
|
||||
this.domNode = domNode;
|
||||
// Assign classes
|
||||
var classes = this["class"].split(" ") || [],
|
||||
isPoppedUp = (this.popup || this.popupTitle) && this.isPoppedUp();
|
||||
@ -64,10 +66,16 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this["aria-label"]) {
|
||||
domNode.setAttribute("aria-label",this["aria-label"]);
|
||||
}
|
||||
if(this.popup || this.popupTitle) {
|
||||
domNode.setAttribute("aria-expanded",isPoppedUp ? "true" : "false");
|
||||
}
|
||||
// Set the tabindex
|
||||
if(this.tabIndex) {
|
||||
domNode.setAttribute("tabindex",this.tabIndex);
|
||||
}
|
||||
if(this.isDisabled === "yes") {
|
||||
domNode.setAttribute("disabled",true);
|
||||
}
|
||||
// Add a click event handler
|
||||
domNode.addEventListener("click",function (event) {
|
||||
var handled = false;
|
||||
@ -197,10 +205,10 @@ ButtonWidget.prototype.execute = function() {
|
||||
this.setTo = this.getAttribute("setTo");
|
||||
this.popup = this.getAttribute("popup");
|
||||
this.hover = this.getAttribute("hover");
|
||||
this["class"] = this.getAttribute("class","");
|
||||
this["aria-label"] = this.getAttribute("aria-label");
|
||||
this.tooltip = this.getAttribute("tooltip");
|
||||
this.style = this.getAttribute("style");
|
||||
this["class"] = this.getAttribute("class","");
|
||||
this.selectedClass = this.getAttribute("selectedClass");
|
||||
this.defaultSetValue = this.getAttribute("default","");
|
||||
this.buttonTag = this.getAttribute("tag");
|
||||
@ -211,18 +219,39 @@ ButtonWidget.prototype.execute = function() {
|
||||
this.setIndex = this.getAttribute("setIndex");
|
||||
this.popupTitle = this.getAttribute("popupTitle");
|
||||
this.tabIndex = this.getAttribute("tabindex");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
ButtonWidget.prototype.updateDomNodeClasses = function() {
|
||||
var domNodeClasses = this.domNode.className.split(" "),
|
||||
oldClasses = this.class.split(" "),
|
||||
newClasses;
|
||||
this["class"] = this.getAttribute("class","");
|
||||
newClasses = this.class.split(" ");
|
||||
//Remove classes assigned from the old value of class attribute
|
||||
$tw.utils.each(oldClasses,function(oldClass){
|
||||
var i = domNodeClasses.indexOf(oldClass);
|
||||
if(i !== -1) {
|
||||
domNodeClasses.splice(i,1);
|
||||
}
|
||||
});
|
||||
//Add new classes from updated class attribute.
|
||||
$tw.utils.pushTop(domNodeClasses,newClasses);
|
||||
this.domNode.className = domNodeClasses.join(" ");
|
||||
}
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes["class"] || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle) {
|
||||
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
@ -41,6 +41,9 @@ CheckboxWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this.getValue()) {
|
||||
this.inputDomNode.setAttribute("checked","true");
|
||||
}
|
||||
if(this.isDisabled === "yes") {
|
||||
this.inputDomNode.setAttribute("disabled",true);
|
||||
}
|
||||
this.labelDomNode.appendChild(this.inputDomNode);
|
||||
this.spanDomNode = this.document.createElement("span");
|
||||
this.labelDomNode.appendChild(this.spanDomNode);
|
||||
@ -181,6 +184,7 @@ CheckboxWidget.prototype.execute = function() {
|
||||
this.checkboxDefault = this.getAttribute("default");
|
||||
this.checkboxClass = this.getAttribute("class","");
|
||||
this.checkboxInvertTag = this.getAttribute("invertTag","");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
@ -190,7 +194,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
CheckboxWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.tag || changedAttributes.invertTag || changedAttributes.field || changedAttributes.index || changedAttributes.checked || changedAttributes.unchecked || changedAttributes["default"] || changedAttributes["class"]) {
|
||||
if(changedAttributes.tiddler || changedAttributes.tag || changedAttributes.invertTag || changedAttributes.field || changedAttributes.index || changedAttributes.checked || changedAttributes.unchecked || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.disabled) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
@ -27,21 +27,21 @@ DroppableWidget.prototype = new Widget();
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
DroppableWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
var self = this,
|
||||
tag = this.parseTreeNode.isBlock ? "div" : "span",
|
||||
domNode;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
var tag = this.parseTreeNode.isBlock ? "div" : "span";
|
||||
if(this.droppableTag && $tw.config.htmlUnsafeElements.indexOf(this.droppableTag) === -1) {
|
||||
tag = this.droppableTag;
|
||||
}
|
||||
// Create element and assign classes
|
||||
var domNode = this.document.createElement(tag),
|
||||
classes = (this.droppableClass || "").split(" ");
|
||||
classes.push("tc-droppable");
|
||||
domNode.className = classes.join(" ");
|
||||
domNode = this.document.createElement(tag);
|
||||
this.domNode = domNode;
|
||||
this.assignDomNodeClasses();
|
||||
// Add event handlers
|
||||
if(this.droppableEnable) {
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
@ -50,6 +50,8 @@ DroppableWidget.prototype.render = function(parent,nextSibling) {
|
||||
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
||||
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"}
|
||||
]);
|
||||
} else {
|
||||
$tw.utils.addClass(this.domNode,this.disabledClass);
|
||||
}
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
@ -144,20 +146,28 @@ DroppableWidget.prototype.execute = function() {
|
||||
this.droppableActions = this.getAttribute("actions");
|
||||
this.droppableEffect = this.getAttribute("effect","copy");
|
||||
this.droppableTag = this.getAttribute("tag");
|
||||
this.droppableClass = this.getAttribute("class");
|
||||
this.droppableEnable = (this.getAttribute("enable") || "yes") === "yes";
|
||||
this.disabledClass = this.getAttribute("disabledClass","");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-droppable");
|
||||
this.domNode.className = classes.join(" ");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
DroppableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes["class"] || changedAttributes.tag || changedAttributes.enable) {
|
||||
if(changedAttributes.tag || changedAttributes.enable || changedAttributes.disabledClass || changedAttributes.actions || changedAttributes.effect) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
@ -12,6 +12,8 @@ Dropzone widget
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var IMPORT_TITLE = "$:/Import";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var DropZoneWidget = function(parseTreeNode,options) {
|
||||
@ -110,10 +112,38 @@ DropZoneWidget.prototype.handleDragEndEvent = function(event) {
|
||||
$tw.utils.removeClass(this.domNodes[0],"tc-dragover");
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.filterByContentTypes = function(tiddlerFieldsArray) {
|
||||
var filteredTypes,
|
||||
filtered = [],
|
||||
types = [];
|
||||
$tw.utils.each(tiddlerFieldsArray,function(tiddlerFields) {
|
||||
types.push(tiddlerFields.type);
|
||||
});
|
||||
filteredTypes = this.wiki.filterTiddlers(this.contentTypesFilter,this,this.wiki.makeTiddlerIterator(types));
|
||||
$tw.utils.each(tiddlerFieldsArray,function(tiddlerFields) {
|
||||
if(filteredTypes.indexOf(tiddlerFields.type) !== -1) {
|
||||
filtered.push(tiddlerFields);
|
||||
}
|
||||
});
|
||||
return filtered;
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.readFileCallback = function(tiddlerFieldsArray) {
|
||||
if(this.contentTypesFilter) {
|
||||
tiddlerFieldsArray = this.filterByContentTypes(tiddlerFieldsArray);
|
||||
}
|
||||
if(tiddlerFieldsArray.length) {
|
||||
this.dispatchEvent({type: "tm-import-tiddlers", param: JSON.stringify(tiddlerFieldsArray), autoOpenOnImport: this.autoOpenOnImport, importTitle: this.importTitle});
|
||||
if(this.actions) {
|
||||
this.invokeActionString(this.actions,this,event,{importTitle: this.importTitle});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DropZoneWidget.prototype.handleDropEvent = function(event) {
|
||||
var self = this,
|
||||
readFileCallback = function(tiddlerFieldsArray) {
|
||||
self.dispatchEvent({type: "tm-import-tiddlers", param: JSON.stringify(tiddlerFieldsArray), autoOpenOnImport: self.autoOpenOnImport, importTitle: self.importTitle});
|
||||
self.readFileCallback(tiddlerFieldsArray);
|
||||
};
|
||||
this.leaveDrag(event);
|
||||
// Check for being over a TEXTAREA or INPUT
|
||||
@ -149,7 +179,7 @@ DropZoneWidget.prototype.handleDropEvent = function(event) {
|
||||
DropZoneWidget.prototype.handlePasteEvent = function(event) {
|
||||
var self = this,
|
||||
readFileCallback = function(tiddlerFieldsArray) {
|
||||
self.dispatchEvent({type: "tm-import-tiddlers", param: JSON.stringify(tiddlerFieldsArray), autoOpenOnImport: self.autoOpenOnImport, importTitle: self.importTitle});
|
||||
self.readFileCallback(tiddlerFieldsArray);
|
||||
};
|
||||
// Let the browser handle it if we're in a textarea or input box
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1 && !event.target.isContentEditable) {
|
||||
@ -176,7 +206,7 @@ DropZoneWidget.prototype.handlePasteEvent = function(event) {
|
||||
if($tw.log.IMPORT) {
|
||||
console.log("Importing string '" + str + "', type: '" + type + "'");
|
||||
}
|
||||
self.dispatchEvent({type: "tm-import-tiddlers", param: JSON.stringify([tiddlerFields]), autoOpenOnImport: self.autoOpenOnImport, importTitle: self.importTitle});
|
||||
readFileCallback([tiddlerFields]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -194,7 +224,9 @@ DropZoneWidget.prototype.execute = function() {
|
||||
this.dropzoneDeserializer = this.getAttribute("deserializer");
|
||||
this.dropzoneEnable = (this.getAttribute("enable") || "yes") === "yes";
|
||||
this.autoOpenOnImport = this.getAttribute("autoOpenOnImport");
|
||||
this.importTitle = this.getAttribute("importTitle");
|
||||
this.importTitle = this.getAttribute("importTitle",IMPORT_TITLE);
|
||||
this.actions = this.getAttribute("actions");
|
||||
this.contentTypesFilter = this.getAttribute("contentTypesFilter");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
@ -204,7 +236,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
DropZoneWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.enable || changedAttributes.autoOpenOnImport || changedAttributes.importTitle || changedAttributes.deserializer || changedAttributes.class) {
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
|
@ -25,11 +25,6 @@ var LINE_WIDTH_TITLE = "$:/config/BitmapEditor/LineWidth",
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var EditBitmapWidget = function(parseTreeNode,options) {
|
||||
// Initialise the editor operations if they've not been done already
|
||||
if(!this.editorOperations) {
|
||||
EditBitmapWidget.prototype.editorOperations = {};
|
||||
$tw.modules.applyMethods("bitmapeditoroperation",this.editorOperations);
|
||||
}
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
@ -43,6 +38,11 @@ Render this widget into the DOM
|
||||
*/
|
||||
EditBitmapWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Initialise the editor operations if they've not been done already
|
||||
if(!this.editorOperations) {
|
||||
EditBitmapWidget.prototype.editorOperations = {};
|
||||
$tw.modules.applyMethods("bitmapeditoroperation",this.editorOperations);
|
||||
}
|
||||
// Save the parent dom node
|
||||
this.parentDomNode = parent;
|
||||
// Compute our attributes
|
||||
|
@ -51,6 +51,7 @@ EditWidget.prototype.execute = function() {
|
||||
this.editCancelPopups = this.getAttribute("cancelPopups","");
|
||||
this.editInputActions = this.getAttribute("inputActions");
|
||||
this.editRefreshTitle = this.getAttribute("refreshTitle");
|
||||
this.editAutoComplete = this.getAttribute("autocomplete");
|
||||
// Choose the appropriate edit widget
|
||||
this.editorType = this.getEditorType();
|
||||
// Make the child widgets
|
||||
@ -89,7 +90,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
EditWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
// Refresh if an attribute has changed, or the type associated with the target tiddler has changed
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || (changedTiddlers[this.editTitle] && this.getEditorType() !== this.editorType)) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || (changedTiddlers[this.editTitle] && this.getEditorType() !== this.editorType)) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
156
core/modules/widgets/eventcatcher.js
Normal file
156
core/modules/widgets/eventcatcher.js
Normal file
@ -0,0 +1,156 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/eventcatcher.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Event handler widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var EventWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
EventWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
EventWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
var tag = this.parseTreeNode.isBlock ? "div" : "span";
|
||||
if(this.elementTag && $tw.config.htmlUnsafeElements.indexOf(this.elementTag) === -1) {
|
||||
tag = this.elementTag;
|
||||
}
|
||||
var domNode = this.document.createElement(tag);
|
||||
this.domNode = domNode;
|
||||
// Assign classes
|
||||
this.assignDomNodeClasses();
|
||||
// Add our event handler
|
||||
$tw.utils.each(this.types,function(type) {
|
||||
domNode.addEventListener(type,function(event) {
|
||||
var selector = self.getAttribute("selector"),
|
||||
actions = self.getAttribute("actions-"+type),
|
||||
selectedNode = event.target,
|
||||
selectedNodeRect,
|
||||
catcherNodeRect,
|
||||
variables = {};
|
||||
if(selector) {
|
||||
// Search ancestors for a node that matches the selector
|
||||
while(!selectedNode.matches(selector) && selectedNode !== domNode) {
|
||||
selectedNode = selectedNode.parentNode;
|
||||
}
|
||||
// If we found one, copy the attributes as variables, otherwise exit
|
||||
if(selectedNode.matches(selector)) {
|
||||
$tw.utils.each(selectedNode.attributes,function(attribute) {
|
||||
variables["dom-" + attribute.name] = attribute.value.toString();
|
||||
});
|
||||
//Add a variable with a popup coordinate string for the selected node
|
||||
variables["tv-popup-coords"] = "(" + selectedNode.offsetLeft + "," + selectedNode.offsetTop +"," + selectedNode.offsetWidth + "," + selectedNode.offsetHeight + ")";
|
||||
|
||||
//Add variables for offset of selected node
|
||||
variables["tv-selectednode-posx"] = selectedNode.offsetLeft.toString();
|
||||
variables["tv-selectednode-posy"] = selectedNode.offsetTop.toString();
|
||||
variables["tv-selectednode-width"] = selectedNode.offsetWidth.toString();
|
||||
variables["tv-selectednode-height"] = selectedNode.offsetHeight.toString();
|
||||
|
||||
//Add variables for event X and Y position relative to selected node
|
||||
selectedNodeRect = selectedNode.getBoundingClientRect();
|
||||
variables["event-fromselected-posx"] = (event.clientX - selectedNodeRect.left).toString();
|
||||
variables["event-fromselected-posy"] = (event.clientY - selectedNodeRect.top).toString();
|
||||
|
||||
//Add variables for event X and Y position relative to event catcher node
|
||||
catcherNodeRect = self.domNode.getBoundingClientRect();
|
||||
variables["event-fromcatcher-posx"] = (event.clientX - catcherNodeRect.left).toString();
|
||||
variables["event-fromcatcher-posy"] = (event.clientY - catcherNodeRect.top).toString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Execute our actions with the variables
|
||||
if(actions) {
|
||||
// Add a variable for the modifier key
|
||||
variables.modifier = $tw.keyboardManager.getEventModifierKeyDescriptor(event);
|
||||
// Add a variable for the mouse button
|
||||
if("button" in event) {
|
||||
if(event.button === 0) {
|
||||
variables["event-mousebutton"] = "left";
|
||||
} else if(event.button === 1) {
|
||||
variables["event-mousebutton"] = "middle";
|
||||
} else if(event.button === 2) {
|
||||
variables["event-mousebutton"] = "right";
|
||||
}
|
||||
}
|
||||
variables["event-type"] = event.type.toString();
|
||||
if(typeof event.detail === "object" && !!event.detail) {
|
||||
$tw.utils.each(event.detail,function(detailValue,detail) {
|
||||
variables["event-detail-" + detail] = detailValue.toString();
|
||||
});
|
||||
} else if(!!event.detail) {
|
||||
variables["event-detail"] = event.detail.toString();
|
||||
}
|
||||
self.invokeActionString(actions,self,event,variables);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},false);
|
||||
});
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
EventWidget.prototype.execute = function() {
|
||||
var self = this;
|
||||
// Get attributes that require a refresh on change
|
||||
this.types = this.getAttribute("events","").split(" ");
|
||||
this.elementTag = this.getAttribute("tag");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
EventWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-eventcatcher");
|
||||
this.domNode.className = classes.join(" ");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
EventWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes["events"] || changedAttributes["tag"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports.eventcatcher = EventWidget;
|
||||
|
||||
})();
|
@ -16,12 +16,6 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var FieldManglerWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tm-remove-field", handler: "handleRemoveFieldEvent"},
|
||||
{type: "tm-add-field", handler: "handleAddFieldEvent"},
|
||||
{type: "tm-remove-tag", handler: "handleRemoveTagEvent"},
|
||||
{type: "tm-add-tag", handler: "handleAddTagEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -33,6 +27,12 @@ FieldManglerWidget.prototype = new Widget();
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
FieldManglerWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.addEventListeners([
|
||||
{type: "tm-remove-field", handler: "handleRemoveFieldEvent"},
|
||||
{type: "tm-add-field", handler: "handleAddFieldEvent"},
|
||||
{type: "tm-remove-tag", handler: "handleRemoveTagEvent"},
|
||||
{type: "tm-add-tag", handler: "handleAddTagEvent"}
|
||||
]);
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
@ -67,7 +67,7 @@ FieldManglerWidget.prototype.handleRemoveFieldEvent = function(event) {
|
||||
deletion = {};
|
||||
deletion[event.param] = undefined;
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,deletion));
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
|
||||
@ -105,7 +105,7 @@ FieldManglerWidget.prototype.handleAddFieldEvent = function(event) {
|
||||
}
|
||||
}
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,addition));
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleRemoveTagEvent = function(event) {
|
||||
@ -122,7 +122,7 @@ FieldManglerWidget.prototype.handleRemoveTagEvent = function(event) {
|
||||
this.wiki.addTiddler(new $tw.Tiddler(tiddler,modification));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
FieldManglerWidget.prototype.handleAddTagEvent = function(event) {
|
||||
@ -140,7 +140,7 @@ FieldManglerWidget.prototype.handleAddTagEvent = function(event) {
|
||||
tag.push(event.param.trim());
|
||||
this.wiki.addTiddler(new $tw.Tiddler({title: this.mangleTitle, tags: tag},modification));
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.fieldmangler = FieldManglerWidget;
|
||||
|
@ -46,13 +46,15 @@ KeyboardWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Add a keyboard event handler
|
||||
domNode.addEventListener("keydown",function (event) {
|
||||
if($tw.keyboardManager.checkKeyDescriptors(event,self.keyInfoArray)) {
|
||||
self.invokeActions(self,event);
|
||||
var handled = self.invokeActions(self,event);
|
||||
if(self.actions) {
|
||||
self.invokeActionString(self.actions,self,event);
|
||||
}
|
||||
self.dispatchMessage(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if(handled || self.actions || self.message) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -16,9 +16,6 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var LinkCatcherWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.addEventListeners([
|
||||
{type: "tm-navigate", handler: "handleNavigateEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -30,6 +27,9 @@ LinkCatcherWidget.prototype = new Widget();
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
LinkCatcherWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.addEventListeners([
|
||||
{type: "tm-navigate", handler: "handleNavigateEvent"}
|
||||
]);
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
|
@ -19,11 +19,6 @@ The list widget creates list element sub-widgets that reach back into the list w
|
||||
*/
|
||||
|
||||
var ListWidget = function(parseTreeNode,options) {
|
||||
// Initialise the storyviews if they've not been done already
|
||||
if(!this.storyViews) {
|
||||
ListWidget.prototype.storyViews = {};
|
||||
$tw.modules.applyMethods("storyview",this.storyViews);
|
||||
}
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
@ -37,6 +32,11 @@ ListWidget.prototype = new Widget();
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ListWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Initialise the storyviews if they've not been done already
|
||||
if(!this.storyViews) {
|
||||
ListWidget.prototype.storyViews = {};
|
||||
$tw.modules.applyMethods("storyview",this.storyViews);
|
||||
}
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
@ -61,6 +61,7 @@ ListWidget.prototype.execute = function() {
|
||||
this.template = this.getAttribute("template");
|
||||
this.editTemplate = this.getAttribute("editTemplate");
|
||||
this.variableName = this.getAttribute("variable","currentTiddler");
|
||||
this.indexName = this.getAttribute("index");
|
||||
this.storyViewName = this.getAttribute("storyview");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Compose the list elements
|
||||
@ -72,7 +73,7 @@ ListWidget.prototype.execute = function() {
|
||||
members = this.getEmptyMessage();
|
||||
} else {
|
||||
$tw.utils.each(this.list,function(title,index) {
|
||||
members.push(self.makeItemTemplate(title));
|
||||
members.push(self.makeItemTemplate(title,index));
|
||||
});
|
||||
}
|
||||
// Construct the child widgets
|
||||
@ -87,8 +88,14 @@ ListWidget.prototype.getTiddlerList = function() {
|
||||
};
|
||||
|
||||
ListWidget.prototype.getEmptyMessage = function() {
|
||||
var emptyMessage = this.getAttribute("emptyMessage",""),
|
||||
parser = this.wiki.parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
|
||||
var parser,
|
||||
emptyMessage = this.getAttribute("emptyMessage","");
|
||||
// this.wiki.parseText() calls
|
||||
// new Parser(..), which should only be done, if needed, because it's heavy!
|
||||
if (emptyMessage === "") {
|
||||
return [];
|
||||
}
|
||||
parser = this.wiki.parseText("text/vnd.tiddlywiki",emptyMessage,{parseAsInline: true});
|
||||
if(parser) {
|
||||
return parser.tree;
|
||||
} else {
|
||||
@ -99,7 +106,7 @@ ListWidget.prototype.getEmptyMessage = function() {
|
||||
/*
|
||||
Compose the template for a list item
|
||||
*/
|
||||
ListWidget.prototype.makeItemTemplate = function(title) {
|
||||
ListWidget.prototype.makeItemTemplate = function(title,index) {
|
||||
// Check if the tiddler is a draft
|
||||
var tiddler = this.wiki.getTiddler(title),
|
||||
isDraft = tiddler && tiddler.hasField("draft.of"),
|
||||
@ -122,7 +129,14 @@ ListWidget.prototype.makeItemTemplate = function(title) {
|
||||
}
|
||||
}
|
||||
// Return the list item
|
||||
return {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree};
|
||||
var parseTreeNode = {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree};
|
||||
if(this.indexName) {
|
||||
parseTreeNode.index = index.toString();
|
||||
parseTreeNode.indexName = this.indexName;
|
||||
parseTreeNode.isFirst = index === 0;
|
||||
parseTreeNode.isLast = index === this.list.length - 1;
|
||||
}
|
||||
return parseTreeNode;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -136,7 +150,7 @@ ListWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.storyview.refreshStart(changedTiddlers,changedAttributes);
|
||||
}
|
||||
// Completely refresh if any of our attributes have changed
|
||||
if(changedAttributes.filter || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) {
|
||||
if(changedAttributes.filter || changedAttributes.variable || changedAttributes.index || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) {
|
||||
this.refreshSelf();
|
||||
result = true;
|
||||
} else {
|
||||
@ -205,23 +219,41 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
|
||||
this.removeChildDomNodes();
|
||||
this.children = [];
|
||||
}
|
||||
// Cycle through the list, inserting and removing list items as needed
|
||||
var hasRefreshed = false;
|
||||
for(var t=0; t<this.list.length; t++) {
|
||||
var index = this.findListItem(t,this.list[t]);
|
||||
if(index === undefined) {
|
||||
// The list item must be inserted
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
// There are intervening list items that must be removed
|
||||
for(var n=index-1; n>=t; n--) {
|
||||
this.removeListItem(n);
|
||||
// If we are providing an index variable then we must refresh the items, otherwise we can rearrange them
|
||||
var hasRefreshed = false,t;
|
||||
if(this.indexName) {
|
||||
// Cycle through the list and remove and re-insert the first item that has changed, and all the remaining items
|
||||
for(t=0; t<this.list.length; t++) {
|
||||
if(hasRefreshed || !this.children[t] || this.children[t].parseTreeNode.itemTitle !== this.list[t]) {
|
||||
if(this.children[t]) {
|
||||
this.removeListItem(t);
|
||||
}
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
// Refresh the item we're reusing
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Cycle through the list, inserting and removing list items as needed
|
||||
for(t=0; t<this.list.length; t++) {
|
||||
var index = this.findListItem(t,this.list[t]);
|
||||
if(index === undefined) {
|
||||
// The list item must be inserted
|
||||
this.insertListItem(t,this.list[t]);
|
||||
hasRefreshed = true;
|
||||
} else {
|
||||
// There are intervening list items that must be removed
|
||||
for(var n=index-1; n>=t; n--) {
|
||||
this.removeListItem(n);
|
||||
hasRefreshed = true;
|
||||
}
|
||||
// Refresh the item we're reusing
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
// Refresh the item we're reusing
|
||||
var refreshed = this.children[t].refresh(changedTiddlers);
|
||||
hasRefreshed = hasRefreshed || refreshed;
|
||||
}
|
||||
}
|
||||
// Remove any left over items
|
||||
@ -251,7 +283,7 @@ Insert a new list item at the specified index
|
||||
*/
|
||||
ListWidget.prototype.insertListItem = function(index,title) {
|
||||
// Create, insert and render the new child widgets
|
||||
var widget = this.makeChildWidget(this.makeItemTemplate(title));
|
||||
var widget = this.makeChildWidget(this.makeItemTemplate(title,index));
|
||||
widget.parentDomNode = this.parentDomNode; // Hack to enable findNextSiblingDomNode() to work
|
||||
this.children.splice(index,0,widget);
|
||||
var nextSibling = widget.findNextSiblingDomNode();
|
||||
@ -305,6 +337,11 @@ Compute the internal state of the widget
|
||||
ListItemWidget.prototype.execute = function() {
|
||||
// Set the current list item title
|
||||
this.setVariable(this.parseTreeNode.variableName,this.parseTreeNode.itemTitle);
|
||||
if(this.parseTreeNode.indexName) {
|
||||
this.setVariable(this.parseTreeNode.indexName,this.parseTreeNode.index);
|
||||
this.setVariable(this.parseTreeNode.indexName + "-first",this.parseTreeNode.isFirst ? "yes" : "no");
|
||||
this.setVariable(this.parseTreeNode.indexName + "-last",this.parseTreeNode.isLast ? "yes" : "no");
|
||||
}
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
30
core/modules/widgets/log.js
Normal file
30
core/modules/widgets/log.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/log.js
|
||||
type: application/javascript
|
||||
module-type: widget-subclass
|
||||
|
||||
Widget to log debug messages
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.baseClass = "action-log";
|
||||
|
||||
exports.name = "log";
|
||||
|
||||
exports.constructor = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
}
|
||||
|
||||
exports.prototype = {};
|
||||
|
||||
exports.prototype.render = function(event) {
|
||||
Object.getPrototypeOf(Object.getPrototypeOf(this)).render.call(this,event);
|
||||
Object.getPrototypeOf(Object.getPrototypeOf(this)).log.call(this);
|
||||
}
|
||||
|
||||
})();
|
@ -55,9 +55,19 @@ MacroCallWidget.prototype.execute = function() {
|
||||
// Are we rendering to HTML?
|
||||
if(this.renderOutput === "text/html") {
|
||||
// If so we'll return the parsed macro
|
||||
var parser = this.wiki.parseText(this.parseType,text,
|
||||
{parseAsInline: !this.parseTreeNode.isBlock});
|
||||
parseTreeNodes = parser ? parser.tree : [];
|
||||
// Check if we've already cached parsing this macro
|
||||
var mode = this.parseTreeNode.isBlock ? "blockParser" : "inlineParser",
|
||||
parser;
|
||||
if(variableInfo.srcVariable && variableInfo.srcVariable[mode]) {
|
||||
parser = variableInfo.srcVariable[mode];
|
||||
} else {
|
||||
parser = this.wiki.parseText(this.parseType,text,
|
||||
{parseAsInline: !this.parseTreeNode.isBlock});
|
||||
if(variableInfo.isCacheable && variableInfo.srcVariable) {
|
||||
variableInfo.srcVariable[mode] = parser;
|
||||
}
|
||||
}
|
||||
var parseTreeNodes = parser ? parser.tree : [];
|
||||
// Wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
|
||||
var attributes = {};
|
||||
$tw.utils.each(variableInfo.params,function(param) {
|
||||
|
100
core/modules/widgets/messagecatcher.js
Normal file
100
core/modules/widgets/messagecatcher.js
Normal file
@ -0,0 +1,100 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/messagecatcher.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Message catcher widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var MessageCatcherWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
MessageCatcherWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
MessageCatcherWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Add our message handler
|
||||
if(this.messageType) {
|
||||
this.addEventListeners([
|
||||
{type: this.messageType, handler: "handleEvent"}
|
||||
]);
|
||||
}
|
||||
// Render children
|
||||
this.renderChildren(parent,null);
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
MessageCatcherWidget.prototype.execute = function() {
|
||||
var self = this;
|
||||
// Get attributes that require a refresh on change
|
||||
this.messageType = this.getAttribute("type");
|
||||
this.messageActions = this.getAttribute("actions");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Handle an event
|
||||
*/
|
||||
MessageCatcherWidget.prototype.handleEvent = function(event) {
|
||||
if(this.messageActions) {
|
||||
// Collect all the event properties into variables
|
||||
var collectProps = function(obj,prefix) {
|
||||
prefix = prefix || "";
|
||||
var props = {};
|
||||
$tw.utils.each(obj,function(value,name) {
|
||||
if(["string","boolean","number"].indexOf(typeof value) !== -1) {
|
||||
props[prefix + name] = value.toString();
|
||||
}
|
||||
});
|
||||
return props;
|
||||
};
|
||||
var variables = $tw.utils.extend(
|
||||
{},
|
||||
collectProps(event.paramObject,"event-paramObject-"),
|
||||
collectProps(event,"event-"),
|
||||
{
|
||||
modifier: $tw.keyboardManager.getEventModifierKeyDescriptor(event)
|
||||
});
|
||||
this.invokeActionString(this.messageActions,this,event,variables);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
MessageCatcherWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes["type"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports.messagecatcher = MessageCatcherWidget;
|
||||
|
||||
})();
|
@ -18,6 +18,17 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var NavigatorWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
NavigatorWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
NavigatorWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.addEventListeners([
|
||||
{type: "tm-navigate", handler: "handleNavigateEvent"},
|
||||
{type: "tm-edit-tiddler", handler: "handleEditTiddlerEvent"},
|
||||
@ -36,17 +47,6 @@ var NavigatorWidget = function(parseTreeNode,options) {
|
||||
{type: "tm-unfold-all-tiddlers", handler: "handleUnfoldAllTiddlersEvent"},
|
||||
{type: "tm-rename-tiddler", handler: "handleRenameTiddlerEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
NavigatorWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
NavigatorWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
@ -62,6 +62,11 @@ NavigatorWidget.prototype.execute = function() {
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
this.setVariable("tv-story-list",this.storyTitle);
|
||||
this.setVariable("tv-history-list",this.historyTitle);
|
||||
this.story = new $tw.Story({
|
||||
wiki: this.wiki,
|
||||
storyTitle: this.storyTitle,
|
||||
historyTitle: this.historyTitle
|
||||
});
|
||||
// Construct the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
@ -123,7 +128,7 @@ NavigatorWidget.prototype.replaceFirstTitleInStory = function(storyList,oldTitle
|
||||
|
||||
NavigatorWidget.prototype.addToStory = function(title,fromTitle) {
|
||||
if(this.storyTitle) {
|
||||
this.wiki.addToStory(title,fromTitle,this.storyTitle,{
|
||||
this.story.addToStory(title,fromTitle,{
|
||||
openLinkFromInsideRiver: this.getAttribute("openLinkFromInsideRiver","top"),
|
||||
openLinkFromOutsideRiver: this.getAttribute("openLinkFromOutsideRiver","top")
|
||||
});
|
||||
@ -136,7 +141,7 @@ title: a title string or an array of title strings
|
||||
fromPageRect: page coordinates of the origin of the navigation
|
||||
*/
|
||||
NavigatorWidget.prototype.addToHistory = function(title,fromPageRect) {
|
||||
this.wiki.addToHistory(title,fromPageRect,this.historyTitle);
|
||||
this.story.addToHistory(title,fromPageRect,this.historyTitle);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -524,6 +529,7 @@ NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
|
||||
$tw.utils.each(importData.tiddlers,function(tiddler,title) {
|
||||
if($tw.utils.count(tiddler) === 0) {
|
||||
newFields["selection-" + title] = "unchecked";
|
||||
newFields["suppressed-" + title] = "yes";
|
||||
}
|
||||
});
|
||||
// Save the $:/Import tiddler
|
||||
@ -558,10 +564,14 @@ NavigatorWidget.prototype.handlePerformImportEvent = function(event) {
|
||||
$tw.utils.each(importData.tiddlers,function(tiddlerFields) {
|
||||
var title = tiddlerFields.title;
|
||||
if(title && importTiddler && importTiddler.fields["selection-" + title] !== "unchecked") {
|
||||
var tiddler = new $tw.Tiddler(tiddlerFields);
|
||||
if($tw.utils.hop(importTiddler.fields,["rename-" + title])) {
|
||||
var tiddler = new $tw.Tiddler(tiddlerFields,{title : importTiddler.fields["rename-" + title]});
|
||||
} else {
|
||||
var tiddler = new $tw.Tiddler(tiddlerFields);
|
||||
}
|
||||
tiddler = $tw.hooks.invokeHook("th-importing-tiddler",tiddler);
|
||||
self.wiki.addTiddler(tiddler);
|
||||
importReport.push("# [[" + tiddlerFields.title + "]]");
|
||||
importReport.push("# [[" + tiddler.fields.title + "]]");
|
||||
}
|
||||
});
|
||||
// Replace the $:/Import tiddler with an import report
|
||||
|
@ -13,7 +13,6 @@ Set a field or index at a given tiddler via radio buttons
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var RadioWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
@ -37,12 +36,15 @@ RadioWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Create our elements
|
||||
this.labelDomNode = this.document.createElement("label");
|
||||
this.labelDomNode.setAttribute("class",
|
||||
"tc-radio " + this.radioClass + (isChecked ? " tc-radio-selected" : "")
|
||||
);
|
||||
"tc-radio " + this.radioClass + (isChecked ? " tc-radio-selected" : "")
|
||||
);
|
||||
this.inputDomNode = this.document.createElement("input");
|
||||
this.inputDomNode.setAttribute("type","radio");
|
||||
if(isChecked) {
|
||||
this.inputDomNode.setAttribute("checked","true");
|
||||
this.inputDomNode.checked = true;
|
||||
}
|
||||
if(this.isDisabled === "yes") {
|
||||
this.inputDomNode.setAttribute("disabled",true);
|
||||
}
|
||||
this.labelDomNode.appendChild(this.inputDomNode);
|
||||
this.spanDomNode = this.document.createElement("span");
|
||||
@ -60,10 +62,14 @@ RadioWidget.prototype.render = function(parent,nextSibling) {
|
||||
RadioWidget.prototype.getValue = function() {
|
||||
var value,
|
||||
tiddler = this.wiki.getTiddler(this.radioTitle);
|
||||
if (this.radioIndex) {
|
||||
value = this.wiki.extractTiddlerDataItem(this.radioTitle,this.radioIndex);
|
||||
if(tiddler) {
|
||||
if(this.radioIndex) {
|
||||
value = this.wiki.extractTiddlerDataItem(this.radioTitle,this.radioIndex);
|
||||
} else {
|
||||
value = tiddler.getFieldString(this.radioField);
|
||||
}
|
||||
} else {
|
||||
value = tiddler && tiddler.getFieldString(this.radioField);
|
||||
value = this.radioDefault;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@ -83,6 +89,10 @@ RadioWidget.prototype.handleChangeEvent = function(event) {
|
||||
if(this.inputDomNode.checked) {
|
||||
this.setValue();
|
||||
}
|
||||
// Trigger actions
|
||||
if(this.radioActions) {
|
||||
this.invokeActionString(this.radioActions,this,event,{"actionValue": this.radioValue});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -95,6 +105,9 @@ RadioWidget.prototype.execute = function() {
|
||||
this.radioIndex = this.getAttribute("index");
|
||||
this.radioValue = this.getAttribute("value");
|
||||
this.radioClass = this.getAttribute("class","");
|
||||
this.radioDefault = this.getAttribute("default");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
this.radioActions = this.getAttribute("actions","");
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
@ -104,16 +117,14 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
RadioWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.value || changedAttributes["class"]) {
|
||||
if(($tw.utils.count(changedAttributes) > 0)) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedTiddlers[this.radioTitle]) {
|
||||
this.inputDomNode.checked = this.getValue() === this.radioValue;
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
} else {
|
||||
var refreshed = false;
|
||||
if(changedTiddlers[this.radioTitle]) {
|
||||
this.inputDomNode.checked = this.getValue() === this.radioValue;
|
||||
refreshed = true;
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers) || refreshed;
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -46,11 +46,16 @@ RangeWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this.increment){
|
||||
this.inputDomNode.setAttribute("step", this.increment);
|
||||
}
|
||||
if(this.isDisabled === "yes") {
|
||||
this.inputDomNode.setAttribute("disabled",true);
|
||||
}
|
||||
this.inputDomNode.value = this.getValue();
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(this.inputDomNode,[
|
||||
{name: "input", handlerObject: this, handlerMethod: "handleInputEvent"},
|
||||
{name: "change", handlerObject: this, handlerMethod: "handleInputEvent"}
|
||||
{name:"mousedown", handlerObject:this, handlerMethod:"handleMouseDownEvent"},
|
||||
{name:"mouseup", handlerObject:this, handlerMethod:"handleMouseUpEvent"},
|
||||
{name:"change", handlerObject:this, handlerMethod:"handleChangeEvent"},
|
||||
{name:"input", handlerObject:this, handlerMethod:"handleInputEvent"},
|
||||
]);
|
||||
// Insert the label into the DOM and render any children
|
||||
parent.insertBefore(this.inputDomNode,nextSibling);
|
||||
@ -59,23 +64,77 @@ RangeWidget.prototype.render = function(parent,nextSibling) {
|
||||
|
||||
RangeWidget.prototype.getValue = function() {
|
||||
var tiddler = this.wiki.getTiddler(this.tiddlerTitle),
|
||||
fieldName = this.tiddlerField || "text",
|
||||
value = this.defaultValue;
|
||||
fieldName = this.tiddlerField,
|
||||
value = this.defaultValue;
|
||||
if(tiddler) {
|
||||
if(this.tiddlerIndex) {
|
||||
value = this.wiki.extractTiddlerDataItem(tiddler,this.tiddlerIndex,this.defaultValue || "");
|
||||
value = this.wiki.extractTiddlerDataItem(tiddler,this.tiddlerIndex,this.defaultValue);
|
||||
} else {
|
||||
if($tw.utils.hop(tiddler.fields,fieldName)) {
|
||||
value = tiddler.fields[fieldName] || "";
|
||||
} else {
|
||||
value = this.defaultValue || "";
|
||||
value = this.defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
RangeWidget.prototype.getActionVariables = function(options) {
|
||||
options = options || {};
|
||||
var hasChanged = (this.startValue !== this.inputDomNode.value) ? "yes" : "no";
|
||||
// Trigger actions. Use variables = {key:value, key:value ...}
|
||||
// the "value" is needed.
|
||||
return $tw.utils.extend({"actionValue": this.inputDomNode.value, "actionValueHasChanged": hasChanged}, options);
|
||||
}
|
||||
|
||||
// actionsStart
|
||||
RangeWidget.prototype.handleMouseDownEvent = function(event) {
|
||||
this.mouseDown = true; // TODO remove once IE is gone.
|
||||
this.startValue = this.inputDomNode.value; // TODO remove this line once IE is gone!
|
||||
this.handleEvent(event);
|
||||
// Trigger actions
|
||||
if(this.actionsMouseDown) {
|
||||
var variables = this.getActionVariables() // TODO this line will go into the function call below.
|
||||
this.invokeActionString(this.actionsMouseDown,this,event,variables);
|
||||
}
|
||||
}
|
||||
|
||||
// actionsStop
|
||||
RangeWidget.prototype.handleMouseUpEvent = function(event) {
|
||||
this.mouseDown = false; // TODO remove once IE is gone.
|
||||
this.handleEvent(event);
|
||||
// Trigger actions
|
||||
if(this.actionsMouseUp) {
|
||||
var variables = this.getActionVariables()
|
||||
this.invokeActionString(this.actionsMouseUp,this,event,variables);
|
||||
}
|
||||
// TODO remove the following if() once IE is gone!
|
||||
if ($tw.browser.isIE) {
|
||||
if (this.startValue !== this.inputDomNode.value) {
|
||||
this.handleChangeEvent(event);
|
||||
this.startValue = this.inputDomNode.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RangeWidget.prototype.handleChangeEvent = function(event) {
|
||||
if (this.mouseDown) { // TODO refactor this function once IE is gone.
|
||||
this.handleInputEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
RangeWidget.prototype.handleInputEvent = function(event) {
|
||||
this.handleEvent(event);
|
||||
// Trigger actions
|
||||
if(this.actionsInput) {
|
||||
// "tiddler" parameter may be missing. See .execute() below
|
||||
var variables = this.getActionVariables({"actionValueHasChanged": "yes"}) // TODO this line will go into the function call below.
|
||||
this.invokeActionString(this.actionsInput,this,event,variables);
|
||||
}
|
||||
};
|
||||
|
||||
RangeWidget.prototype.handleEvent = function(event) {
|
||||
if(this.getValue() !== this.inputDomNode.value) {
|
||||
if(this.tiddlerIndex) {
|
||||
this.wiki.setText(this.tiddlerTitle,"",this.tiddlerIndex,this.inputDomNode.value);
|
||||
@ -89,15 +148,24 @@ RangeWidget.prototype.handleInputEvent = function(event) {
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
RangeWidget.prototype.execute = function() {
|
||||
// TODO remove the next 1 lines once IE is gone!
|
||||
this.mouseUp = true; // Needed for IE10
|
||||
// Get the parameters from the attributes
|
||||
this.tiddlerTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.tiddlerField = this.getAttribute("field");
|
||||
this.tiddlerField = this.getAttribute("field","text");
|
||||
this.tiddlerIndex = this.getAttribute("index");
|
||||
this.minValue = this.getAttribute("min");
|
||||
this.maxValue = this.getAttribute("max");
|
||||
this.increment = this.getAttribute("increment");
|
||||
this.defaultValue = this.getAttribute("default");
|
||||
this.defaultValue = this.getAttribute("default","");
|
||||
this.elementClass = this.getAttribute("class","");
|
||||
this.isDisabled = this.getAttribute("disabled","no");
|
||||
// Actions since 5.1.23
|
||||
// Next 2 only fire once!
|
||||
this.actionsMouseDown = this.getAttribute("actionsStart","");
|
||||
this.actionsMouseUp = this.getAttribute("actionsStop","");
|
||||
// Input fires very often!
|
||||
this.actionsInput = this.getAttribute("actions","");
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
@ -107,7 +175,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
RangeWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes['min'] || changedAttributes['max'] || changedAttributes['increment'] || changedAttributes["default"] || changedAttributes["class"]) {
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
@ -35,9 +35,8 @@ RevealWidget.prototype.render = function(parent,nextSibling) {
|
||||
tag = this.revealTag;
|
||||
}
|
||||
var domNode = this.document.createElement(tag);
|
||||
var classes = this["class"].split(" ") || [];
|
||||
classes.push("tc-reveal");
|
||||
domNode.className = classes.join(" ");
|
||||
this.domNode = domNode;
|
||||
this.assignDomNodeClasses();
|
||||
if(this.style) {
|
||||
domNode.setAttribute("style",this.style);
|
||||
}
|
||||
@ -70,6 +69,10 @@ RevealWidget.prototype.positionPopup = function(domNode) {
|
||||
left = this.popup.left + this.popup.width;
|
||||
top = this.popup.top + this.popup.height - domNode.offsetHeight;
|
||||
break;
|
||||
case "belowright":
|
||||
left = this.popup.left + this.popup.width;
|
||||
top = this.popup.top + this.popup.height;
|
||||
break;
|
||||
case "right":
|
||||
left = this.popup.left + this.popup.width;
|
||||
top = this.popup.top;
|
||||
@ -78,6 +81,10 @@ RevealWidget.prototype.positionPopup = function(domNode) {
|
||||
left = this.popup.left + this.popup.width - domNode.offsetWidth;
|
||||
top = this.popup.top + this.popup.height;
|
||||
break;
|
||||
case "aboveleft":
|
||||
left = this.popup.left - domNode.offsetWidth;
|
||||
top = this.popup.top - domNode.offsetHeight;
|
||||
break;
|
||||
default: // Below
|
||||
left = this.popup.left;
|
||||
top = this.popup.top + this.popup.height;
|
||||
@ -102,13 +109,14 @@ RevealWidget.prototype.execute = function() {
|
||||
this.text = this.getAttribute("text");
|
||||
this.position = this.getAttribute("position");
|
||||
this.positionAllowNegative = this.getAttribute("positionAllowNegative") === "yes";
|
||||
this["class"] = this.getAttribute("class","");
|
||||
// class attribute handled in assignDomNodeClasses()
|
||||
this.style = this.getAttribute("style","");
|
||||
this["default"] = this.getAttribute("default","");
|
||||
this.animate = this.getAttribute("animate","no");
|
||||
this.retain = this.getAttribute("retain","no");
|
||||
this.openAnimation = this.animate === "no" ? undefined : "open";
|
||||
this.closeAnimation = this.animate === "no" ? undefined : "close";
|
||||
this.updatePopupPosition = this.getAttribute("updatePopupPosition","no") === "yes";
|
||||
// Compute the title of the state tiddler and read it
|
||||
this.stateTiddlerTitle = this.state;
|
||||
this.stateTitle = this.getAttribute("stateTitle");
|
||||
@ -194,6 +202,12 @@ RevealWidget.prototype.readPopupState = function(state) {
|
||||
}
|
||||
};
|
||||
|
||||
RevealWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-reveal");
|
||||
this.domNode.className = classes.join(" ");
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
@ -212,6 +226,14 @@ RevealWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
} else if(this.type === "popup" && this.updatePopupPosition && (changedTiddlers[this.state] || changedTiddlers[this.stateTitle])) {
|
||||
this.positionPopup(this.domNode);
|
||||
}
|
||||
if(changedAttributes.style) {
|
||||
this.domNode.style = this.getAttribute("style","");
|
||||
}
|
||||
if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
|
@ -16,26 +16,6 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var ScrollableWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
this.scaleFactor = 1;
|
||||
this.addEventListeners([
|
||||
{type: "tm-scroll", handler: "handleScrollEvent"}
|
||||
]);
|
||||
if($tw.browser) {
|
||||
this.requestAnimationFrame = window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.setTimeout(callback, 1000/60);
|
||||
};
|
||||
this.cancelAnimationFrame = window.cancelAnimationFrame ||
|
||||
window.webkitCancelAnimationFrame ||
|
||||
window.webkitCancelRequestAnimationFrame ||
|
||||
window.mozCancelAnimationFrame ||
|
||||
window.mozCancelRequestAnimationFrame ||
|
||||
function(id) {
|
||||
window.clearTimeout(id);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -147,6 +127,26 @@ Render this widget into the DOM
|
||||
*/
|
||||
ScrollableWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
this.scaleFactor = 1;
|
||||
this.addEventListeners([
|
||||
{type: "tm-scroll", handler: "handleScrollEvent"}
|
||||
]);
|
||||
if($tw.browser) {
|
||||
this.requestAnimationFrame = window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
return window.setTimeout(callback, 1000/60);
|
||||
};
|
||||
this.cancelAnimationFrame = window.cancelAnimationFrame ||
|
||||
window.webkitCancelAnimationFrame ||
|
||||
window.webkitCancelRequestAnimationFrame ||
|
||||
window.mozCancelAnimationFrame ||
|
||||
window.mozCancelRequestAnimationFrame ||
|
||||
function(id) {
|
||||
window.clearTimeout(id);
|
||||
};
|
||||
}
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
|
@ -43,6 +43,7 @@ TranscludeWidget.prototype.execute = function() {
|
||||
this.transcludeField = this.getAttribute("field");
|
||||
this.transcludeIndex = this.getAttribute("index");
|
||||
this.transcludeMode = this.getAttribute("mode");
|
||||
this.recursionMarker = this.getAttribute("recursionMarker","yes");
|
||||
// Parse the text reference
|
||||
var parseAsInline = !this.parseTreeNode.isBlock;
|
||||
if(this.transcludeMode === "inline") {
|
||||
@ -61,7 +62,9 @@ TranscludeWidget.prototype.execute = function() {
|
||||
parseTreeNodes = parser ? parser.tree : this.parseTreeNode.children;
|
||||
// Set context variables for recursion detection
|
||||
var recursionMarker = this.makeRecursionMarker();
|
||||
this.setVariable("transclusion",recursionMarker);
|
||||
if(this.recursionMarker === "yes") {
|
||||
this.setVariable("transclusion",recursionMarker);
|
||||
}
|
||||
// Check for recursion
|
||||
if(parser) {
|
||||
if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user