mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-27 21:31:25 +00:00
Compare commits
49 Commits
revert-942
...
fix-browse
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88619fc215 | ||
|
|
dfc5e9e6e5 | ||
|
|
1b8a2caa23 | ||
|
|
ecba671bcf | ||
|
|
fc1e53a777 | ||
|
|
2b6bdcbf97 | ||
|
|
119706a918 | ||
|
|
86d15585b6 | ||
|
|
4b824795c8 | ||
|
|
cd173a959d | ||
|
|
86d61e09bd | ||
|
|
7c197f6ecc | ||
|
|
8f8b46dab7 | ||
|
|
7b013af240 | ||
|
|
05f3e6d5a0 | ||
|
|
48eeb4603a | ||
|
|
fc74219c0b | ||
|
|
86c4770a28 | ||
|
|
ffde2da16c | ||
|
|
da41a55f29 | ||
|
|
846ac9a0dd | ||
|
|
65edda224b | ||
|
|
41dac42f3b | ||
|
|
87d9754a4e | ||
|
|
5cd3084298 | ||
|
|
4c27c09b4d | ||
|
|
4bb0bc5527 | ||
|
|
6cb333b65b | ||
|
|
3378497816 | ||
|
|
033d5cf225 | ||
|
|
52d73eb1a8 | ||
|
|
5d1c1eaf87 | ||
|
|
ed4a186c9c | ||
|
|
f16c0d769b | ||
|
|
298508c104 | ||
|
|
f1e1532949 | ||
|
|
3e1078eff1 | ||
|
|
cc348fee96 | ||
|
|
24d45fd318 | ||
|
|
9fd345ec06 | ||
|
|
30e9b4f3b7 | ||
|
|
c6556d5207 | ||
|
|
a8da7e0207 | ||
|
|
d5762b1fbb | ||
|
|
2b0739f06e | ||
|
|
d81204c6ab | ||
|
|
e339f112a4 | ||
|
|
e001c21bf5 | ||
|
|
7a9235e9d1 |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
boot/** -linguist-generated
|
||||
**/tiddlywiki.files linguist-language=JSON
|
||||
**/tiddlywiki.info linguist-language=JSON
|
||||
**/plugin.info linguist-language=JSON
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve TiddlyWiki 5
|
||||
title: "[BUG] "
|
||||
type: bug
|
||||
body:
|
||||
- type: textarea
|
||||
id: Describe
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -4,6 +4,7 @@ about: Suggest an idea for TiddlyWiki 5
|
||||
title: "[IDEA]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
type: feature
|
||||
|
||||
---
|
||||
|
||||
|
||||
2
.github/workflows/pr-check-build-size.yml
vendored
2
.github/workflows/pr-check-build-size.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
steps:
|
||||
- name: build-size-check
|
||||
id: get_sizes
|
||||
uses: TiddlyWiki/cerebrus@v5
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
|
||||
2
.github/workflows/pr-comment-build-size.yml
vendored
2
.github/workflows/pr-comment-build-size.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Build and check size
|
||||
uses: TiddlyWiki/cerebrus@v5
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ inputs.pr_number }}
|
||||
repo: ${{ github.repository }}
|
||||
|
||||
8
.github/workflows/pr-validation.yml
vendored
8
.github/workflows/pr-validation.yml
vendored
@@ -15,22 +15,22 @@ jobs:
|
||||
steps:
|
||||
# Step 1: Validate PR paths
|
||||
- name: Validate PR Paths
|
||||
uses: TiddlyWiki/cerebrus@v5
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
base_ref: ${{ github.base_ref }}
|
||||
base_ref: ${{ github.event.pull_request.base.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
mode: rules
|
||||
continue-on-error: true
|
||||
|
||||
# Step 2: Validate change notes
|
||||
- name: Validate Change Notes
|
||||
uses: TiddlyWiki/cerebrus@v5
|
||||
uses: TiddlyWiki/cerebrus@v6
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
repo: ${{ github.repository }}
|
||||
base_ref: ${{ github.base_ref }}
|
||||
base_ref: ${{ github.event.pull_request.base.ref }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
mode: changenotes
|
||||
continue-on-error: false
|
||||
|
||||
82
boot/boot.js
82
boot/boot.js
@@ -8,6 +8,8 @@ On the server this file is executed directly to boot TiddlyWiki. In the browser,
|
||||
|
||||
\*/
|
||||
|
||||
/* eslint-disable @stylistic/indent */
|
||||
|
||||
var _boot = (function($tw) {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
@@ -44,12 +46,8 @@ $tw.utils.hop = function(object,property) {
|
||||
return object ? Object.prototype.hasOwnProperty.call(object,property) : false;
|
||||
};
|
||||
|
||||
/*
|
||||
Determine if a value is an array
|
||||
*/
|
||||
$tw.utils.isArray = function(value) {
|
||||
return Object.prototype.toString.call(value) == "[object Array]";
|
||||
};
|
||||
/** @deprecated Use Array.isArray instead */
|
||||
$tw.utils.isArray = value => Array.isArray(value);
|
||||
|
||||
/*
|
||||
Check if an array is equal by value and by reference.
|
||||
@@ -128,35 +126,22 @@ $tw.utils.pushTop = function(array,value) {
|
||||
return array;
|
||||
};
|
||||
|
||||
/*
|
||||
Determine if a value is a date
|
||||
*/
|
||||
$tw.utils.isDate = function(value) {
|
||||
return Object.prototype.toString.call(value) === "[object Date]";
|
||||
};
|
||||
/** @deprecated Use instanceof Date instead */
|
||||
$tw.utils.isDate = value => value instanceof Date;
|
||||
|
||||
/*
|
||||
Iterate through all the own properties of an object or array. Callback is invoked with (element,title,object)
|
||||
*/
|
||||
/** @deprecated Use array iterative methods instead */
|
||||
$tw.utils.each = function(object,callback) {
|
||||
var next,f,length;
|
||||
if(object) {
|
||||
if(Object.prototype.toString.call(object) == "[object Array]") {
|
||||
for(f=0, length=object.length; f<length; f++) {
|
||||
next = callback(object[f],f,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(Array.isArray(object)) {
|
||||
object.every((element,index,array) => {
|
||||
const next = callback(element,index,array);
|
||||
return next !== false;
|
||||
});
|
||||
} else {
|
||||
var keys = Object.keys(object);
|
||||
for(f=0, length=keys.length; f<length; f++) {
|
||||
var key = keys[f];
|
||||
next = callback(object[key],key,object);
|
||||
if(next === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Object.entries(object).every(entry => {
|
||||
const next = callback(entry[1], entry[0], object);
|
||||
return next !== false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -331,32 +316,13 @@ $tw.utils.htmlDecode = function(s) {
|
||||
return s.toString().replace(/</mg,"<").replace(/ /mg,"\xA0").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
|
||||
};
|
||||
|
||||
/*
|
||||
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 href = window.location.href;
|
||||
var idx = href.indexOf('#');
|
||||
if(idx === -1) {
|
||||
return "#";
|
||||
} else if(href.substr(idx + 1,1) === "#" || href.substr(idx + 1,3) === "%23") {
|
||||
// Special case: ignore location hash if it itself starts with a #
|
||||
return "#";
|
||||
} else {
|
||||
return href.substring(idx);
|
||||
}
|
||||
};
|
||||
/** @deprecated Use window.location.hash instead. */
|
||||
$tw.utils.getLocationHash = () => window.location.hash;
|
||||
|
||||
/*
|
||||
Pad a string to a given length with "0"s. Length defaults to 2
|
||||
*/
|
||||
$tw.utils.pad = function(value,length) {
|
||||
length = length || 2;
|
||||
var s = value.toString();
|
||||
if(s.length < length) {
|
||||
s = "000000000000000000000000000".substr(0,length - s.length) + s;
|
||||
}
|
||||
return s;
|
||||
/** @deprecated Pad a string to a given length with "0"s. Length defaults to 2 */
|
||||
$tw.utils.pad = function(value,length = 2) {
|
||||
const s = value.toString();
|
||||
return s.padStart(length, "0");
|
||||
};
|
||||
|
||||
// Convert a date into UTC YYYYMMDDHHMMSSmmm format
|
||||
@@ -630,7 +596,7 @@ $tw.utils.evalGlobal = function(code,context,filename,sandbox,allowGlobals) {
|
||||
// Compile the code into a function
|
||||
var fn;
|
||||
if($tw.browser) {
|
||||
fn = window["eval"](code + "\n\n//# sourceURL=" + filename);
|
||||
fn = window["eval"](code + "\n\n//# sourceURL=" + filename); // eslint-disable-line no-eval -- See https://github.com/TiddlyWiki/TiddlyWiki5/issues/6839
|
||||
} else {
|
||||
if(sandbox){
|
||||
fn = vm.runInContext(code,sandbox,filename)
|
||||
@@ -2801,6 +2767,8 @@ return $tw;
|
||||
|
||||
});
|
||||
|
||||
/* eslint-enable @stylistic/indent */
|
||||
|
||||
if(typeof(exports) !== "undefined") {
|
||||
exports.TiddlyWiki = _boot;
|
||||
} else {
|
||||
|
||||
@@ -12,6 +12,8 @@ See Boot.js for further details of the boot process.
|
||||
|
||||
\*/
|
||||
|
||||
/* eslint-disable @stylistic/indent */
|
||||
|
||||
var _bootprefix = (function($tw) {
|
||||
|
||||
"use strict";
|
||||
@@ -114,6 +116,8 @@ return $tw;
|
||||
|
||||
});
|
||||
|
||||
/* eslint-enable @stylistic/indent */
|
||||
|
||||
if(typeof(exports) === "undefined") {
|
||||
// Set up $tw global for the browser
|
||||
window.$tw = _bootprefix(window.$tw);
|
||||
|
||||
14
community/people/ChristianByron.tid
Normal file
14
community/people/ChristianByron.tid
Normal file
@@ -0,0 +1,14 @@
|
||||
title: @Christian_Byron
|
||||
tags: Community/Person
|
||||
fullname: Christian Byron
|
||||
talk.tiddlywiki.org: Christian_Byron
|
||||
github: ceebeetree
|
||||
linkedin: www.linkedin.com/in/christian-byron-b84a594/
|
||||
avatar: /9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAgICAgJCAkKCgkNDgwODRMREBARExwUFhQWFBwrGx8bGx8bKyYuJSMlLiZENS8vNUROQj5CTl9VVV93cXecnNEBCAgICAkICQoKCQ0ODA4NExEQEBETHBQWFBYUHCsbHxsbHxsrJi4lIyUuJkQ1Ly81RE5CPkJOX1VVX3dxd5yc0f/CABEIACAAIAMBIgACEQEDEQH/xAAuAAEBAAMBAAAAAAAAAAAAAAAHBgEDBQQBAAMBAAAAAAAAAAAAAAAAAAABAwX/2gAMAwEAAhADEAAAADv2xtJlY03sqePW3ARS1RSydIhcH//EACcQAAICAgIBAgYDAAAAAAAAAAECAwQFEQASMRMhBhBBk8HRIzJx/9oACAEBAAE/AMFQxs+NExqJLMCwYE+SOT4bF3qr+hAIpRsDQ6lWH0Yco4S/eVniRVQHXZzrZ5dwGQpQtNII2RfJVvHMRl5cbKxC94n/ALp+RxfiKpNcgMMUqPIwjcnWip/I5XtUowaL3Ujir/xt79Glb6/4OZ7MV5oEpUzuIa7MPB14A5jpoYLsEsydo1bbLre+CWEEEYab7Uf74ZYSSThpvtR/vmRmhnuzywp1jZtquta+VPM49qlcy24lf017At7g8uZnHrUsGK3Ez+m3UBvcnXy//8QAHhEAAgEFAAMAAAAAAAAAAAAAAQIDAAQRIkEyUaH/2gAIAQIBAT8AmiuVlZkLEeQOflJPcvMAF0z65V+h0YIW52rBDuxUrztf/8QAIxEBAAEDAwMFAAAAAAAAAAAAAgEAAxEEBSMSQcEiMVJxof/aAAgBAwEBPwC/Z1ZvNBOYz1Gc/lDUat3ySPRM/H2P3W4hcbIldpxnxW3BcjQk9oznzX//2Q==
|
||||
|
||||
Hello ~TiddlyWikiers - I have been a long time fan, recent contributor to the TW community.
|
||||
Recently I have volunteered to run the [[TiddlyWiki Newsletter|https://tiddlywiki.substack.com/]] to spread the great news about TW.
|
||||
|
||||
I have been in the IT industry for about thirty years, mostly as a consultant and technical arcitect.
|
||||
More recently I went back to study a masters in IT focussing on AI and data science.
|
||||
Now my partner and I have started our own business ([[Sphere Innovations|https://sphere-innovations.com.au]]) - in consulting and building web applications for small to medium size businesses here in Australia.
|
||||
@@ -1,6 +0,0 @@
|
||||
title: Newsletter Team
|
||||
tags: Community/Team
|
||||
modified: 20250909171928024
|
||||
created: 20250909171928024
|
||||
|
||||
The Newsletter Team is responsible for producing the TiddlyWiki Newsletter, a monthly email newsletter that highlights news, updates, and community contributions related to TiddlyWiki.
|
||||
11
community/project/teams/Quality Assurance Team.tid
Normal file
11
community/project/teams/Quality Assurance Team.tid
Normal file
@@ -0,0 +1,11 @@
|
||||
title: Quality Assurance Team
|
||||
created: 20251112125742296
|
||||
modified: 20251112125742296
|
||||
tags: Community/Team
|
||||
team:
|
||||
leader: @Leilei332
|
||||
|
||||
title: Quality Assurance Team
|
||||
|
||||
The Quality Assurance Team is responsible for ensuring the quality and reliability of TiddlyWiki releases. This includes reviewing code submissions, testing new features, identifying bugs, and verifying that fixes are effective.
|
||||
|
||||
15
community/project/teams/TiddlyWiki Newsletter Team.tid
Normal file
15
community/project/teams/TiddlyWiki Newsletter Team.tid
Normal file
@@ -0,0 +1,15 @@
|
||||
title: TiddlyWiki Newsletter Team
|
||||
tags: Community/Team
|
||||
modified: 20251219090709874
|
||||
created: 20250909171928024
|
||||
leader: @Christian_Byron
|
||||
|
||||
The Newsletter Team is responsible for producing the [[TiddlyWiki Newsletter]]. We would love to have your help if you would like to get involved.
|
||||
|
||||
! Audience
|
||||
|
||||
The newsletter is intended for TiddlyWiki end users who do not track all the discussions on https://talk.tiddlywiki.org/.
|
||||
|
||||
Coverage of developer topics such as JavaScript and intricate wikitext should be handled thoughtfully to avoid alienating the core audience of end users.
|
||||
|
||||
Subscribing to the newsletter is intended to give people confidence that they will not miss any important developments.
|
||||
@@ -1,5 +1,5 @@
|
||||
title: Community/Team
|
||||
modified: 20250909171928024
|
||||
created: 20250909171928024
|
||||
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Infrastructure Team]] [[Succession Team]]
|
||||
list: [[Project Team]] [[Core Team]] [[Documentation Team]] [[Quality Assurance Team]] [[Infrastructure Team]] [[MultiWikiServer Team]] [[Newsletter Team]] [[Succession Team]]
|
||||
|
||||
|
||||
@@ -156,8 +156,8 @@ Fix the height of textarea to fit content
|
||||
FramedEngine.prototype.fixHeight = function() {
|
||||
// Make sure styles are updated
|
||||
this.copyStyles();
|
||||
// Adjust height
|
||||
if(this.widget.editTag === "textarea") {
|
||||
// If .editRows is initialised, it takes precedence
|
||||
if(this.widget.editTag === "textarea" && !this.widget.editRows) {
|
||||
if(this.widget.editAutoHeight) {
|
||||
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
||||
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
||||
|
||||
@@ -100,7 +100,8 @@ SimpleEngine.prototype.getText = function() {
|
||||
Fix the height of textarea to fit content
|
||||
*/
|
||||
SimpleEngine.prototype.fixHeight = function() {
|
||||
if(this.widget.editTag === "textarea") {
|
||||
// If .editRows is initialised, it takes precedence
|
||||
if((this.widget.editTag === "textarea") && !this.widget.editRows) {
|
||||
if(this.widget.editAutoHeight) {
|
||||
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
||||
$tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
||||
|
||||
41
core/modules/filterrunprefixes/let.js
Normal file
41
core/modules/filterrunprefixes/let.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/*\
|
||||
title: $:/core/modules/filterrunprefixes/let.js
|
||||
type: application/javascript
|
||||
module-type: filterrunprefix
|
||||
|
||||
Assign a value to a variable
|
||||
|
||||
\*/
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Export our filter prefix function
|
||||
*/
|
||||
exports.let = function(operationSubFunction,options) {
|
||||
// Return the filter run prefix function
|
||||
return function(results,source,widget) {
|
||||
// Save the result list
|
||||
var resultList = results.toArray();
|
||||
// Clear the results
|
||||
results.clear();
|
||||
// Evaluate the subfunction to get the variable name
|
||||
var subFunctionResults = operationSubFunction(source,widget);
|
||||
if(subFunctionResults.length === 0) {
|
||||
return;
|
||||
}
|
||||
var name = subFunctionResults[0];
|
||||
if(typeof name !== "string" || name.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Assign the result of the subfunction to the variable
|
||||
var variables = {};
|
||||
variables[name] = resultList;
|
||||
// Return the variables
|
||||
return {
|
||||
variables: variables
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -35,7 +35,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
operator.prefix = filterString.charAt(p++);
|
||||
}
|
||||
// Get the operator name
|
||||
nextBracketPos = filterString.substring(p).search(/[\[\{<\/]/);
|
||||
nextBracketPos = filterString.substring(p).search(/[\[\{<\/\(]/);
|
||||
if(nextBracketPos === -1) {
|
||||
throw "Missing [ in filter expression";
|
||||
}
|
||||
@@ -79,13 +79,17 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
operand.variable = true;
|
||||
nextBracketPos = filterString.indexOf(">",p);
|
||||
break;
|
||||
case "(": // Round brackets
|
||||
operand.multiValuedVariable = 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);
|
||||
// DEPRECATION WARNING
|
||||
console.log("WARNING: Filter",operator.operator,"has a deprecated regexp operand",operator.regexp);
|
||||
nextBracketPos = p + rex.lastIndex - 1;
|
||||
}
|
||||
else {
|
||||
@@ -112,7 +116,7 @@ function parseFilterOperation(operators,filterString,p) {
|
||||
// Check for multiple operands
|
||||
while(filterString.charAt(p) === ",") {
|
||||
p++;
|
||||
if(/^[\[\{<\/]/.test(filterString.substring(p))) {
|
||||
if(/^[\[\{<\/\(]/.test(filterString.substring(p))) {
|
||||
nextBracketPos = p;
|
||||
p++;
|
||||
parseOperand(filterString.charAt(nextBracketPos));
|
||||
@@ -141,7 +145,15 @@ exports.parseFilter = function(filterString) {
|
||||
p = 0, // Current position in the filter string
|
||||
match;
|
||||
var whitespaceRegExp = /(\s+)/mg,
|
||||
operandRegExp = /((?:\+|\-|~|=|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
// Groups:
|
||||
// 1 - entire filter run prefix
|
||||
// 2 - filter run prefix itself
|
||||
// 3 - filter run prefix suffixes
|
||||
// 4 - opening square bracket following filter run prefix
|
||||
// 5 - double quoted string following filter run prefix
|
||||
// 6 - single quoted string following filter run prefix
|
||||
// 7 - anything except for whitespace and square brackets
|
||||
operandRegExp = /((?:\+|\-|~|(?:=>?)|\:(\w+)(?:\:([\w\:, ]*))?)?)(?:(\[)|(?:"([^"]*)")|(?:'([^']*)')|([^\s\[\]]+))/mg;
|
||||
while(p < filterString.length) {
|
||||
// Skip any whitespace
|
||||
whitespaceRegExp.lastIndex = p;
|
||||
@@ -152,38 +164,45 @@ exports.parseFilter = function(filterString) {
|
||||
// Match the start of the operation
|
||||
if(p < filterString.length) {
|
||||
operandRegExp.lastIndex = p;
|
||||
match = operandRegExp.exec(filterString);
|
||||
if(!match || match.index !== p) {
|
||||
throw $tw.language.getString("Error/FilterSyntax");
|
||||
}
|
||||
var operation = {
|
||||
prefix: "",
|
||||
operators: []
|
||||
};
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p = p + operation.prefix.length;
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
match = operandRegExp.exec(filterString);
|
||||
if(match && match.index === p) {
|
||||
// If there is a filter run prefix
|
||||
if(match[1]) {
|
||||
operation.prefix = match[1];
|
||||
p = p + operation.prefix.length;
|
||||
// Name for named prefixes
|
||||
if(match[2]) {
|
||||
operation.namedPrefix = match[2];
|
||||
}
|
||||
// Suffixes for filter run prefix
|
||||
if(match[3]) {
|
||||
operation.suffixes = [];
|
||||
$tw.utils.each(match[3].split(":"),function(subsuffix) {
|
||||
operation.suffixes.push([]);
|
||||
$tw.utils.each(subsuffix.split(","),function(entry) {
|
||||
entry = $tw.utils.trim(entry);
|
||||
if(entry) {
|
||||
operation.suffixes[operation.suffixes.length -1].push(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// Opening square bracket
|
||||
if(match[4]) {
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
}
|
||||
}
|
||||
if(match[4]) { // Opening square bracket
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
} else {
|
||||
p = match.index + match[0].length;
|
||||
// No filter run prefix
|
||||
p = parseFilterOperation(operation.operators,filterString,p);
|
||||
}
|
||||
// Quoted strings and unquoted title
|
||||
if(match[5] || match[6] || match[7]) { // Double quoted string, single quoted string or unquoted title
|
||||
operation.operators.push(
|
||||
{operator: "title", operands: [{text: match[5] || match[6] || match[7]}]}
|
||||
@@ -213,7 +232,12 @@ exports.getFilterRunPrefixes = function() {
|
||||
|
||||
exports.filterTiddlers = function(filterString,widget,source) {
|
||||
var fn = this.compileFilter(filterString);
|
||||
return fn.call(this,source,widget);
|
||||
try {
|
||||
const fnResult = fn.call(this,source,widget);
|
||||
return fnResult;
|
||||
} catch(e) {
|
||||
return [`${$tw.language.getString("Error/Filter")}: ${e}`];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -251,6 +275,8 @@ exports.compileFilter = function(filterString) {
|
||||
results = [];
|
||||
$tw.utils.each(operation.operators,function(operator) {
|
||||
var operands = [],
|
||||
multiValueOperands = [],
|
||||
isMultiValueOperand = [],
|
||||
operatorFunction;
|
||||
if(!operator.operator) {
|
||||
// Use the "title" operator if no operator is specified
|
||||
@@ -266,28 +292,46 @@ exports.compileFilter = function(filterString) {
|
||||
if(operand.indirect) {
|
||||
var currTiddlerTitle = widget && widget.getVariable("currentTiddler");
|
||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||
operand.multiValue = [operand.value];
|
||||
} else if(operand.variable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||
operand.multiValue = [operand.value];
|
||||
} else if(operand.multiValuedVariable) {
|
||||
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
|
||||
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
|
||||
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
|
||||
operand.value = operand.multiValue[0] || "";
|
||||
} else {
|
||||
operand.value = "";
|
||||
operand.multiValue = [];
|
||||
}
|
||||
operand.isMultiValueOperand = true;
|
||||
} else {
|
||||
operand.value = operand.text;
|
||||
operand.multiValue = [operand.value];
|
||||
}
|
||||
operands.push(operand.value);
|
||||
multiValueOperands.push(operand.multiValue);
|
||||
isMultiValueOperand.push(!!operand.isMultiValueOperand);
|
||||
});
|
||||
|
||||
// Invoke the appropriate filteroperator module
|
||||
results = operatorFunction(accumulator,{
|
||||
operator: operator.operator,
|
||||
operand: operands.length > 0 ? operands[0] : undefined,
|
||||
operands: operands,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
regexp: operator.regexp
|
||||
},{
|
||||
wiki: self,
|
||||
widget: widget
|
||||
});
|
||||
operator: operator.operator,
|
||||
operand: operands.length > 0 ? operands[0] : undefined,
|
||||
operands: operands,
|
||||
multiValueOperands: multiValueOperands,
|
||||
isMultiValueOperand: isMultiValueOperand,
|
||||
prefix: operator.prefix,
|
||||
suffix: operator.suffix,
|
||||
suffixes: operator.suffixes,
|
||||
regexp: operator.regexp
|
||||
},{
|
||||
wiki: self,
|
||||
widget: widget
|
||||
});
|
||||
if($tw.utils.isArray(results)) {
|
||||
accumulator = self.makeTiddlerIterator(results);
|
||||
} else {
|
||||
@@ -319,6 +363,8 @@ exports.compileFilter = function(filterString) {
|
||||
return filterRunPrefixes["and"](operationSubFunction, options);
|
||||
case "~": // This operation is unioned into the result only if the main result so far is empty
|
||||
return filterRunPrefixes["else"](operationSubFunction, options);
|
||||
case "=>": // This operation is applied to the main results so far, and the results are assigned to a variable
|
||||
return filterRunPrefixes["let"](operationSubFunction, options);
|
||||
default:
|
||||
if(operation.namedPrefix && filterRunPrefixes[operation.namedPrefix]) {
|
||||
return filterRunPrefixes[operation.namedPrefix](operationSubFunction, options);
|
||||
@@ -345,7 +391,13 @@ exports.compileFilter = function(filterString) {
|
||||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||
operationFunction(results,source,widget);
|
||||
var operationResult = operationFunction(results,source,widget);
|
||||
if(operationResult) {
|
||||
if(operationResult.variables) {
|
||||
// If the filter run prefix has returned variables, create a new fake widget with those variables
|
||||
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push("/**-- Excessive filter recursion --**/");
|
||||
|
||||
@@ -16,8 +16,8 @@ exports.function = function(source,operator,options) {
|
||||
var functionName = operator.operands[0],
|
||||
params = [],
|
||||
results;
|
||||
$tw.utils.each(operator.operands.slice(1),function(param) {
|
||||
params.push({value: param});
|
||||
$tw.utils.each(operator.multiValueOperands.slice(1),function(paramList) {
|
||||
params.push({value: paramList[0] || "",multiValue: paramList});
|
||||
});
|
||||
// console.log(`Calling ${functionName} with params ${JSON.stringify(params)}`);
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source});
|
||||
|
||||
@@ -113,6 +113,22 @@ exports["jsonset"] = function(source,operator,options) {
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsondelete"] = function(source,operator,options) {
|
||||
var indexes = operator.operands,
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
// If parsing failed (data equals original title and is a string), return unchanged
|
||||
if(data === title && typeof data === "string") {
|
||||
results.push(title);
|
||||
} else if(data) {
|
||||
data = deleteDataItem(data,indexes);
|
||||
results.push(JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
@@ -144,7 +160,7 @@ function convertDataItemValueToStrings(item) {
|
||||
return ["null"]
|
||||
} else if(typeof item === "object") {
|
||||
var results = [],i,t;
|
||||
if($tw.utils.isArray(item)) {
|
||||
if(Array.isArray(item)) {
|
||||
// Return all the items in arrays recursively
|
||||
for(i=0; i<item.length; i++) {
|
||||
t = convertDataItemValueToStrings(item[i])
|
||||
@@ -178,7 +194,7 @@ function convertDataItemKeysToStrings(item) {
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
if($tw.utils.isArray(item)) {
|
||||
if(Array.isArray(item)) {
|
||||
for(var i=0; i<item.length; i++) {
|
||||
results.push(i.toString());
|
||||
}
|
||||
@@ -201,7 +217,7 @@ function getDataItemType(data,indexes) {
|
||||
return item;
|
||||
} else if(item === null) {
|
||||
return "null";
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
} else if(Array.isArray(item)) {
|
||||
return "array";
|
||||
} else if(typeof item === "object") {
|
||||
return "object";
|
||||
@@ -213,7 +229,7 @@ function getDataItemType(data,indexes) {
|
||||
function getItemAtIndex(item,index) {
|
||||
if($tw.utils.hop(item,index)) {
|
||||
return item[index];
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
} else if(Array.isArray(item)) {
|
||||
index = $tw.utils.parseInt(index);
|
||||
if(index < 0) { index = index + item.length };
|
||||
return item[index]; // Will be undefined if index was out-of-bounds
|
||||
@@ -223,15 +239,16 @@ function getItemAtIndex(item,index) {
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
Traverse the index chain and return the item at the specified depth.
|
||||
Returns the item at the end of the traversal, or undefined if traversal fails.
|
||||
*/
|
||||
function getDataItem(data,indexes) {
|
||||
function traverseIndexChain(data,indexes,stopBeforeLast) {
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return data;
|
||||
}
|
||||
// Get the item
|
||||
var item = data;
|
||||
for(var i=0; i<indexes.length; i++) {
|
||||
var stopIndex = stopBeforeLast ? indexes.length - 1 : indexes.length;
|
||||
for(var i = 0; i < stopIndex; i++) {
|
||||
if(item !== undefined) {
|
||||
if(item !== null && ["number","string","boolean"].indexOf(typeof item) === -1) {
|
||||
item = getItemAtIndex(item,indexes[i]);
|
||||
@@ -243,6 +260,13 @@ function getDataItem(data,indexes) {
|
||||
return item;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItem(data,indexes) {
|
||||
return traverseIndexChain(data,indexes,false);
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure, an array of index strings and a value, return the data structure with the value added at the end of the index chain. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then a different data object will be returned
|
||||
*/
|
||||
@@ -255,18 +279,15 @@ function setDataItem(data,indexes,value) {
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return value;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain
|
||||
var current = data;
|
||||
for(var i = 0; i < indexes.length - 1; i++) {
|
||||
current = getItemAtIndex(current,indexes[i]);
|
||||
if(current === undefined) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain up to the parent
|
||||
var current = traverseIndexChain(data,indexes,true);
|
||||
if(current === undefined) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
// Add the value to the end of the index chain
|
||||
var lastIndex = indexes[indexes.length - 1];
|
||||
if($tw.utils.isArray(current)) {
|
||||
if(Array.isArray(current)) {
|
||||
lastIndex = $tw.utils.parseInt(lastIndex);
|
||||
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
|
||||
}
|
||||
@@ -276,3 +297,32 @@ function setDataItem(data,indexes,value) {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the data structure with the item at the end of the index chain deleted. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then the JSON data structure is returned unmodified.
|
||||
*/
|
||||
function deleteDataItem(data,indexes) {
|
||||
// Check for the root item - don't delete the root
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return data;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain up to the parent
|
||||
var current = traverseIndexChain(data,indexes,true);
|
||||
if(current === undefined || current === null) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
// Delete the item at the end of the index chain
|
||||
var lastIndex = indexes[indexes.length - 1];
|
||||
if(Array.isArray(current) && current !== null) {
|
||||
lastIndex = $tw.utils.parseInt(lastIndex);
|
||||
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
|
||||
// Check if index is valid before splicing
|
||||
if(lastIndex >= 0 && lastIndex < current.length) {
|
||||
current.splice(lastIndex,1);
|
||||
}
|
||||
} else if(typeof current === "object" && current !== null) {
|
||||
delete current[lastIndex];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@ exports.title = function(source,operator,options) {
|
||||
var results = [];
|
||||
if(operator.prefix === "!") {
|
||||
source(function(tiddler,title) {
|
||||
if(tiddler && tiddler.fields.title !== operator.operand) {
|
||||
var titleList = operator.multiValueOperands[0] || [];
|
||||
if(tiddler && titleList.indexOf(tiddler.fields.title) === -1) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push(operator.operand);
|
||||
Array.prototype.push.apply(results,operator.multiValueOperands[0]);
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -20,8 +20,8 @@ exports["[unknown]"] = function(source,operator,options) {
|
||||
// Check for a user defined filter operator
|
||||
if(operator.operator.indexOf(".") !== -1) {
|
||||
var params = [];
|
||||
$tw.utils.each(operator.operands,function(param) {
|
||||
params.push({value: param});
|
||||
$tw.utils.each(operator.multiValueOperands,function(paramList) {
|
||||
params.push({value: paramList[0] || "",multiValue: paramList});
|
||||
});
|
||||
var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source});
|
||||
if(variableInfo && variableInfo.srcVariable) {
|
||||
|
||||
@@ -11,10 +11,13 @@ The CSV text parser processes CSV files into a table wrapped in a scrollable wid
|
||||
|
||||
var CsvParser = function(type,text,options) {
|
||||
// Special handler for tab-delimited files
|
||||
if (type === 'text/tab-delimited-values' && !options.separator) {
|
||||
if(
|
||||
!options.separator &&
|
||||
(type === "text/tab-delimited-values" || type === "text/tab-separated-values")
|
||||
) {
|
||||
options.separator = "\t";
|
||||
}
|
||||
|
||||
|
||||
// Table framework
|
||||
this.tree = [{
|
||||
"type": "scrollable", "children": [{
|
||||
@@ -32,7 +35,7 @@ var CsvParser = function(type,text,options) {
|
||||
$tw.utils.each(lines, function(columns) {
|
||||
maxColumns = Math.max(columns.length, maxColumns);
|
||||
});
|
||||
|
||||
|
||||
for(var line=0; line<lines.length; line++) {
|
||||
var columns = lines[line];
|
||||
var row = {
|
||||
@@ -55,3 +58,4 @@ var CsvParser = function(type,text,options) {
|
||||
|
||||
exports["text/csv"] = CsvParser;
|
||||
exports["text/tab-delimited-values"] = CsvParser;
|
||||
exports["text/tab-separated-values"] = CsvParser;
|
||||
|
||||
@@ -18,6 +18,7 @@ exports.synchronous = true;
|
||||
// Default story and history lists
|
||||
var PAGE_TITLE_TITLE = "$:/core/wiki/title";
|
||||
var PAGE_STYLESHEET_TITLE = "$:/core/ui/PageStylesheet";
|
||||
var ROOT_STYLESHEET_TITLE = "$:/core/ui/RootStylesheet";
|
||||
var PAGE_TEMPLATE_TITLE = "$:/core/ui/RootTemplate";
|
||||
|
||||
// Time (in ms) that we defer refreshing changes to draft tiddlers
|
||||
@@ -44,22 +45,13 @@ exports.startup = function() {
|
||||
publishTitle();
|
||||
}
|
||||
});
|
||||
// Set up the styles
|
||||
$tw.styleWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_STYLESHEET_TITLE,{document: $tw.fakeDocument});
|
||||
$tw.styleContainer = $tw.fakeDocument.createElement("style");
|
||||
$tw.styleWidgetNode.render($tw.styleContainer,null);
|
||||
$tw.styleWidgetNode.assignedStyles = $tw.styleContainer.textContent;
|
||||
$tw.styleElement = document.createElement("style");
|
||||
$tw.styleElement.innerHTML = $tw.styleWidgetNode.assignedStyles;
|
||||
document.head.insertBefore($tw.styleElement,document.head.firstChild);
|
||||
|
||||
var styleParser = $tw.wiki.parseTiddler(ROOT_STYLESHEET_TITLE,{parseAsInline: true}),
|
||||
styleWidgetNode = $tw.wiki.makeWidget(styleParser,{document: document});
|
||||
styleWidgetNode.render(document.head,null);
|
||||
|
||||
$tw.wiki.addEventListener("change",$tw.perf.report("styleRefresh",function(changes) {
|
||||
if($tw.styleWidgetNode.refresh(changes,$tw.styleContainer,null)) {
|
||||
var newStyles = $tw.styleContainer.textContent;
|
||||
if(newStyles !== $tw.styleWidgetNode.assignedStyles) {
|
||||
$tw.styleWidgetNode.assignedStyles = newStyles;
|
||||
$tw.styleElement.innerHTML = $tw.styleWidgetNode.assignedStyles;
|
||||
}
|
||||
}
|
||||
styleWidgetNode.refresh(changes,document.head,null);
|
||||
}));
|
||||
// Display the $:/core/ui/PageTemplate tiddler to kick off the display
|
||||
$tw.perf.report("mainRender",function() {
|
||||
@@ -68,7 +60,7 @@ exports.startup = function() {
|
||||
$tw.utils.addClass($tw.pageContainer,"tc-page-container-wrapper");
|
||||
document.body.insertBefore($tw.pageContainer,document.body.firstChild);
|
||||
$tw.pageWidgetNode.render($tw.pageContainer,null);
|
||||
$tw.hooks.invokeHook("th-page-refreshed");
|
||||
$tw.hooks.invokeHook("th-page-refreshed");
|
||||
})();
|
||||
// Remove any splash screen elements
|
||||
var removeList = document.querySelectorAll(".tc-remove-when-wiki-loaded");
|
||||
|
||||
@@ -63,24 +63,16 @@ exports.startup = function() {
|
||||
$tw.eventBus.emit("window:closed",{windowID});
|
||||
},false);
|
||||
// Set up the styles
|
||||
var styleWidgetNode = $tw.wiki.makeTranscludeWidget("$:/core/ui/PageStylesheet",{
|
||||
document: $tw.fakeDocument,
|
||||
variables: variables,
|
||||
importPageMacros: true}),
|
||||
styleContainer = $tw.fakeDocument.createElement("style");
|
||||
styleWidgetNode.render(styleContainer,null);
|
||||
var styleElement = srcDocument.createElement("style");
|
||||
styleElement.innerHTML = styleContainer.textContent;
|
||||
srcDocument.head.insertBefore(styleElement,srcDocument.head.firstChild);
|
||||
var styleParser = $tw.wiki.parseTiddler("$:/core/ui/RootStylesheet",{parseAsInline: true}),
|
||||
styleWidgetNode = $tw.wiki.makeWidget(styleParser,{document: srcDocument});
|
||||
styleWidgetNode.render(srcDocument.head,null);
|
||||
// Render the text of the tiddler
|
||||
var parser = $tw.wiki.parseTiddler(template),
|
||||
widgetNode = $tw.wiki.makeWidget(parser,{document: srcDocument, parentWidget: $tw.rootWidget, variables: variables});
|
||||
widgetNode.render(srcDocument.body,srcDocument.body.firstChild);
|
||||
// Function to handle refreshes
|
||||
refreshHandler = function(changes) {
|
||||
if(styleWidgetNode.refresh(changes,styleContainer,null)) {
|
||||
styleElement.innerHTML = styleContainer.textContent;
|
||||
}
|
||||
styleWidgetNode.refresh(changes);
|
||||
widgetNode.refresh(changes);
|
||||
};
|
||||
$tw.wiki.addEventListener("change",refreshHandler);
|
||||
|
||||
58
core/modules/utils/deprecated.js
Normal file
58
core/modules/utils/deprecated.js
Normal file
@@ -0,0 +1,58 @@
|
||||
/*\
|
||||
title: $:/core/modules/utils/deprecated.js
|
||||
type: application/javascript
|
||||
module-type: utils
|
||||
|
||||
Deprecated util functions
|
||||
|
||||
\*/
|
||||
|
||||
exports.logTable = data => console.table(data);
|
||||
|
||||
exports.repeat = (str,count) => str.repeat(count);
|
||||
|
||||
exports.startsWith = (str,search) => str.startsWith(search);
|
||||
|
||||
exports.endsWith = (str,search) => str.endsWith(search);
|
||||
|
||||
exports.trim = function(str) {
|
||||
if(typeof str === "string") {
|
||||
return str.trim();
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
exports.hopArray = (object,array) => array.some(element => $tw.utils.hop(object,element));
|
||||
|
||||
exports.sign = Math.sign;
|
||||
|
||||
exports.strEndsWith = (str,ending,position) => str.endsWith(ending,position);
|
||||
|
||||
exports.stringifyNumber = num => num.toString();
|
||||
|
||||
exports.tagToCssSelector = function(tagName) {
|
||||
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
|
||||
return "\\" + c;
|
||||
});
|
||||
};
|
||||
|
||||
exports.domContains = (a,b) => a.compareDocumentPosition(b) & 16;
|
||||
|
||||
exports.domMatchesSelector = (node,selector) => node.matches(selector);
|
||||
|
||||
exports.hasClass = (el,className) => el.classList && el.classList.contains(className);
|
||||
|
||||
exports.addClass = function(el,className) {
|
||||
el.classList && el.classList.add(className);
|
||||
};
|
||||
|
||||
exports.removeClass = function(el,className) {
|
||||
el.classList && el.classList.remove(className);
|
||||
};
|
||||
|
||||
exports.toggleClass = function(el,className,status) {
|
||||
el.classList && el.classList.toggle(className, status);
|
||||
};
|
||||
|
||||
exports.getLocationPath = () => window.location.origin + window.location.pathname;
|
||||
@@ -11,19 +11,6 @@ Various static DOM-related utility functions.
|
||||
|
||||
var Popup = require("$:/core/modules/utils/dom/popup.js");
|
||||
|
||||
/*
|
||||
Determines whether element 'a' contains element 'b'
|
||||
Code thanks to John Resig, http://ejohn.org/blog/comparing-document-position/
|
||||
*/
|
||||
exports.domContains = function(a,b) {
|
||||
return a.contains ?
|
||||
a !== b && a.contains(b) :
|
||||
!!(a.compareDocumentPosition(b) & 16);
|
||||
};
|
||||
|
||||
exports.domMatchesSelector = function(node,selector) {
|
||||
return node.matches ? node.matches(selector) : node.msMatchesSelector(selector);
|
||||
};
|
||||
|
||||
/*
|
||||
Select text in a an input or textarea (setSelectionRange crashes on certain input types)
|
||||
@@ -49,38 +36,6 @@ exports.removeChildren = function(node) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.hasClass = function(el,className) {
|
||||
return el && el.hasAttribute && el.hasAttribute("class") && el.getAttribute("class").split(" ").indexOf(className) !== -1;
|
||||
};
|
||||
|
||||
exports.addClass = function(el,className) {
|
||||
var c = (el.getAttribute("class") || "").split(" ");
|
||||
if(c.indexOf(className) === -1) {
|
||||
c.push(className);
|
||||
el.setAttribute("class",c.join(" "));
|
||||
}
|
||||
};
|
||||
|
||||
exports.removeClass = function(el,className) {
|
||||
var c = (el.getAttribute("class") || "").split(" "),
|
||||
p = c.indexOf(className);
|
||||
if(p !== -1) {
|
||||
c.splice(p,1);
|
||||
el.setAttribute("class",c.join(" "));
|
||||
}
|
||||
};
|
||||
|
||||
exports.toggleClass = function(el,className,status) {
|
||||
if(status === undefined) {
|
||||
status = !exports.hasClass(el,className);
|
||||
}
|
||||
if(status) {
|
||||
exports.addClass(el,className);
|
||||
} else {
|
||||
exports.removeClass(el,className);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Get the first parent element that has scrollbars or use the body as fallback.
|
||||
*/
|
||||
@@ -297,10 +252,6 @@ exports.copyToClipboard = function(text,options) {
|
||||
document.body.removeChild(textArea);
|
||||
};
|
||||
|
||||
exports.getLocationPath = function() {
|
||||
return window.location.toString().split("#")[0];
|
||||
};
|
||||
|
||||
/*
|
||||
Collect DOM variables
|
||||
*/
|
||||
|
||||
@@ -60,7 +60,7 @@ exports.repackPlugin = function(title,additionalTiddlers,excludeTiddlers) {
|
||||
version += "+" + pluginVersion.build;
|
||||
}
|
||||
// Save the tiddler
|
||||
$tw.wiki.addTiddler(new $tw.Tiddler(pluginTiddler,{text: JSON.stringify({tiddlers: plugins},null,4), version: version}));
|
||||
$tw.wiki.addTiddler(new $tw.Tiddler(pluginTiddler,{text: JSON.stringify({tiddlers: plugins},null,4), version: version},$tw.wiki.getModificationFields()));
|
||||
// Delete any non-shadow constituent tiddlers
|
||||
$tw.utils.each(tiddlers,function(title) {
|
||||
if($tw.wiki.tiddlerExists(title)) {
|
||||
|
||||
@@ -48,19 +48,6 @@ 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.
|
||||
@@ -79,43 +66,6 @@ exports.replaceString = function(text,search,replace) {
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Repeats a string
|
||||
*/
|
||||
exports.repeat = function(str,count) {
|
||||
var result = "";
|
||||
for(var t=0;t<count;t++) {
|
||||
result += str;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
Check if a string starts with another string
|
||||
*/
|
||||
exports.startsWith = function(str,search) {
|
||||
return str.substring(0, search.length) === search;
|
||||
};
|
||||
|
||||
/*
|
||||
Check if a string ends with another string
|
||||
*/
|
||||
exports.endsWith = function(str,search) {
|
||||
return str.substring(str.length - search.length) === search;
|
||||
};
|
||||
|
||||
/*
|
||||
Trim whitespace from the start and end of a string
|
||||
Thanks to Steven Levithan, http://blog.stevenlevithan.com/archives/faster-trim-javascript
|
||||
*/
|
||||
exports.trim = function(str) {
|
||||
if(typeof str === "string") {
|
||||
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
exports.trimPrefix = function(str,unwanted) {
|
||||
if(typeof str === "string" && typeof unwanted === "string") {
|
||||
if(unwanted === "") {
|
||||
@@ -200,18 +150,6 @@ exports.count = function(object) {
|
||||
return Object.keys(object || {}).length;
|
||||
};
|
||||
|
||||
/*
|
||||
Determine whether an array-item is an object-property
|
||||
*/
|
||||
exports.hopArray = function(object,array) {
|
||||
for(var i=0; i<array.length; i++) {
|
||||
if($tw.utils.hop(object,array[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
Remove entries from an array
|
||||
array: array to modify
|
||||
@@ -940,44 +878,6 @@ exports.makeDataUri = function(text,type,_canonical_uri) {
|
||||
return parts.join("");
|
||||
};
|
||||
|
||||
/*
|
||||
Useful for finding out the fully escaped CSS selector equivalent to a given tag. For example:
|
||||
|
||||
$tw.utils.tagToCssSelector("$:/tags/Stylesheet") --> tc-tagged-\%24\%3A\%2Ftags\%2FStylesheet
|
||||
*/
|
||||
exports.tagToCssSelector = function(tagName) {
|
||||
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
|
||||
return "\\" + c;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
IE does not have sign function
|
||||
*/
|
||||
exports.sign = Math.sign || function(x) {
|
||||
x = +x; // convert to a number
|
||||
if(x === 0 || isNaN(x)) {
|
||||
return x;
|
||||
}
|
||||
return x > 0 ? 1 : -1;
|
||||
};
|
||||
|
||||
/*
|
||||
IE does not have an endsWith function
|
||||
*/
|
||||
exports.strEndsWith = function(str,ending,position) {
|
||||
if(str.endsWith) {
|
||||
return str.endsWith(ending,position);
|
||||
} else {
|
||||
if(typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > str.length) {
|
||||
position = str.length;
|
||||
}
|
||||
position -= ending.length;
|
||||
var lastIndex = str.indexOf(ending, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Return system information useful for debugging
|
||||
*/
|
||||
@@ -1004,10 +904,6 @@ exports.parseInt = function(str) {
|
||||
return parseInt(str,10) || 0;
|
||||
};
|
||||
|
||||
exports.stringifyNumber = function(num) {
|
||||
return num + "";
|
||||
};
|
||||
|
||||
exports.makeCompareFunction = function(type,options) {
|
||||
options = options || {};
|
||||
// set isCaseSensitive to true if not defined in options
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
/*\
|
||||
title: $:/core/modules/widgets/action-log.js
|
||||
type: application/javascript
|
||||
@@ -32,7 +33,7 @@ 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
|
||||
@@ -51,23 +52,30 @@ LogWidget.prototype.invokeAction = function(triggeringWidget,event) {
|
||||
};
|
||||
|
||||
LogWidget.prototype.log = function() {
|
||||
var data = {},
|
||||
var self = this,
|
||||
data = {}, // Hashmap by attribute name with string or array of string values
|
||||
dataCount,
|
||||
allVars = {},
|
||||
allVars = {}, // Hashmap by variable name with string or array of string values
|
||||
filteredVars;
|
||||
|
||||
$tw.utils.each(this.attributes,function(attribute,name) {
|
||||
// Collect the attributes to be logged
|
||||
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
||||
if(name.substring(0,2) !== "$$") {
|
||||
data[name] = attribute;
|
||||
var resultList = self.computeAttribute(attribute,{asList: true});
|
||||
if(resultList.length <= 1) {
|
||||
data[name] = resultList[0] || "";
|
||||
} else {
|
||||
data[name] = resultList;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Collect values of all variables, using the source text for functions
|
||||
for(var v in this.variables) {
|
||||
var variable = this.parentWidget && this.parentWidget.variables[v];
|
||||
if(variable && variable.isFunctionDefinition) {
|
||||
allVars[v] = variable.value;
|
||||
} else {
|
||||
allVars[v] = this.getVariable(v,{defaultValue:""});
|
||||
var variableInfo = this.getVariableInfo(v);
|
||||
allVars[v] = variableInfo.resultList.length > 1 ? variableInfo.resultList : variableInfo.text;
|
||||
}
|
||||
}
|
||||
if(this.filter) {
|
||||
@@ -88,6 +96,6 @@ LogWidget.prototype.log = function() {
|
||||
console.groupEnd();
|
||||
}
|
||||
console.groupEnd();
|
||||
}
|
||||
};
|
||||
|
||||
exports["action-log"] = LogWidget;
|
||||
|
||||
@@ -34,9 +34,10 @@ DiffTextWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create the diff
|
||||
var dmpObject = new dmp.diff_match_patch(),
|
||||
diffs = dmpObject.diff_main(this.getAttribute("source",""),this.getAttribute("dest",""));
|
||||
// Create the diff object
|
||||
var dmpObject = new dmp.diff_match_patch();
|
||||
dmpObject.Diff_EditCost = $tw.utils.parseNumber(this.getAttribute("editcost","4"));
|
||||
var diffs = dmpObject.diff_main(this.getAttribute("source",""),this.getAttribute("dest",""));
|
||||
// Apply required cleanup
|
||||
switch(this.getAttribute("cleanup","semantic")) {
|
||||
case "none":
|
||||
@@ -132,7 +133,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
DiffTextWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.source || changedAttributes.dest || changedAttributes.cleanup) {
|
||||
if(changedAttributes.source || changedAttributes.dest || changedAttributes.cleanup || changedAttributes.editcost) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -177,7 +177,7 @@ DroppableWidget.prototype.execute = function() {
|
||||
DroppableWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-droppable");
|
||||
this.domNode.className = classes.join(" ");
|
||||
this.domNode.className = classes.join(" ").trim();
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -130,7 +130,7 @@ EventWidget.prototype.execute = function() {
|
||||
EventWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-eventcatcher");
|
||||
this.domNode.className = classes.join(" ");
|
||||
this.domNode.className = classes.join(" ").trim();
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -110,7 +110,7 @@ KeyboardWidget.prototype.execute = function() {
|
||||
KeyboardWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-keyboard");
|
||||
this.domNode.className = classes.join(" ");
|
||||
this.domNode.className = classes.join(" ").trim();
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -46,7 +46,7 @@ LetWidget.prototype.computeAttributes = function() {
|
||||
self = this;
|
||||
this.currentValueFor = Object.create(null);
|
||||
$tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(this.parseTreeNode),function(attribute) {
|
||||
var value = self.computeAttribute(attribute),
|
||||
var value = self.computeAttribute(attribute,{asList: true}),
|
||||
name = attribute.name;
|
||||
// Now that it's prepped, we're allowed to look this variable up
|
||||
// when defining later variables
|
||||
@@ -56,7 +56,7 @@ LetWidget.prototype.computeAttributes = function() {
|
||||
});
|
||||
// Run through again, setting variables and looking for differences
|
||||
$tw.utils.each(this.currentValueFor,function(value,name) {
|
||||
if (self.attributes[name] !== value) {
|
||||
if(!$tw.utils.isArrayEqual(self.attributes[name],value)) {
|
||||
self.attributes[name] = value;
|
||||
self.setVariable(name,value);
|
||||
changedAttributes[name] = true;
|
||||
@@ -69,8 +69,10 @@ LetWidget.prototype.getVariableInfo = function(name,options) {
|
||||
// Special handling: If this variable exists in this very $let, we can
|
||||
// use it, but only if it's been staged.
|
||||
if ($tw.utils.hop(this.currentValueFor,name)) {
|
||||
var value = this.currentValueFor[name];
|
||||
return {
|
||||
text: this.currentValueFor[name]
|
||||
text: value[0] || "",
|
||||
resultList: value
|
||||
};
|
||||
}
|
||||
return Widget.prototype.getVariableInfo.call(this,name,options);
|
||||
|
||||
@@ -89,13 +89,33 @@ RevealWidget.prototype.positionPopup = function(domNode) {
|
||||
top = this.popup.top + this.popup.height;
|
||||
break;
|
||||
}
|
||||
// if requested, clamp the popup so that it will always be fully inside its parent (the first upstream element with position:relative), as long as the popup is smaller than its parent
|
||||
// if position is absolute then clamping is done to the canvas boundary, since there is no "parent"
|
||||
if(this.clampToParent !== "none") {
|
||||
if(this.popup.absolute) {
|
||||
var parentWidth = window.innerWidth,
|
||||
parentHeight = window.innerHeight;
|
||||
} else {
|
||||
var parentWidth = domNode.offsetParent.offsetWidth,
|
||||
parentHeight = domNode.offsetParent.offsetHeight;
|
||||
}
|
||||
var right = left + domNode.offsetWidth,
|
||||
bottom = top + domNode.offsetHeight;
|
||||
if((this.clampToParent === "both" || this.clampToParent === "right") && right > parentWidth) {
|
||||
left = parentWidth - domNode.offsetWidth;
|
||||
}
|
||||
if((this.clampToParent === "both" || this.clampToParent === "bottom") && bottom > parentHeight) {
|
||||
top = parentHeight - domNode.offsetHeight;
|
||||
}
|
||||
// clamping on left and top sides is taken care of by positionAllowNegative
|
||||
}
|
||||
if(!this.positionAllowNegative) {
|
||||
left = Math.max(0,left);
|
||||
top = Math.max(0,top);
|
||||
}
|
||||
if (this.popup.absolute) {
|
||||
if(this.popup.absolute) {
|
||||
// Traverse the offsetParent chain and correct the offset to make it relative to the parent node.
|
||||
for (var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
|
||||
for(var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
|
||||
left -= offsetParentDomNode.offsetLeft;
|
||||
top -= offsetParentDomNode.offsetTop;
|
||||
}
|
||||
@@ -123,6 +143,7 @@ RevealWidget.prototype.execute = function() {
|
||||
this.openAnimation = this.animate === "no" ? undefined : "open";
|
||||
this.closeAnimation = this.animate === "no" ? undefined : "close";
|
||||
this.updatePopupPosition = this.getAttribute("updatePopupPosition","no") === "yes";
|
||||
this.clampToParent = this.getAttribute("clamp","none");
|
||||
// Compute the title of the state tiddler and read it
|
||||
this.stateTiddlerTitle = this.state;
|
||||
this.stateTitle = this.getAttribute("stateTitle");
|
||||
@@ -141,7 +162,7 @@ Read the state tiddler
|
||||
RevealWidget.prototype.readState = function() {
|
||||
// Read the information from the state tiddler
|
||||
var state,
|
||||
defaultState = this["default"];
|
||||
defaultState = this["default"];
|
||||
if(this.stateTitle) {
|
||||
var stateTitleTiddler = this.wiki.getTiddler(this.stateTitle);
|
||||
if(this.stateField) {
|
||||
@@ -203,7 +224,7 @@ RevealWidget.prototype.readPopupState = function(state) {
|
||||
RevealWidget.prototype.assignDomNodeClasses = function() {
|
||||
var classes = this.getAttribute("class","").split(" ");
|
||||
classes.push("tc-reveal");
|
||||
this.domNode.className = classes.join(" ");
|
||||
this.domNode.className = classes.join(" ").trim();
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -252,18 +273,18 @@ RevealWidget.prototype.updateState = function() {
|
||||
this.renderChildren(domNode,null);
|
||||
}
|
||||
// Animate our DOM node
|
||||
if(!domNode.isTiddlyWikiFakeDom && this.type === "popup" && this.isOpen) {
|
||||
this.positionPopup(domNode);
|
||||
$tw.utils.addClass(domNode,"tc-popup"); // Make sure that clicks don't dismiss popups within the revealed content
|
||||
|
||||
}
|
||||
if(this.isOpen) {
|
||||
domNode.removeAttribute("hidden");
|
||||
$tw.anim.perform(this.openAnimation,domNode);
|
||||
// Position popup after making it visible to ensure correct dimensions
|
||||
if(!domNode.isTiddlyWikiFakeDom && this.type === "popup") {
|
||||
this.positionPopup(domNode);
|
||||
$tw.utils.addClass(domNode,"tc-popup"); // Make sure that clicks don't dismiss popups within the revealed content
|
||||
}
|
||||
$tw.anim.perform(this.openAnimation,domNode);
|
||||
} else {
|
||||
$tw.anim.perform(this.closeAnimation,domNode,{callback: function() {
|
||||
//make sure that the state hasn't changed during the close animation
|
||||
self.readState()
|
||||
self.readState();
|
||||
if(!self.isOpen) {
|
||||
domNode.setAttribute("hidden","true");
|
||||
}
|
||||
|
||||
@@ -9,215 +9,376 @@ View widget
|
||||
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var ViewWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
const Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
==========================================
|
||||
ViewHandler Base Class
|
||||
==========================================
|
||||
Base class for all view format handlers.
|
||||
Provides common functionality and defines the interface for subclasses.
|
||||
*/
|
||||
ViewWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
ViewWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
if(this.text) {
|
||||
var textNode = this.document.createTextNode(this.text);
|
||||
parent.insertBefore(textNode,nextSibling);
|
||||
this.domNodes.push(textNode);
|
||||
} else {
|
||||
this.makeChildWidgets();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
class ViewHandler {
|
||||
constructor(widget) {
|
||||
this.widget = widget;
|
||||
this.wiki = widget.wiki;
|
||||
this.document = widget.document;
|
||||
this.viewTitle = widget.viewTitle;
|
||||
this.viewField = widget.viewField;
|
||||
this.viewIndex = widget.viewIndex;
|
||||
this.viewSubtiddler = widget.viewSubtiddler;
|
||||
this.viewTemplate = widget.viewTemplate;
|
||||
this.viewMode = widget.viewMode;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
ViewWidget.prototype.execute = function() {
|
||||
// Get parameters from our attributes
|
||||
this.viewTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
||||
this.viewSubtiddler = this.getAttribute("subtiddler");
|
||||
this.viewField = this.getAttribute("field","text");
|
||||
this.viewIndex = this.getAttribute("index");
|
||||
this.viewFormat = this.getAttribute("format","text");
|
||||
this.viewTemplate = this.getAttribute("template","");
|
||||
this.viewMode = this.getAttribute("mode","block");
|
||||
switch(this.viewFormat) {
|
||||
case "htmlwikified":
|
||||
this.text = this.getValueAsHtmlWikified(this.viewMode);
|
||||
break;
|
||||
case "plainwikified":
|
||||
this.text = this.getValueAsPlainWikified(this.viewMode);
|
||||
break;
|
||||
case "htmlencodedplainwikified":
|
||||
this.text = this.getValueAsHtmlEncodedPlainWikified(this.viewMode);
|
||||
break;
|
||||
case "htmlencoded":
|
||||
this.text = this.getValueAsHtmlEncoded();
|
||||
break;
|
||||
case "htmltextencoded":
|
||||
this.text = this.getValueAsHtmlTextEncoded();
|
||||
break;
|
||||
case "urlencoded":
|
||||
this.text = this.getValueAsUrlEncoded();
|
||||
break;
|
||||
case "doubleurlencoded":
|
||||
this.text = this.getValueAsDoubleUrlEncoded();
|
||||
break;
|
||||
case "date":
|
||||
this.text = this.getValueAsDate(this.viewTemplate);
|
||||
break;
|
||||
case "relativedate":
|
||||
this.text = this.getValueAsRelativeDate();
|
||||
break;
|
||||
case "stripcomments":
|
||||
this.text = this.getValueAsStrippedComments();
|
||||
break;
|
||||
case "jsencoded":
|
||||
this.text = this.getValueAsJsEncoded();
|
||||
break;
|
||||
default: // "text"
|
||||
this.text = this.getValueAsText();
|
||||
break;
|
||||
render(parent, nextSibling) {
|
||||
this.text = this.getValue();
|
||||
this.createTextNode(parent, nextSibling);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
The various formatter functions are baked into this widget for the moment. Eventually they will be replaced by macro functions
|
||||
*/
|
||||
getValue() {
|
||||
return this.widget.getValueAsText();
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieve the value of the widget. Options are:
|
||||
asString: Optionally return the value as a string
|
||||
*/
|
||||
ViewWidget.prototype.getValue = function(options) {
|
||||
options = options || {};
|
||||
var value = options.asString ? "" : undefined;
|
||||
if(this.viewIndex) {
|
||||
value = this.wiki.extractTiddlerDataItem(this.viewTitle,this.viewIndex);
|
||||
} else {
|
||||
var tiddler;
|
||||
if(this.viewSubtiddler) {
|
||||
tiddler = this.wiki.getSubTiddler(this.viewTitle,this.viewSubtiddler);
|
||||
createTextNode(parent, nextSibling) {
|
||||
if(this.text) {
|
||||
const textNode = this.document.createTextNode(this.text);
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.widget.domNodes.push(textNode);
|
||||
} else {
|
||||
tiddler = this.wiki.getTiddler(this.viewTitle);
|
||||
}
|
||||
if(tiddler) {
|
||||
if(this.viewField === "text" && !this.viewSubtiddler) {
|
||||
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
|
||||
value = this.wiki.getTiddlerText(this.viewTitle);
|
||||
} else {
|
||||
if($tw.utils.hop(tiddler.fields,this.viewField)) {
|
||||
if(options.asString) {
|
||||
value = tiddler.getFieldString(this.viewField);
|
||||
} else {
|
||||
value = tiddler.fields[this.viewField];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(this.viewField === "title") {
|
||||
value = this.viewTitle;
|
||||
}
|
||||
this.widget.makeChildWidgets();
|
||||
this.widget.renderChildren(parent, nextSibling);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsText = function() {
|
||||
return this.getValue({asString: true});
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlWikified = function(mode) {
|
||||
return this.wiki.renderText("text/html","text/vnd.tiddlywiki",this.getValueAsText(),{
|
||||
parseAsInline: mode !== "block",
|
||||
parentWidget: this
|
||||
});
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsPlainWikified = function(mode) {
|
||||
return this.wiki.renderText("text/plain","text/vnd.tiddlywiki",this.getValueAsText(),{
|
||||
parseAsInline: mode !== "block",
|
||||
parentWidget: this
|
||||
});
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlEncodedPlainWikified = function(mode) {
|
||||
return $tw.utils.htmlEncode(this.wiki.renderText("text/plain","text/vnd.tiddlywiki",this.getValueAsText(),{
|
||||
parseAsInline: mode !== "block",
|
||||
parentWidget: this
|
||||
}));
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlEncoded = function() {
|
||||
return $tw.utils.htmlEncode(this.getValueAsText());
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsHtmlTextEncoded = function() {
|
||||
return $tw.utils.htmlTextEncode(this.getValueAsText());
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsUrlEncoded = function() {
|
||||
return $tw.utils.encodeURIComponentExtended(this.getValueAsText());
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsDoubleUrlEncoded = function() {
|
||||
return $tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(this.getValueAsText()));
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsDate = function(format) {
|
||||
format = format || "YYYY MM DD 0hh:0mm";
|
||||
var value = $tw.utils.parseDate(this.getValue());
|
||||
if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") {
|
||||
return $tw.utils.formatDateString(value,format);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsRelativeDate = function(format) {
|
||||
var value = $tw.utils.parseDate(this.getValue());
|
||||
if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") {
|
||||
return $tw.utils.getRelativeDate((new Date()) - (new Date(value))).description;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsStrippedComments = function() {
|
||||
var lines = this.getValueAsText().split("\n"),
|
||||
out = [];
|
||||
for(var line=0; line<lines.length; line++) {
|
||||
var text = lines[line];
|
||||
if(!/^\s*\/\/#/.test(text)) {
|
||||
out.push(text);
|
||||
}
|
||||
}
|
||||
return out.join("\n");
|
||||
};
|
||||
|
||||
ViewWidget.prototype.getValueAsJsEncoded = function() {
|
||||
return $tw.utils.stringify(this.getValueAsText());
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
ViewWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.template || changedAttributes.format || changedTiddlers[this.viewTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
refresh() {
|
||||
var self = this;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Wikified View Handler Base
|
||||
==========================================
|
||||
Base class for wikified view handlers
|
||||
*/
|
||||
class WikifiedViewHandler extends ViewHandler {
|
||||
constructor(widget) {
|
||||
super(widget);
|
||||
this.fakeWidget = null;
|
||||
this.fakeNode = null;
|
||||
}
|
||||
|
||||
render(parent, nextSibling) {
|
||||
this.createFakeWidget();
|
||||
this.text = this.getValue();
|
||||
this.createWikifiedTextNode(parent, nextSibling);
|
||||
}
|
||||
|
||||
createFakeWidget() {
|
||||
this.fakeWidget = this.wiki.makeTranscludeWidget(this.viewTitle, {
|
||||
document: $tw.fakeDocument,
|
||||
field: this.viewField,
|
||||
index: this.viewIndex,
|
||||
parseAsInline: this.viewMode !== "block",
|
||||
mode: this.viewMode === "block" ? "block" : "inline",
|
||||
parentWidget: this.widget,
|
||||
subTiddler: this.viewSubtiddler
|
||||
});
|
||||
this.fakeNode = $tw.fakeDocument.createElement("div");
|
||||
this.fakeWidget.makeChildWidgets();
|
||||
this.fakeWidget.render(this.fakeNode, null);
|
||||
}
|
||||
|
||||
createWikifiedTextNode(parent, nextSibling) {
|
||||
const textNode = this.document.createTextNode(this.text || "");
|
||||
parent.insertBefore(textNode, nextSibling);
|
||||
this.widget.domNodes.push(textNode);
|
||||
}
|
||||
|
||||
refresh(changedTiddlers) {
|
||||
const refreshed = this.fakeWidget.refresh(changedTiddlers);
|
||||
if(refreshed) {
|
||||
const newText = this.getValue();
|
||||
if(newText !== this.text) {
|
||||
this.widget.domNodes[0].textContent = newText;
|
||||
this.text = newText;
|
||||
}
|
||||
}
|
||||
return refreshed;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Text View Handler
|
||||
==========================================
|
||||
Default handler for plain text display
|
||||
*/
|
||||
class TextViewHandler extends ViewHandler {}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
HTML Wikified View Handler
|
||||
==========================================
|
||||
Handler for wikified HTML content
|
||||
*/
|
||||
class HTMLWikifiedViewHandler extends WikifiedViewHandler {
|
||||
getValue() {
|
||||
return this.fakeNode.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Plain Wikified View Handler
|
||||
==========================================
|
||||
Handler for wikified plain text content
|
||||
*/
|
||||
class PlainWikifiedViewHandler extends WikifiedViewHandler {
|
||||
getValue() {
|
||||
return this.fakeNode.textContent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
HTML Encoded Plain Wikified View Handler
|
||||
==========================================
|
||||
Handler for HTML-encoded wikified plain text
|
||||
*/
|
||||
class HTMLEncodedPlainWikifiedViewHandler extends WikifiedViewHandler {
|
||||
getValue() {
|
||||
return $tw.utils.htmlEncode(this.fakeNode.textContent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
HTML Encoded View Handler
|
||||
==========================================
|
||||
Handler for HTML-encoded text
|
||||
*/
|
||||
class HTMLEncodedViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
return $tw.utils.htmlEncode(this.widget.getValueAsText());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
HTML Text Encoded View Handler
|
||||
==========================================
|
||||
Handler for HTML text-encoded content
|
||||
*/
|
||||
class HTMLTextEncodedViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
return $tw.utils.htmlTextEncode(this.widget.getValueAsText());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
URL Encoded View Handler
|
||||
==========================================
|
||||
Handler for URL-encoded text
|
||||
*/
|
||||
class URLEncodedViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
return $tw.utils.encodeURIComponentExtended(this.widget.getValueAsText());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Double URL Encoded View Handler
|
||||
==========================================
|
||||
Handler for double URL-encoded text
|
||||
*/
|
||||
class DoubleURLEncodedViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
const text = this.widget.getValueAsText();
|
||||
return $tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(text));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Date View Handler
|
||||
==========================================
|
||||
Handler for date formatting
|
||||
*/
|
||||
class DateViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
const format = this.viewTemplate || "YYYY MM DD 0hh:0mm";
|
||||
const rawValue = this.widget.getValueAsText();
|
||||
const value = $tw.utils.parseDate(rawValue);
|
||||
if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") {
|
||||
return $tw.utils.formatDateString(value, format);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Relative Date View Handler
|
||||
==========================================
|
||||
Handler for relative date display
|
||||
*/
|
||||
class RelativeDateViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
const rawValue = this.widget.getValueAsText();
|
||||
const value = $tw.utils.parseDate(rawValue);
|
||||
if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") {
|
||||
return $tw.utils.getRelativeDate((new Date()) - (new Date(value))).description;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
Strip Comments View Handler
|
||||
==========================================
|
||||
Handler for stripping comments from text
|
||||
*/
|
||||
class StripCommentsViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
const lines = this.widget.getValueAsText().split("\n");
|
||||
const out = [];
|
||||
for(const text of lines) {
|
||||
if(!/^\s*\/\/#/.test(text)) {
|
||||
out.push(text);
|
||||
}
|
||||
}
|
||||
return out.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
JS Encoded View Handler
|
||||
==========================================
|
||||
Handler for JavaScript string encoding
|
||||
*/
|
||||
class JSEncodedViewHandler extends ViewHandler {
|
||||
getValue() {
|
||||
return $tw.utils.stringify(this.widget.getValueAsText());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==========================================
|
||||
ViewHandlerFactory
|
||||
==========================================
|
||||
Factory for creating appropriate view handlers based on format
|
||||
*/
|
||||
const ViewHandlerFactory = {
|
||||
handlers: {
|
||||
"text": TextViewHandler,
|
||||
"htmlwikified": HTMLWikifiedViewHandler,
|
||||
"plainwikified": PlainWikifiedViewHandler,
|
||||
"htmlencodedplainwikified": HTMLEncodedPlainWikifiedViewHandler,
|
||||
"htmlencoded": HTMLEncodedViewHandler,
|
||||
"htmltextencoded": HTMLTextEncodedViewHandler,
|
||||
"urlencoded": URLEncodedViewHandler,
|
||||
"doubleurlencoded": DoubleURLEncodedViewHandler,
|
||||
"date": DateViewHandler,
|
||||
"relativedate": RelativeDateViewHandler,
|
||||
"stripcomments": StripCommentsViewHandler,
|
||||
"jsencoded": JSEncodedViewHandler
|
||||
},
|
||||
|
||||
createHandler(format, widget) {
|
||||
const HandlerClass = this.handlers[format] || this.handlers["text"];
|
||||
return new HandlerClass(widget);
|
||||
},
|
||||
|
||||
registerHandler(format, handlerClass) {
|
||||
this.handlers[format] = handlerClass;
|
||||
}
|
||||
};
|
||||
|
||||
exports.view = ViewWidget;
|
||||
/*
|
||||
==========================================
|
||||
ViewWidget
|
||||
==========================================
|
||||
Main widget class that orchestrates view handlers
|
||||
*/
|
||||
class ViewWidget extends Widget {
|
||||
constructor(parseTreeNode, options) {
|
||||
super();
|
||||
this.initialise(parseTreeNode, options);
|
||||
}
|
||||
|
||||
render(parent, nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.viewHandler = ViewHandlerFactory.createHandler(this.viewFormat, this);
|
||||
this.viewHandler.render(parent, nextSibling);
|
||||
}
|
||||
|
||||
execute() {
|
||||
this.viewTitle = this.getAttribute("tiddler", this.getVariable("currentTiddler"));
|
||||
this.viewSubtiddler = this.getAttribute("subtiddler");
|
||||
this.viewField = this.getAttribute("field", "text");
|
||||
this.viewIndex = this.getAttribute("index");
|
||||
this.viewFormat = this.getAttribute("format", "text");
|
||||
this.viewTemplate = this.getAttribute("template", "");
|
||||
this.viewMode = this.getAttribute("mode", "block");
|
||||
}
|
||||
|
||||
getValue(options = {}) {
|
||||
let value = options.asString ? "" : undefined;
|
||||
if(this.viewIndex) {
|
||||
value = this.wiki.extractTiddlerDataItem(this.viewTitle, this.viewIndex);
|
||||
} else {
|
||||
let tiddler;
|
||||
if(this.viewSubtiddler) {
|
||||
tiddler = this.wiki.getSubTiddler(this.viewTitle, this.viewSubtiddler);
|
||||
} else {
|
||||
tiddler = this.wiki.getTiddler(this.viewTitle);
|
||||
}
|
||||
if(tiddler) {
|
||||
if(this.viewField === "text" && !this.viewSubtiddler) {
|
||||
value = this.wiki.getTiddlerText(this.viewTitle);
|
||||
} else {
|
||||
if($tw.utils.hop(tiddler.fields, this.viewField)) {
|
||||
if(options.asString) {
|
||||
value = tiddler.getFieldString(this.viewField);
|
||||
} else {
|
||||
value = tiddler.fields[this.viewField];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(this.viewField === "title") {
|
||||
value = this.viewTitle;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
getValueAsText() {
|
||||
return this.getValue({asString: true});
|
||||
}
|
||||
|
||||
refresh(changedTiddlers) {
|
||||
const changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index ||
|
||||
changedAttributes.template || changedAttributes.format || changedTiddlers[this.viewTitle]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.viewHandler.refresh(changedTiddlers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.view = ViewWidget;
|
||||
@@ -80,7 +80,7 @@ Widget.prototype.execute = function() {
|
||||
/*
|
||||
Set the value of a context variable
|
||||
name: name of the variable
|
||||
value: value of the variable
|
||||
value: value of the variable, can be a string or an array
|
||||
params: array of {name:, default:} for each parameter
|
||||
isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
|
||||
options includes:
|
||||
@@ -90,8 +90,10 @@ options includes:
|
||||
*/
|
||||
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
|
||||
options = options || {};
|
||||
var valueIsArray = $tw.utils.isArray(value);
|
||||
this.variables[name] = {
|
||||
value: value,
|
||||
value: valueIsArray ? (value[0] || "") : value,
|
||||
resultList: valueIsArray ? value : [value],
|
||||
params: params,
|
||||
isMacroDefinition: !!isMacroDefinition,
|
||||
isFunctionDefinition: !!options.isFunctionDefinition,
|
||||
@@ -114,7 +116,7 @@ allowSelfAssigned: if true, includes the current widget in the context chain ins
|
||||
|
||||
Returns an object with the following fields:
|
||||
|
||||
params: array of {name:,value:} or {value:} of parameters to be applied
|
||||
params: array of {name:,value:,multiValue:} of parameters to be applied (name is optional)
|
||||
text: text of variable, with parameters properly substituted
|
||||
resultList: result of variable evaluation as an array
|
||||
srcVariable: reference to the object defining the variable
|
||||
@@ -140,7 +142,9 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
||||
params = self.resolveVariableParameters(variable.params,actualParams);
|
||||
// Substitute any parameters specified in the definition
|
||||
$tw.utils.each(params,function(param) {
|
||||
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
|
||||
if("name" in param) {
|
||||
value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value);
|
||||
}
|
||||
});
|
||||
value = self.substituteVariableReferences(value,options);
|
||||
resultList = [value];
|
||||
@@ -154,13 +158,20 @@ Widget.prototype.getVariableInfo = function(name,options) {
|
||||
variables[param.name] = param["default"];
|
||||
}
|
||||
});
|
||||
// Parameters are an array of {value:} or {name:, value:} pairs
|
||||
// Parameters are an array of {name:, value:, multivalue:} pairs (name and multivalue are optional)
|
||||
$tw.utils.each(params,function(param) {
|
||||
variables[param.name] = param.value;
|
||||
if(param.multiValue) {
|
||||
variables[param.name] = param.multiValue;
|
||||
} else {
|
||||
variables[param.name] = param.value || "";
|
||||
}
|
||||
});
|
||||
resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source);
|
||||
value = resultList[0] || "";
|
||||
} else {
|
||||
if(variable.resultList) {
|
||||
resultList = variable.resultList;
|
||||
}
|
||||
params = variable.params;
|
||||
}
|
||||
return {
|
||||
@@ -192,22 +203,24 @@ Widget.prototype.getVariable = function(name,options) {
|
||||
/*
|
||||
Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects
|
||||
formalParams - Array of {name:,default:} (default value is optional)
|
||||
actualParams - Array of string values or {name:,value:} (name is optional)
|
||||
actualParams - Array of string values or {name:,value:,multiValue} (name and multiValue is optional)
|
||||
*/
|
||||
Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) {
|
||||
formalParams = formalParams || [];
|
||||
actualParams = actualParams || [];
|
||||
var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call
|
||||
paramInfo, paramValue,
|
||||
paramInfo, paramValue, paramMultiValue,
|
||||
results = [];
|
||||
// Step through each of the parameters in the macro definition
|
||||
for(var p=0; p<formalParams.length; p++) {
|
||||
// Check if we've got a macro call parameter with the same name
|
||||
paramInfo = formalParams[p];
|
||||
paramValue = undefined;
|
||||
paramMultiValue = undefined;
|
||||
for(var m=0; m<actualParams.length; m++) {
|
||||
if(typeof actualParams[m] !== "string" && actualParams[m].name === paramInfo.name) {
|
||||
paramValue = actualParams[m].value;
|
||||
paramMultiValue = actualParams[m].multiValue || [paramValue]
|
||||
}
|
||||
}
|
||||
// If not, use the next available anonymous macro call parameter
|
||||
@@ -217,11 +230,13 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams)
|
||||
if(paramValue === undefined && nextAnonParameter < actualParams.length) {
|
||||
var param = actualParams[nextAnonParameter++];
|
||||
paramValue = typeof param === "string" ? param : param.value;
|
||||
paramMultiValue = typeof param === "string" ? [param] : (param.multiValue || [paramValue]);
|
||||
}
|
||||
// If we've still not got a value, use the default, if any
|
||||
paramValue = paramValue || paramInfo["default"] || "";
|
||||
paramMultiValue = paramMultiValue || [paramValue];
|
||||
// Store the parameter name and value
|
||||
results.push({name: paramInfo.name, value: paramValue});
|
||||
results.push({name: paramInfo.name, value: paramValue, multiValue: paramMultiValue});
|
||||
}
|
||||
return results;
|
||||
};
|
||||
@@ -310,7 +325,7 @@ Widget.prototype.getStateQualifier = function(name) {
|
||||
};
|
||||
|
||||
/*
|
||||
Make a fake widget with specified variables, suitable for variable lookup in filters
|
||||
Make a fake widget with specified variables, suitable for variable lookup in filters. Each variable can be a string or an array of strings
|
||||
*/
|
||||
Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
||||
var self = this,
|
||||
@@ -318,7 +333,12 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
||||
return {
|
||||
getVariable: function(name,opts) {
|
||||
if($tw.utils.hop(variables,name)) {
|
||||
return variables[name];
|
||||
var value = variables[name];
|
||||
if($tw.utils.isArray(value)) {
|
||||
return value[0];
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
opts = opts || {};
|
||||
opts.variables = variables;
|
||||
@@ -327,9 +347,18 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) {
|
||||
},
|
||||
getVariableInfo: function(name,opts) {
|
||||
if($tw.utils.hop(variables,name)) {
|
||||
return {
|
||||
text: variables[name]
|
||||
};
|
||||
var value = variables[name];
|
||||
if($tw.utils.isArray(value)) {
|
||||
return {
|
||||
text: value[0],
|
||||
resultList: value
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
text: value,
|
||||
resultList: [value]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
opts = opts || {};
|
||||
opts.variables = $tw.utils.extend({},variables,opts.variables);
|
||||
@@ -366,20 +395,45 @@ Widget.prototype.computeAttributes = function(options) {
|
||||
return changedAttributes;
|
||||
};
|
||||
|
||||
Widget.prototype.computeAttribute = function(attribute) {
|
||||
/*
|
||||
Compute the value of a single attribute. Options include:
|
||||
asList: boolean if true returns results as an array instead of a single value
|
||||
*/
|
||||
Widget.prototype.computeAttribute = function(attribute,options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
value;
|
||||
if(attribute.type === "filtered") {
|
||||
value = this.wiki.filterTiddlers(attribute.filter,this)[0] || "";
|
||||
value = this.wiki.filterTiddlers(attribute.filter,this);
|
||||
if(!options.asList) {
|
||||
value = value[0] || "";
|
||||
}
|
||||
} else if(attribute.type === "indirect") {
|
||||
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || "";
|
||||
value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler"));
|
||||
if(value && options.asList) {
|
||||
value = [value];
|
||||
}
|
||||
} else if(attribute.type === "macro") {
|
||||
var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params});
|
||||
value = variableInfo.text;
|
||||
if(options.asList) {
|
||||
value = variableInfo.resultList;
|
||||
} else {
|
||||
value = variableInfo.text;
|
||||
}
|
||||
} else if(attribute.type === "substituted") {
|
||||
value = this.wiki.getSubstitutedText(attribute.rawValue,this) || "";
|
||||
if(options.asList) {
|
||||
value = [value];
|
||||
}
|
||||
} else { // String attribute
|
||||
value = attribute.value;
|
||||
if(options.asList) {
|
||||
if(value === undefined) {
|
||||
value = [];
|
||||
} else {
|
||||
value = [value];
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
@@ -200,7 +200,7 @@ exports.generateNewTitle = function(baseTitle,options) {
|
||||
c = (parseInt(options.startCount,10) > 0) ? parseInt(options.startCount,10) : 0,
|
||||
prefix = (typeof(options.prefix) === "string") ? options.prefix : " ";
|
||||
|
||||
if (template) {
|
||||
if(template) {
|
||||
// "count" is important to avoid an endless loop in while(...)!!
|
||||
template = (/\$count:?(\d+)?\$/i.test(template)) ? template : template + "$count$";
|
||||
// .formatTitleString() expects strings as input
|
||||
@@ -209,7 +209,7 @@ exports.generateNewTitle = function(baseTitle,options) {
|
||||
title = $tw.utils.formatTitleString(template,{"base":baseTitle,"separator":prefix,"counter":(++c)+""});
|
||||
}
|
||||
} else {
|
||||
if (c > 0) {
|
||||
if(c > 0) {
|
||||
title = baseTitle + prefix + c;
|
||||
}
|
||||
while(this.tiddlerExists(title) || this.isShadowTiddler(title) || this.findDraft(title)) {
|
||||
@@ -532,7 +532,7 @@ exports.getTiddlerLinks = function(title) {
|
||||
return self.extractLinks(parser.tree);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}).slice(0);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -551,8 +551,9 @@ exports.getTiddlerBacklinks = function(targetTitle) {
|
||||
backlinks.push(title);
|
||||
}
|
||||
});
|
||||
return backlinks;
|
||||
}
|
||||
return backlinks;
|
||||
return backlinks.slice(0);
|
||||
};
|
||||
|
||||
|
||||
@@ -578,7 +579,7 @@ exports.extractTranscludes = function(parseTreeRoot, title) {
|
||||
}
|
||||
}
|
||||
} else if(parseTreeNode.attributes.tiddler) {
|
||||
if (parseTreeNode.attributes.tiddler.type === "string") {
|
||||
if(parseTreeNode.attributes.tiddler.type === "string") {
|
||||
// Old transclude widget usage
|
||||
value = parseTreeNode.attributes.tiddler.value;
|
||||
}
|
||||
@@ -618,7 +619,7 @@ exports.getTiddlerTranscludes = function(title) {
|
||||
return self.extractTranscludes(parser.tree,title);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}).slice(0);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -630,9 +631,9 @@ exports.getTiddlerBacktranscludes = function(targetTitle) {
|
||||
backtranscludes = backIndexer && backIndexer.subIndexers.transclude.lookup(targetTitle);
|
||||
|
||||
if(!backtranscludes) {
|
||||
backtranscludes = [];
|
||||
return [];
|
||||
}
|
||||
return backtranscludes;
|
||||
return backtranscludes.slice(0);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -641,7 +642,7 @@ Return a hashmap of tiddler titles that are referenced but not defined. Each val
|
||||
exports.getMissingTitles = function() {
|
||||
var self = this,
|
||||
missing = [];
|
||||
// We should cache the missing tiddler list, even if we recreate it every time any tiddler is modified
|
||||
// We should cache the missing tiddler list, even if we recreate it every time any tiddler is modified
|
||||
this.forEachTiddler(function(title,tiddler) {
|
||||
var links = self.getTiddlerLinks(title);
|
||||
$tw.utils.each(links,function(link) {
|
||||
@@ -683,7 +684,7 @@ exports.getTiddlersWithTag = function(tag) {
|
||||
return self.sortByList(tagmap[tag],tag);
|
||||
});
|
||||
}
|
||||
return results;
|
||||
return results.slice(0);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -734,7 +735,7 @@ exports.findListingsOfTiddler = function(targetTitle,fieldName) {
|
||||
for(var i = 0; i < list.length; i++) {
|
||||
var listItem = list[i],
|
||||
listing = listings[listItem] || [];
|
||||
if (listing.indexOf(title) === -1) {
|
||||
if(listing.indexOf(title) === -1) {
|
||||
listing.push(title);
|
||||
}
|
||||
listings[listItem] = listing;
|
||||
@@ -743,7 +744,7 @@ exports.findListingsOfTiddler = function(targetTitle,fieldName) {
|
||||
});
|
||||
return listings;
|
||||
});
|
||||
return listings[targetTitle] || [];
|
||||
return (listings[targetTitle] || []).slice(0);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -782,7 +783,7 @@ exports.sortByList = function(array,listTitle) {
|
||||
}
|
||||
}
|
||||
// If a new position is specified, let's move it
|
||||
if (newPos !== -1) {
|
||||
if(newPos !== -1) {
|
||||
// get its current Pos, and make sure
|
||||
// sure that it's _actually_ in the list
|
||||
// and that it would _actually_ move
|
||||
@@ -1059,7 +1060,7 @@ Options include:
|
||||
exports.parseText = function(type,text,options) {
|
||||
text = text || "";
|
||||
options = options || {};
|
||||
var Parser = $tw.utils.getParser(type,options)
|
||||
var Parser = $tw.utils.getParser(type,options);
|
||||
// Return the parser instance
|
||||
return new Parser(type,text,{
|
||||
parseAsInline: options.parseAsInline,
|
||||
@@ -1078,11 +1079,11 @@ exports.parseTiddler = function(title,options) {
|
||||
tiddler = this.getTiddler(title),
|
||||
self = this;
|
||||
return tiddler ? this.getCacheForTiddler(title,cacheType,function() {
|
||||
if(tiddler.hasField("_canonical_uri")) {
|
||||
options._canonical_uri = tiddler.fields._canonical_uri;
|
||||
}
|
||||
return self.parseText(tiddler.fields.type,tiddler.fields.text,options);
|
||||
}) : null;
|
||||
if(tiddler.hasField("_canonical_uri")) {
|
||||
options._canonical_uri = tiddler.fields._canonical_uri;
|
||||
}
|
||||
return self.parseText(tiddler.fields.type,tiddler.fields.text,options);
|
||||
}) : null;
|
||||
};
|
||||
|
||||
exports.parseTextReference = function(title,field,index,options) {
|
||||
@@ -1138,7 +1139,7 @@ exports.getTextReferenceParserInfo = function(title,field,index,options) {
|
||||
parserInfo.parserType = null;
|
||||
}
|
||||
return parserInfo;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Parse a block of text of a specified MIME type
|
||||
@@ -1164,7 +1165,7 @@ exports.getSubstitutedText = function(text,widget,options) {
|
||||
});
|
||||
// Substitute any variable references with their values
|
||||
return output.replace(/\$\((.+?)\)\$/g, function(match,varname) {
|
||||
return widget.getVariable(varname,{defaultValue: ""})
|
||||
return widget.getVariable(varname,{defaultValue: ""});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1242,7 +1243,7 @@ exports.makeTranscludeWidget = function(title,options) {
|
||||
name: "recursionMarker",
|
||||
type: "string",
|
||||
value: options.recursionMarker || "yes"
|
||||
},
|
||||
},
|
||||
tiddler: {
|
||||
name: "tiddler",
|
||||
type: "string",
|
||||
@@ -1385,7 +1386,7 @@ exports.search = function(text,options) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Accumulate the array of fields to be searched or excluded from the search
|
||||
// Accumulate the array of fields to be searched or excluded from the search
|
||||
var fields = [];
|
||||
if(options.field) {
|
||||
if($tw.utils.isArray(options.field)) {
|
||||
@@ -1518,7 +1519,7 @@ exports.checkTiddlerText = function(title,targetText,options) {
|
||||
targetText = targetText.toLowerCase();
|
||||
}
|
||||
return text === targetText;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Execute an action string without an associated context widget
|
||||
@@ -1642,7 +1643,7 @@ exports.findDraft = function(targetTitle) {
|
||||
}
|
||||
});
|
||||
return draftTitle;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Check whether the specified draft tiddler has been modified.
|
||||
@@ -1669,7 +1670,7 @@ historyTitle: title of history tiddler (defaults to $:/HistoryList)
|
||||
exports.addToHistory = function(title,fromPageRect,historyTitle) {
|
||||
var story = new $tw.Story({wiki: this, historyTitle: historyTitle});
|
||||
story.addToHistory(title,fromPageRect);
|
||||
console.log("$tw.wiki.addToHistory() is deprecated since V5.1.23! Use the this.story.addToHistory() from the story-object!")
|
||||
console.log("$tw.wiki.addToHistory() is deprecated since V5.1.23! Use the this.story.addToHistory() from the story-object!");
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1682,7 +1683,7 @@ options: see story.js
|
||||
exports.addToStory = function(title,fromTitle,storyTitle,options) {
|
||||
var story = new $tw.Story({wiki: this, storyTitle: storyTitle});
|
||||
story.addToStory(title,fromTitle,options);
|
||||
console.log("$tw.wiki.addToStory() is deprecated since V5.1.23! Use the this.story.addToStory() from the story-object!")
|
||||
console.log("$tw.wiki.addToStory() is deprecated since V5.1.23! Use the this.story.addToStory() from the story-object!");
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
title: $:/core/templates/tiddlywiki5-external-js.html
|
||||
|
||||
<$set name="saveTiddlerAndShadowsFilter" filter="[subfilter<saveTiddlerFilter>] [subfilter<saveTiddlerFilter>plugintiddlers[]]">
|
||||
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] +[[$:/core]plugintiddlers[]]">
|
||||
<$set name="rawMarkupFilter" filter="[enlist<saveTiddlerAndShadowsFilter>] [[$:/core]plugintiddlers[]]">
|
||||
`<!doctype html>
|
||||
`{{$:/core/templates/MOTW.html}}`<html lang="`<$text text={{{ [{$:/language}get[name]] }}}/>`">
|
||||
<head>
|
||||
|
||||
@@ -25,6 +25,7 @@ title: $:/core/templates/tiddlywiki5.html
|
||||
`{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/core/wiki/rawmarkup]] ||$:/core/templates/plain-text-tiddler}}}
|
||||
{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkup]] ||$:/core/templates/plain-text-tiddler}}}
|
||||
{{{ [enlist<saveTiddlerAndShadowsFilter>tag[$:/tags/RawMarkupWikified]] ||$:/core/templates/raw-static-tiddler}}}`
|
||||
<!--~~ Style section start ~~-->
|
||||
</head>
|
||||
<body class="tc-body">
|
||||
<!--~~ Raw markup for the top of the body section ~~-->
|
||||
|
||||
@@ -49,6 +49,27 @@ title: $:/core/ui/ImportListing
|
||||
\end
|
||||
|
||||
\whitespace trim
|
||||
<$let importJson={{{ [{$:/Import}] }}}>
|
||||
<$let importTitles={{{ [<importJson>jsonindexes[tiddlers]] }}}>
|
||||
<$let importTypes={{{ [(importTitles)] :map[<importJson>jsonget[tiddlers],<currentTiddler>,[type]] }}}>
|
||||
<$let anyMatch={{{ [all[shadows+tiddlers]tag[$:/tags/ImportOptions]get[condition]] :map[(importTypes)subfilter<currentTiddler>] +[!is[blank]limit[1]] }}}>
|
||||
<%if [<anyMatch>!is[blank]] %>
|
||||
<div class="tc-import-option">
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ImportOptions]]" variable="importOption">
|
||||
<$let condition={{{ [<importOption>get[condition]] }}}>
|
||||
<$let hasMatch={{{ [(importTypes)subfilter<condition>limit[1]] }}}>
|
||||
<%if [<hasMatch>!is[blank]] %>
|
||||
<$transclude tiddler=<<importOption>>/>
|
||||
<%endif%>
|
||||
</$let>
|
||||
</$let>
|
||||
</$list>
|
||||
</div>
|
||||
<%endif%>
|
||||
</$let>
|
||||
</$let>
|
||||
</$let>
|
||||
</$let>
|
||||
<div class="tc-table-wrapper">
|
||||
<table class="tc-import-table">
|
||||
<tbody>
|
||||
|
||||
15
core/ui/RootStylesheet.tid
Normal file
15
core/ui/RootStylesheet.tid
Normal file
@@ -0,0 +1,15 @@
|
||||
title: $:/core/ui/RootStylesheet
|
||||
code-body: yes
|
||||
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
\whitespace trim
|
||||
<$let currentTiddler={{$:/language}} languageTitle={{!!name}}>
|
||||
<style type="text/css">
|
||||
<$view tiddler="$:/themes/tiddlywiki/vanilla/reset" format="text" mode="block"/>
|
||||
</style>
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Stylesheet]!is[draft]] -[[$:/themes/tiddlywiki/vanilla/reset]]">
|
||||
<style type="text/css">
|
||||
<$view format={{{ [<currentTiddler>tag[$:/tags/Stylesheet/Static]then[text]else[plainwikified]] }}} mode="block"/>
|
||||
</style>
|
||||
</$list>
|
||||
</$let>
|
||||
@@ -1,13 +1,12 @@
|
||||
created: 20240313100515958
|
||||
modified: 20241222104855231
|
||||
original-modified: 20240313103959789
|
||||
modified: 20251214111324789
|
||||
original-modified: 20251023154747366
|
||||
tags: Editions
|
||||
title: TiddlyWiki Docs PR Maker
|
||||
ja-title: TiddlyWikiドキュメントPRメーカー
|
||||
|
||||
[[@saqimtiaz|https://github.com/saqimtiaz/]]が作成した''~TiddlyWikiドキュメントPRメーカー''は、ドキュメントへの貢献と改善を支援するために設計された、tiddlywiki.comの特別エディションです。
|
||||
|
||||
https://saqimtiaz.github.io/tw5-docs-pr-maker/
|
||||
''~TiddlyWikiドキュメントPRメーカー''は、ドキュメントへの貢献と改善を支援するために設計された、tiddlywiki.comの特別エディションです。
|
||||
https://edit.tiddlywiki.com
|
||||
|
||||
ドキュメントに加えられたすべての変更は、GitHubに簡単に送信できます。 -- プルリクエストは自動的に作成されるため、エディションの名前は"PRメーカー"になります。
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
list: HelloThere [[Quick Start]] [[Find Out More]] [[TiddlyWiki on the Web]] [[Testimonials and Reviews]] GettingStarted Community
|
||||
tags: TableOfContents
|
||||
list-before:
|
||||
title: Welcome
|
||||
ja-title: ようこそ
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<<list-links filter:"[tag<currentTiddler>]" >>
|
||||
<$transclude $tiddler="HelloThere"/>
|
||||
|
||||
''詳細については、トピックを選択してください:''
|
||||
<div class="tc-table-of-contents"><<toc-selective-expandable "Welcome">></div>
|
||||
@@ -1,6 +1,6 @@
|
||||
created: 20231005205623086
|
||||
modified: 20241226114558500
|
||||
original-modified: 20241115193649399
|
||||
modified: 20251208113045167
|
||||
original-modified: 20250807100434131
|
||||
tags: About
|
||||
title: TiddlyWiki Archive
|
||||
ja-title: TiddlyWikiアーカイブ
|
||||
@@ -10,7 +10,7 @@ ja-title: TiddlyWikiアーカイブ
|
||||
5.1.10 5.1.11 5.1.12 5.1.13 5.1.14 5.1.15 5.1.16 5.1.17 5.1.18 5.1.19
|
||||
5.1.20 5.1.21 5.1.22 5.1.23
|
||||
5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7
|
||||
5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6
|
||||
5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8
|
||||
\end
|
||||
|
||||
TiddlyWikiの古いバージョンは[[アーカイブ|https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages/tree/master/archive]]で入手できます:
|
||||
|
||||
@@ -1,31 +1,13 @@
|
||||
created: 20150412191004348
|
||||
modified:
|
||||
original-modified: 20240925114810504
|
||||
modified: 20251208113458455
|
||||
original-modified: 20251022153208584
|
||||
tags: Community Reference
|
||||
title: Developers
|
||||
ja-title: 開発者
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
! [[GitHubの統計|https://github.com/TiddlyWiki/TiddlyWiki5/graphs/contributors]]
|
||||
|
||||
開発者がTiddlyWikiについて学び、開発について議論し、貢献するためのリソースがあります。
|
||||
|
||||
> [img[https://repobeats.axiom.co/api/embed/b92b1b363e2b5f26837ae573a60d39b4248b50a0.svg]]
|
||||
|
||||
* [[tiddlywiki.com/dev|https://tiddlywiki.com/dev]]は公式の開発者ドキュメントです
|
||||
|
||||
* [[GitHubでの開発|https://github.com/TiddlyWiki/TiddlyWiki5]]に参加する
|
||||
|
||||
* [[GitHubディスカッション|https://github.com/TiddlyWiki/TiddlyWiki5/discussions]]はQ&Aとオープンな形式のディスカッションです
|
||||
* [[GitHubイシュー|https://github.com/TiddlyWiki/TiddlyWiki5/issues]]は、バグレポートを作成し、具体的で実現可能な新しいアイデアを提案するためのものです
|
||||
|
||||
* 古い~TiddlyWikiDevGoogleグループは閉鎖され、[[TiddlyWikiトーク|https://talk.tiddlywiki.org/]]と[[GitHubディスカッション|https://github.com/TiddlyWiki/TiddlyWiki5/discussions]]に代わりました
|
||||
** 有用なアーカイブとして残っています: https://groups.google.com/group/TiddlyWikiDev
|
||||
*** 強化されたグループ検索機能は[[mail-archive.com|https://www.mail-archive.com/tiddlywikidev@googlegroups.com/]]で利用できます
|
||||
|
||||
* https://gitter.im/TiddlyWiki/public でチャットしてください(開発ルームは近日公開予定)
|
||||
|
||||
! Twitter
|
||||
|
||||
* 最新ニュースは[[Twitterで@TiddlyWiki|http://twitter.com/#!/TiddlyWiki]]をフォローしてください
|
||||
|
||||
* プロジェクトに貢献する方法についてのガイドラインについては、[[貢献|Contributing]]を参照してください
|
||||
|
||||
@@ -1,88 +1,14 @@
|
||||
created: 20140908114400000
|
||||
modified: 20241225112134741
|
||||
original-modified: 20241016125145988
|
||||
modified: 20251208115007037
|
||||
original-modified: 20251122174540932
|
||||
tags: About
|
||||
title: History of TiddlyWiki
|
||||
ja-title: TiddlyWikiの歴史
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
! ~TiddlyWikiの20年
|
||||
|
||||
~TiddlyWikiの20周年を祝うために、いくつかのライブストリームを開催しました。録画はここで視聴できます:
|
||||
|
||||
* 2024年9月19日 - https://youtube.com/live/z9slx92TyrU
|
||||
* 2024年9月20日 - https://youtube.com/live/puFdN-FgOjg
|
||||
* 2024年9月21日 - https://youtube.com/live/0SjsHvwjHGE
|
||||
* 2024年9月22日 - https://youtube.com/live/oD7Jtq2D4lg
|
||||
|
||||
GitHubでは、TiddlyWikiの貢献者に記念日の感想を[[聞いて|https://github.com/TiddlyWiki/TiddlyWiki5/discussions/7983]]お祝いしました。興味深く思慮深い回答がいくつか寄せられました。たとえば、[[@FND|https://github.com/FND]]からの回答は次の通りです:
|
||||
|
||||
> TiddlyWikiは、私のキャリアだけでなく、価値観にも計り知れないほどの永続的な影響を与えました。今日に至るまで、私はTiddlyWikiから学んだ[[基本的なコンセプト|https://prepitaph.org/articles/creative-privacy/]] - その多くは、他では忘れられたり無視されたりしています - を頻繁に参照しています。このバックグラウンドがあることで、技術的な複雑さを崇拝したり、テクノロジーの世界に人間が存在することを思い出したりする場合でも、この業界で仕事をする上で自分の方向性を保つことができます。
|
||||
|
||||
> TiddlyWikiとは、人々のことです。このコミュニティや、Jeremyがその周りに築いたグループと交流し、そこから学ぶことができたのは、私にとって計り知れない特権でした。また、この特権がまったくの偶然によって私に与えられたものだということを思い出すのにも役立ちます。この恩返しをしていきたいと思います。
|
||||
|
||||
~TiddlyWikiを特集した最近のポッドキャスト:
|
||||
|
||||
* 2016年のチェンジログ ポッドキャスト - https://changelog.com/podcast/196 ~TiddlyWikiの背景について議論しています
|
||||
* 2021年のFloss Weeklyの録画 - https://twit.tv/shows/floss-weekly/episodes/620
|
||||
|
||||
! TiddlyWikiの起源
|
||||
|
||||
遡ること1997年に、同僚が私に[[Ward Cunningham のオリジナル wiki|http://c2.com/cgi/wiki]]を紹介してくれました。これほど強力なものがわずか700行のPerlに収まることに感銘を受け、セキュリティとパーミッションの根本的な再考に魅了されました。他の多くの開発者と同様に、私もあらゆる機会を利用してさまざまなWikiを試し、仕事での使用法を模索しました
|
||||
|
||||
私にとってWikiの魅力は、印刷物中心のドキュメントやEメールの一般的なパラダイムを最終的に破壊する可能性があるという感覚でした
|
||||
|
||||
人々がWikiを使用する様子を数年間観察した結果、パワーユーザーは複数のブラウザタブで複数のWikiページを同時に開く機能を多用しており、ページの比較やレビュー、ページ間でのテキストのコピー、未読ページの一種のキューとして用いることが容易になっていることに気づきました
|
||||
|
||||
一度に複数のページを操作するこの機能がWikiをリファクタリングする機能の中心であると感じました。また、愛情を込めてリファクタリングされたWikiはより便利になる傾向があると一般に認められています。それでも、標準のWikiユーザーインターフェイスは常に、単一ページを一度に表示し操作すること専用にデザインされてきました
|
||||
|
||||
2004年4月にGMailを見たとき、すべての考えがまとまりました。GMailは、Ajaxを巧みに使用して個々のメールをスレッド化された会話に融合させました。
|
||||
|
||||
このアイデアをさらに探求するために、HTMLとJavaScriptを試し始めました。私にはどちらの経験もなく、以前の活動で、いくつかの静的ページと単純なASPサイトをまとめただけでした。これらのクライアント側テクノロジーについて理解するのは大変でした。他の皆さんと同じように、私もWebプログラミングの非互換性と矛盾がどれほど恐ろしいものであるかを知り、愕然としました
|
||||
|
||||
! TiddlyWikiのラウンチ
|
||||
|
||||
そうして、2004年9月に、私は原始的な[[TiddlyWikiの最初のバージョン|https://classic.tiddlywiki.com/firstversion.html]]をリリースしました。これは、アイデアを実証するための最小のもので、シンプルで自己完結型の静的な48KB HTMLファイルでした
|
||||
|
||||
TiddlyWikiの最初のバージョンをこの方法で作成することの欠点は、編集に使用するのが完全に非現実的になることでした。'変更を保存'をクリックすると、ファイルシステムにHTMLページを書き込むために、保存されるデータを示すウィンドウがポップアップ表示されるだけでした
|
||||
|
||||
初期のフィードバックの多くは、TiddlyWikiは優れているが、変更を適切に保存できればさらに便利になるというものでした。ブラウザで実行されているHTMLファイルがローカルファイルシステムに変更を保存することは不可能であることはわかっていると思っていたので、少しイライラしました
|
||||
|
||||
数か月以内に、TiddlyWikiがブラウザに変更を保存できるようにする実験的なFirefox拡張機能を目にしました。コードを調べてみると、ファイルシステムへの書き込みに使用されていたAPIは、`file://` URI経由でロードされている場合に限り、実際には通常のHTMLファイルで利用できることがわかりました
|
||||
|
||||
私はFirefoxコードをTiddlyWikiのコアに適合させ、すぐにInternet Explorerにも同様の機能を追加しました(MicrosoftがInternet Explorerとともに配布した古い[[ActiveX|https://en.wikipedia.org/wiki/ActiveX]]コントロールを利用しています)
|
||||
|
||||
! TiddlyWikiの成長
|
||||
|
||||
TiddlyWikiの成長における大きなマイルストーンは、Nathan Bowersによる"GTDTiddlyWiki"の作成でした。彼はバニラのTiddlyWiki製品を採用し、一般的なGetting Things Done方法論を使用してタスクをトラックするという特定のアプリケーションに適応させました。GTDTiddlyWikiはすぐに人気を博し、[[LifeHacker|https://lifehacker.com/]]などのWebサイトで熱狂的に歓迎されました
|
||||
|
||||
その後数年間にわたって、TiddlyWikiの人気は高まり続け、新しい機能や能力も獲得しました。1年以内に、私はTiddlyWikiでオーダーメイドの開発作業を行うことで自活できるようになり、特にWikiパイオニアである[[SocialText|https://en.wikipedia.org/wiki/Socialtext]]と協力して変更をオンラインサーバと同期する機能に取り組みました
|
||||
|
||||
! BTの獲得
|
||||
|
||||
2007年5月に、[[BT]]は私のコンサルティング会社である[[Osmosoft]]を買収しました。従業員が1人で、収益がほんの少ししかない会社を買収するというのは、異例の決断でした。[[Osmosoft]]は、コミュニティの将来を保証するために私がTiddlyWikiの知的財産をUnaMesaに譲渡したため、TiddlyWikiの知的財産さえ所有していませんでした
|
||||
|
||||
[[BT]]の動機は、コミュニティベースのエコシステムを理解することでした。私は"オープンソースイノベーション責任者"として組織に加わり、オープンソースガバナンスの責任を負い、オープンソースコミュニティへの参加方法に関するアドバイスと専門知識を提供しました
|
||||
|
||||
! [[Osmosoft]]とTiddlySpace
|
||||
|
||||
私はBTに[[Osmosoft]]という名前でチームを作りました。私たちの目的は、オープンソースのメリットを広め、他のチームが実際にそのメリットを実感できるよう支援することでした。また、Webの使用全般、特にWeb標準の使用を普及する必要があることもわかりました
|
||||
|
||||
私たちのアプローチは、話すことよりも見せることに重点を置くことでした。私たちはTiddlyWikiコミュニティと協力してエコシステムを拡張し、BT用の多数の内部システムを構築しました(TiddlyWikiに基づくものもあれば、そうでないものもあります)
|
||||
|
||||
TiddlyWikiコミュニティへの[[Osmosoft]]の主な貢献は、TiddlyWebとTiddlySpaceの作成でした。TiddlyWebは、Tiddlerのための堅牢なインターネット規模のサーバであり、TiddlerのTiddlyWikiビューを構成することもできました。TiddlySpaceは、TiddlyWebをより直接的に使用可能な形式にパッケージ化する試みでした
|
||||
|
||||
! BTを離れる
|
||||
|
||||
2011年の終わりまでに、私はBTという企業の枠外でTiddlyWikiの可能性を実現するのがより適切な立場にあると感じるようになりました。そうして、私は退職して独立した開発者として働き始め、主にTiddlyWiki5という形でTiddlyWikiを新しくリブートすることに取り組みました
|
||||
|
||||
! TiddlyWiki5の開発
|
||||
|
||||
私は2011年11月からTiddlyWikiの新しいリリースに取り組みました。プログラマーとして、すでに書いたものの"バージョン 2.0"に取り組むことは非常に魅力的な提案です。これは、要件が完全に理解され、目的の機能をサポートするために必要なアーキテクチャの進化に集中できることを意味します
|
||||
|
||||
! 将来
|
||||
|
||||
TiddlyWiki5がついに"ベータ"ステータスを脱した今、私の希望は、それが長く存続することです。HTML5とNode.jsの標準機能のみを使用しているため、今後何年にもわたって完全に動作しない理由はありません。私の目標は、少なくとも25年は続けることです
|
||||
|
||||
//Jeremy Ruston, 2014年9月20日//
|
||||
~TiddlyWikiの歴史をまとめています。このプロジェクトは現在進行中です。寄稿や思い出話など、ぜひお寄せください。
|
||||
|
||||
* [[TiddlyWiki物語|The Story of TiddlyWiki]] – [[@Jermolene]]によるTiddlyWikiの物語、その起源と進化に関する個人的な記録
|
||||
* https://github.com/TiddlyWiki/LaunchArchive – 2004年のTiddlyWiki立ち上げ時からのブログとツイート
|
||||
* [[TiddlyWiki記念日|TiddlyWiki Anniversaries]] – TiddlyWikiの主要な記念日のお祝いイベントの記録
|
||||
* [[フィルタシンタックスの歴史|Filter Syntax History]] – TiddlyWiki5におけるフィルタシンタックスの進化の簡単な歴史
|
||||
71
editions/ja-JP/tiddlers/about/The Story of TiddlyWiki.tid
Normal file
71
editions/ja-JP/tiddlers/about/The Story of TiddlyWiki.tid
Normal file
@@ -0,0 +1,71 @@
|
||||
title: The Story of TiddlyWiki
|
||||
ja-title: TiddlyWiki物語
|
||||
tags: [[History of TiddlyWiki]]
|
||||
modifier: Jeremy Ruston
|
||||
created: 20140908114400000
|
||||
modified: 20251215111120830
|
||||
original-modified: 20250730154331065
|
||||
|
||||
これは、2004年9月20日の最初のリリース以来のTiddlyWikiの起源と進化のストーリーについての個人的な記録です。
|
||||
|
||||
! TiddlyWikiの起源
|
||||
|
||||
遡ること1997年に、同僚が私に[[Ward Cunningham のオリジナル wiki|http://c2.com/cgi/wiki]]を紹介してくれました。これほど強力なものがわずか700行のPerlに収まることに感銘を受け、セキュリティとパーミッションの根本的な再考に魅了されました。他の多くの開発者と同様に、私もあらゆる機会を利用してさまざまなWikiを試し、仕事での使用法を模索しました
|
||||
|
||||
私にとってWikiの魅力は、印刷物中心のドキュメントやEメールの一般的なパラダイムを最終的に破壊する可能性があるという感覚でした
|
||||
|
||||
人々がWikiを使用する様子を数年間観察した結果、パワーユーザーは複数のブラウザタブで複数のWikiページを同時に開く機能を多用しており、ページの比較やレビュー、ページ間でのテキストのコピー、未読ページの一種のキューとして用いることが容易になっていることに気づきました
|
||||
|
||||
一度に複数のページを操作するこの機能がWikiをリファクタリングする機能の中心であると感じました。また、愛情を込めてリファクタリングされたWikiはより便利になる傾向があると一般に認められています。それでも、標準のWikiユーザーインターフェイスは常に、単一ページを一度に表示し操作すること専用にデザインされてきました
|
||||
|
||||
2004年4月にGMailを見たとき、すべての考えがまとまりました。GMailは、Ajaxを巧みに使用して個々のメールをスレッド化された会話に融合させました。
|
||||
|
||||
このアイデアをさらに探求するために、HTMLとJavaScriptを試し始めました。私にはどちらの経験もなく、以前の活動で、いくつかの静的ページと単純なASPサイトをまとめただけでした。これらのクライアント側テクノロジーについて理解するのは大変でした。他の皆さんと同じように、私もWebプログラミングの非互換性と矛盾がどれほど恐ろしいものであるかを知り、愕然としました
|
||||
|
||||
! TiddlyWikiのラウンチ
|
||||
|
||||
そうして、2004年9月に、私は原始的な[[TiddlyWikiの最初のバージョン|https://classic.tiddlywiki.com/firstversion.html]]をリリースしました。これは、アイデアを実証するための最小のもので、シンプルで自己完結型の静的な48KB HTMLファイルでした
|
||||
|
||||
TiddlyWikiの最初のバージョンをこの方法で作成することの欠点は、編集に使用するのが完全に非現実的になることでした。'変更を保存'をクリックすると、ファイルシステムにHTMLページを書き込むために、保存されるデータを示すウィンドウがポップアップ表示されるだけでした
|
||||
|
||||
初期のフィードバックの多くは、TiddlyWikiは優れているが、変更を適切に保存できればさらに便利になるというものでした。ブラウザで実行されているHTMLファイルがローカルファイルシステムに変更を保存することは不可能であることはわかっていると思っていたので、少しイライラしました
|
||||
|
||||
数か月以内に、TiddlyWikiがブラウザに変更を保存できるようにする実験的なFirefox拡張機能を目にしました。コードを調べてみると、ファイルシステムへの書き込みに使用されていたAPIは、`file://` URI経由でロードされている場合に限り、実際には通常のHTMLファイルで利用できることがわかりました
|
||||
|
||||
私はFirefoxコードをTiddlyWikiのコアに適合させ、すぐにInternet Explorerにも同様の機能を追加しました(MicrosoftがInternet Explorerとともに配布した古い[[ActiveX|https://en.wikipedia.org/wiki/ActiveX]]コントロールを利用しています)
|
||||
|
||||
! TiddlyWikiの成長
|
||||
|
||||
TiddlyWikiの成長における大きなマイルストーンは、Nathan Bowersによる"GTDTiddlyWiki"の作成でした。彼はバニラのTiddlyWiki製品を採用し、一般的なGetting Things Done方法論を使用してタスクをトラックするという特定のアプリケーションに適応させました。GTDTiddlyWikiはすぐに人気を博し、[[LifeHacker|https://lifehacker.com/]]などのWebサイトで熱狂的に歓迎されました
|
||||
|
||||
その後数年間にわたって、TiddlyWikiの人気は高まり続け、新しい機能や能力も獲得しました。1年以内に、私はTiddlyWikiでオーダーメイドの開発作業を行うことで自活できるようになり、特にWikiパイオニアである[[SocialText|https://en.wikipedia.org/wiki/Socialtext]]と協力して変更をオンラインサーバと同期する機能に取り組みました
|
||||
|
||||
! BTの獲得
|
||||
|
||||
2007年5月に、[[BT]]は私のコンサルティング会社である[[Osmosoft]]を買収しました。従業員が1人で、収益がほんの少ししかない会社を買収するというのは、異例の決断でした。[[Osmosoft]]は、コミュニティの将来を保証するために私がTiddlyWikiの知的財産をUnaMesaに譲渡したため、TiddlyWikiの知的財産さえ所有していませんでした
|
||||
|
||||
[[BT]]の動機は、コミュニティベースのエコシステムを理解することでした。私は"オープンソースイノベーション責任者"として組織に加わり、オープンソースガバナンスの責任を負い、オープンソースコミュニティへの参加方法に関するアドバイスと専門知識を提供しました
|
||||
|
||||
! [[Osmosoft]]とTiddlySpace
|
||||
|
||||
私はBTに[[Osmosoft]]という名前でチームを作りました。私たちの目的は、オープンソースのメリットを広め、他のチームが実際にそのメリットを実感できるよう支援することでした。また、Webの使用全般、特にWeb標準の使用を普及する必要があることもわかりました
|
||||
|
||||
私たちのアプローチは、話すことよりも見せることに重点を置くことでした。私たちはTiddlyWikiコミュニティと協力してエコシステムを拡張し、BT用の多数の内部システムを構築しました(TiddlyWikiに基づくものもあれば、そうでないものもあります)
|
||||
|
||||
TiddlyWikiコミュニティへの[[Osmosoft]]の主な貢献は、TiddlyWebとTiddlySpaceの作成でした。TiddlyWebは、Tiddlerのための堅牢なインターネット規模のサーバであり、TiddlerのTiddlyWikiビューを構成することもできました。TiddlySpaceは、TiddlyWebをより直接的に使用可能な形式にパッケージ化する試みでした
|
||||
|
||||
! BTを離れる
|
||||
|
||||
2011年の終わりまでに、私はBTという企業の枠外でTiddlyWikiの可能性を実現するのがより適切な立場にあると感じるようになりました。そうして、私は退職して独立した開発者として働き始め、主にTiddlyWiki5という形でTiddlyWikiを新しくリブートすることに取り組みました
|
||||
|
||||
! TiddlyWiki5の開発
|
||||
|
||||
私は2011年11月からTiddlyWikiの新しいリリースに取り組みました。プログラマーとして、すでに書いたものの"バージョン 2.0"に取り組むことは非常に魅力的な提案です。これは、要件が完全に理解され、目的の機能をサポートするために必要なアーキテクチャの進化に集中できることを意味します
|
||||
|
||||
! 将来
|
||||
|
||||
2014年、 TiddlyWiki5を初めてリリースした直後に、私は次のように書きました:
|
||||
|
||||
> TiddlyWiki5がついに"ベータ"ステータスを脱した今、私の希望は、それが長く存続することです。HTML5とNode.jsの標準機能のみを使用しているため、今後何年にもわたって完全に動作しない理由はありません。私の目標は、少なくとも25年は続けることです
|
||||
|
||||
これを書いている時点で、TiddlyWiki5は目標の44%を達成しています。コミュニティの皆様のご支援と熱意により、このプロジェクトは今後も発展し、進化していくと確信しています。
|
||||
33
editions/ja-JP/tiddlers/about/TiddlyWiki Anniversaries.tid
Normal file
33
editions/ja-JP/tiddlers/about/TiddlyWiki Anniversaries.tid
Normal file
@@ -0,0 +1,33 @@
|
||||
title: TiddlyWiki Anniversaries
|
||||
ja-title: TiddlyWiki記念日
|
||||
tags: [[History of TiddlyWiki]]
|
||||
created: 20250730154331065
|
||||
modified: 20251215105745893
|
||||
original-modified: 20250730154331065
|
||||
|
||||
! ~TiddlyWikiの20周年
|
||||
|
||||
~TiddlyWikiの20周年を祝うために、いくつかのライブストリームを開催しました。録画はここで視聴できます:
|
||||
|
||||
* 2024年9月19日 - https://youtube.com/live/z9slx92TyrU
|
||||
* 2024年9月20日 - https://youtube.com/live/puFdN-FgOjg
|
||||
* 2024年9月21日 - https://youtube.com/live/0SjsHvwjHGE
|
||||
* 2024年9月22日 - https://youtube.com/live/oD7Jtq2D4lg
|
||||
|
||||
GitHubでは、TiddlyWikiの貢献者に記念日の感想を[[聞いて|https://github.com/TiddlyWiki/TiddlyWiki5/discussions/7983]]お祝いしました。興味深く思慮深い回答がいくつか寄せられました。たとえば、[[@FND|https://github.com/FND]]からの回答は次の通りです:
|
||||
|
||||
> TiddlyWikiは、私のキャリアだけでなく、価値観にも計り知れないほどの永続的な影響を与えました。今日に至るまで、私はTiddlyWikiから学んだ[[基本的なコンセプト|https://prepitaph.org/articles/creative-privacy/]] - その多くは、他では忘れられたり無視されたりしています - を頻繁に参照しています。このバックグラウンドがあることで、技術的な複雑さを崇拝したり、テクノロジーの世界に人間が存在することを思い出したりする場合でも、この業界で仕事をする上で自分の方向性を保つことができます。
|
||||
|
||||
> TiddlyWikiとは、人々のことです。このコミュニティや、Jeremyがその周りに築いたグループと交流し、そこから学ぶことができたのは、私にとって計り知れない特権でした。また、この特権がまったくの偶然によって私に与えられたものだということを思い出すのにも役立ちます。この恩返しをしていきたいと思います。
|
||||
|
||||
~TiddlyWikiを特集した最近のポッドキャスト:
|
||||
|
||||
* 2016年のチェンジログ ポッドキャスト - https://changelog.com/podcast/196 ~TiddlyWikiの背景について議論しています
|
||||
* 2021年のFloss Weeklyの録画 - https://twit.tv/shows/floss-weekly/episodes/620
|
||||
|
||||
! ~TiddlyWikiの10周年
|
||||
|
||||
2014年9月20日に行われた、TiddlyWikiの10周年を祝うライブストリームはここで視聴できます:
|
||||
|
||||
https://www.youtube.com/watch?v=f_02ZV0J9NY
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
created: 20130909151600000
|
||||
modified: 20241002113817654
|
||||
original-modified: 20210322152237662
|
||||
modified: 20251210110939585
|
||||
original-modified: 20250909171928024
|
||||
tags: TableOfContents Welcome
|
||||
title: Community
|
||||
ja-title: コミュニティ
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<<.tip "最新の有益なリンクが[[コミュニティリンク収集|Community Links Aggregator]]に集められています。">>
|
||||
TiddlyWikiコミュニティは、TiddlyWikiをより良くするために協力し、お互いにベストを尽せるよう支援する熱心なユーザーと開発者のグループです。
|
||||
|
||||
すべての関連リンクがこれらのエントリに書かれると、メインのtiddlywiki.comサイトからは削除されます。
|
||||
|
||||
<<tabs "Forums Latest Tutorials [[Community Editions]] [[Community Plugins]] [[Community Themes]] [[Community Palettes]] [[Other Resources]] Examples Articles Meetups" "Latest">>
|
||||
<<tabs "[[TiddlyWiki Project]] [[TiddlyWiki People]] Forums" "TiddlyWiki Project">>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
color: #808
|
||||
created: 20241009150445080
|
||||
icon: $:/core/images/link
|
||||
list: TalkTiddlyWiki [[TiddlyWiki on YouTube]] [[TiddlyWiki on Reddit]] [[TiddlyWiki on Discord]] [[TiddlyWiki on GitHub]] [[TiddlyWiki on Mastodon]] [[TiddlyWiki on Twitter]] [[TiddlyWiki on Gitter]] [[TiddlyWiki on Open Collective]]
|
||||
modified: 20241010114936568
|
||||
original-modified: 20241009150453139
|
||||
list: TalkTiddlyWiki [[TiddlyWiki on YouTube]] [[TiddlyWiki on Reddit]] [[TiddlyWiki on Discord]] [[TiddlyWiki on GitHub]] [[TiddlyWiki on Mastodon]] [[TiddlyWiki on Open Collective]]
|
||||
modified: 20251210112417726
|
||||
original-modified: 20241115170824144
|
||||
tags: Welcome
|
||||
title: TiddlyWiki on the Web
|
||||
ja-title: ウェブ上のTiddlyWiki
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
created: 20140610213500000
|
||||
modified: 20241209112247651
|
||||
original-modified: 20241030132047048
|
||||
modified: 20251212111127961
|
||||
original-modified: 20250217154855572
|
||||
tags: Concepts Features
|
||||
title: ExternalImages
|
||||
ja-title: 外部画像
|
||||
@@ -25,22 +25,28 @@ TiddlyWikiの外部画像は、画像データすべてを埋め込むのでは
|
||||
参照される外部画像を含む''images''フォルダーを伴うWikiの静的HTMLファイルバージョンを作成するには、次の手順を使用します:
|
||||
|
||||
# 通常の方法でTiddlyWikiFoldersに画像Tiddlerを作成します
|
||||
# 画像を別ファイルとして保存します (慣例により、''images''という名前のサブフォルダに保存します)
|
||||
# 画像を別ファイルとして保存します (慣例により、''/images''という名前のサブフォルダに保存します)
|
||||
# ''_canonical_uri''フィールドを追加して画像Tiddlerを外部化します
|
||||
# メインのHTMLファイルを保存します
|
||||
|
||||
画像ファイルは外部化する前に保存する必要があることに注意してください。外部化すると、Wikiストアのメモリ内コピーの''text''フィールドが破壊され、保存の試みが失敗します。
|
||||
|
||||
たとえば、''tw5.com'' Wikiの''externalimages''ビルドターゲットを参照してください:
|
||||
!! 外部画像の構成
|
||||
|
||||
たとえば、''tiddlywiki.info''ファイル内に、''externalimages''ビルドターゲットを作成します:
|
||||
|
||||
```
|
||||
--save [is[image]] images
|
||||
--setfield [is[image]] _canonical_uri $:/core/templates/canonical-uri-external-image text/plain
|
||||
--setfield [is[image]] text "" text/plain
|
||||
--render $:/core/save/all externalimages.html text/plain
|
||||
"build": {
|
||||
"externalimages": [
|
||||
--save [is[image]] images
|
||||
--setfield [is[image]] _canonical_uri $:/core/templates/canonical-uri-external-image text/plain
|
||||
--setfield [is[image]] text "" text/plain
|
||||
--render $:/core/save/all externalimages.html text/plain
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
!! 個別の画像ファイルを保存する
|
||||
!! 画像Tiddlerの外部化
|
||||
|
||||
次の`--save`コマンド ([[Saveコマンド|SaveCommand]]を参照)を使用すると、Wikiの画像を''images''サブフォルダーに保存できます:
|
||||
|
||||
@@ -48,8 +54,6 @@ TiddlyWikiの外部画像は、画像データすべてを埋め込むのでは
|
||||
--save [is[image]] images
|
||||
```
|
||||
|
||||
!! 画像Tiddlerの外部化
|
||||
|
||||
2つの`--setfield`コマンドが使用されています: 最初のコマンドは、''_canonical_uri''フィールドをTiddlerのタイトルから派生したURIに設定し、2番目のコマンドはtextフィールドをクリアします。
|
||||
|
||||
```
|
||||
@@ -67,6 +71,20 @@ TiddlyWikiの外部画像は、画像データすべてを埋め込むのでは
|
||||
|
||||
これらの操作により、Wikiストア内のTiddlerが変更されるため、後続のコマンド操作に影響する可能性があることに注意してください。
|
||||
|
||||
!! 外部画像をビルドするためのNode.jsコマンド
|
||||
|
||||
次のコマンドは、`myWiki/output`フォルダー内に外部画像を作成します。
|
||||
|
||||
```
|
||||
tiddlywiki myWiki --build externalimages
|
||||
```
|
||||
|
||||
Windowsでは、次のコマンドを実行すると、[[tw5.comエディション|https://github.com/TiddlyWiki/TiddlyWiki5/blob/master/editions/tw5.com/tiddlywiki.info#L26]]の一部である外部画像が作成されます。ファイルは出力ディレクトリに作成されます。
|
||||
|
||||
```
|
||||
tiddlywiki .\editions\tw5.com --build index
|
||||
```
|
||||
|
||||
! 外部画像の使用について
|
||||
|
||||
URIフィールドを別の画像を指すように変更しない限り、ブラウザで外部画像を直接編集することはできません。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
created: 20150124182127000
|
||||
modified: 20250222110925130
|
||||
original-modified: 20230710074414361
|
||||
modified: 20251213102731461
|
||||
original-modified: 20250731101041336
|
||||
tags: [[Filter Run]]
|
||||
title: Filter Step
|
||||
ja-title: フィルタステップ
|
||||
@@ -13,10 +13,10 @@ type: text/vnd.tiddlywiki
|
||||
<$railroad text="""
|
||||
\start none
|
||||
\end none
|
||||
[:"!"]
|
||||
["!"]
|
||||
( / "省略の場合のデフォルト: title" /|:
|
||||
( - | :[[<"オペレータ">|"Filter Operators"]] )
|
||||
{ [:":" [[<"サフィックス">|"Filter Operators"]] ] } )
|
||||
( :[[<"オペレータ">|"Filter Operators"]] )
|
||||
[ {":" [: [[<"サフィックス">|"Filter Operators"]] ] }] )
|
||||
{ [[<"パラメータ">|"Filter Parameter"]] + "," }
|
||||
"""/>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
color: #880
|
||||
created: 20241009150347613
|
||||
icon: $:/core/images/help
|
||||
modified: 20241010114029686
|
||||
original-modified: 20241009150430229
|
||||
modified: 20251213103718012
|
||||
original-modified: 20241115170824144
|
||||
tags: Welcome
|
||||
title: Find Out More
|
||||
ja-title: さらに詳しく
|
||||
|
||||
@@ -1,19 +1,41 @@
|
||||
created: 20130822170200000
|
||||
icon: $:/core/icon
|
||||
list: [[A Gentle Guide to TiddlyWiki]] [[Discover TiddlyWiki]] [[Some of the things you can do with TiddlyWiki]] [[Ten reasons to switch to TiddlyWiki]] Examples [[What happened to the original TiddlyWiki?]]
|
||||
modified: 20241010112232871
|
||||
original-modified: 20241009150333146
|
||||
modified: 20251213104120189
|
||||
original-modified: 20250807084952911
|
||||
tags: Welcome
|
||||
title: HelloThere
|
||||
ja-title: こんにちは
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
!!.tc-hero-heading ''TiddlyWikiへようこそ。 TiddlyWikiは複雑な情報を[[収集|Creating and editing tiddlers]]し、[[整理|Structuring TiddlyWiki]]し、[[共有|Sharing your tiddlers with others]]するためのユニークな[[非線形|Philosophy of Tiddlers]]ノートブックです。''
|
||||
<h2
|
||||
style="
|
||||
background: red;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 0.5em;
|
||||
box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5;
|
||||
background-image: linear-gradient(90deg, rgb(34, 132, 224), rgb(95, 174, 248), rgb(34, 132, 224));
|
||||
">
|
||||
メモを有効活用する
|
||||
</h2>
|
||||
|
||||
<h2
|
||||
class="tc-hero-heading"
|
||||
style="
|
||||
text-align: center;
|
||||
">
|
||||
TiddlyWikiへようこそ。 TiddlyWikiは複雑な情報を[[収集|Creating and editing tiddlers]]し、[[整理|Structuring TiddlyWiki]]し、[[共有|Sharing your tiddlers with others]]するためのユニークな[[非線形|Philosophy of Tiddlers]]ノートブックです
|
||||
</h2>
|
||||
|
||||
[[ToDo リスト|TaskManagementExample]]を管理したり、[[エッセイや小説|"TiddlyWiki for Scholars" by Alberto Molina]]を練ったり、結婚式を準備したりするために使えます。頭をよぎるすべての考えを記録し、柔軟で応答性の高いWebサイトを構築できます。
|
||||
|
||||
* ~TiddlyWikiを使用すると、データの保存場所を選択でき、今日とったメモを今後数十年も[[使用できること|Future Proof]]が保証されます。
|
||||
* ~TiddlyWikiはデータとコードを単一のHTMLファイルに保存し、インストールや外部依存を必要とせず、Webブラウザのみで動作します
|
||||
|
||||
* ~TiddlyWikiは、新しい機能を追加する多くのプラグインにより、無限にカスタマイズ、拡張可能です。
|
||||
* ~TiddlyWikiを使用すると、データの保存場所を選択でき、今日とったメモを今後数十年も[[使用できること|Future Proof]]が保証されます
|
||||
|
||||
* ~TiddlyWikiは、大規模なユーザーコミュニティの一部である開発者集団の製品です。
|
||||
* ~TiddlyWikiは、新しい機能を追加する多くのプラグインにより、無限にカスタマイズ、拡張可能です
|
||||
|
||||
* ~TiddlyWikiは、大規模なユーザーコミュニティの一部である開発者集団の製品です
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
color: #088
|
||||
icon: $:/core/images/star-filled
|
||||
modified: 20241010114508408
|
||||
original-modified: 20241001141521924
|
||||
modified: 20251214104051278
|
||||
original-modified: 20241115170824144
|
||||
tags: Welcome
|
||||
title: Testimonials and Reviews
|
||||
ja-title: 利用者の声とレビュー
|
||||
|
||||
@@ -2,8 +2,8 @@ color: #cc9
|
||||
created: 20241009163451663
|
||||
icon: $:/core/images/tip
|
||||
list: GettingStarted [[Getting Started Video]] [[Find Out More]] [[TiddlyWiki on the Web]] [[Testimonials and Reviews]]
|
||||
modified: 20241010112536263
|
||||
original-modified: 20241009163521037
|
||||
modified: 20251214103541254
|
||||
original-modified: 20241115170824144
|
||||
tags: Welcome
|
||||
title: Quick Start
|
||||
ja-title: クイックスタート
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
created: 20240907042443909
|
||||
modified: 20241116104031988
|
||||
original-modified: 20240907042629405
|
||||
modified: 20251214104453831
|
||||
original-modified: 20241120225606237
|
||||
tags: [[Hidden Settings]]
|
||||
title: Hidden Setting: Default Tiddler Colour
|
||||
ja-title: 隠し設定: TIddlerのデフォルト色
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
デフォルトのTiddler色は、$:/config/DefaultTiddlerColourというTiddlerを作成し、CSSの色の値を含むことによって指定できます。
|
||||
デフォルトのtag-tiddler色は、$:/config/DefaultTiddlerColourというTiddlerを作成し、CSSの色の値を含むことによって指定できます。詳細については、[[Tiddlerカラーカスケード|Tiddler Colour Cascade]]を参照してください
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
created: 20201228143125000
|
||||
modified: 20241222103744408
|
||||
original-modified: 20201228143125000
|
||||
tags: OfficialPlugins
|
||||
modified: 20251214105341696
|
||||
original-modified: 20250302051857380
|
||||
tags: OfficialPlugins [[Plugin Editions]]
|
||||
title: Share Plugin
|
||||
ja-title: シェアプラグイン
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -11,4 +11,6 @@ type: text/vnd.tiddlywiki
|
||||
この実験的なプラグインは、URL経由でTiddlerを共有するためのツールを提供します。これには以下が含まれます:
|
||||
|
||||
* 起動時にブラウザのロケーションハッシュからTiddlerのグループをロードする機能
|
||||
* TiddlerのグループからURLを作成するためのウィザードとテンプレート
|
||||
* TiddlerのグループからURLを作成するためのウィザードとテンプレート
|
||||
|
||||
デモは[ext[https://tiddlywiki.com/share|share]]を参照してください
|
||||
@@ -1,12 +1,12 @@
|
||||
created: 20130825160900000
|
||||
modified: 20241110103519303
|
||||
original-modified: 20160610083350724
|
||||
modified: 20251214105948181
|
||||
original-modified: 20241106165307259
|
||||
tags: Features [[Working with TiddlyWiki]]
|
||||
title: Encryption
|
||||
ja-title: 暗号化
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
TiddlyWiki5を単一のHTMLファイルとして使用すると、[[Stanford JavaScript Crypto Library]]を使用してコンテンツを暗号化できます。
|
||||
TiddlyWiki5を単一のHTMLファイルとして使用すると、[[Stanford JavaScript Crypto Library]]を使用してCCMモードのAES 128ビット暗号化でコンテンツを暗号化できます。
|
||||
|
||||
# サイドバーの''ツール''タブに切り替えて、南京錠アイコンのボタンを探します
|
||||
# ボタンに<<.icon $:/core/images/unlocked-padlock>> ''パスワードの設定''と表示されている場合、現在のウィキは暗号化されていません。ボタンをクリックすると、以降の保存を暗号化するために使用されるパスワードの入力を求められます
|
||||
|
||||
@@ -4,7 +4,8 @@ created: 20160216191710789
|
||||
delivery: Protocol
|
||||
description: SharePointなどの製品で利用できる標準Webプロトコル
|
||||
method: save
|
||||
modified: 20220615155048712
|
||||
modified: 20251214110658333
|
||||
original-modified: 20220615155048712
|
||||
tags: Android Chrome Firefox [[Internet Explorer]] Linux Mac Opera PHP Safari Saving Windows iOS Edge
|
||||
title: Saving via WebDAV
|
||||
ja-title: WebDAV経由の保存
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
created: 20140908163900000
|
||||
modified: 20241016111248572
|
||||
original-modified: 20201228143412000
|
||||
modified: 20251214112408533
|
||||
original-modified: 20230803052125981
|
||||
tags: Learning
|
||||
title: Sharing your tiddlers with others
|
||||
ja-title: Tiddlerを他の人と共有する
|
||||
@@ -12,7 +12,7 @@ type: text/vnd.tiddlywiki
|
||||
* ~TiddlyWikiをオンラインで公開し、リンクを取得して他の人に送信、または、伝言できます:
|
||||
** ~TiddlyWikiファイル全体のWebアドレスへのリンク
|
||||
** 特定のTiddlerへの[[パーマリンク|PermaLinks]](<<.icon $:/core/images/permalink-button>>)
|
||||
** 現在開いているすべてのTiddlerの[[パーマビュー|PermaViews]](<<.icon $:/core/images/permaview-button>>)リンク
|
||||
** 現在開いているすべてのTiddlerの[[パーマビュー|PermaLinks]](<<.icon $:/core/images/permaview-button>>)リンク
|
||||
* [[TiddlyWikiへのDropboxリンクを共有|Sharing a TiddlyWiki on Dropbox]]できます
|
||||
* テキスト、静的HTML、カンマ区切り値(つまり、スプレッドシート互換)などのさまざまな形式で[[Tiddlerをエクスポート|How to export tiddlers]](<<.icon $:/core/images/export-button>>)できます
|
||||
* また、~TiddlyWikiを他の人がアクセスできるようにするだけで、例えば、オンラインで公開して、そこから[[Tiddlerをインポート|Importing Tiddlers]]できるようにするだけで、Tiddlerを共有できます
|
||||
|
||||
@@ -10,4 +10,4 @@ HelloThere
|
||||
[[TiddlyWiki on the Web]]
|
||||
[[Testimonials and Reviews]]
|
||||
GettingStarted
|
||||
Community
|
||||
Community
|
||||
@@ -27,4 +27,4 @@ Block forced inline
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<div class=" tc-reveal"><p>Block</p></div><div class=" tc-reveal"><p>Block forced block</p></div><span class=" tc-reveal"><p>Block forced inline</p></span><p><span class=" tc-reveal">Inline</span><div class=" tc-reveal">Inline forced block</div><span class=" tc-reveal">Inline forced inline</span></p>
|
||||
<div class="tc-reveal"><p>Block</p></div><div class="tc-reveal"><p>Block forced block</p></div><span class="tc-reveal"><p>Block forced inline</p></span><p><span class="tc-reveal">Inline</span><div class="tc-reveal">Inline forced block</div><span class="tc-reveal">Inline forced inline</span></p>
|
||||
@@ -0,0 +1,12 @@
|
||||
title: LetFilterRunPrefix/ResultList
|
||||
description: Using the "let" filter run prefix to store result lists, not just single values
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [all[tiddlers]] :let[[varname]] [(varname)sort[]join[,]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>$:/core,ExpectedResult,Output</p>
|
||||
@@ -0,0 +1,12 @@
|
||||
title: LetFilterRunPrefix/ResultListUnnamedVariable
|
||||
description: Using the "let" filter run prefix to store result lists, not just single values
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [all[tiddlers]] :let[all[]tag[nothing]] [(varname)sort[]join[,]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p></p>
|
||||
@@ -0,0 +1,12 @@
|
||||
title: LetFilterRunPrefix/ShortcutSyntax
|
||||
description: Simple usage of "let" filter run prefix
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [[magpie]] =>varname [<varname>] +[join[-]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>magpie</p>
|
||||
@@ -0,0 +1,12 @@
|
||||
title: LetFilterRunPrefix/Simple
|
||||
description: Simple usage of "let" filter run prefix
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [[magpie]] :let[[varname]] [<varname>] +[join[-]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>magpie</p>
|
||||
@@ -0,0 +1,18 @@
|
||||
title: MultiValuedVariables/Function
|
||||
description: Multi-valued functions
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function myfunc() [all[tiddlers]sort[]]
|
||||
|
||||
<$let varname=<<myfunc>>>
|
||||
<$text text={{{ [(varname)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
@@ -0,0 +1,12 @@
|
||||
title: MultiValuedVariables/MissingVariable
|
||||
description: Using multivalued operands with a missing variable
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$text text={{{ [(varname)] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p></p>
|
||||
@@ -0,0 +1,18 @@
|
||||
title: MultiValuedVariables/NegatedTitle
|
||||
description: Multi-valued operands
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$let
|
||||
exclude={{{ $:/core Output }}}
|
||||
>
|
||||
<$text text={{{ [all[tiddlers]!title(exclude)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
ExpectedResult
|
||||
</p>
|
||||
@@ -0,0 +1,18 @@
|
||||
title: MultiValuedVariables/Operands
|
||||
description: Multi-valued operands
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function myfunc() [all[tiddlers]sort[]]
|
||||
|
||||
<$let varname=<<myfunc>>>
|
||||
<$text text={{{ [(varname)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
@@ -0,0 +1,21 @@
|
||||
title: MultiValuedVariables/Parameters
|
||||
description: Multi-valued function parameters
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function myfunc(input) [(input)sort[]]
|
||||
|
||||
<$let
|
||||
input={{{ [all[tiddlers]] }}}
|
||||
output={{{ [function[myfunc],(input)] }}}
|
||||
>
|
||||
<$text text={{{ [(output)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
@@ -0,0 +1,21 @@
|
||||
title: MultiValuedVariables/ParametersShortcut
|
||||
description: Multi-valued function parameters using shortcut syntax
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function my.func(input) [(input)sort[]]
|
||||
|
||||
<$let
|
||||
input={{{ [all[tiddlers]] }}}
|
||||
output={{{ [my.func(input)] }}}
|
||||
>
|
||||
<$text text={{{ [(output)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
@@ -0,0 +1,17 @@
|
||||
title: MultiValuedVariables/Simple
|
||||
description: Simple usage of multivalued assignments with the "let" widget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
<$let varname={{{ [all[tiddlers]sort[]] }}}
|
||||
varname2=<<varname>>>
|
||||
<$text text={{{ [(varname2)] +[join[-]] }}}/>
|
||||
</$let>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>
|
||||
$:/core-ExpectedResult-Output
|
||||
</p>
|
||||
12
editions/test/tiddlers/tests/data/serialize/VoidElements.tid
Normal file
12
editions/test/tiddlers/tests/data/serialize/VoidElements.tid
Normal file
@@ -0,0 +1,12 @@
|
||||
tags: $:/tags/wikitext-serialize-test-spec
|
||||
title: Serialize/VoidElements
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
This tests void elements like <br/> and <br> (without `/`).
|
||||
|
||||
Line one<br>Line two
|
||||
Line three<br/>Line four
|
||||
|
||||
<hr>
|
||||
|
||||
Images are also void: <img src="test.png">
|
||||
@@ -2,4 +2,4 @@ title: expected-test-tabs-horizontal-a
|
||||
type: text/html
|
||||
description: Horizontal tabs test - This is the expected HTML output from a test in test-wikitext-tabs-macro.js
|
||||
|
||||
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal"><p>Text tab 2</p></div><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal" hidden="true"></div></div></div></p>
|
||||
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class="tc-reveal" hidden="true"></div><div class="tc-reveal"><p>Text tab 2</p></div><div class="tc-reveal" hidden="true"></div><div class="tc-reveal" hidden="true"></div></div></div></p>
|
||||
@@ -2,4 +2,4 @@ title: expected-test-tabs-horizontal-all
|
||||
type: text/html
|
||||
description: Horizontal tabs with all parameters active. This is the expected HTML output from a test in test-wikitext-tabs-macro.js
|
||||
|
||||
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">desc</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal"><h2 class="">TabTwo</h2><p><p>Text tab 2</p></p></div><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal" hidden="true"></div></div></div></p>
|
||||
<p><div class="tc-tab-set "><div class="tc-tab-buttons "><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">desc</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider "></div><div class="tc-tab-content "><div class="tc-reveal" hidden="true"></div><div class="tc-reveal"><h2 class="">TabTwo</h2><p><p>Text tab 2</p></p></div><div class="tc-reveal" hidden="true"></div><div class="tc-reveal" hidden="true"></div></div></div></p>
|
||||
@@ -2,4 +2,4 @@ title: expected-test-tabs-vertical
|
||||
type: text/html
|
||||
description: Vertical tabs test -- This is the expected HTML output from the test in test-wikitext-tabs-macro.js
|
||||
|
||||
<p><div class="tc-tab-set tc-vertical"><div class="tc-tab-buttons tc-vertical"><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider tc-vertical"></div><div class="tc-tab-content tc-vertical"><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal"><p>Text tab 2</p></div><div class=" tc-reveal" hidden="true"></div><div class=" tc-reveal" hidden="true"></div></div></div></p>
|
||||
<p><div class="tc-tab-set tc-vertical"><div class="tc-tab-buttons tc-vertical"><button class="" data-tab-title="TabOne" role="switch">t 1</button><button aria-checked="true" class=" tc-tab-selected" data-tab-title="TabTwo" role="switch">t 2</button><button class="" data-tab-title="TabThree" role="switch">t 3</button><button class="" data-tab-title="TabFour" role="switch">TabFour</button></div><div class="tc-tab-divider tc-vertical"></div><div class="tc-tab-content tc-vertical"><div class="tc-reveal" hidden="true"></div><div class="tc-reveal"><p>Text tab 2</p></div><div class="tc-reveal" hidden="true"></div><div class="tc-reveal" hidden="true"></div></div></div></p>
|
||||
@@ -143,6 +143,33 @@ describe("json filter tests", function() {
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:json[notjson]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
});
|
||||
|
||||
it("should support the jsondelete operator", function() {
|
||||
// Delete top-level object property
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[a]]")).toEqual(['{"b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[b]]")).toEqual(['{"a":"one","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[c]]")).toEqual(['{"a":"one","b":"","d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
// Delete nested object property
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[e]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"f":["five","six",true,false,null]}}']);
|
||||
// Delete array element
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[0]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[1]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five",true,false,null]}}']);
|
||||
// Delete using negative array index
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[-1]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[-2]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[f],[-5]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["six",true,false,null]}}']);
|
||||
// Delete from array
|
||||
expect(wiki.filterTiddlers("[{Second}jsondelete[0]]")).toEqual(['["deux","trois",["quatre","cinq"]]']);
|
||||
expect(wiki.filterTiddlers("[{Second}jsondelete[1]]")).toEqual(['["une","trois",["quatre","cinq"]]']);
|
||||
expect(wiki.filterTiddlers("[{Second}jsondelete[-1]]")).toEqual(['["une","deux","trois"]']);
|
||||
// Attempting to delete non-existent property should return unchanged
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[missing-property]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[d],[missing]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
// Attempting to delete root should return unchanged
|
||||
expect(wiki.filterTiddlers("[{First}jsondelete[]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
// Non-JSON input should return unchanged
|
||||
expect(wiki.filterTiddlers("[{Third}jsondelete[a]]")).toEqual(["This is not JSON"]);
|
||||
});
|
||||
|
||||
it("should support the format:json operator", function() {
|
||||
expect(wiki.filterTiddlers("[{First}format:json[]]")).toEqual(["{\"a\":\"one\",\"b\":\"\",\"c\":1.618,\"d\":{\"e\":\"four\",\"f\":[\"five\",\"six\",true,false,null]}}"]);
|
||||
expect(wiki.filterTiddlers("[{First}format:json[4]]")).toEqual(["{\n \"a\": \"one\",\n \"b\": \"\",\n \"c\": 1.618,\n \"d\": {\n \"e\": \"four\",\n \"f\": [\n \"five\",\n \"six\",\n true,\n false,\n null\n ]\n }\n}"]);
|
||||
|
||||
@@ -60,10 +60,10 @@ describe("Widget module", function() {
|
||||
childNode = childNode.children[0];
|
||||
}
|
||||
|
||||
expect(childNode.getVariableInfo("macro",{allowSelfAssigned:true}).params).toEqual([{name:"a",value:"aa"}]);
|
||||
expect(childNode.getVariableInfo("macro",{allowSelfAssigned:true}).params).toEqual([{name:"a",value:"aa",multiValue:["aa"]}]);
|
||||
|
||||
// function params
|
||||
expect(childNode.getVariableInfo("fn", {allowSelfAssigned:true}).params).toEqual([{name:"f",value:"ff"}]);
|
||||
expect(childNode.getVariableInfo("fn", {allowSelfAssigned:true}).params).toEqual([{name:"f",value:"ff",multiValue:["ff"]}]);
|
||||
// functions have a text and a value
|
||||
expect(childNode.getVariableInfo("x", {allowSelfAssigned:true}).text).toBe("fff");
|
||||
expect(childNode.getVariableInfo("x", {allowSelfAssigned:true}).srcVariable.value).toBe("[<fn>]");
|
||||
@@ -73,7 +73,7 @@ describe("Widget module", function() {
|
||||
expect(childNode.getVariableInfo("$my.widget", {allowSelfAssigned:true}).params).toEqual([{name:"w",default:"ww"}]);
|
||||
|
||||
// no params expected
|
||||
expect(childNode.getVariableInfo("abc", {allowSelfAssigned:true})).toEqual({text:"def"});
|
||||
expect(childNode.getVariableInfo("abc", {allowSelfAssigned:true})).toEqual({text:"def", resultList: [ "def" ]});
|
||||
|
||||
// debugger; Find code in browser
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
created: 20140908114400000
|
||||
modified: 20250730154331065
|
||||
modified: 20251122174540932
|
||||
tags: About
|
||||
title: History of TiddlyWiki
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Gathering the history of ~TiddlyWiki. This is an ongoing project. Contributions and reminiscences are welcome.
|
||||
|
||||
Here is a brief history of TiddlyWiki, its origins and its evolution since it was first released on 20th September 2004. Contributions and reminiscences are welcome.
|
||||
|
||||
* [[The Story of TiddlyWiki]] – a personal account of the story of TiddlyWiki, its origins and evolution
|
||||
* [[TiddlyWiki Anniversaries]] – relive the celebrations of TiddlyWiki's major anniversaries
|
||||
* [[Filter Syntax History]] – gives a brief history of the evolution of the filter syntax in TiddlyWiki5
|
||||
* [[The Story of TiddlyWiki]] – A personal account from [[@Jermolene]] of the story of TiddlyWiki, its origins and evolution
|
||||
* https://github.com/TiddlyWiki/LaunchArchive – Blog posts and tweets from TiddlyWiki's launch in 2004
|
||||
* [[TiddlyWiki Anniversaries]] – Relive the celebrations of TiddlyWiki's major anniversaries
|
||||
* [[Filter Syntax History]] – A brief history of the evolution of the filter syntax in TiddlyWiki5
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
title: TiddlyWiki Newsletter Team
|
||||
modified: 20241009171916728
|
||||
tags: Community
|
||||
|
||||
The ~TiddlyWiki Newsletter is produced by a small team of volunteers. We would love to have your help if you want to get involved.
|
||||
|
||||
! Team
|
||||
|
||||
The newsletter team currently consists of:
|
||||
|
||||
* [[@jeremyruston|https://talk.tiddlywiki.org/u/jeremyruston]]
|
||||
* [[@CodaCoder|https://talk.tiddlywiki.org/u/codacoder]]
|
||||
* [[@Springer|https://talk.tiddlywiki.org/u/springer]]
|
||||
|
||||
! Audience
|
||||
|
||||
The newsletter is intended for TiddlyWiki end users who do not track all the discussions on https://talk.tiddlywiki.org/.
|
||||
|
||||
Coverage of developer topics such as JavaScript and intricate wikitext should be handled thoughtfully to avoid alienating the core audience of end users.
|
||||
|
||||
Subscribing to the newsletter should give people confidence that they will not miss any important developments.
|
||||
|
||||
! Process
|
||||
|
||||
The process is:
|
||||
|
||||
# Determine which discussion forum threads should be included
|
||||
# Decide whether to link to the thread itself or to link to the subject of the thread
|
||||
# Write a 30-70 word introduction
|
||||
# Optionally, choose or make an image/screenshot to illustrate the story
|
||||
|
||||
These steps are described in more detail below.
|
||||
|
||||
!! Criteria for Inclusion
|
||||
|
||||
The criteria for inclusion are necessarily loose. Editorial judgement is required to decide whether an item is sufficiently interesting to a broad enough audience.
|
||||
|
||||
Important categories of threads that should be considered:
|
||||
|
||||
* All announcements of new releases of TiddlyWiki and TiddlyDesktop
|
||||
* Community news and developments
|
||||
* New plugins
|
||||
* Updates to widely used plugins
|
||||
* Showcases of interesting TiddlyWiki's in the wild
|
||||
|
||||
!! Linking
|
||||
|
||||
In most cases, news items should link to the opening post in the corresponding thread. There might be situations where it makes more sense to link to the item concerned which will be listed here.
|
||||
|
||||
!! Writing News Items
|
||||
|
||||
Items would be a 30-70 word introduction with a link, and optionally an image (the newsletter looks much more inviting with some images included).
|
||||
|
||||
!! Images
|
||||
|
||||
Well chosen images can be informative and add visual interest to the newsletter as a whole. Some points to consider when choosing an image:
|
||||
|
||||
* Images that work best of all are those that trigger an emotional response by including a human face. It is fairly unlikely that a ~TiddlyWiki news story would have a reason to be illustrated by a picture of a smiling baby, but we should strive to do so if we can
|
||||
* If using a screenshot, remember that the image will be displayed fairly small in the newsletter so it is better to crop screenshots to show a smaller area of interest rather than the entire browser window
|
||||
* Avoid using AI generated images
|
||||
@@ -1,10 +1,10 @@
|
||||
title: TiddlyWiki Newsletter
|
||||
modified: 20241009171916728
|
||||
modified: 20251219090240895
|
||||
|
||||
The ~TiddlyWiki Newsletter is a fortnightly overview of news, announcements and discussions within the ~TiddlyWiki community. It has been on hiatus for the last few months, but we are hoping to revive it soon.
|
||||
The ~TiddlyWiki Newsletter is a collection of news, announcements and discussions from the ~TiddlyWiki community.
|
||||
|
||||
Subscribe to the ~TiddlyWiki Newsletter here:
|
||||
Subscribe here:
|
||||
|
||||
<iframe src="https://tiddlywiki.substack.com/embed" width="480" height="320" style="border:1px solid #EEE; background:white;" frameborder="0" scrolling="no"></iframe>
|
||||
!! https://tiddlywiki.substack.com/
|
||||
|
||||
The newsletter is produced by a small team of volunteers. We would welcome your help. See [[TiddlyWiki Newsletter Team]].
|
||||
The newsletter is produced by a small team of volunteers. We would welcome your help. See [[TiddlyWiki Newsletter Team]].
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
created: 20211117003509226
|
||||
modified: 20211117003657902
|
||||
modified: 20251119135921343
|
||||
tags: sampletag1 sampletag2 [[Widget Examples]]
|
||||
title: SampleTiddlerFirst
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
This is a test tiddler called SampleTiddlerFirst.
|
||||
This is a test tiddler called SampleTidlerFirst.
|
||||
59
editions/tw5.com/tiddlers/filters/examples/jsondelete.tid
Normal file
59
editions/tw5.com/tiddlers/filters/examples/jsondelete.tid
Normal file
@@ -0,0 +1,59 @@
|
||||
created: 20250115120000000
|
||||
modified: 20250115120000000
|
||||
tags: [[Operator Examples]] [[jsondelete Operator]]
|
||||
title: jsondelete Operator (Examples)
|
||||
|
||||
<$let object-a="""{
|
||||
"a": "one",
|
||||
"b": "",
|
||||
"c": "three",
|
||||
"d": {
|
||||
"e": "four",
|
||||
"f": [
|
||||
"five",
|
||||
"six",
|
||||
true,
|
||||
false,
|
||||
null
|
||||
],
|
||||
"g": {
|
||||
"x": "max",
|
||||
"y": "may",
|
||||
"z": "maize"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
object-b="""{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}"""
|
||||
array-a="""["une","deux","trois",["quatre","cinq"]]""">
|
||||
|
||||
The examples below assume the following JSON object is contained in the variable `object-a`:
|
||||
|
||||
<pre><<object-a>></pre>
|
||||
|
||||
<<.operator-example 1 "[<object-a>jsondelete[a]]" "Delete a top-level object property">>
|
||||
<<.operator-example 2 "[<object-a>jsondelete[d],[e]]" "Delete a nested object property">>
|
||||
<<.operator-example 3 "[<object-a>jsondelete[d],[f],[0]]" "Delete the first element from an array">>
|
||||
<<.operator-example 4 "[<object-a>jsondelete[d],[f],[-1]]" "Delete the last element from an array using negative index">>
|
||||
<<.operator-example 5 "[<object-a>jsondelete[d],[f],[-2]]" "Delete the second-to-last element from an array using negative index">>
|
||||
<<.operator-example 6 "[<object-a>jsondelete[d],[g],[x]]" "Delete a deeply nested object property">>
|
||||
<<.operator-example 7 "[<object-a>jsondelete[]]" "If no parameters are specified, the JSON object is returned unchanged">>
|
||||
<<.operator-example 8 "[<object-a>jsondelete[missing]]" "If the property does not exist, the JSON object is returned unchanged">>
|
||||
|
||||
The examples below assume the following JSON object is contained in the variable `object-b`:
|
||||
|
||||
<pre><<object-b>></pre>
|
||||
|
||||
<<.operator-example 9 "[<object-b>jsondelete[b]]" "Delete an empty string property">>
|
||||
<<.operator-example 10 "[<object-b>jsondelete[d],[f],[1]]" "Delete a middle element from an array">>
|
||||
|
||||
The examples below assume the following JSON array is contained in the variable `array-a`:
|
||||
|
||||
<pre><<array-a>></pre>
|
||||
|
||||
<<.operator-example 11 "[<array-a>jsondelete[0]]" "Delete the first element from a top-level array">>
|
||||
<<.operator-example 12 "[<array-a>jsondelete[-1]]" "Delete the last element from a top-level array using negative index">>
|
||||
<<.operator-example 13 "[<array-a>jsondelete[3],[0]]" "Delete an element from a nested array">>
|
||||
|
||||
<<.operator-example 14 "[<object-a>] [<object-b>] :and[jsondelete[a]]" "If the input consists of multiple JSON objects with matching properties, the property is deleted from all of them">>
|
||||
|
||||
54
editions/tw5.com/tiddlers/filters/jsondelete.tid
Normal file
54
editions/tw5.com/tiddlers/filters/jsondelete.tid
Normal file
@@ -0,0 +1,54 @@
|
||||
caption: jsondelete
|
||||
created: 20250115120000000
|
||||
modified: 20250115120000000
|
||||
op-input: a selection of JSON objects
|
||||
op-output: the JSON objects with the specified property deleted
|
||||
op-parameter: one or more indexes of the property to delete
|
||||
op-purpose: delete a property from JSON objects
|
||||
tags: [[Filter Operators]] [[JSON Operators]]
|
||||
title: jsondelete Operator
|
||||
|
||||
<<.from-version "5.4.0">> The <<.op jsondelete>> operator is used to delete a property from JSON strings. See [[JSON in TiddlyWiki]] for background. See also the following related operators:
|
||||
|
||||
* <<.olink jsonset>> to set values within JSON objects
|
||||
* <<.olink jsonget>> to retrieve the values of a property in JSON data
|
||||
* <<.olink jsontype>> to retrieve the type of a JSON value
|
||||
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
|
||||
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
|
||||
|
||||
Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
|
||||
|
||||
```
|
||||
{
|
||||
"a": "one",
|
||||
"b": "",
|
||||
"c": "three",
|
||||
"d": {
|
||||
"e": "four",
|
||||
"f": [
|
||||
"five",
|
||||
"six",
|
||||
true,
|
||||
false,
|
||||
null
|
||||
],
|
||||
"g": {
|
||||
"x": "max",
|
||||
"y": "may",
|
||||
"z": "maize"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The <<.op jsondelete>> operator uses multiple parameters to specify the indexes of the property to delete. For object properties, the property is removed using JavaScript's `delete` operator. For array elements, the element is removed using `splice`, which shifts remaining elements.
|
||||
|
||||
Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on.
|
||||
|
||||
Indexes can be dynamically composed from variables and transclusions, e.g. `[<jsondata>jsondelete<variable>,{!!field},[0]]`.
|
||||
|
||||
If the specified property does not exist, the JSON object is returned unchanged. If you attempt to delete the root object itself (by providing no indexes or a blank index), the JSON object is returned unchanged.
|
||||
|
||||
If the input consists of multiple JSON objects, the property is deleted from all of them.
|
||||
|
||||
<<.operator-examples "jsondelete">>
|
||||
@@ -14,6 +14,7 @@ In technical / logical terms:
|
||||
|`-[run]` |`:except[run]` |difference of sets |... AND NOT run |
|
||||
|`~[run]` |`:else[run]` |else |... ELSE run |
|
||||
|`=[run]` |`:all[run]` |union of sets without de-duplication |... OR run |
|
||||
|`=>[run]` |`:let[run]` |<<.from-version "5.4.0">> assign results to a variable |... LET run |
|
||||
|
||||
The input of a run is normally a list of all the non-[[shadow|ShadowTiddlers]] tiddler titles in the wiki (in no particular order).<br>But the `+` prefix can change this:
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
created: 20250307212252946
|
||||
from-version: 5.4.0
|
||||
modified: 20250307212252946
|
||||
rp-input: all titles from previous filter runs
|
||||
rp-output: an empty title list is always returned from the "let" filter run prefix
|
||||
rp-purpose: assign the title list resulting from previous filter runs to a multi-valued variable
|
||||
tags: [[Named Filter Run Prefix]]
|
||||
title: Let Filter Run Prefix
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<$railroad text="""
|
||||
\start none
|
||||
\end none
|
||||
( ":let" )
|
||||
[[run|"Filter Run"]]
|
||||
"""/>
|
||||
|
||||
The `:let` filter run prefix assigns the title list resulting from previous filter runs to a [[multi-valued variable|Multi-Valued Variable]]. The variable is named with the first result returned by the filter run.
|
||||
|
||||
The variable is made available to the remaining [[filter runs|Filter Run]] in the [[filter expression|Filter Expression]]. Only the first item in the result list is returned when the variable is accessed in the usual way (or an empty string if the result list is empty). Using round brackets instead of angle brackets around a variable name as an operand retrieves the complete list of items in the result list.
|
||||
|
||||
This prefix has an optional [[shortcut syntax|Shortcut Filter Run Prefix]] symbol `=>run`. For example:
|
||||
|
||||
```
|
||||
=[<myfun1>] =[<myfun2>] =>myvar
|
||||
```
|
||||
|
||||
The `:let` filter run prefix always clears the current result list.
|
||||
@@ -23,6 +23,7 @@ A named filter run prefix can precede any [[run|Filter Run]] of a [[filter expre
|
||||
[[<":or"> |"Or Filter Run Prefix"]] |
|
||||
[[<":reduce"> |"Reduce Filter Run Prefix"]] |
|
||||
[[<":sort"> /"v5.2.0"/ |"Sort Filter Run Prefix"]] |
|
||||
[[<":let"> /"v5.4.0"/ |"Let Filter Run Prefix"]] |
|
||||
[[<":then"> /"v5.3.0"/ |"Then Filter Run Prefix"]]) [[run|"Filter Run"]]
|
||||
"""/>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Shortcut prefixes are commonly used by advanced users because they are fast to t
|
||||
<$railroad text="""
|
||||
\start none
|
||||
\end none
|
||||
(-|:"+"|"-"|"~"|"=")
|
||||
(-|:"+"|"-"|"~"|"="|"=>")
|
||||
[[run|"Filter Run"]]
|
||||
"""/>
|
||||
|
||||
@@ -23,7 +23,8 @@ If a run has:
|
||||
|
||||
* the prefix `~`, if the filter output so far is an empty list then the output titles of the run are [[dominantly appended|Dominant Append]] to the filter's output. If the filter output so far is not an empty list then the run is ignored. <<.from-version "5.1.18">>
|
||||
|
||||
* the prefix `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">>
|
||||
* the prefix `=`, output titles are appended to the filter's output without de-duplication. <<.from-version "5.1.20">>
|
||||
|
||||
* the prefix `=>`, the input is assigned to the variable named with the output title. <<.from-version "5.4.0">>
|
||||
|
||||
{{Interchangeable Filter Run Prefixes}}
|
||||
@@ -16,4 +16,6 @@ op-neg-output: the input, but with tiddler <<.place T>> filtered out if it exist
|
||||
|
||||
<<.op title>> is a [[constructor|Selection Constructors]] (except in the form `!title`), but <<.olink2 "field:title" field>> is a [[modifier|Selection Constructors]].
|
||||
|
||||
<<.from-version "5.4.0">> If the operand is quoted with round brackets then the <<.op title>> operator returns the complete list of titles assigned to the multi-valued variable. When negated, the title operator with multi-valued operands returns all the titles that are not present in the operand list.
|
||||
|
||||
<<.operator-examples "title">>
|
||||
|
||||
@@ -6,4 +6,4 @@ image: TiddlyWiki Newsletter Badge
|
||||
color: #fff
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Subscribe to the ~TiddlyWiki Newsletter, a fortnightly summary of the most interesting and relevant news from the ~TiddlyWiki community
|
||||
Subscribe to the ~TiddlyWiki Newsletter, a summary of the most interesting and relevant news from the ~TiddlyWiki community
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 53 KiB |
11
editions/tw5.com/tiddlers/releasenotes/5.4.0/#7898.tid
Normal file
11
editions/tw5.com/tiddlers/releasenotes/5.4.0/#7898.tid
Normal file
@@ -0,0 +1,11 @@
|
||||
title: $:/changenotes/5.4.0/#7898
|
||||
description: Add clamp attribute to RevealWidget
|
||||
release: 5.4.0
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: enhancement
|
||||
change-category: widget
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/7898
|
||||
github-contributors: yaisog
|
||||
|
||||
The attribute `clamp` is added to the RevealWidget to force the popup to be displayed inside its container without overflowing. The value can be `right`, `bottom` or `both`.
|
||||
Clamping to left and top can be accomplished via the `positionAllowNegative` attribute.
|
||||
@@ -0,0 +1,17 @@
|
||||
title: $:/changenotes/5.4.0/#8130
|
||||
created: 20251212171804335
|
||||
modified: 20251212171804335
|
||||
tags: $:/tags/ChangeNote
|
||||
change-type: performance
|
||||
change-category: internal
|
||||
description: Different Stylesheets are now included as single `<style>` tags in the Header
|
||||
release: 5.4.0
|
||||
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/8130
|
||||
github-contributors: BurningTreeC
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Previously, all stylesheets were combined in a single `<style>` tag in the `<head>`.
|
||||
|
||||
Now, a separate `<style>` tag is created in the `<head>` for each stylesheet.
|
||||
|
||||
As part of these changes, the 'view widget' has also been updated. It can now respond dynamically to changes in the 'wikified' formats as well.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user