mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-07-13 23:32:44 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d391595836 | |||
| 967140a148 | |||
| 95e68b0437 | |||
| 8ddede1611 | |||
| ceb200b08c | |||
| 00e5f48a59 | |||
| ea0e9105bc | |||
| 51459815ba | |||
| 8c62935a01 | |||
| 839fa2417d | |||
| 1b8610e4d8 | |||
| ca063cbe90 | |||
| 7d4328b2fb | |||
| 1ae224ab74 | |||
| c308fc44c4 | |||
| aa3fb85919 | |||
| bd052a33f8 | |||
| bb766c36c3 | |||
| 566a341328 | |||
| 9316234001 | |||
| c1cb93c9df | |||
| e46361fbc0 | |||
| e730836d6f | |||
| d050dd30cc | |||
| 635f6b5e76 | |||
| 40d0d542f7 | |||
| a66d9a596c | |||
| 23063ea9c8 | |||
| 9c1f69e9c1 | |||
| 6abfcb631d | |||
| 14b11575d0 | |||
| c9b1fbbb76 | |||
| 27c568e53e | |||
| 0023d3f6fa | |||
| 76ebe9d8e9 | |||
| 40444eade5 | |||
| fe8723be49 | |||
| 56ea0789e1 | |||
| fff648b959 | |||
| e1cff6068c | |||
| 6721164f15 | |||
| 3c37aa267d | |||
| a603146b3c | |||
| f992f4ffab | |||
| dfedd8594f |
@@ -3,3 +3,4 @@
|
|||||||
tmp/
|
tmp/
|
||||||
output/
|
output/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.claude/
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
# Default to the current version number for building the plugin library
|
# Default to the current version number for building the plugin library
|
||||||
|
|
||||||
if [ -z "$TW5_BUILD_VERSION" ]; then
|
if [ -z "$TW5_BUILD_VERSION" ]; then
|
||||||
TW5_BUILD_VERSION=v5.5.0
|
TW5_BUILD_VERSION=v5.4.1.
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]"
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ var _boot = (function($tw) {
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-boot-start"); }
|
|
||||||
|
|
||||||
// Include bootprefix if we're not given module data
|
// Include bootprefix if we're not given module data
|
||||||
if(!$tw) {
|
if(!$tw) {
|
||||||
$tw = require("./bootprefix.js").bootprefix();
|
$tw = require("./bootprefix.js").bootprefix();
|
||||||
@@ -1160,30 +1158,6 @@ $tw.Wiki = function(options) {
|
|||||||
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
|
pluginTiddlers = [], // Array of tiddlers containing registered plugins, ordered by priority
|
||||||
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
|
pluginInfo = Object.create(null), // Hashmap of parsed plugin content
|
||||||
shadowTiddlers = Object.create(null), // Hashmap by title of {source:, tiddler:}
|
shadowTiddlers = Object.create(null), // Hashmap by title of {source:, tiddler:}
|
||||||
systemTiddlerTitles = null, // Array of system tiddler titles (starting with "$:/")
|
|
||||||
nonSystemTiddlerTitles = null, // Array of non-system tiddler titles
|
|
||||||
partitionTiddlerTitles = function() {
|
|
||||||
if(systemTiddlerTitles === null) {
|
|
||||||
systemTiddlerTitles = [];
|
|
||||||
nonSystemTiddlerTitles = [];
|
|
||||||
var titles = getTiddlerTitles();
|
|
||||||
for(var i = 0, length = titles.length; i < length; i++) {
|
|
||||||
if(titles[i].indexOf("$:/") === 0) {
|
|
||||||
systemTiddlerTitles.push(titles[i]);
|
|
||||||
} else {
|
|
||||||
nonSystemTiddlerTitles.push(titles[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getSystemTiddlerTitles = function() {
|
|
||||||
partitionTiddlerTitles();
|
|
||||||
return systemTiddlerTitles;
|
|
||||||
},
|
|
||||||
getNonSystemTiddlerTitles = function() {
|
|
||||||
partitionTiddlerTitles();
|
|
||||||
return nonSystemTiddlerTitles;
|
|
||||||
},
|
|
||||||
shadowTiddlerTitles = null,
|
shadowTiddlerTitles = null,
|
||||||
getShadowTiddlerTitles = function() {
|
getShadowTiddlerTitles = function() {
|
||||||
if(!shadowTiddlerTitles) {
|
if(!shadowTiddlerTitles) {
|
||||||
@@ -1232,14 +1206,6 @@ $tw.Wiki = function(options) {
|
|||||||
tiddlers[title] = tiddler;
|
tiddlers[title] = tiddler;
|
||||||
// Check we've got the title
|
// Check we've got the title
|
||||||
tiddlerTitles = $tw.utils.insertSortedArray(tiddlerTitles || [],title);
|
tiddlerTitles = $tw.utils.insertSortedArray(tiddlerTitles || [],title);
|
||||||
// Maintain system/non-system partitions
|
|
||||||
if(systemTiddlerTitles !== null) {
|
|
||||||
if(title.indexOf("$:/") === 0) {
|
|
||||||
$tw.utils.insertSortedArray(systemTiddlerTitles,title);
|
|
||||||
} else {
|
|
||||||
$tw.utils.insertSortedArray(nonSystemTiddlerTitles,title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Record the new tiddler state
|
// Record the new tiddler state
|
||||||
updateDescriptor["new"] = {
|
updateDescriptor["new"] = {
|
||||||
tiddler: tiddler,
|
tiddler: tiddler,
|
||||||
@@ -1280,14 +1246,6 @@ $tw.Wiki = function(options) {
|
|||||||
tiddlerTitles.splice(index,1);
|
tiddlerTitles.splice(index,1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delete from system/non-system partitions
|
|
||||||
if(systemTiddlerTitles !== null) {
|
|
||||||
var partitionArray = title.indexOf("$:/") === 0 ? systemTiddlerTitles : nonSystemTiddlerTitles;
|
|
||||||
var partitionIndex = partitionArray.indexOf(title);
|
|
||||||
if(partitionIndex !== -1) {
|
|
||||||
partitionArray.splice(partitionIndex,1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Record the new tiddler state
|
// Record the new tiddler state
|
||||||
updateDescriptor["new"] = {
|
updateDescriptor["new"] = {
|
||||||
tiddler: this.getTiddler(title),
|
tiddler: this.getTiddler(title),
|
||||||
@@ -1326,16 +1284,6 @@ $tw.Wiki = function(options) {
|
|||||||
return getTiddlerTitles().slice(0);
|
return getTiddlerTitles().slice(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get an array of all system tiddler titles (returns cached array; do not mutate)
|
|
||||||
this.allSystemTitles = function() {
|
|
||||||
return getSystemTiddlerTitles();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get an array of all non-system tiddler titles (returns cached array; do not mutate)
|
|
||||||
this.allNonSystemTitles = function() {
|
|
||||||
return getNonSystemTiddlerTitles();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Iterate through all tiddler titles
|
// Iterate through all tiddler titles
|
||||||
this.each = function(callback) {
|
this.each = function(callback) {
|
||||||
var titles = getTiddlerTitles(),
|
var titles = getTiddlerTitles(),
|
||||||
@@ -2591,9 +2539,7 @@ $tw.boot.loadStartup = function(options){
|
|||||||
|
|
||||||
// Load tiddlers
|
// Load tiddlers
|
||||||
if($tw.boot.tasks.readBrowserTiddlers) {
|
if($tw.boot.tasks.readBrowserTiddlers) {
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-boot-store-read-start"); }
|
|
||||||
$tw.loadTiddlersBrowser();
|
$tw.loadTiddlersBrowser();
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-boot-store-read-end"); }
|
|
||||||
} else {
|
} else {
|
||||||
$tw.loadTiddlersNode();
|
$tw.loadTiddlersNode();
|
||||||
}
|
}
|
||||||
@@ -2605,7 +2551,6 @@ $tw.boot.loadStartup = function(options){
|
|||||||
$tw.hooks.invokeHook("th-boot-tiddlers-loaded");
|
$tw.hooks.invokeHook("th-boot-tiddlers-loaded");
|
||||||
};
|
};
|
||||||
$tw.boot.execStartup = function(options){
|
$tw.boot.execStartup = function(options){
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-boot-exec-start"); }
|
|
||||||
// Unpack plugin tiddlers
|
// Unpack plugin tiddlers
|
||||||
$tw.wiki.readPluginInfo();
|
$tw.wiki.readPluginInfo();
|
||||||
$tw.wiki.registerPluginTiddlers("plugin",$tw.safeMode ? ["$:/core"] : undefined);
|
$tw.wiki.registerPluginTiddlers("plugin",$tw.safeMode ? ["$:/core"] : undefined);
|
||||||
@@ -2633,7 +2578,6 @@ $tw.boot.execStartup = function(options){
|
|||||||
$tw.boot.executedStartupModules = Object.create(null);
|
$tw.boot.executedStartupModules = Object.create(null);
|
||||||
$tw.boot.disabledStartupModules = $tw.boot.disabledStartupModules || [];
|
$tw.boot.disabledStartupModules = $tw.boot.disabledStartupModules || [];
|
||||||
// Repeatedly execute the next eligible task
|
// Repeatedly execute the next eligible task
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-boot-startup-modules-start"); }
|
|
||||||
$tw.boot.executeNextStartupTask(options.callback);
|
$tw.boot.executeNextStartupTask(options.callback);
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
@@ -2701,7 +2645,6 @@ $tw.boot.executeNextStartupTask = function(callback) {
|
|||||||
}
|
}
|
||||||
taskIndex++;
|
taskIndex++;
|
||||||
}
|
}
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-boot-complete"); }
|
|
||||||
if(typeof callback === "function") {
|
if(typeof callback === "function") {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -14,8 +14,6 @@ See Boot.js for further details of the boot process.
|
|||||||
|
|
||||||
/* eslint-disable @stylistic/indent */
|
/* eslint-disable @stylistic/indent */
|
||||||
|
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-bootprefix-start"); }
|
|
||||||
|
|
||||||
var _bootprefix = (function($tw) {
|
var _bootprefix = (function($tw) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
@@ -25,7 +23,7 @@ $tw.boot = $tw.boot || Object.create(null);
|
|||||||
|
|
||||||
// Config
|
// Config
|
||||||
$tw.config = $tw.config || Object.create(null);
|
$tw.config = $tw.config || Object.create(null);
|
||||||
$tw.config.maxEditFileSize = 200 * 1024 * 1024; // 200MB
|
$tw.config.maxEditFileSize = 100 * 1024 * 1024; // 100MB
|
||||||
|
|
||||||
// Detect platforms
|
// Detect platforms
|
||||||
if(!("browser" in $tw)) {
|
if(!("browser" in $tw)) {
|
||||||
@@ -123,7 +121,6 @@ return $tw;
|
|||||||
if(typeof(exports) === "undefined") {
|
if(typeof(exports) === "undefined") {
|
||||||
// Set up $tw global for the browser
|
// Set up $tw global for the browser
|
||||||
window.$tw = _bootprefix(window.$tw);
|
window.$tw = _bootprefix(window.$tw);
|
||||||
if(typeof performance !== "undefined") { performance.mark("tw-bootprefix-end"); }
|
|
||||||
} else {
|
} else {
|
||||||
// Export functionality as a module
|
// Export functionality as a module
|
||||||
exports.bootprefix = _bootprefix;
|
exports.bootprefix = _bootprefix;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
|||||||
|
title: TiddlyWiki Team
|
||||||
|
tags: Community/Team
|
||||||
|
modified: 20260708095630754
|
||||||
|
created: 20260708095630754
|
||||||
|
leader: @BurningTreeC
|
||||||
|
team:
|
||||||
|
|
||||||
|
The TiddlyDesktop development repository is at https://github.com/TiddlyWiki/TiddlyDesktop
|
||||||
@@ -156,14 +156,13 @@ Fix the height of textarea to fit content
|
|||||||
FramedEngine.prototype.fixHeight = function() {
|
FramedEngine.prototype.fixHeight = function() {
|
||||||
// Make sure styles are updated
|
// Make sure styles are updated
|
||||||
this.copyStyles();
|
this.copyStyles();
|
||||||
// If .editRows is initialised, it takes precedence
|
if(this.widget.editTag === "textarea") {
|
||||||
if(this.widget.editTag === "textarea" && !this.widget.editRows) {
|
|
||||||
if(this.widget.editAutoHeight) {
|
if(this.widget.editAutoHeight) {
|
||||||
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
||||||
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
var newHeight = $tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
||||||
this.iframeNode.style.height = newHeight + "px";
|
this.iframeNode.style.height = newHeight + "px";
|
||||||
}
|
}
|
||||||
} else {
|
} else if(!this.widget.editRows) {
|
||||||
var fixedHeight = parseInt(this.widget.wiki.getTiddlerText(HEIGHT_VALUE_TITLE,"400px"),10);
|
var fixedHeight = parseInt(this.widget.wiki.getTiddlerText(HEIGHT_VALUE_TITLE,"400px"),10);
|
||||||
fixedHeight = Math.max(fixedHeight,20);
|
fixedHeight = Math.max(fixedHeight,20);
|
||||||
this.domNode.style.height = fixedHeight + "px";
|
this.domNode.style.height = fixedHeight + "px";
|
||||||
|
|||||||
@@ -100,13 +100,12 @@ SimpleEngine.prototype.getText = function() {
|
|||||||
Fix the height of textarea to fit content
|
Fix the height of textarea to fit content
|
||||||
*/
|
*/
|
||||||
SimpleEngine.prototype.fixHeight = function() {
|
SimpleEngine.prototype.fixHeight = function() {
|
||||||
// If .editRows is initialised, it takes precedence
|
if(this.widget.editTag === "textarea") {
|
||||||
if((this.widget.editTag === "textarea") && !this.widget.editRows) {
|
|
||||||
if(this.widget.editAutoHeight) {
|
if(this.widget.editAutoHeight) {
|
||||||
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
if(this.domNode && !this.domNode.isTiddlyWikiFakeDom) {
|
||||||
$tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
$tw.utils.resizeTextAreaToFit(this.domNode,this.widget.editMinHeight);
|
||||||
}
|
}
|
||||||
} else {
|
} else if(!this.widget.editRows) {
|
||||||
var fixedHeight = parseInt(this.widget.wiki.getTiddlerText(HEIGHT_VALUE_TITLE,"400px"),10);
|
var fixedHeight = parseInt(this.widget.wiki.getTiddlerText(HEIGHT_VALUE_TITLE,"400px"),10);
|
||||||
fixedHeight = Math.max(fixedHeight,20);
|
fixedHeight = Math.max(fixedHeight,20);
|
||||||
this.domNode.style.height = fixedHeight + "px";
|
this.domNode.style.height = fixedHeight + "px";
|
||||||
|
|||||||
+17
-34
@@ -261,7 +261,6 @@ exports.compileFilter = function(filterString) {
|
|||||||
var filterOperators = this.getFilterOperators();
|
var filterOperators = this.getFilterOperators();
|
||||||
// Assemble array of functions, one for each operation
|
// Assemble array of functions, one for each operation
|
||||||
var operationFunctions = [];
|
var operationFunctions = [];
|
||||||
var operationSubFunctions = []; // Unwrapped sub-functions for fast path
|
|
||||||
// Step through the operations
|
// Step through the operations
|
||||||
var self = this;
|
var self = this;
|
||||||
$tw.utils.each(filterParseTree,function(operation) {
|
$tw.utils.each(filterParseTree,function(operation) {
|
||||||
@@ -290,24 +289,20 @@ exports.compileFilter = function(filterString) {
|
|||||||
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
operand.value = self.getTextReference(operand.text,"",currTiddlerTitle);
|
||||||
operand.multiValue = [operand.value];
|
operand.multiValue = [operand.value];
|
||||||
} else if(operand.variable) {
|
} else if(operand.variable) {
|
||||||
if(!operand._varTree) {
|
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||||
operand._varTree = $tw.utils.parseFilterVariable(operand.text);
|
operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || "";
|
||||||
}
|
|
||||||
operand.value = widgetClass.evaluateVariable(widget,operand._varTree.name,{params: operand._varTree.params, source: source})[0] || "";
|
|
||||||
operand.multiValue = [operand.value];
|
operand.multiValue = [operand.value];
|
||||||
} else if(operand.multiValuedVariable) {
|
} else if(operand.multiValuedVariable) {
|
||||||
if(!operand._varTree) {
|
var varTree = $tw.utils.parseFilterVariable(operand.text);
|
||||||
operand._varTree = $tw.utils.parseFilterVariable(operand.text);
|
var resultList = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source});
|
||||||
}
|
|
||||||
var resultList = widgetClass.evaluateVariable(widget,operand._varTree.name,{params: operand._varTree.params, source: source});
|
|
||||||
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
|
if((resultList.length > 0 && resultList[0] !== undefined) || resultList.length === 0) {
|
||||||
operand.multiValue = widgetClass.evaluateVariable(widget,operand._varTree.name,{params: operand._varTree.params, source: source}) || [];
|
operand.multiValue = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source}) || [];
|
||||||
operand.value = operand.multiValue[0] || "";
|
operand.value = operand.multiValue[0] || "";
|
||||||
} else {
|
} else {
|
||||||
operand.value = "";
|
operand.value = "";
|
||||||
operand.multiValue = [];
|
operand.multiValue = [];
|
||||||
}
|
}
|
||||||
operand.isMultiValueOperand = true;
|
operand.isMultiValueOperand = true;
|
||||||
} else {
|
} else {
|
||||||
operand.value = operand.text;
|
operand.value = operand.text;
|
||||||
operand.multiValue = [operand.value];
|
operand.multiValue = [operand.value];
|
||||||
@@ -348,7 +343,6 @@ exports.compileFilter = function(filterString) {
|
|||||||
return resultArray;
|
return resultArray;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
operationSubFunctions.push(operationSubFunction);
|
|
||||||
var filterRunPrefixes = self.getFilterRunPrefixes();
|
var filterRunPrefixes = self.getFilterRunPrefixes();
|
||||||
// Wrap the operator functions in a wrapper function that depends on the prefix
|
// Wrap the operator functions in a wrapper function that depends on the prefix
|
||||||
operationFunctions.push((function() {
|
operationFunctions.push((function() {
|
||||||
@@ -378,10 +372,6 @@ exports.compileFilter = function(filterString) {
|
|||||||
}
|
}
|
||||||
})());
|
})());
|
||||||
});
|
});
|
||||||
// Detect single "or" run for fast path (bypass LinkedList)
|
|
||||||
var isSingleOrRun = filterParseTree.length === 1 &&
|
|
||||||
(!filterParseTree[0].prefix || filterParseTree[0].prefix === "");
|
|
||||||
var singleOrSubFunction = isSingleOrRun ? operationSubFunctions[0] : null;
|
|
||||||
// Return a function that applies the operations to a source iterator of tiddler titles
|
// Return a function that applies the operations to a source iterator of tiddler titles
|
||||||
var fnMeasured = $tw.perf.measure("filter: " + filterString,function filterFunction(source,widget) {
|
var fnMeasured = $tw.perf.measure("filter: " + filterString,function filterFunction(source,widget) {
|
||||||
if(!source) {
|
if(!source) {
|
||||||
@@ -392,30 +382,23 @@ exports.compileFilter = function(filterString) {
|
|||||||
if(!widget) {
|
if(!widget) {
|
||||||
widget = $tw.rootWidget;
|
widget = $tw.rootWidget;
|
||||||
}
|
}
|
||||||
|
var results = new $tw.utils.LinkedList();
|
||||||
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
self.filterRecursionCount = (self.filterRecursionCount || 0) + 1;
|
||||||
var resultArray;
|
|
||||||
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
if(self.filterRecursionCount < MAX_FILTER_DEPTH) {
|
||||||
if(singleOrSubFunction) {
|
$tw.utils.each(operationFunctions,function(operationFunction) {
|
||||||
// Fast path: single "or" run, return array directly without LinkedList
|
var operationResult = operationFunction(results,source,widget);
|
||||||
resultArray = singleOrSubFunction(source,widget);
|
if(operationResult) {
|
||||||
} else {
|
if(operationResult.variables) {
|
||||||
var results = new $tw.utils.LinkedList();
|
// If the filter run prefix has returned variables, create a new fake widget with those variables
|
||||||
$tw.utils.each(operationFunctions,function(operationFunction) {
|
widget = widget.makeFakeWidgetWithVariables(operationResult.variables);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
resultArray = results.toArray();
|
});
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
resultArray = ["/**-- Excessive filter recursion --**/"];
|
results.push("/**-- Excessive filter recursion --**/");
|
||||||
}
|
}
|
||||||
self.filterRecursionCount = self.filterRecursionCount - 1;
|
self.filterRecursionCount = self.filterRecursionCount - 1;
|
||||||
return resultArray;
|
return results.toArray();
|
||||||
});
|
});
|
||||||
if(this.filterCacheCount >= 2000) {
|
if(this.filterCacheCount >= 2000) {
|
||||||
// To prevent memory leak, we maintain an upper limit for cache size.
|
// To prevent memory leak, we maintain an upper limit for cache size.
|
||||||
|
|||||||
@@ -13,18 +13,6 @@ Filter function for [is[shadow]]
|
|||||||
Export our filter function
|
Export our filter function
|
||||||
*/
|
*/
|
||||||
exports.shadow = function(source,prefix,options) {
|
exports.shadow = function(source,prefix,options) {
|
||||||
// Fast path: when source is wiki.each (all real tiddlers), use shadow title list
|
|
||||||
if(source === options.wiki.each && prefix !== "!") {
|
|
||||||
// Return real tiddlers that are also shadow tiddlers (overridden shadows)
|
|
||||||
var results = [],
|
|
||||||
shadowTitles = options.wiki.allShadowTitles();
|
|
||||||
for(var i = 0, len = shadowTitles.length; i < len; i++) {
|
|
||||||
if(options.wiki.tiddlerExists(shadowTitles[i])) {
|
|
||||||
results.push(shadowTitles[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
var results = [];
|
var results = [];
|
||||||
if(prefix === "!") {
|
if(prefix === "!") {
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
|
|||||||
@@ -13,14 +13,6 @@ Filter function for [is[system]]
|
|||||||
Export our filter function
|
Export our filter function
|
||||||
*/
|
*/
|
||||||
exports.system = function(source,prefix,options) {
|
exports.system = function(source,prefix,options) {
|
||||||
// Fast path: when iterating all tiddlers, use pre-partitioned arrays
|
|
||||||
if(source === options.wiki.each) {
|
|
||||||
if(prefix === "!") {
|
|
||||||
return options.wiki.allNonSystemTitles();
|
|
||||||
} else {
|
|
||||||
return options.wiki.allSystemTitles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var results = [];
|
var results = [];
|
||||||
if(prefix === "!") {
|
if(prefix === "!") {
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ Filter function for [is[tiddler]]
|
|||||||
Export our filter function
|
Export our filter function
|
||||||
*/
|
*/
|
||||||
exports.tiddler = function(source,prefix,options) {
|
exports.tiddler = function(source,prefix,options) {
|
||||||
// Fast path: wiki.each only iterates real tiddlers, all of which exist
|
|
||||||
if(source === options.wiki.each) {
|
|
||||||
if(prefix === "!") {
|
|
||||||
return []; // No real tiddler fails tiddlerExists
|
|
||||||
}
|
|
||||||
return source; // Return iterator directly; all real tiddlers pass
|
|
||||||
}
|
|
||||||
var results = [];
|
var results = [];
|
||||||
if(prefix === "!") {
|
if(prefix === "!") {
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
|
|||||||
@@ -13,21 +13,6 @@ Filter operator returning all the tags of the selected tiddlers
|
|||||||
Export our filter function
|
Export our filter function
|
||||||
*/
|
*/
|
||||||
exports.tags = function(source,operator,options) {
|
exports.tags = function(source,operator,options) {
|
||||||
// Fast path: cache result when iterating all tiddlers
|
|
||||||
if(source === options.wiki.each) {
|
|
||||||
return options.wiki.getGlobalCache("filter-tags-all-tiddlers",function() {
|
|
||||||
var tags = {};
|
|
||||||
source(function(tiddler,title) {
|
|
||||||
var t, length;
|
|
||||||
if(tiddler && tiddler.fields.tags) {
|
|
||||||
for(t=0, length=tiddler.fields.tags.length; t<length; t++) {
|
|
||||||
tags[tiddler.fields.tags[t]] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Object.keys(tags);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var tags = {};
|
var tags = {};
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
var t, length;
|
var t, length;
|
||||||
|
|||||||
@@ -67,57 +67,7 @@ TagSubIndexer.prototype.rebuild = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TagSubIndexer.prototype.update = function(updateDescriptor) {
|
TagSubIndexer.prototype.update = function(updateDescriptor) {
|
||||||
// If the index hasn't been built yet, no update needed
|
this.index = null;
|
||||||
if(this.index === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Determine whether the old/new tiddler is visible to this iterator
|
|
||||||
var oldVisible = this._isVisible(updateDescriptor.old),
|
|
||||||
newVisible = this._isVisible(updateDescriptor["new"]),
|
|
||||||
self = this;
|
|
||||||
// Remove old tags from index
|
|
||||||
if(oldVisible && updateDescriptor.old.tiddler) {
|
|
||||||
var oldTitle = updateDescriptor.old.tiddler.fields.title,
|
|
||||||
oldTags = updateDescriptor.old.tiddler.fields.tags || [];
|
|
||||||
$tw.utils.each(oldTags,function(tag) {
|
|
||||||
if(self.index[tag]) {
|
|
||||||
var idx = self.index[tag].titles.indexOf(oldTitle);
|
|
||||||
if(idx !== -1) {
|
|
||||||
self.index[tag].titles.splice(idx,1);
|
|
||||||
if(self.index[tag].titles.length === 0) {
|
|
||||||
delete self.index[tag];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Add new tags to index
|
|
||||||
if(newVisible && updateDescriptor["new"].tiddler) {
|
|
||||||
var newTitle = updateDescriptor["new"].tiddler.fields.title,
|
|
||||||
newTags = updateDescriptor["new"].tiddler.fields.tags || [];
|
|
||||||
$tw.utils.each(newTags,function(tag) {
|
|
||||||
if(!self.index[tag]) {
|
|
||||||
self.index[tag] = {isSorted: false, titles: [newTitle]};
|
|
||||||
} else if(self.index[tag].titles.indexOf(newTitle) === -1) {
|
|
||||||
self.index[tag].titles.push(newTitle);
|
|
||||||
self.index[tag].isSorted = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Determine whether a tiddler described by a descriptor is visible to this sub-indexer's iterator
|
|
||||||
*/
|
|
||||||
TagSubIndexer.prototype._isVisible = function(descriptor) {
|
|
||||||
if(this.iteratorMethod === "each") {
|
|
||||||
return descriptor.exists;
|
|
||||||
} else if(this.iteratorMethod === "eachShadow") {
|
|
||||||
return descriptor.shadow;
|
|
||||||
} else {
|
|
||||||
// eachTiddlerPlusShadows and eachShadowPlusTiddlers both visit all tiddlers and shadows
|
|
||||||
return descriptor.exists || descriptor.shadow;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TagSubIndexer.prototype.lookup = function(tag) {
|
TagSubIndexer.prototype.lookup = function(tag) {
|
||||||
|
|||||||
@@ -31,13 +31,16 @@ exports.parse = function() {
|
|||||||
reEnd.lastIndex = this.parser.pos;
|
reEnd.lastIndex = this.parser.pos;
|
||||||
var match = reEnd.exec(this.parser.source),
|
var match = reEnd.exec(this.parser.source),
|
||||||
text,
|
text,
|
||||||
start = this.parser.pos;
|
start = this.parser.pos,
|
||||||
|
textEnd;
|
||||||
// Process the text
|
// Process the text
|
||||||
if(match) {
|
if(match) {
|
||||||
text = this.parser.source.substring(this.parser.pos,match.index);
|
text = this.parser.source.substring(this.parser.pos,match.index);
|
||||||
|
textEnd = match.index;
|
||||||
this.parser.pos = match.index + match[0].length;
|
this.parser.pos = match.index + match[0].length;
|
||||||
} else {
|
} else {
|
||||||
text = this.parser.source.substr(this.parser.pos);
|
text = this.parser.source.substr(this.parser.pos);
|
||||||
|
textEnd = this.parser.sourceLength;
|
||||||
this.parser.pos = this.parser.sourceLength;
|
this.parser.pos = this.parser.sourceLength;
|
||||||
}
|
}
|
||||||
return [{
|
return [{
|
||||||
@@ -47,7 +50,7 @@ exports.parse = function() {
|
|||||||
type: "text",
|
type: "text",
|
||||||
text: text,
|
text: text,
|
||||||
start: start,
|
start: start,
|
||||||
end: this.parser.pos
|
end: textEnd
|
||||||
}]
|
}]
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ exports.parse = function() {
|
|||||||
this.parser.pos = this.matchRegExp.lastIndex;
|
this.parser.pos = this.matchRegExp.lastIndex;
|
||||||
// Create the link unless it is suppressed
|
// Create the link unless it is suppressed
|
||||||
if(this.match[0].substr(0,1) === "~") {
|
if(this.match[0].substr(0,1) === "~") {
|
||||||
return [{type: "text", text: this.match[0].substr(1), start: start, end: this.parser.pos}];
|
// Start after the suppressing "~" so the span matches the plain text
|
||||||
|
return [{type: "text", text: this.match[0].substr(1), start: start + 1, end: this.parser.pos}];
|
||||||
} else {
|
} else {
|
||||||
return [{
|
return [{
|
||||||
type: "element",
|
type: "element",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ exports.parse = function() {
|
|||||||
// Parse the filter terminated by a line break
|
// Parse the filter terminated by a line break
|
||||||
var reMatch = /(.*)(?:$|\r?\n)/mg;
|
var reMatch = /(.*)(?:$|\r?\n)/mg;
|
||||||
reMatch.lastIndex = this.parser.pos;
|
reMatch.lastIndex = this.parser.pos;
|
||||||
var filterStart = this.parser.source;
|
var filterStart = this.parser.pos;
|
||||||
var match = reMatch.exec(this.parser.source);
|
var match = reMatch.exec(this.parser.source);
|
||||||
this.parser.pos = reMatch.lastIndex;
|
this.parser.pos = reMatch.lastIndex;
|
||||||
// Parse tree nodes to return
|
// Parse tree nodes to return
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ Parse the most recent match
|
|||||||
*/
|
*/
|
||||||
exports.parse = function() {
|
exports.parse = function() {
|
||||||
// Get the details of the match
|
// Get the details of the match
|
||||||
var linkText = this.match[0];
|
var linkText = this.match[0],
|
||||||
|
// Start after the suppressing "~" so the span matches the plain text
|
||||||
|
start = this.parser.pos + 1;
|
||||||
// Move past the wikilink
|
// Move past the wikilink
|
||||||
this.parser.pos = this.matchRegExp.lastIndex;
|
this.parser.pos = this.matchRegExp.lastIndex;
|
||||||
// Return the link without unwikilink character as plain text
|
// Return the link without unwikilink character as plain text
|
||||||
return [{type: "text", text: linkText.substr(1)}];
|
return [{type: "text", text: linkText.substr(1), start: start, end: this.parser.pos}];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ exports.startup = function() {
|
|||||||
// The rest of the startup process here is not strictly to do with loading modules, but are needed before other startup
|
// The rest of the startup process here is not strictly to do with loading modules, but are needed before other startup
|
||||||
// modules are executed. It is easier to put them here than to introduce a new startup module
|
// modules are executed. It is easier to put them here than to introduce a new startup module
|
||||||
// --------------------------
|
// --------------------------
|
||||||
|
// Set up the performance framework
|
||||||
|
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
|
||||||
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
// Create a root widget for attaching event handlers. By using it as the parentWidget for another widget tree, one can reuse the event handlers
|
||||||
$tw.rootWidget = new widget.widget({
|
$tw.rootWidget = new widget.widget({
|
||||||
type: "widget",
|
type: "widget",
|
||||||
@@ -48,8 +50,6 @@ exports.startup = function() {
|
|||||||
wiki: $tw.wiki,
|
wiki: $tw.wiki,
|
||||||
document: $tw.browser ? document : $tw.fakeDocument
|
document: $tw.browser ? document : $tw.fakeDocument
|
||||||
});
|
});
|
||||||
// Set up the performance framework
|
|
||||||
$tw.perf = new $tw.Performance($tw.wiki.getTiddlerText(PERFORMANCE_INSTRUMENTATION_CONFIG_TITLE,"no") === "yes");
|
|
||||||
// Kick off the filter tracker
|
// Kick off the filter tracker
|
||||||
$tw.filterTracker = new $tw.FilterTracker($tw.wiki);
|
$tw.filterTracker = new $tw.FilterTracker($tw.wiki);
|
||||||
$tw.wiki.addEventListener("change",function(changes) {
|
$tw.wiki.addEventListener("change",function(changes) {
|
||||||
|
|||||||
@@ -3,18 +3,43 @@ title: $:/core/modules/utils/deprecated.js
|
|||||||
type: application/javascript
|
type: application/javascript
|
||||||
module-type: utils
|
module-type: utils
|
||||||
|
|
||||||
Deprecated util functions
|
Deprecated util functions. These preserve the pre-5.4.0 signatures and
|
||||||
|
behaviour for backwards compatibility with plugins and external scripts.
|
||||||
|
Prefer modern alternatives in new code (Array.prototype methods, classList,
|
||||||
|
Math.sign, String.prototype.repeat, etc.).
|
||||||
|
|
||||||
\*/
|
\*/
|
||||||
|
|
||||||
exports.logTable = (data) => console.table(data);
|
exports.logTable = (data) => console.table(data);
|
||||||
|
|
||||||
exports.repeat = (str,count) => str.repeat(count);
|
/*
|
||||||
|
Repeats a string
|
||||||
|
*/
|
||||||
|
exports.repeat = function(str,count) {
|
||||||
|
var result = "";
|
||||||
|
for(var t=0;t<count;t++) {
|
||||||
|
result += str;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
exports.startsWith = (str,search) => str.startsWith(search);
|
/*
|
||||||
|
Check if a string starts with another string
|
||||||
|
*/
|
||||||
|
exports.startsWith = function(str,search) {
|
||||||
|
return str.substring(0, search.length) === search;
|
||||||
|
};
|
||||||
|
|
||||||
exports.endsWith = (str,search) => str.endsWith(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
|
||||||
|
*/
|
||||||
exports.trim = function(str) {
|
exports.trim = function(str) {
|
||||||
if(typeof str === "string") {
|
if(typeof str === "string") {
|
||||||
return str.trim();
|
return str.trim();
|
||||||
@@ -29,30 +54,54 @@ exports.sign = Math.sign;
|
|||||||
|
|
||||||
exports.strEndsWith = (str,ending,position) => str.endsWith(ending,position);
|
exports.strEndsWith = (str,ending,position) => str.endsWith(ending,position);
|
||||||
|
|
||||||
exports.stringifyNumber = (num) => num.toString();
|
exports.stringifyNumber = function(num) {
|
||||||
|
return num + "";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the fully escaped CSS selector for a tag, e.g.
|
||||||
|
// "$:/tags/Stylesheet" -> "tc-tagged-\%24\%3A\%2Ftags\%2FStylesheet"
|
||||||
exports.tagToCssSelector = function(tagName) {
|
exports.tagToCssSelector = function(tagName) {
|
||||||
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
|
return "tc-tagged-" + encodeURIComponent(tagName).replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^`{\|}~,]/mg,function(c) {
|
||||||
return "\\" + c;
|
return "\\" + c;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.domContains = (a,b) => a.compareDocumentPosition(b) & 16;
|
/*
|
||||||
|
Determines whether element 'a' contains element 'b'.
|
||||||
|
Returns false when a === b (matches the original John Resig semantics).
|
||||||
|
*/
|
||||||
|
exports.domContains = function(a,b) {
|
||||||
|
return a !== b && a.contains(b);
|
||||||
|
};
|
||||||
|
|
||||||
exports.domMatchesSelector = (node,selector) => node.matches(selector);
|
exports.domMatchesSelector = (node,selector) => node.matches(selector);
|
||||||
|
|
||||||
exports.hasClass = (el,className) => el.classList && el.classList.contains(className);
|
exports.hasClass = function(el,className) {
|
||||||
|
return !!(el && el.classList && el.classList.contains(className));
|
||||||
|
};
|
||||||
|
|
||||||
|
// addClass/removeClass/toggleClass split on whitespace to preserve the
|
||||||
|
// original setAttribute("class", ...) acceptance of "foo bar" as two
|
||||||
|
// classes. Regressed in #9251.
|
||||||
|
function splitClasses(className) {
|
||||||
|
return (typeof className === "string" && className.match(/\S+/g)) || [];
|
||||||
|
}
|
||||||
|
|
||||||
exports.addClass = function(el,className) {
|
exports.addClass = function(el,className) {
|
||||||
el.classList && className && el.classList.add(className);
|
if(!el.classList) return;
|
||||||
|
splitClasses(className).forEach(function(c) { el.classList.add(c); });
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.removeClass = function(el,className) {
|
exports.removeClass = function(el,className) {
|
||||||
el.classList && className && el.classList.remove(className);
|
if(!el.classList) return;
|
||||||
|
splitClasses(className).forEach(function(c) { el.classList.remove(c); });
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.toggleClass = function(el,className,status) {
|
exports.toggleClass = function(el,className,status) {
|
||||||
el.classList && className && el.classList.toggle(className, status);
|
if(!el.classList) return;
|
||||||
|
splitClasses(className).forEach(function(c) { el.classList.toggle(c,status); });
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getLocationPath = () => window.location.origin + window.location.pathname;
|
exports.getLocationPath = function() {
|
||||||
|
return window.location.toString().split("#")[0];
|
||||||
|
};
|
||||||
|
|||||||
@@ -77,10 +77,23 @@ exports.resizeTextAreaToFit = function(domNode,minHeight) {
|
|||||||
// Measure the specified minimum height
|
// Measure the specified minimum height
|
||||||
domNode.style.height = minHeight;
|
domNode.style.height = minHeight;
|
||||||
var measuredHeight = domNode.offsetHeight || parseInt(minHeight,10);
|
var measuredHeight = domNode.offsetHeight || parseInt(minHeight,10);
|
||||||
|
// Temporarily force rows=1 during auto-measurement so the intrinsic floor
|
||||||
|
// is one row rather than the HTML default of two; restore afterwards
|
||||||
|
var hadRowsAttr = domNode.hasAttribute("rows"),
|
||||||
|
savedRows = hadRowsAttr ? domNode.getAttribute("rows") : null;
|
||||||
|
if(!hadRowsAttr) {
|
||||||
|
domNode.setAttribute("rows","1");
|
||||||
|
}
|
||||||
// Set its height to auto so that it snaps to the correct height
|
// Set its height to auto so that it snaps to the correct height
|
||||||
domNode.style.height = "auto";
|
domNode.style.height = "auto";
|
||||||
// Calculate the revised height
|
// Calculate the revised height
|
||||||
var newHeight = Math.max(domNode.scrollHeight + domNode.offsetHeight - domNode.clientHeight,measuredHeight);
|
var newHeight = Math.max(domNode.scrollHeight + domNode.offsetHeight - domNode.clientHeight,measuredHeight);
|
||||||
|
// Restore the original rows attribute state
|
||||||
|
if(!hadRowsAttr) {
|
||||||
|
domNode.removeAttribute("rows");
|
||||||
|
} else {
|
||||||
|
domNode.setAttribute("rows",savedRows);
|
||||||
|
}
|
||||||
// Only try to change the height if it has changed
|
// Only try to change the height if it has changed
|
||||||
if(newHeight !== domNode.offsetHeight) {
|
if(newHeight !== domNode.offsetHeight) {
|
||||||
domNode.style.height = newHeight + "px";
|
domNode.style.height = newHeight + "px";
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ var TW_Style = function(el) {
|
|||||||
// Return a Proxy to handle direct access to individual style properties
|
// Return a Proxy to handle direct access to individual style properties
|
||||||
return new Proxy(styleObject, {
|
return new Proxy(styleObject, {
|
||||||
get: function(target, property) {
|
get: function(target, property) {
|
||||||
|
// Real CSSStyleDeclaration returns undefined for non-string keys.
|
||||||
|
// Guards against crashes when consumers probe Symbol.toPrimitive etc.
|
||||||
|
if(typeof property !== "string") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
// If the property exists on styleObject, return it (get, set, setProperty methods)
|
// If the property exists on styleObject, return it (get, set, setProperty methods)
|
||||||
if(property in target) {
|
if(property in target) {
|
||||||
return target[property];
|
return target[property];
|
||||||
@@ -90,6 +95,10 @@ var TW_Style = function(el) {
|
|||||||
return el._style[$tw.utils.convertStyleNameToPropertyName(property)] || "";
|
return el._style[$tw.utils.convertStyleNameToPropertyName(property)] || "";
|
||||||
},
|
},
|
||||||
set: function(target, property, value) {
|
set: function(target, property, value) {
|
||||||
|
// Mirror the get trap: ignore non-string keys instead of crashing.
|
||||||
|
if(typeof property !== "string") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Set the property in _style
|
// Set the property in _style
|
||||||
el._style[$tw.utils.convertStyleNameToPropertyName(property)] = value;
|
el._style[$tw.utils.convertStyleNameToPropertyName(property)] = value;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -59,48 +59,14 @@ LinkedList.prototype.push = function(/* values */) {
|
|||||||
LinkedList.prototype.pushTop = function(value) {
|
LinkedList.prototype.pushTop = function(value) {
|
||||||
var t;
|
var t;
|
||||||
if($tw.utils.isArray(value)) {
|
if($tw.utils.isArray(value)) {
|
||||||
if(this.length === 0) {
|
for(t=0; t<value.length; t++) {
|
||||||
// Fast path for empty list: skip removal pass
|
_assertString(value[t]);
|
||||||
for(t = 0; t < value.length; t++) {
|
}
|
||||||
_assertString(value[t]);
|
for(t=0; t<value.length; t++) {
|
||||||
}
|
_removeOne(this,value[t]);
|
||||||
var prev = null,
|
}
|
||||||
useInline = true;
|
for(t=0; t<value.length; t++) {
|
||||||
for(t = 0; t < value.length; t++) {
|
_linkToEnd(this,value[t]);
|
||||||
if(useInline) {
|
|
||||||
var v = value[t];
|
|
||||||
var old = this.next.get(v);
|
|
||||||
if(old !== undefined) {
|
|
||||||
// Duplicate found: switch to _linkToEnd for this and all remaining elements
|
|
||||||
useInline = false;
|
|
||||||
_linkToEnd(this,v);
|
|
||||||
} else {
|
|
||||||
// Inline the common case of _linkToEnd for new unique values
|
|
||||||
this.next.set(v,null);
|
|
||||||
this.prev.set(v,prev);
|
|
||||||
if(prev !== null) {
|
|
||||||
this.next.set(prev,v);
|
|
||||||
} else {
|
|
||||||
this.next.set(null,v);
|
|
||||||
}
|
|
||||||
this.prev.set(null,v);
|
|
||||||
this.length++;
|
|
||||||
prev = v;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_linkToEnd(this,value[t]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(t=0; t<value.length; t++) {
|
|
||||||
_assertString(value[t]);
|
|
||||||
}
|
|
||||||
for(t=0; t<value.length; t++) {
|
|
||||||
_removeOne(this,value[t]);
|
|
||||||
}
|
|
||||||
for(t=0; t<value.length; t++) {
|
|
||||||
_linkToEnd(this,value[t]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_assertString(value);
|
_assertString(value);
|
||||||
|
|||||||
@@ -243,8 +243,28 @@ exports.slowInSlowOut = function(t) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.copyObjectPropertiesSafe = function(object) {
|
exports.copyObjectPropertiesSafe = function(object) {
|
||||||
const seen = new Set(),
|
const seen = new Set();
|
||||||
isDOMElement = (value) => value instanceof Node || value instanceof Window;
|
|
||||||
|
function isDOMElement(value) {
|
||||||
|
if(!value || typeof value !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross-realm DOM nodes
|
||||||
|
if(typeof value.nodeType === "number" &&
|
||||||
|
typeof value.nodeName === "string") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross-realm Window objects
|
||||||
|
if(value.window === value &&
|
||||||
|
value.document &&
|
||||||
|
value.location) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function safeCopy(obj) {
|
function safeCopy(obj) {
|
||||||
// skip circular references
|
// skip circular references
|
||||||
@@ -255,10 +275,6 @@ exports.copyObjectPropertiesSafe = function(object) {
|
|||||||
if(typeof obj !== "object" || obj === null) {
|
if(typeof obj !== "object" || obj === null) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
// skip DOM elements
|
|
||||||
if(isDOMElement(obj)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
// copy arrays, preserving positions
|
// copy arrays, preserving positions
|
||||||
if(Array.isArray(obj)) {
|
if(Array.isArray(obj)) {
|
||||||
return obj.map((item) => {
|
return obj.map((item) => {
|
||||||
@@ -266,7 +282,11 @@ exports.copyObjectPropertiesSafe = function(object) {
|
|||||||
return value === undefined ? null : value;
|
return value === undefined ? null : value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// skip DOM elements
|
||||||
|
if(isDOMElement(obj)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
seen.add(obj);
|
seen.add(obj);
|
||||||
const copy = {};
|
const copy = {};
|
||||||
let key,
|
let key,
|
||||||
|
|||||||
@@ -125,13 +125,12 @@ SelectWidget.prototype.setSelectValue = function() {
|
|||||||
values = Array.isArray(value) ? value : $tw.utils.parseStringArray(value);
|
values = Array.isArray(value) ? value : $tw.utils.parseStringArray(value);
|
||||||
for(var i=0; i < select.children.length; i++){
|
for(var i=0; i < select.children.length; i++){
|
||||||
child=select.children[i];
|
child=select.children[i];
|
||||||
if(child.children.length === 0){
|
if(child.tagName && child.tagName.toUpperCase() === "OPTGROUP"){
|
||||||
child.selected = values.indexOf(child.value) !== -1;
|
|
||||||
} else {
|
|
||||||
// grouped options
|
|
||||||
for(var y=0; y < child.children.length; y++){
|
for(var y=0; y < child.children.length; y++){
|
||||||
child.children[y].selected = values.indexOf(child.children[y].value) !== -1;
|
child.children[y].selected = values.indexOf(child.children[y].value) !== -1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
child.selected = values.indexOf(child.value) !== -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+6
-33
@@ -1400,15 +1400,15 @@ exports.search = function(text,options) {
|
|||||||
fields.push("text");
|
fields.push("text");
|
||||||
}
|
}
|
||||||
// Function to check a given tiddler for the search term
|
// Function to check a given tiddler for the search term
|
||||||
var searchTiddler = function(tiddler,title) {
|
var searchTiddler = function(title) {
|
||||||
if(!searchTermsRegExps) {
|
if(!searchTermsRegExps) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
var notYetFound = searchTermsRegExps.slice();
|
||||||
|
|
||||||
|
var tiddler = self.getTiddler(title);
|
||||||
if(!tiddler) {
|
if(!tiddler) {
|
||||||
tiddler = self.getTiddler(title);
|
tiddler = new $tw.Tiddler({title: title, text: "", type: "text/vnd.tiddlywiki"});
|
||||||
if(!tiddler) {
|
|
||||||
tiddler = new $tw.Tiddler({title: title, text: "", type: "text/vnd.tiddlywiki"});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var contentTypeInfo = $tw.config.contentTypeInfo[tiddler.fields.type] || $tw.config.contentTypeInfo["text/vnd.tiddlywiki"],
|
var contentTypeInfo = $tw.config.contentTypeInfo[tiddler.fields.type] || $tw.config.contentTypeInfo["text/vnd.tiddlywiki"],
|
||||||
searchFields;
|
searchFields;
|
||||||
@@ -1424,33 +1424,6 @@ exports.search = function(text,options) {
|
|||||||
} else {
|
} else {
|
||||||
searchFields = fields;
|
searchFields = fields;
|
||||||
}
|
}
|
||||||
// Fast path for single search term (avoids array slice/splice per tiddler)
|
|
||||||
if(searchTermsRegExps.length === 1) {
|
|
||||||
var singleRegExp = searchTermsRegExps[0];
|
|
||||||
for(var fieldIndex=0; fieldIndex<searchFields.length; fieldIndex++) {
|
|
||||||
var fieldName = searchFields[fieldIndex];
|
|
||||||
if(fieldName === "text" && contentTypeInfo.encoding !== "utf8") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var str = tiddler.fields[fieldName];
|
|
||||||
if(str) {
|
|
||||||
if($tw.utils.isArray(str)) {
|
|
||||||
for(var s=0; s<str.length; s++) {
|
|
||||||
if(singleRegExp.test(str[s])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str = tiddler.getFieldString(fieldName);
|
|
||||||
if(singleRegExp.test(str)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var notYetFound = searchTermsRegExps.slice();
|
|
||||||
for(var fieldIndex=0; notYetFound.length>0 && fieldIndex<searchFields.length; fieldIndex++) {
|
for(var fieldIndex=0; notYetFound.length>0 && fieldIndex<searchFields.length; fieldIndex++) {
|
||||||
// Don't search the text field if the content type is binary
|
// Don't search the text field if the content type is binary
|
||||||
var fieldName = searchFields[fieldIndex];
|
var fieldName = searchFields[fieldIndex];
|
||||||
@@ -1490,7 +1463,7 @@ exports.search = function(text,options) {
|
|||||||
var results = [],
|
var results = [],
|
||||||
source = options.source || this.each;
|
source = options.source || this.each;
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
if(searchTiddler(tiddler,title) !== invert) {
|
if(searchTiddler(title) !== invert) {
|
||||||
results.push(title);
|
results.push(title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
title: $:/config/OfficialPluginLibrary
|
title: $:/config/OfficialPluginLibrary
|
||||||
tags: $:/tags/PluginLibrary
|
tags: $:/tags/PluginLibrary
|
||||||
url: https://tiddlywiki.com/library/v5.5.0/index.html
|
url: https://tiddlywiki.com/library/v5.4.1/index.html
|
||||||
caption: {{$:/language/OfficialPluginLibrary}}
|
caption: {{$:/language/OfficialPluginLibrary}}
|
||||||
|
|
||||||
{{$:/language/OfficialPluginLibrary/Hint}}
|
{{$:/language/OfficialPluginLibrary/Hint}}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
"description": "Developer documentation from https://tiddlywiki.com/dev/",
|
"description": "Developer documentation from https://tiddlywiki.com/dev/",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"tiddlywiki/highlight",
|
"tiddlywiki/highlight",
|
||||||
"tiddlywiki/nodewebkitsaver",
|
|
||||||
"tiddlywiki/github-fork-ribbon",
|
"tiddlywiki/github-fork-ribbon",
|
||||||
"tiddlywiki/menubar",
|
"tiddlywiki/menubar",
|
||||||
"tiddlywiki/internals",
|
"tiddlywiki/internals",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"description": "A step by step introduction to TiddlyWiki",
|
"description": "A step by step introduction to TiddlyWiki",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"tiddlywiki/cecily",
|
|
||||||
"tiddlywiki/codemirror",
|
"tiddlywiki/codemirror",
|
||||||
"tiddlywiki/highlight",
|
"tiddlywiki/highlight",
|
||||||
"tiddlywiki/katex"
|
"tiddlywiki/katex"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ ja-title: TiddlyWikiアーカイブ
|
|||||||
5.1.20 5.1.21 5.1.22 5.1.23
|
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.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.7 5.3.8
|
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
|
||||||
|
5.4.0
|
||||||
\end
|
\end
|
||||||
|
|
||||||
TiddlyWikiの古いバージョンは[[アーカイブ|https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages/tree/master/archive]]で入手できます:
|
TiddlyWikiの古いバージョンは[[アーカイブ|https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages/tree/master/archive]]で入手できます:
|
||||||
|
|||||||
@@ -21,3 +21,5 @@ type: text/vnd.tiddlywiki
|
|||||||
<<.tip """上に示したように、図に開始線と終了線が1つある場合は、リンクされた上位レベルにさらに情報があることを意味します。パンくずリストはナビゲーションに使用できます""">>
|
<<.tip """上に示したように、図に開始線と終了線が1つある場合は、リンクされた上位レベルにさらに情報があることを意味します。パンくずリストはナビゲーションに使用できます""">>
|
||||||
|
|
||||||
<<.tip """下位レベルで使用されているように、図に開始点と終了点がない場合は、読みやすさと単純さを高めるために、上位レベルのシンタックス要素が削除されていることを意味します。パンくずリストはナビゲーションに使用できます""">>
|
<<.tip """下位レベルで使用されているように、図に開始点と終了点がない場合は、読みやすさと単純さを高めるために、上位レベルのシンタックス要素が削除されていることを意味します。パンくずリストはナビゲーションに使用できます""">>
|
||||||
|
|
||||||
|
<<.note """フィルタ式の再帰深度は最大300です。この制限を超えて自身を再帰的に呼び出すフィルタ(例えば、<<.olink subfilter>>や<<.olink filter>>演算子を使ったもの)は、エラーメッセージ`/**-- Excessive filter recursion --**/`を返します。これは無限ループを防ぐためです。""">>
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ type: text/vnd.tiddlywiki
|
|||||||
"{" [: <-"間接"-> /"}以外"/] "}"
|
"{" [: <-"間接"-> /"}以外"/] "}"
|
||||||
|
|
|
|
||||||
"<" [: <-"変数"-> /">以外"/] ">"
|
"<" [: <-"変数"-> /">以外"/] ">"
|
||||||
|
|
|
||||||
|
"(" [: <-"複数値変数"-> /")以外"/ ] ")" /"v5.4.0"/
|
||||||
)
|
)
|
||||||
"""/>
|
"""/>
|
||||||
|
|
||||||
@@ -22,6 +24,7 @@ type: text/vnd.tiddlywiki
|
|||||||
;<<.def ハード>>
|
;<<.def ハード>>
|
||||||
: `[パラメータ例]`
|
: `[パラメータ例]`
|
||||||
: パラメータは、角括弧内のテキストそのものです。
|
: パラメータは、角括弧内のテキストそのものです。
|
||||||
|
|
||||||
;<<.def ソフト>>
|
;<<.def ソフト>>
|
||||||
: <<.def 間接>>
|
: <<.def 間接>>
|
||||||
:: `{パラメータ例}`
|
:: `{パラメータ例}`
|
||||||
@@ -30,6 +33,9 @@ type: text/vnd.tiddlywiki
|
|||||||
:: `<パラメータ例>`
|
:: `<パラメータ例>`
|
||||||
:: パラメータは、山括弧内の[[変数|Variables]]の現在値です。マクロパラメータは、v5.2.0まではサポートされて<<.em いません>>。
|
:: パラメータは、山括弧内の[[変数|Variables]]の現在値です。マクロパラメータは、v5.2.0まではサポートされて<<.em いません>>。
|
||||||
::<<.from-version "5.2.0">> リテラルマクロパラメータがサポートされています。例: `[<now [UTC]YYYY0MM0DD0hh0mm0ssXXX>]`。
|
::<<.from-version "5.2.0">> リテラルマクロパラメータがサポートされています。例: `[<now [UTC]YYYY0MM0DD0hh0mm0ssXXX>]`。
|
||||||
|
: <<.def "複数値変数">>
|
||||||
|
:: `(パラメータ例)`
|
||||||
|
:: <<.from-version "5.4.0">> このパラメータは、丸括弧で囲まれた名前の[[複数値変数|Multi-Valued Variables]]に格納されている値のリスト全体に展開されます。このようにアクセスすると、フィルタ演算子は最初の値だけでなく、すべての値を受け取ります。詳細については、[[複数値変数|Multi-Valued Variables]]を参照してください。
|
||||||
|
|
||||||
<<.note """すべての[[フィルタオペレータ|filter Operator]]の後にはパラメータ式が続く必要があります。[[パラメータの無いオペレータ|Operators without parameters]]の場合、その式は空になります(`[<currentTiddler>links[]]`のフィルタオペレータ<<.olink links>>と同様)。""">>
|
<<.note """すべての[[フィルタオペレータ|filter Operator]]の後にはパラメータ式が続く必要があります。[[パラメータの無いオペレータ|Operators without parameters]]の場合、その式は空になります(`[<currentTiddler>links[]]`のフィルタオペレータ<<.olink links>>と同様)。""">>
|
||||||
|
|
||||||
@@ -37,4 +43,8 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
<<.from-version "5.1.23">> [[フィルタステップ|Filter Step]]では、`,`文字で区切られた複数のパラメータがサポートされます。
|
<<.from-version "5.1.23">> [[フィルタステップ|Filter Step]]では、`,`文字で区切られた複数のパラメータがサポートされます。
|
||||||
|
|
||||||
例えば: `[param1],[param2]`や`<param1>,{param2}`
|
例えば: `[param1],[param2]`や`<param1>,{param2}`、 `[param1],(param2)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<<.warning """`/regexp/(flags)`オペランド構文は非推奨であり、ブラウザのコンソールに警告が表示されます。代わりに<<.olink regexp>>演算子を使用してください。""">>
|
||||||
@@ -24,13 +24,13 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
多くのステップでは、ステップの実行内容をさらに定義する明示的な<<.def パラメータ>>が必要です。
|
多くのステップでは、ステップの実行内容をさらに定義する明示的な<<.def パラメータ>>が必要です。
|
||||||
|
|
||||||
<<.def サフィックス>>は、特定のオペレータの意味を拡張する追加テキスト(多くの場合、[[フィールド|TiddlerFields]]名)です。
|
<<.def サフィックス>>は、特定のオペレータの意味を拡張する追加テキスト(多くの場合、[[フィールド|TiddlerFields]]名)です。サフィックスは`:`文字で区切られ、各サフィックスグループには、カンマで区切られた複数の値を含めることができます。例えば、`compare:number:gteq`は、最初のサフィックスは`number`で、2番目のサフィックスは`gteq`です。`:sort:string:reverse,casesensitive`は、最初のサフィックスは`string`で、2番目のサフィックスグループには`reverse`と`casesensitive`の両方が含まれます。
|
||||||
|
|
||||||
ステップの<<.def オペレータ>>と<<.def サフィックス>>がすべて省略されている場合は、デフォルトで[[title|title Operator]]オペレータが使用されます。
|
ステップの<<.def オペレータ>>と<<.def サフィックス>>がすべて//省略//されている場合は、デフォルトで[[title|title Operator]]オペレータが使用されます。サフィックスが存在するが、コロンの前のオペレータ名が空の場合(例: `[:fieldname[value]]`)、オペレータはデフォルトで<<.olink field>>になります。
|
||||||
|
|
||||||
<<.from-version "5.1.23">> いくつかのステップでは、`,`文字で区切られた複数の<<.def パラメータ>>を受け入れます。
|
<<.from-version "5.1.23">> いくつかのステップでは、`,`文字で区切られた複数の<<.def パラメータ>>を受け入れます。
|
||||||
|
|
||||||
認識されないオペレータは、<<.olink field>>オペレータのサフィックスであるかのように扱われます。
|
認識されないオペレータは、<<.olink field>>オペレータのサフィックスであるかのように扱われます。<<.from-version "5.3.0">> ただし、名前に`.`(ドット)文字が含まれる認識されないオペレータは、ユーザー定義のフィルタオペレータとして扱われ、その名前の変数または関数を検索することで解決されます。.
|
||||||
|
|
||||||
フィルタオペレータはプラグインによって拡張できます。
|
フィルタオペレータはプラグインによって拡張できます。
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type: text/vnd.tiddlywiki
|
|||||||
|`-[run]` |`:except[run]` |差集合 |... AND NOT ラン |
|
|`-[run]` |`:except[run]` |差集合 |... AND NOT ラン |
|
||||||
|`~[run]` |`:else[run]` |それ以外 |... ELSE ラン |
|
|`~[run]` |`:else[run]` |それ以外 |... ELSE ラン |
|
||||||
|`=[run]` |`:all[run]` |重複を排除しない和集合 |... OR ラン |
|
|`=[run]` |`:all[run]` |重複を排除しない和集合 |... OR ラン |
|
||||||
|
|`=>[run]` |`:let[run]` |<<.from-version "5.4.0">> 結果を変数に割りあてる |... LET ラン |
|
||||||
|
|
||||||
ランのインプットは通常、Wiki内の[[隠し|ShadowTiddlers]]Tiddler以外のすべてのTiddlerタイトルのリストです(順不同)。<br>ただし、`+`プレフィックスによってこれを変更できます:
|
ランのインプットは通常、Wiki内の[[隠し|ShadowTiddlers]]Tiddler以外のすべてのTiddlerタイトルのリストです(順不同)。<br>ただし、`+`プレフィックスによってこれを変更できます:
|
||||||
|
|
||||||
@@ -45,3 +46,5 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
新しいフィルタランプレフィックスを作成するには、`filterrunprefix`の[[モジュールタイプ|ModuleType]]で
|
新しいフィルタランプレフィックスを作成するには、`filterrunprefix`の[[モジュールタイプ|ModuleType]]で
|
||||||
[[Javascriptモジュール|Modules]]を作成します。
|
[[Javascriptモジュール|Modules]]を作成します。
|
||||||
|
|
||||||
|
コンパイルされたフィルタ式はパフォーマンスのためキャッシュされます。キャッシュには最大2000件のエントリが保存され、この制限を超えるとキャッシュ全体がリセットされます。
|
||||||
@@ -17,6 +17,15 @@ type: text/vnd.tiddlywiki
|
|||||||
[["ラン"|"Filter Run"]]
|
[["ラン"|"Filter Run"]]
|
||||||
"""/>
|
"""/>
|
||||||
|
|
||||||
前のランからのフィルタアウトプットは保留されます。`:intersection`フィルタランは、すべてのTiddlerタイトルをインプットとして開始されます。この最後のフィルタランが完了すると、最後のアウトプットが保留アウトプットと比較されます。保留アウトプットと最新アウトプットの両方に表われるタイトルのみを含む新しいアウトプットが生成されます。
|
前のランからのフィルタアウトプットは保留されます。`:intersection`フィルタランは、すべてのTiddlerタイトルをインプットとして開始されます。この最後のフィルタランが完了すると、最後のアウトプットが保留アウトプットと比較されます。保留アウトプットと最新アウトプットの両方に表われるタイトルのみを含む新しいアウトプットが生成されます。アウトプットにおけるタイトルの順序は、蓄積された結果から保持されます。
|
||||||
|
|
||||||
|
!! `:and` / `+`との違い
|
||||||
|
|
||||||
|
`:intersection`と`:and` (または `+`)の主な違いは、''インプット''として何を受け取るかです:
|
||||||
|
|
||||||
|
* `:and` / `+`は、''これまでに蓄積された結果''をフィルタランへのインプットとして渡します。ラン内のオペレータは、結果に既に含まれているタイトルのみを表示・操作できます。
|
||||||
|
* `:intersection`は、''すべてのTiddlerタイトル''をフィルタランへのインプットとして渡し(プレフィックスなしのランと同様)、その後、蓄積された結果にも含まれるタイトルのみを保持します。
|
||||||
|
|
||||||
|
これは、`:intersection`がTiddlerプール全体から開始する必要のあるオペレータを使用してタイトルを照合できることを意味します。詳細については、[[交換可能なフィルタランプレフィックス|Interchangeable Filter Run Prefixes]]を参照してください。
|
||||||
|
|
||||||
[[intersectionフィルタランプレフィックス(例)|Intersection Filter Run Prefix (Examples)]]
|
[[intersectionフィルタランプレフィックス(例)|Intersection Filter Run Prefix (Examples)]]
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
created: 20260222152853141
|
||||||
|
modified: 20260506104906199
|
||||||
|
original-modified: 20260222184916224
|
||||||
|
tags: [[Let Filter Run Prefix]]
|
||||||
|
title: Let Filter Run Prefix (Examples)
|
||||||
|
ja-title: letフィルタランプレフィックス(例)
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
<<.operator-example 1 "3 2 1 4 :let[[myvar]] 6 7 8 [(myvar)sort[]]" "名前付きプレフィックスを使用">>
|
||||||
|
<<.operator-example 2 "3 2 1 4 =>myvar 6 7 8 [(myvar)sort[]]" "短縮形のプレフィックスを使用">>
|
||||||
|
<<.operator-example 3 "3 2 1 4 =>myvar 6 7 8 [<myvar>]" "山括弧は最初の値のみを返却">>
|
||||||
|
<<.operator-example 4 "3 2 1 4 =>mynumbers [(mynumbers)sum[]] [(mynumbers)average[]]">>
|
||||||
|
<<.operator-example 5 '"[0-9]" =>digitsRE abc 123 de45 67fg hij :filter[regexp<digitsRE>]' "角括弧で囲まれた正規表現では、変数パラメータを使用">>
|
||||||
|
<<.operator-example 6 '"[prefix[ca]then[ca]]" "[suffix[at]then[at]]" other =>myfilters cat can bat bug :cascade[(myfilters)]' "[[cascadeフィルタランプレフィックス|Cascade Filter Run Prefix]]で使用するフィルタを定義">>
|
||||||
|
<<.operator-example 7 "[[⁎ ]] [[⁑ ]] [[⁂ ]] :let[[prefixList]] [tag[Learning]first[3]] :map:flat[(prefixList)addsuffix<currentTiddler>]">>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
created: 20250307212252946
|
||||||
|
from-version: 5.4.0
|
||||||
|
modified: 20260502101033176
|
||||||
|
original-modified: 20250307212252946
|
||||||
|
rp-input: 前回のフィルタランからのすべてのタイトル
|
||||||
|
rp-output: "let"フィルタランプレフィックスからはいつも空のタイトルリストが返されます
|
||||||
|
rp-purpose: 前回のフィルタラン結果のタイトルリストを複数値変数へ代入
|
||||||
|
tags: [[Named Filter Run Prefix]]
|
||||||
|
title: Let Filter Run Prefix
|
||||||
|
ja-title: letフィルタランプレフィックス
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
<$railroad text="""
|
||||||
|
\start none
|
||||||
|
\end none
|
||||||
|
( ":let" )
|
||||||
|
[["ラン"|"Filter Run"]]
|
||||||
|
"""/>
|
||||||
|
|
||||||
|
`:let`フィルタランプレフィックスは、前回のフィルタランの結果として得られたタイトルリストを[[他数値変数|Multi-Valued Variables]]に割り当てます。この変数には、フィルタランによって返された最初の結果が名前として付けられます。
|
||||||
|
|
||||||
|
この変数は、[[フィルタ式|Filter Expression]]内の残りの[[フィルタラン|Filter Run]]で使用可能になります。通常の方法で変数にアクセスすると、結果リストの最初の項目のみが返されます(結果リストが空の場合は空の文字列が返されます)。オペランドとして変数名を山括弧ではなく丸括弧で囲むと、結果リスト内のすべての項目が取得できます。
|
||||||
|
|
||||||
|
このプレフィックスには、オプションの[[ショートカット構文|Shortcut Filter Run Prefix]]記号`=>run`があります。例:
|
||||||
|
|
||||||
|
```
|
||||||
|
=[<myfun1>] =[<myfun2>] =>myvar
|
||||||
|
```
|
||||||
|
|
||||||
|
`:let`フィルタランプレフィックスを指定すると、常に現在の結果リストがクリアされます。
|
||||||
|
|
||||||
|
[[letフィルタランプレフィックス(例)|Let Filter Run Prefix (Examples)]]
|
||||||
@@ -25,6 +25,7 @@ type: text/vnd.tiddlywiki
|
|||||||
[[<":or"> |"Or Filter Run Prefix"]] |
|
[[<":or"> |"Or Filter Run Prefix"]] |
|
||||||
[[<":reduce"> |"Reduce Filter Run Prefix"]] |
|
[[<":reduce"> |"Reduce Filter Run Prefix"]] |
|
||||||
[[<":sort"> /"v5.2.0"/ |"Sort 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"]]
|
[[<":then"> /"v5.3.0"/ |"Then Filter Run Prefix"]]) [[run|"Filter Run"]]
|
||||||
"""/>
|
"""/>
|
||||||
|
|
||||||
@@ -35,4 +36,14 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
<<.tip """フィルタランプレフィックス`:reduce`、`:sort`、`:map`、`:filter`内では、変数<<.var currentTiddler>>は処理中のTiddlerのタイトルに設定されます。<br>サブフィルタ外のcurrentTiddlerの値は、変数<<.var "..currentTiddler">>で使用できます <<.from-version "5.2.0">>""" >>
|
<<.tip """フィルタランプレフィックス`:reduce`、`:sort`、`:map`、`:filter`内では、変数<<.var currentTiddler>>は処理中のTiddlerのタイトルに設定されます。<br>サブフィルタ外のcurrentTiddlerの値は、変数<<.var "..currentTiddler">>で使用できます <<.from-version "5.2.0">>""" >>
|
||||||
|
|
||||||
|
!! サフィックス
|
||||||
|
|
||||||
|
名前付きフィルタランプレフィックスは、`:`文字で区切られたサフィックスを受け入れることができ、各サフィックスにはオプションでカンマ区切りの値を含めることができます。一般的な構文は`:prefixname:suffix1:suffix2,...`です。現在、次のプレフィックスがサフィックスを受け入れます:
|
||||||
|
|
||||||
|
|!プレフィックス |!サフィッス |!例 |
|
||||||
|
|`:map` |`flat` — 項目ごとに最初の結果だけでなく、すべての結果を返す |`:map:flat[...]` |
|
||||||
|
|`:sort` |型 (''string'', ''alphanumeric'', ''number'', ''integer'', ''version'', ''date'')とフラグ (''reverse'', ''casesensitive'', ''caseinsensitive'') |`:sort:number:reverse[...]` |
|
||||||
|
|
||||||
|
その他の名前付きプレフィックス(`:all`, `:and`, `:cascade`, `:else`, `:except`, `:filter`, `:intersection`, `:let`, `:or`, `:reduce`, `:then`)は現在サフィックスを受け付けません。
|
||||||
|
|
||||||
参照: [[交換可能なフィルターランプレフィックス|Interchangeable Filter Run Prefixes]]
|
参照: [[交換可能なフィルターランプレフィックス|Interchangeable Filter Run Prefixes]]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ type: text/vnd.tiddlywiki
|
|||||||
<$railroad text="""
|
<$railroad text="""
|
||||||
\start none
|
\start none
|
||||||
\end none
|
\end none
|
||||||
(-|:"+"|"-"|"~"|"=")
|
(-|:"+"|"-"|"~"|"="|"=>")
|
||||||
[["ラン"|"Filter Run"]]
|
[["ラン"|"Filter Run"]]
|
||||||
"""/>
|
"""/>
|
||||||
|
|
||||||
@@ -23,8 +23,10 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
* プレフィックス`-`は、アウトプットタイトルがフィルタのアウトプットから<<.em 取り除か>>れます(そのようなTiddlerが存在する場合)
|
* プレフィックス`-`は、アウトプットタイトルがフィルタのアウトプットから<<.em 取り除か>>れます(そのようなTiddlerが存在する場合)
|
||||||
|
|
||||||
* プレフィックス`~`は、フィルタアウトプットが空リストの場合、ランの結果のタイトルがフィルタアウトプットに[[優先的に追加|Dominant Append]]されます。フィルタアウトプットが空リストでない場合、ランは無視されます。<<.from-version "5.1.18">>
|
* プレフィックス`~`<<.from-version "5.1.18">>は、フィルタアウトプットが空リストの場合、ランの結果のタイトルがフィルタアウトプットに[[優先的に追加|Dominant Append]]されます。フィルタアウトプットが空リストでない場合、ランは無視されます。
|
||||||
|
|
||||||
* プレフィックス`=`は、アウトプットタイトルが重複排除されずにフィルタのアウトプットに追加されます。<<.from-version "5.1.20">>
|
* プレフィックス`=`<<.from-version "5.1.20">>は、アウトプットタイトルが重複排除されずにフィルタのアウトプットに追加されます。
|
||||||
|
|
||||||
|
* プレフィックス`=>` <<.from-version "5.4.0">>は、これまでのすべてのランの累積結果は、このランの最初の結果が名前となる[[複数値変数|Multi-Valued Variables]]として割り当てられます。その後、累積結果はクリアされます。
|
||||||
|
|
||||||
{{Interchangeable Filter Run Prefixes}}
|
{{Interchangeable Filter Run Prefixes}}
|
||||||
@@ -2,7 +2,7 @@ created: 20210618133745003
|
|||||||
from-version: 5.3.0
|
from-version: 5.3.0
|
||||||
modified: 20250423104147235
|
modified: 20250423104147235
|
||||||
original-modified: 20230710074225410
|
original-modified: 20230710074225410
|
||||||
rp-input: <<.olink すべて>>のTiddlerタイトル
|
rp-input: <<.olink all>>のTiddlerタイトル (前回のランのアウトプットが空でない場合にのみ評価されます)
|
||||||
rp-output: フィルタランのアウトプットは、空のリストでない限り、前回までのランのアウトプットを置き換えます(以下を参照)。
|
rp-output: フィルタランのアウトプットは、空のリストでない限り、前回までのランのアウトプットを置き換えます(以下を参照)。
|
||||||
rp-purpose: フィルタランへのインプットをそのアウトプットで置き換え、インプットがある場合にのみランを評価
|
rp-purpose: フィルタランへのインプットをそのアウトプットで置き換え、インプットがある場合にのみランを評価
|
||||||
search:
|
search:
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ type: text/vnd.tiddlywiki
|
|||||||
|
|
||||||
関数はいくつかの方法で呼び出すことができます:
|
関数はいくつかの方法で呼び出すことができます:
|
||||||
|
|
||||||
* 構文 `<<myfun param:"value">>` を使用して関数を直接トランスクルードする
|
* [[Calls]]構文を使用する:
|
||||||
* 構文 `<div class=<<myfun param:"value">>>` を使用してウィジェット属性に関数を割り当てる
|
** 構文 `<<myfun param:"value">>` を使用して関数を直接トランスクルードする
|
||||||
|
** 構文 `<div class=<<myfun param:"value">>>` を使用してウィジェット属性に関数を割り当てる
|
||||||
* 構文 `[function[myfun],[value],...]` を使用して [[関数オペレータ|function Operator]] を介して関数を呼び出す
|
* 構文 `[function[myfun],[value],...]` を使用して [[関数オペレータ|function Operator]] を介して関数を呼び出す
|
||||||
* 構文 `[my.fun[value]]` または `[.myfun[value]]` で、カスタムフィルター演算子として名前にピリオドが含まれる関数を直接呼び出す
|
* 構文 `[my.fun[value]]` または `[.myfun[value]]` で、カスタムフィルター演算子として名前にピリオドが含まれる関数を直接呼び出す
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
caption: Internet Explorer
|
|
||||||
created: 20140811172058274
|
|
||||||
modified: 20241029105938082
|
|
||||||
original-modified: 20211114031651879
|
|
||||||
tags: GettingStarted $:/deprecated
|
|
||||||
title: GettingStarted - Internet Explorer
|
|
||||||
ja-title: はじめに - Internet Explorer
|
|
||||||
type: text/vnd.tiddlywiki
|
|
||||||
|
|
||||||
{{Saving with TiddlyIE}}
|
|
||||||
|
|
||||||
The [[Windows HTA Hack]] describes an alternative method of using TiddlyWiki with Internet Explorer.
|
|
||||||
[[Windows HTAのハック|Windows HTA Hack]]では、Internet ExplorerでTiddlyWikiを使用する別の方法について説明します。
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
created: 20130822170200000
|
created: 20130822170200000
|
||||||
icon: $:/core/icon
|
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?]]
|
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: 20251213104120189
|
modified: 20260430010447598
|
||||||
original-modified: 20250807084952911
|
original-modified: 20260420192600833
|
||||||
tags: Welcome
|
tags: Welcome
|
||||||
title: HelloThere
|
title: HelloThere
|
||||||
ja-title: こんにちは
|
ja-title: こんにちは
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
created: 20150414070451144
|
created: 20150414070451144
|
||||||
list: list: [[HelloThumbnail - Donations]] [[HelloThumbnail - Newsletter]] [[HelloThumbnail - Community Survey 2025]] [[HelloThumbnail - Introduction Video]] [[HelloThumbnail - Grok TiddlyWiki]] [[HelloThumbnail - Latest Version]] [[HelloThumbnail - MultiWikiServer]] [[HelloThumbnail - Twenty Years of TiddlyWiki]] [[HelloThumbnail - TiddlyWiki Privacy]] [[HelloThumbnail - Marketplace]] [[HelloThumbnail - Intertwingled Innovations]] [[HelloThumbnail - TiddlyWikiLinks]]
|
list: [[HelloThumbnail - Latest Version]] [[HelloThumbnail - Donations]] [[HelloThumbnail - Newsletter]] [[HelloThumbnail - Community Survey 2025]] [[HelloThumbnail - Introduction Video]] [[HelloThumbnail - Grok TiddlyWiki]] [[HelloThumbnail - MultiWikiServer]] [[HelloThumbnail - Twenty Years of TiddlyWiki]] [[HelloThumbnail - TiddlyWiki Privacy]] [[HelloThumbnail - Marketplace]] [[HelloThumbnail - Intertwingled Innovations]] [[HelloThumbnail - TiddlyWikiLinks]]
|
||||||
modified: 20260406043045066
|
modified: 20260406043045066
|
||||||
original-modified: 20150414070948246
|
original-modified: 20150414070948246
|
||||||
title: HelloThumbnail
|
title: HelloThumbnail
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ caption: v<<version>>の新着情報
|
|||||||
link: Releases
|
link: Releases
|
||||||
image: New Release Banner
|
image: New Release Banner
|
||||||
color: #fff
|
color: #fff
|
||||||
|
ribbon-text: NEW
|
||||||
|
|
||||||
\define prerelease-regexp() [0-9]+\.[0-9]+\.[0-9]+\-prerelease
|
\define prerelease-regexp() [0-9]+\.[0-9]+\.[0-9]+\-prerelease
|
||||||
<$list filter="[<version>!regexp<prerelease-regexp>]" variable="ignore">
|
<$list filter="[<version>!regexp<prerelease-regexp>]" variable="ignore">
|
||||||
|
|||||||
@@ -6,6 +6,5 @@ link: TiddlyWiki Newsletter
|
|||||||
image: TiddlyWiki Newsletter Badge
|
image: TiddlyWiki Newsletter Badge
|
||||||
color: #fff
|
color: #fff
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
ribbon-text: NEW
|
|
||||||
|
|
||||||
~TiddlyWikiコミュニティからの最も興味深く関連性のあるニュースをまとめた~TiddlyWikiニュースレターを購読できます
|
~TiddlyWikiコミュニティからの最も興味深く関連性のあるニュースをまとめた~TiddlyWikiニュースレターを購読できます
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
caption: HTA Hack
|
|
||||||
color: #F06292
|
|
||||||
created: 20131212223146250
|
|
||||||
delivery: DIY
|
|
||||||
description: Internet Explorerで変更を手動で直接保存する方法
|
|
||||||
method: save
|
|
||||||
modified: 20241012122755089
|
|
||||||
original-modified: 20200507110355115
|
|
||||||
tags: Saving Windows $:/deprecated
|
|
||||||
title: Windows HTA Hack
|
|
||||||
ja-title: Windows HTAのハック
|
|
||||||
type: text/vnd.tiddlywiki
|
|
||||||
|
|
||||||
<<.deprecated-since "5.3.6">>
|
|
||||||
Windowsでは、HTMLファイルの拡張子を`*.hta`に名前変更することで、TiddlyWikiを真のローカルアプリケーションに変換できます。その後、''fsosaver''モジュールは~ActiveX ~FileSystemObjectを使用して変更を保存できます。
|
|
||||||
|
|
||||||
この方法の欠点の1つは、TiddlyWikiファイルがUTF-16フォーマットで保存されるため、通常のUTF-8エンコードの場合と比べて2倍の大きさになることです。ただし、別の保存方法でファイルを開いて保存すると、ファイルはUTF-8に再エンコードされます。
|
|
||||||
|
|
||||||
詳細については、Wikipediaを参照してください: https://en.wikipedia.org/wiki/HTML_Application
|
|
||||||
@@ -1,58 +1,9 @@
|
|||||||
caption: プロシージャ呼び出し
|
caption: プロシージャ呼び出し
|
||||||
created: 20221007130006705
|
created: 20221007130006705
|
||||||
modified: 20260327114525571
|
modified: 20260429105903754
|
||||||
original-modified: 20230419103154329
|
original-modified: 20260125212303316
|
||||||
tags: WikiText Procedures
|
|
||||||
title: Procedure Calls
|
title: Procedure Calls
|
||||||
ja-title: プロシージャ呼び出し
|
ja-title: プロシージャ呼び出し
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
!! 紹介
|
<<.deprecated-since "5.4.0" "Calls">>
|
||||||
|
|
||||||
このTiddlerでは、[[プロシージャ|Procedures]]を呼び出すさまざまな方法について説明します。
|
|
||||||
|
|
||||||
!! トランスクルージョンショートカットでのプロシージャ呼び出し
|
|
||||||
|
|
||||||
[[プロシージャ|Procedures]]を呼び出すには、プロシージャ名とパラメータ値を`<<`二重の山括弧で囲みます`>>`。
|
|
||||||
|
|
||||||
```
|
|
||||||
<<my-procedure param:"これはパラメータ値です">>
|
|
||||||
```
|
|
||||||
|
|
||||||
デフォルトでは、パラメータはプロシージャの定義と同じ順序でリストされます。パラメータに名前とコロンを付けることで、異なる順序でリストすることができます。
|
|
||||||
|
|
||||||
パラメータに値が指定されていない場合は、[[プロシージャ定義|Procedure Definitions]]でそのパラメータに指定されているデフォルト値が使用されます。(デフォルト値が定義されていない場合は、パラメータは空白になります。)
|
|
||||||
|
|
||||||
各パラメータ値は、`'`シングルクォーテーション`'`、`"`ダブルクォーテーション`"`、`"""`三重のダブルクォーテーション`"""`、`[[`二重の角括弧`]]`で囲むことができます。三重のダブルクォーテーションを使用すると、値にほぼあらゆる文字列を含めることができます。値にスペース、シングルクォーテーション、ダブルクォーテーションが含まれていない場合は、区切り文字は不要です。
|
|
||||||
|
|
||||||
[[パーサーモード|WikiText parser mode: macro examples]]に関する議論を参照してください。
|
|
||||||
|
|
||||||
!! <<.wlink TranscludeWidget>>ウィジェットでのプロシージャ呼び出し
|
|
||||||
|
|
||||||
ショートカット構文は、トランスクルードするプロシージャの名前を指定する`$variable`属性を持つ<<.wlink TranscludeWidget>>ウィジェットに展開されます。
|
|
||||||
|
|
||||||
```
|
|
||||||
<$transclude $variable="my-procedure" param="これはパラメータ値です"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
ウィジェットは、ショートカット構文よりも柔軟性が高く、動的なパラメータ値を指定することも可能です。
|
|
||||||
|
|
||||||
!! 属性値にプロシージャ呼び出しを割り当て
|
|
||||||
|
|
||||||
プロシージャテキストは、ウィジェットやHTML要素の属性に直接割り当てることができます。プロシージャの結果はWiki化されないため、[[パラメータ処理|Procedure Parameter Handling]]は行われません。
|
|
||||||
|
|
||||||
```
|
|
||||||
<div class=<<myclasses>>>
|
|
||||||
...
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
!! フィルタでのプロシージャ呼び出しの使用
|
|
||||||
|
|
||||||
プロシージャ呼び出しはフィルタ内で使用できます。テキストはWiki化されないため、パラメータは無視されます。
|
|
||||||
|
|
||||||
```
|
|
||||||
<$list filter="[<my-procedure>]">
|
|
||||||
...
|
|
||||||
</$list>
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
caption: 呼び出し
|
||||||
|
created: 20221007130006705
|
||||||
|
modified: 20260429224543285
|
||||||
|
original-modified: 20260301030947969
|
||||||
|
tags: WikiText Procedures Functions Macros
|
||||||
|
title: Calls
|
||||||
|
ja-title: 呼び出し
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
!! 紹介
|
||||||
|
|
||||||
|
このTiddlerでは、[[プロシージャ|Procedures]]、[[関数|Functions]]、[[マクロ|Macros]]を呼び出すさまざまな方法について説明します。構文の正式な説明については、[[呼び出し構文|Call Syntax]]を参照してください。
|
||||||
|
|
||||||
|
!! トランスクルージョンショートカットでの呼び出し
|
||||||
|
|
||||||
|
呼び出しを実行するには、呼び出し先の名前とパラメータ値を`<<`二重の山括弧で囲みます`>>`。
|
||||||
|
|
||||||
|
```
|
||||||
|
<<my-procedure param:"これはパラメータ値です">>
|
||||||
|
```
|
||||||
|
|
||||||
|
デフォルトでは、パラメータは定義と同じ順序で解釈されます。パラメータ値に名前と等号を付けることで、異なる順序でリストすることができます。
|
||||||
|
|
||||||
|
パラメータに値が指定されていない場合は、[[プロシージャ定義|Procedure Definitions]]、[[関数定義|Function Definitions]]、[[マクロ定義|Macro Definitions]]でそのパラメータに指定されているデフォルト値が使用されます。(デフォルト値が定義されていない場合は、パラメータは空白になります。)
|
||||||
|
|
||||||
|
各パラメータ値は、`'`シングルクォーテーション`'`、`"`ダブルクォーテーション`"`、`"""`三重のダブルクォーテーション`"""`、`[[`二重の角括弧`]]`で囲むことができます。三重のダブルクォーテーションを使用すると、値にほぼあらゆる文字列を含めることができます。値にスペース、シングルクォーテーション、ダブルクォーテーションが含まれていない場合は、区切り文字は不要です。シングルクォーテーション、または、三重のバッククォーテーションで囲まれた[[置換属性値|Substituted Attribute Values]]もサポートされています。
|
||||||
|
|
||||||
|
[[パーサーモード|WikiText parser mode: macro examples]]に関する議論を参照してください。
|
||||||
|
|
||||||
|
!!! 例
|
||||||
|
|
||||||
|
<<testcase TestCases/Calls/ProcedureStaticAttributes>>
|
||||||
|
|
||||||
|
<<testcase TestCases/Calls/ProcedureDynamicAttributes>>
|
||||||
|
|
||||||
|
!! <<.wlink TranscludeWidget>>ウィジェットを用いた呼び出し
|
||||||
|
|
||||||
|
ショートカット構文は、トランスクルードするプロシージャの名前を指定する`$variable`属性を持つ<<.wlink TranscludeWidget>>ウィジェットに展開されます。
|
||||||
|
|
||||||
|
```
|
||||||
|
<$transclude $variable="my-procedure" param="これはパラメータ値です"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
ウィジェットは、ショートカット構文よりも柔軟性が高く、カスタムウィジェットで上書きすることも可能です。
|
||||||
|
|
||||||
|
!! 呼び出し結果を属性値に割り当てる
|
||||||
|
|
||||||
|
呼び出しから返されるテキストは、ウィジェットやHTML要素の属性に直接割り当てることができます。呼び出しの結果はWiki化されないため、[[パラメータ処理|Procedure Parameter Handling]]は行われません。
|
||||||
|
|
||||||
|
```
|
||||||
|
<div class=<<myclasses>>>
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
!! フィルタでの呼び出しの使用
|
||||||
|
|
||||||
|
呼び出しはフィルタ内で使用できます。テキストはWiki化されないため、パラメータは無視されます。
|
||||||
|
|
||||||
|
//現在サポートされているのはリテラル文字列パラメータのみであることに注意してください//
|
||||||
|
|
||||||
|
```
|
||||||
|
<$list filter="[<my-procedure>]">
|
||||||
|
...
|
||||||
|
</$list>
|
||||||
|
```
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
created: 20130825160900000
|
created: 20130825160900000
|
||||||
modified: 20251214105948181
|
modified: 20260430074718157
|
||||||
original-modified: 20241106165307259
|
original-modified: 20250617140259415
|
||||||
tags: Features [[Working with TiddlyWiki]]
|
tags: Features [[Working with TiddlyWiki]]
|
||||||
title: Encryption
|
title: Encryption
|
||||||
ja-title: 暗号化
|
ja-title: 暗号化
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
TiddlyWiki5を単一のHTMLファイルとして使用すると、[[Stanford JavaScript Crypto Library]]を使用してCCMモードのAES 128ビット暗号化でコンテンツを暗号化できます。
|
TiddlyWiki5を単一のHTMLファイルとして使用すると、[[Stanford JavaScript Crypto Library]]を使用してCCMモードのAES 256ビット暗号化でコンテンツを暗号化できます。
|
||||||
|
|
||||||
# サイドバーの''ツール''タブに切り替えて、南京錠アイコンのボタンを探します
|
# サイドバーの''ツール''タブに切り替えて、南京錠アイコンのボタンを探します
|
||||||
# ボタンに<<.icon $:/core/images/unlocked-padlock>> ''パスワードの設定''と表示されている場合、現在のウィキは暗号化されていません。ボタンをクリックすると、以降の保存を暗号化するために使用されるパスワードの入力を求められます
|
# ボタンに<<.icon $:/core/images/unlocked-padlock>> ''パスワードの設定''と表示されている場合、現在のウィキは暗号化されていません。ボタンをクリックすると、以降の保存を暗号化するために使用されるパスワードの入力を求められます
|
||||||
@@ -16,5 +16,5 @@ TiddlyWiki5を単一のHTMLファイルとして使用すると、[[Stanford Jav
|
|||||||
|
|
||||||
TiddlyWikiには、パスワード/暗号化に関連する、2つの無関係な機能があることに注意してください:
|
TiddlyWikiには、パスワード/暗号化に関連する、2つの無関係な機能があることに注意してください:
|
||||||
|
|
||||||
* TiddlySpotに保存するときにパスワードを設定する機能。これは、''コントロールパネル'' <<.icon $:/core/images/options-button>>の"保存"タブで行います。
|
* [[Tiddlyhost]]に保存するときにパスワードを設定する機能。これは、''コントロールパネル'' <<.icon $:/core/images/options-button>>の"保存"タブで行います。
|
||||||
* [[Node.js|TiddlyWiki on Node.js]]のサーバ構成で標準のHTTP基本認証を使用する機能。これは、ServerCommandを使用してコマンドラインで実行されます。SSLと組み合わせると、GoogleやDropboxなどのオンラインサービスで得られるのと同じレベルの暗号化転送が実現されますが、ディスク上のデータは暗号化されません
|
* [[Node.js|TiddlyWiki on Node.js]]のサーバ構成で標準のHTTP基本認証を使用する機能。これは、[[Listenコマンド|ListenCommand]]を使用してコマンドラインで実行されます。SSLと組み合わせると、GoogleやDropboxなどのオンラインサービスで得られるのと同じレベルの暗号化転送が実現されますが、ディスク上のデータは暗号化されません
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
caption: ~TiddlyIE
|
|
||||||
color: #4DB6AC
|
|
||||||
community-author: David Jade
|
|
||||||
created: 20131211220000000
|
|
||||||
delivery: Browser Extension
|
|
||||||
description: Internet Explorer用のブラウザ拡張
|
|
||||||
method: save
|
|
||||||
modified: 20241009114650356
|
|
||||||
original-modified: 20200507201415232
|
|
||||||
tags: [[Internet Explorer]] Saving $:/deprecated
|
|
||||||
title: Saving with TiddlyIE
|
|
||||||
ja-title: TiddlyIEでの保存
|
|
||||||
type: text/vnd.tiddlywiki
|
|
||||||
|
|
||||||
<<.deprecated-since "5.3.6">>
|
|
||||||
# TiddlyIEアドオンを次の場所からインストールします:
|
|
||||||
#* https://github.com/davidjade/TiddlyIE/releases
|
|
||||||
# Internet Explorerを再起動します。IEはTiddlyIEアドオンを有効にするように要求します。
|
|
||||||
#> //Microsoft Script Runtime//を有効にするプロンプトが表示される場合もあります。
|
|
||||||
# 次のリンクを[[ダウンロード|Download]]し、空のTiddlyWikiを保存します:
|
|
||||||
#> https://tiddlywiki.com/empty.html
|
|
||||||
# ダウンロードしたファイルを見つけます
|
|
||||||
#* ファイル名を変更することもできますが、拡張子`.html`か`.htm`はそのままとしてください
|
|
||||||
# Internet Explorerでファイルを開きます
|
|
||||||
# サイドバーの''新しいTiddlerを作成します'' <<.icon $:/core/images/new-button>>ボタンを使用して、新しいTiddlerを作成してみてください。Tiddlerのコンテンツを入力し、<<.icon $:/core/images/done-button>> ''編集内容を確定します''ボタンをクリックします
|
|
||||||
# サイドバーの<<.icon $:/core/images/save-button-dynamic>> ''Wikiを保存します''ボタンをクリックして変更を保存します。Internet Explorerは、''名前を付けて保存''ダイアログを表示して、ファイルをローカルに保存することに同意するかどうかを確認します。
|
|
||||||
# ブラウザウィンドウを更新して、変更が正しく保存されたことを確認してください
|
|
||||||
|
|
||||||
+4
-2
@@ -3,11 +3,13 @@ modified: 20260404110322627
|
|||||||
original-modified: 20240619210723396
|
original-modified: 20240619210723396
|
||||||
tags: [[Variable Usage]]
|
tags: [[Variable Usage]]
|
||||||
title: Behaviour of variables invoked via widget attributes
|
title: Behaviour of variables invoked via widget attributes
|
||||||
ja-title:
|
ja-title: ウィジェット属性を介して呼び出される変数の動作
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|tc-first-col-min-width|k
|
|tc-first-col-min-width|k
|
||||||
|!宣言方法|!動作|
|
|!宣言方法|!動作|
|
||||||
|\define|本文テキストに対してパラメータのテキスト置換が実行されます。それ以上の処理は行われません。テキスト置換後の結果が属性の値として使用されます|
|
|\define|本文テキストに対してパラメータのテキスト置換が実行されます。それ以上の処理は行われません。テキスト置換後の結果が属性の値として使用されます|
|
||||||
|<<.wlink SetWidget>>, <<.wlink LetWidget>>, <<.wlink VarsWidget>>, \procedure, \widget|本文はそのまま取得され、属性の値として使用されます|
|
|<<.wlink SetWidget>>, <<.wlink LetWidget>>, <<.wlink VarsWidget>>, \procedure, \widget|本文はそのまま取得され、属性の値として使用されます|
|
||||||
|\function|ファンクション(例: .myfun)が`<div class=<<.myfun>>/>`で呼び出されると、`<div class={{{[function[.myfun]]}}}/>`の同義になります。他のフィルタ付きトランスクルージョン(つまり、三重中括弧)と同様に、最初の結果以外はすべて破棄されます。最初の結果が属性の値として使用されます。この形式で呼び出された場合でも、ファンクションは再帰的に処理されることに注意してください。つまり、ファンクション内のフィルタ式は別のファンクションを呼び出すことができ、処理は継続されます|
|
|\function|ファンクション(例: .myfun)が`<div class=<<.myfun>>/>`で呼び出されると、`<div class={{{[function[.myfun]]}}}/>`の同義になります。他のフィルタ付きトランスクルージョン(つまり、三重中括弧)と同様に、最初の結果以外はすべて破棄されます。最初の結果が属性の値として使用されます。この形式で呼び出された場合でも、ファンクションは再帰的に処理されることに注意してください。つまり、ファンクション内のフィルタ式は別のファンクションを呼び出すことができ、処理は継続されます|
|
||||||
|
|
||||||
|
<<.from-version "5.4.0">> `<<var>>`の代わりに[[複数値変数の属性|Multi-Valued Variable Attribute Values]]構文`((var))`を使用すると、最初の値だけでなく、値のリスト全体が属性に渡されます。これは主に、TranscludeWidgetを介して[[複数値変数|Multi-Valued Variables]]をプロシージャや関数のパラメーターに渡す場合に便利です。
|
||||||
|
|||||||
@@ -1,16 +1,46 @@
|
|||||||
caption: 説明リスト
|
caption: 説明リスト
|
||||||
created: 20131205160424246
|
created: 20131205160424246
|
||||||
modified: 20260221103246354
|
modified: 20260409111112126
|
||||||
original-modified: 20251229110936191
|
original-modified: 20260405082055301
|
||||||
tags: WikiText
|
tags: WikiText
|
||||||
title: Description Lists in WikiText
|
title: Description Lists in WikiText
|
||||||
ja-title: Wikiテキストでの説明リスト
|
ja-title: Wikiテキストでの説明リスト
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
!! 基本的な構文
|
||||||
|
|
||||||
HTML説明リスト(<abbr title="またの名を">別名</abbr> 定義リスト)は次の構文で作成できます:
|
HTML説明リスト(<abbr title="またの名を">別名</abbr> 定義リスト)は次の構文で作成できます:
|
||||||
|
|
||||||
<<wikitext-example src:"; 説明する用語
|
<<wikitext-example src:"; 説明する用語
|
||||||
: その用語の説明/定義
|
: その用語の説明/定義
|
||||||
; 別の用語
|
; 別の用語
|
||||||
: 別の説明/定義
|
: 別の説明/定義
|
||||||
">>
|
">>
|
||||||
|
|
||||||
|
!! 複数の用語と説明
|
||||||
|
|
||||||
|
1つの用語に対して複数の説明を作成することも、1つの説明に対して複数の用語を作成することもできます:
|
||||||
|
|
||||||
|
<<wikitext-example src:"; ねずみ
|
||||||
|
: 体が小さく尾が長い齧歯類
|
||||||
|
: コンピュータを操作するために使用するポインティングデバイス
|
||||||
|
; りんご
|
||||||
|
; なし
|
||||||
|
: バラ科に属する果物の一種
|
||||||
|
">>
|
||||||
|
|
||||||
|
!! 入れ子になった説明リスト
|
||||||
|
|
||||||
|
説明リストはネストして、リストの中にリストを作成することもできます:
|
||||||
|
|
||||||
|
<<wikitext-example src:"; コーヒー
|
||||||
|
: 焙煎したコーヒー豆から作られた飲み物
|
||||||
|
:; ブラックコーヒー
|
||||||
|
:: 添加物を一切使用しないコーヒー
|
||||||
|
:; ミルクコーヒー
|
||||||
|
:: コーヒーにスチームミルクやフォームミルクを加えたもの
|
||||||
|
::; ラテ
|
||||||
|
::: エスプレッソとスチームミルクで作ったコーヒー
|
||||||
|
; ティー
|
||||||
|
: 一般的に茶葉から作られる飲み物
|
||||||
|
">>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ type: text/vnd.tiddlywiki
|
|||||||
|`__下線テキスト__`には二重アンダースコアを使用します|`<u>下線テキスト<u>` |
|
|`__下線テキスト__`には二重アンダースコアを使用します|`<u>下線テキスト<u>` |
|
||||||
|`^^上付き文字^^`テキストには二重サーカムフレックスアクセントを使用します |`<sup>上付き文字</sup>`テキストには二重サーカムフレックスアクセントを使用します |
|
|`^^上付き文字^^`テキストには二重サーカムフレックスアクセントを使用します |`<sup>上付き文字</sup>`テキストには二重サーカムフレックスアクセントを使用します |
|
||||||
|`,,下付き文字,,`テキストには二重カンマを使用します |`<sub>下付き文字</sub>`テキストには二重カンマを使用します |
|
|`,,下付き文字,,`テキストには二重カンマを使用します |`<sub>下付き文字</sub>`テキストには二重カンマを使用します |
|
||||||
|`~~取り消し線~~`テキストには二重チルダ記号を使用します |`<strike>取り消し線</strike>`テキストには二重チルダ記号を使用します |
|
|`~~取り消し線~~`テキストには二重チルダ記号を使用します |`<s>取り消し線</s>`テキストには二重チルダ記号を使用します |
|
||||||
|```等幅文字` ``には単一のバッククォートを使用します |`<code>等幅文字</code>`には単一のバッククォートを使用します |
|
|```等幅文字` ``には単一のバッククォートを使用します |`<code>等幅文字</code>`には単一のバッククォートを使用します |
|
||||||
|`@@ハイライト@@`するには二重@記号を使用します |`<span class="tc-inline-style">ハイライト</span>`するには二重@記号を使用します |
|
|`@@ハイライト@@`するには二重@記号を使用します |`<span class="tc-inline-style">ハイライト</span>`するには二重@記号を使用します |
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
caption: リスト
|
caption: リスト
|
||||||
created: 20131205160257619
|
created: 20131205160257619
|
||||||
modified: 20260225114001626
|
modified: 20260409112505342
|
||||||
original-modified: 20160607093103220
|
original-modified: 20260405094604397
|
||||||
tags: WikiText Lists
|
tags: WikiText Lists
|
||||||
title: Lists in WikiText
|
title: Lists in WikiText
|
||||||
ja-title: Wikiテキストでのリスト
|
ja-title: Wikiテキストでのリスト
|
||||||
@@ -45,6 +45,10 @@ type: text/vnd.tiddlywiki
|
|||||||
#** そしてもう一つ
|
#** そしてもう一つ
|
||||||
">>
|
">>
|
||||||
|
|
||||||
|
! 説明リスト
|
||||||
|
|
||||||
|
{{Description Lists in WikiText}}
|
||||||
|
|
||||||
! CSSクラス
|
! CSSクラス
|
||||||
|
|
||||||
次の表記法を使用して、リストの個々のメンバーにCSSクラスを割り当てることもできます:
|
次の表記法を使用して、リストの個々のメンバーにCSSクラスを割り当てることもできます:
|
||||||
|
|||||||
@@ -2,59 +2,8 @@ caption: マクロ呼び出し
|
|||||||
created: 20150220182252000
|
created: 20150220182252000
|
||||||
modified: 20260321104805874
|
modified: 20260321104805874
|
||||||
original-modified: 20230419103154328
|
original-modified: 20230419103154328
|
||||||
tags: WikiText Macros
|
|
||||||
title: Macro Calls
|
title: Macro Calls
|
||||||
ja-title: マクロ呼び出し
|
ja-title: マクロ呼び出し
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
!! 紹介
|
<<.deprecated-since "5.4.0" "Calls">>
|
||||||
|
|
||||||
このTiddlerでは、[[マクロ|Macros]]を呼び出すさまざまな方法について説明します。
|
|
||||||
|
|
||||||
!! マクロ呼び出しトランスクルージョンのショートカット
|
|
||||||
|
|
||||||
[[マクロ|Macros]]を呼び出すには、マクロ名とパラメータ値を`<<`二重山括弧`>>`で囲みます。
|
|
||||||
|
|
||||||
```
|
|
||||||
<<mymacro param:"これはパラメータ値です">>
|
|
||||||
```
|
|
||||||
|
|
||||||
デフォルトでは、パラメータはマクロの定義と同じ順序でリストします。パラメータに名前とコロンを付けることで、異なる順序でリストすることも可能です。
|
|
||||||
|
|
||||||
パラメータに値が指定されていない場合は、[[マクロ定義|Macro Definitions]]でそのパラメータに指定されているデフォルト値が代わりに使用されます。(デフォルト値が定義されていない場合は、パラメータはブランクになります。)
|
|
||||||
|
|
||||||
各パラメータ値は、`'`シングルクォーテーション`'`、`"`ダブルクォーテーション`"`、`"""`三重ダブルクォーテーション`"""`、または`[[`二重角括弧`]]`で囲むことができます。三重ダブルクォーテーションを使用すると、値にほぼあらゆる文字列を含めることができます。値にスペース、シングルクォーテーション、ダブルクォーテーションが含まれていない場合は、区切り文字は必要ありません。
|
|
||||||
|
|
||||||
この構文の正式な[[説明|Macro Call Syntax]]も利用可能です。
|
|
||||||
|
|
||||||
[[パーサーモード|WikiText parser mode: macro examples]]に関する[[例|Macro Calls in WikiText (Examples)]]と議論を参照してください。
|
|
||||||
|
|
||||||
!! <<.wlink TranscludeWidget>>ウィジェットを用いたマクロ呼び出し
|
|
||||||
|
|
||||||
ショートカット構文は、トランスクルードするマクロの名前を指定する`$variable`属性を持つ<<.wlink TranscludeWidget>>ウィジェットに展開されます。
|
|
||||||
|
|
||||||
```
|
|
||||||
<$transclude $variable="mymacro" param="これはパラメータ値です"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
ウィジェット自体は、パラメータ値を指定できるなど、ショートカット構文よりも柔軟です。
|
|
||||||
|
|
||||||
!! 属性値にマクロ呼び出しを割り当てる
|
|
||||||
|
|
||||||
マクロの実行結果は、ウィジェットやHTML要素の属性に割り当てることができます。マクロの実行結果はWiki変換されませんが、[[パラメータの置換|Macro Parameter Handling]]は実行されます。
|
|
||||||
|
|
||||||
```
|
|
||||||
<div class=<<myclasses "Horizontal">>>
|
|
||||||
...
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
!! フィルターでマクロ呼び出しを使用する
|
|
||||||
|
|
||||||
マクロ呼び出しはフィルタ内で使用できます:
|
|
||||||
|
|
||||||
```
|
|
||||||
<$list filter="[<mymacro param:'value'>]">
|
|
||||||
...
|
|
||||||
</$list>
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
created: 20250208120000000
|
||||||
|
modified: 20260507110511201
|
||||||
|
original-modified: 20250208120000000
|
||||||
|
tags: WikiText [[Widget Attributes]]
|
||||||
|
title: Multi-Valued Variable Attribute Values
|
||||||
|
ja-title: 複数値変数の属性値
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
<<.from-version "5.4.0">> 複数値変数の属性値は、変数名を二重の丸括弧で囲むことで指定できるようになりました。これにより、[[複数値変数|Multi-Valued Variables]]の最初の値だけでなく、すべての値が属性に渡されます。
|
||||||
|
|
||||||
|
```
|
||||||
|
<$transclude $variable="myproc" items=((myvar))/>
|
||||||
|
```
|
||||||
|
|
||||||
|
これは、下位互換性のために最初の値のみを返す[[変数の属性値|Variable Attribute Values]]構文`<<var>>`の複数値版です。この関係は、`<var>`が単一の値を返し、`(var)`がすべての値を返すフィルタオペランドの既存の慣例を反映しています。
|
||||||
|
|
||||||
|
! 複数値変数非対応の属性
|
||||||
|
|
||||||
|
`((var))`が複数値変数をサポートしていないウィジェット属性([[textウィジェット|TextWidget]]の`text`属性など)で使用される場合、最初の値のみが使用されます:
|
||||||
|
|
||||||
|
```
|
||||||
|
<$text text=((myvar))/>
|
||||||
|
```
|
||||||
|
|
||||||
|
! プロシージャに複数値変数を渡す
|
||||||
|
|
||||||
|
この構文の主なユースケースは、複数の値を持つ変数を`$transclude`パイプラインを通してプロシージャや関数に渡すことです:
|
||||||
|
|
||||||
|
```
|
||||||
|
\procedure showItems(itemList)
|
||||||
|
<$text text={{{ [(itemList)join[-]] }}}/>
|
||||||
|
\end
|
||||||
|
|
||||||
|
<$let items={{{ [all[tiddlers]sort[]] }}}>
|
||||||
|
<$transclude $variable="showItems" itemList=((items))/>
|
||||||
|
</$let>
|
||||||
|
```
|
||||||
|
|
||||||
|
この例では、`items`に格納されているTiddlerタイトルの完全なリストが`showItems`プロシージャの`itemList`パラメータに渡されます。
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
created: 20220817153236691
|
created: 20220817153236691
|
||||||
modified: 20260404112841923
|
modified: 20260409114243582
|
||||||
original-modified: 20221010074314452
|
original-modified: 20221010074314452
|
||||||
tags: [[Tables in WikiText]]
|
tags: [[Tables in WikiText]]
|
||||||
title: Tables in WikiText CSS Utility Classes
|
title: Tables in WikiText CSS Utility Classes
|
||||||
@@ -81,3 +81,14 @@ full-tiddler-widthである表に、左右の余白を追加するには、`tc-m
|
|||||||
| セル1|<$edit-text tiddler="$:/temp/test-table-input" tag="input" field="test"/> |
|
| セル1|<$edit-text tiddler="$:/temp/test-table-input" tag="input" field="test"/> |
|
||||||
|^ [[Tiddlerへのリンク|Link to a tiddler]]<br>さらに詳しい説明|<$edit-text tiddler="$:/temp/test-table-input" field="text"/> |
|
|^ [[Tiddlerへのリンク|Link to a tiddler]]<br>さらに詳しい説明|<$edit-text tiddler="$:/temp/test-table-input" field="text"/> |
|
||||||
""">>
|
""">>
|
||||||
|
|
||||||
|
!! 行スタイルを交互にした表
|
||||||
|
|
||||||
|
独自のスタイルシートを作成する必要があります。[[スタイルシートの使用|Using Stylesheets]]を参照してください。考え方は、`:nth-of-type(even/odd)`を使用してルールを作成することです。例:
|
||||||
|
|
||||||
|
```
|
||||||
|
.myclass tbody tr:nth-of-type(even) { background-color: <<color tiddler-editor-fields-even>>; }
|
||||||
|
.myclass tbody tr:nth-of-type(odd) { background-color: <<color tiddler-editor-fields-odd>>; }
|
||||||
|
```
|
||||||
|
|
||||||
|
<<.note """~TiddlyWikiは、テーブルの行に`evenRow`クラスと`oddRow`クラスを自動的に適用します。ただし、これらのクラスの使用は推奨されません。行は'even'(想定とは逆)で始まり、ヘッダー、フッター、本文のいずれに表示されるかに関わらず、テーブルのすべての行は単一の結合された行セットとして扱われます。""">>
|
||||||
@@ -66,6 +66,28 @@ or, when used with a template, `{{{ [tag[mechanism]]||TemplateTitle }}}` expands
|
|||||||
|
|
||||||
<<.tip "//Internals//プラグインをインストールすると、生成されたウィジェットツリーをエディターのプレビューペインに表示できます">>
|
<<.tip "//Internals//プラグインをインストールすると、生成されたウィジェットツリーをエディターのプレビューペインに表示できます">>
|
||||||
|
|
||||||
|
!! 複数値変数の表示
|
||||||
|
|
||||||
|
<<.from-version "5.4.0">> `((var))`構文をインラインで使用して、[[複数値変数|Multi-Valued Variables]]の値を、デフォルトではカンマとスペースで連結して表示できるようになりました:
|
||||||
|
|
||||||
|
```
|
||||||
|
((myvar))
|
||||||
|
((myvar||:))
|
||||||
|
```
|
||||||
|
|
||||||
|
オプションの`||`区切り文字は、カスタムの区切り文字を指定します。
|
||||||
|
|
||||||
|
!! インラインフィルター表示
|
||||||
|
|
||||||
|
<<.from-version "5.4.0">> `(((filter)))`構文によりフィルタ式の結果をデフォルトではカンマとスペースで連結してインラインで表示します:
|
||||||
|
|
||||||
|
```
|
||||||
|
((( [all[tiddlers]sort[]] )))
|
||||||
|
((( [all[tiddlers]sort[]] ||: )))
|
||||||
|
```
|
||||||
|
|
||||||
|
オプションの`||`区切り文字は、カスタムの区切り文字を指定します。これは、フィルタリングされたトランスクルージョン構文`{{{ }}}`に対応するインライン表示の表現です。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
参照:
|
参照:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type: text/vnd.tiddlywiki
|
|||||||
* [[マクロ/変数のトランスクルージョン|Variable Attribute Values]]
|
* [[マクロ/変数のトランスクルージョン|Variable Attribute Values]]
|
||||||
* [[フィルター式の結果として|Filtered Attribute Values]]
|
* [[フィルター式の結果として|Filtered Attribute Values]]
|
||||||
* <<.from-version "5.3.0">> [[指定された文字列に対してフィルタと変数置換を実行した結果として|Substituted Attribute Values]]
|
* <<.from-version "5.3.0">> [[指定された文字列に対してフィルタと変数置換を実行した結果として|Substituted Attribute Values]]
|
||||||
|
* <<.from-version "5.4.0">> [[複数値変数の参照として|Multi-Valued Variable Attribute Values]]
|
||||||
|
|
||||||
|属性タイプ|構文|h
|
|属性タイプ|構文|h
|
||||||
|リテラル |スペースを含まない値には、シングルクォーテーション、ダブルクォーテーション、トリプルクォーテーション、またはクォーテーションなし |
|
|リテラル |スペースを含まない値には、シングルクォーテーション、ダブルクォーテーション、トリプルクォーテーション、またはクォーテーションなし |
|
||||||
@@ -20,9 +21,9 @@ type: text/vnd.tiddlywiki
|
|||||||
|変数 |マクロまたは変数呼び出しを囲む二重の山括弧 |
|
|変数 |マクロまたは変数呼び出しを囲む二重の山括弧 |
|
||||||
|フィルタリング結果 |フィルター式を囲む三重の中括弧|
|
|フィルタリング結果 |フィルター式を囲む三重の中括弧|
|
||||||
|置換結果|置換処理を行うテキストの、単一または三重のバッククォート|
|
|置換結果|置換処理を行うテキストの、単一または三重のバッククォート|
|
||||||
|
|複数値変数 |変数名を囲む二重の丸括弧 |
|
||||||
|
|
||||||
|
<$list filter="[[Literal Attribute Values]] [[Transcluded Attribute Values]] [[Variable Attribute Values]] [[Filtered Attribute Values]] [[Substituted Attribute Values]] [[Multi-Valued Variable Attribute Values]]">
|
||||||
<$list filter="[[Literal Attribute Values]] [[Transcluded Attribute Values]] [[Variable Attribute Values]] [[Filtered Attribute Values]] [[Substituted Attribute Values]]">
|
|
||||||
<$link><h1><$view tiddler=<<currentTiddler>> field=ja-title/></h1></$link>
|
<$link><h1><$view tiddler=<<currentTiddler>> field=ja-title/></h1></$link>
|
||||||
<$transclude mode="block"/>
|
<$transclude mode="block"/>
|
||||||
</$list>
|
</$list>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "Performance measurement edition",
|
|
||||||
"plugins": [
|
|
||||||
"tiddlywiki/performance"
|
|
||||||
],
|
|
||||||
"themes": [
|
|
||||||
"tiddlywiki/vanilla",
|
|
||||||
"tiddlywiki/snowwhite"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
title: $:/config/LocalPluginLibrary
|
title: $:/config/LocalPluginLibrary
|
||||||
tags: $:/tags/PluginLibrary
|
tags: $:/tags/PluginLibrary
|
||||||
url: http://127.0.0.1:8080/prerelease/library/v5.5.0/index.html
|
url: http://127.0.0.1:8080/prerelease/library/v5.4.1/index.html
|
||||||
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease Local)
|
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease Local)
|
||||||
|
|
||||||
A locally installed version of the official ~TiddlyWiki plugin library at tiddlywiki.com for testing and debugging. //Requires a local web server to share the library//
|
A locally installed version of the official ~TiddlyWiki plugin library at tiddlywiki.com for testing and debugging. //Requires a local web server to share the library//
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
title: $:/config/OfficialPluginLibrary
|
title: $:/config/OfficialPluginLibrary
|
||||||
tags: $:/tags/PluginLibrary
|
tags: $:/tags/PluginLibrary
|
||||||
url: https://tiddlywiki.com/prerelease/library/v5.5.0/index.html
|
url: https://tiddlywiki.com/prerelease/library/v5.4.1/index.html
|
||||||
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease)
|
caption: {{$:/language/OfficialPluginLibrary}} (Prerelease)
|
||||||
|
|
||||||
The prerelease version of the official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.
|
The prerelease version of the official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team.
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
"tiddlywiki/jszip",
|
"tiddlywiki/jszip",
|
||||||
"tiddlywiki/confetti",
|
"tiddlywiki/confetti",
|
||||||
"tiddlywiki/tour",
|
"tiddlywiki/tour",
|
||||||
"tiddlywiki/performance",
|
|
||||||
"tiddlywiki/dom-to-image"
|
"tiddlywiki/dom-to-image"
|
||||||
],
|
],
|
||||||
"themes": [
|
"themes": [
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const crossPlatformIndexPath = indexPath.replace(/^\/+/, "");
|
|||||||
|
|
||||||
test("get started link", async ({ page }) => {
|
test("get started link", async ({ page }) => {
|
||||||
// The tests can take a while to run
|
// The tests can take a while to run
|
||||||
const timeout = 1000 * 30;
|
const timeout = 1000 * 60;
|
||||||
test.setTimeout(timeout);
|
test.setTimeout(timeout);
|
||||||
|
|
||||||
// Load the generated test TW html
|
// Load the generated test TW html
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Fast test runner that boots the test edition and runs selected test specs.
|
||||||
|
Skips the expensive --rendertiddler step from --build index.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
node test-parser-quick.js [test-files...]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
node test-parser-quick.js # Run ALL specs
|
||||||
|
node test-parser-quick.js test-wikitext-parser # Run one spec file
|
||||||
|
node test-parser-quick.js test-wikitext-parser test-filters # Run multiple spec files
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var $tw = require("../../boot/boot.js").TiddlyWiki();
|
||||||
|
|
||||||
|
$tw.boot.argv = ["editions/test"];
|
||||||
|
|
||||||
|
// Disable startup modules that aren't needed for tests
|
||||||
|
$tw.boot.disabledStartupModules = [
|
||||||
|
"favicon", "password", "browser-messaging", "info",
|
||||||
|
"render", "rootwidget", "story", "windows"
|
||||||
|
];
|
||||||
|
|
||||||
|
$tw.boot.boot(function() {
|
||||||
|
var args = process.argv.slice(2);
|
||||||
|
var allTests = $tw.wiki.filterTiddlers("[all[tiddlers+shadows]type[application/javascript]tag[$:/tags/test-spec]]");
|
||||||
|
|
||||||
|
// Filter test tiddlers if arguments provided
|
||||||
|
var testsToRun;
|
||||||
|
if(args.length > 0) {
|
||||||
|
testsToRun = allTests.filter(function(title) {
|
||||||
|
return args.some(function(arg) {
|
||||||
|
return title.toLowerCase().indexOf(arg.toLowerCase()) !== -1;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if(testsToRun.length === 0) {
|
||||||
|
console.error("No test files matched: " + args.join(", "));
|
||||||
|
console.error("Available test files:");
|
||||||
|
allTests.forEach(function(t) { console.error(" " + t); });
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
testsToRun = allTests;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override the test filter to only include our selected tests
|
||||||
|
var titlesSet = Object.create(null);
|
||||||
|
testsToRun.forEach(function(t) { titlesSet[t] = true; });
|
||||||
|
|
||||||
|
var origFilterTiddlers = $tw.wiki.filterTiddlers.bind($tw.wiki);
|
||||||
|
$tw.wiki.filterTiddlers = function(filterString) {
|
||||||
|
var result = origFilterTiddlers.apply(null, arguments);
|
||||||
|
if(filterString.indexOf("$:/tags/test-spec") !== -1) {
|
||||||
|
return result.filter(function(t) { return titlesSet[t]; });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Running " + testsToRun.length + " of " + allTests.length + " test files");
|
||||||
|
|
||||||
|
// Use the jasmine plugin's own runTests function
|
||||||
|
var jasmine = $tw.modules.execute("$:/plugins/tiddlywiki/jasmine/jasmine-plugin.js");
|
||||||
|
jasmine.runTests(function(err) {
|
||||||
|
if(err) {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
/*\
|
||||||
|
title: test-deprecated.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Regression-guard tests for $:/core/modules/utils/deprecated.js.
|
||||||
|
|
||||||
|
Locks in pre-5.4.0 tolerant behaviour of $tw.utils helpers that regressed
|
||||||
|
in PR #9251 (one-line modern equivalents diverge on edge-case inputs).
|
||||||
|
Without the companion restoration patch to core/modules/utils/deprecated.js:
|
||||||
|
8 specs fail. With the patch: green.
|
||||||
|
|
||||||
|
The addClass/removeClass/toggleClass specs at the end were moved from
|
||||||
|
test-utils.js — RSOD guard for the SampleWizard report (class field
|
||||||
|
"aaa bbb" crashing classList.add with InvalidCharacterError).
|
||||||
|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("deprecated.js — backwards-compat",function() {
|
||||||
|
|
||||||
|
describe("$tw.utils.repeat",function() {
|
||||||
|
it("returns '' for zero or negative count (does not throw)",function() {
|
||||||
|
expect($tw.utils.repeat("x",0)).toBe("");
|
||||||
|
expect($tw.utils.repeat("x",-1)).toBe("");
|
||||||
|
expect($tw.utils.repeat("x",-100)).toBe("");
|
||||||
|
});
|
||||||
|
it("coerces null/undefined str rather than throwing",function() {
|
||||||
|
expect($tw.utils.repeat(null,3)).toBe("nullnullnull");
|
||||||
|
expect($tw.utils.repeat(undefined,2)).toBe("undefinedundefined");
|
||||||
|
});
|
||||||
|
it("still works for normal inputs",function() {
|
||||||
|
expect($tw.utils.repeat("ab",3)).toBe("ababab");
|
||||||
|
expect($tw.utils.repeat("-",5)).toBe("-----");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("$tw.utils.startsWith / $tw.utils.endsWith",function() {
|
||||||
|
it("tolerate a RegExp search argument without throwing",function() {
|
||||||
|
// Old impl uses substring coercion; native String.prototype.startsWith
|
||||||
|
// throws TypeError when passed a RegExp.
|
||||||
|
expect(function() { $tw.utils.startsWith("abc",/a/); }).not.toThrow();
|
||||||
|
expect(function() { $tw.utils.endsWith("abc",/c/); }).not.toThrow();
|
||||||
|
});
|
||||||
|
it("still match normal string inputs",function() {
|
||||||
|
expect($tw.utils.startsWith("abcdef","abc")).toBe(true);
|
||||||
|
expect($tw.utils.startsWith("abcdef","xyz")).toBe(false);
|
||||||
|
expect($tw.utils.endsWith("abcdef","def")).toBe(true);
|
||||||
|
expect($tw.utils.endsWith("abcdef","xyz")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("$tw.utils.stringifyNumber",function() {
|
||||||
|
it("coerces null/undefined via string-concat rather than throwing",function() {
|
||||||
|
expect($tw.utils.stringifyNumber(null)).toBe("null");
|
||||||
|
expect($tw.utils.stringifyNumber(undefined)).toBe("undefined");
|
||||||
|
});
|
||||||
|
it("still returns a number's string form",function() {
|
||||||
|
expect($tw.utils.stringifyNumber(42)).toBe("42");
|
||||||
|
expect($tw.utils.stringifyNumber(-3.14)).toBe("-3.14");
|
||||||
|
expect($tw.utils.stringifyNumber(0)).toBe("0");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("$tw.utils.domContains",function() {
|
||||||
|
// Stub nodes expose both .contains() and .compareDocumentPosition() so
|
||||||
|
// both the old (compareDocumentPosition & 16 → number) and new
|
||||||
|
// (a !== b && a.contains(b) → boolean) forms can be observed.
|
||||||
|
function makeNode(children) {
|
||||||
|
children = children || [];
|
||||||
|
var self;
|
||||||
|
self = {
|
||||||
|
contains: function(other) {
|
||||||
|
if(other === self) { return true; }
|
||||||
|
return children.some(function(c) { return c === other || c.contains(other); });
|
||||||
|
},
|
||||||
|
compareDocumentPosition: function(other) {
|
||||||
|
if(other === self) { return 0; }
|
||||||
|
return self.contains(other) ? 16 : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
it("returns strictly boolean true/false, not a bit-mask number",function() {
|
||||||
|
var child = makeNode();
|
||||||
|
var parent = makeNode([child]);
|
||||||
|
var unrelated = makeNode();
|
||||||
|
expect($tw.utils.domContains(parent,child)).toBe(true);
|
||||||
|
expect($tw.utils.domContains(parent,unrelated)).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns false for domContains(x, x)",function() {
|
||||||
|
var a = makeNode();
|
||||||
|
expect($tw.utils.domContains(a,a)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("$tw.utils.hasClass",function() {
|
||||||
|
it("returns false for null/undefined element without throwing",function() {
|
||||||
|
expect(function() { $tw.utils.hasClass(null,"foo"); }).not.toThrow();
|
||||||
|
expect($tw.utils.hasClass(null,"foo")).toBe(false);
|
||||||
|
expect(function() { $tw.utils.hasClass(undefined,"foo"); }).not.toThrow();
|
||||||
|
expect($tw.utils.hasClass(undefined,"foo")).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns strictly false (not undefined) for elements without classList",function() {
|
||||||
|
expect($tw.utils.hasClass({},"foo")).toBe(false);
|
||||||
|
});
|
||||||
|
it("delegates to classList.contains for real elements",function() {
|
||||||
|
var el = { classList: { contains: function(c) { return c === "a" || c === "b"; } } };
|
||||||
|
expect($tw.utils.hasClass(el,"a")).toBe(true);
|
||||||
|
expect($tw.utils.hasClass(el,"b")).toBe(true);
|
||||||
|
expect($tw.utils.hasClass(el,"c")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// getLocationPath reads window.location: specs pend in Node (no `window`
|
||||||
|
// in the TW5 sandbox) and use history.replaceState in the browser —
|
||||||
|
// assigning to window.location would trigger a navigation and reload.
|
||||||
|
describe("$tw.utils.getLocationPath",function() {
|
||||||
|
var originalUrl;
|
||||||
|
beforeEach(function() {
|
||||||
|
if(!$tw.browser) { return; }
|
||||||
|
originalUrl = window.location.href;
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
if(!$tw.browser) { return; }
|
||||||
|
history.replaceState(null,"",originalUrl);
|
||||||
|
});
|
||||||
|
it("preserves the query string in the returned path",function() {
|
||||||
|
if(!$tw.browser) { pending("browser-only: requires window.location - run in browser"); return; }
|
||||||
|
history.replaceState(null,"","?lang=de&x=1#Intro");
|
||||||
|
var path = $tw.utils.getLocationPath();
|
||||||
|
expect(path).toContain("?lang=de&x=1");
|
||||||
|
expect(path).not.toContain("#Intro");
|
||||||
|
});
|
||||||
|
it("strips the hash fragment",function() {
|
||||||
|
if(!$tw.browser) { pending("browser-only: requires window.location - run in browser"); return; }
|
||||||
|
history.replaceState(null,"","#SomeTiddler");
|
||||||
|
// Sanity check: replaceState actually changed the hash.
|
||||||
|
expect(window.location.hash).toBe("#SomeTiddler");
|
||||||
|
var path = $tw.utils.getLocationPath();
|
||||||
|
expect(path).not.toContain("#");
|
||||||
|
// Rebuild expected href without the hash — works on http(s):// and file://.
|
||||||
|
var expected = window.location.href.split("#")[0];
|
||||||
|
expect(path).toBe(expected);
|
||||||
|
});
|
||||||
|
it("includes the query string when no hash is present",function() {
|
||||||
|
if(!$tw.browser) { pending("browser-only: requires window.location - run in browser"); return; }
|
||||||
|
history.replaceState(null,"","?x=1");
|
||||||
|
var path = $tw.utils.getLocationPath();
|
||||||
|
expect(path).toMatch(/\?x=1$/);
|
||||||
|
expect(path).not.toContain("#");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Regression guard: classList.add/remove/toggle throw InvalidCharacterError on
|
||||||
|
// whitespace. Manual repro: tw5-com #SampleWizard, class="aaa bbb", Done, popup
|
||||||
|
// -> OK -> nested popup -> RSOD. Stub classList mirrors real DOM semantics
|
||||||
|
// (reject whitespace, de-dupe on add, no-op on remove of missing token).
|
||||||
|
describe("addClass/removeClass/toggleClass",function() {
|
||||||
|
function makeEl() {
|
||||||
|
var tokens = [];
|
||||||
|
function reject(t) { if(/\s/.test(t)) { throw new Error("InvalidCharacterError: '" + t + "'"); } }
|
||||||
|
return {
|
||||||
|
classList: {
|
||||||
|
add: function() {
|
||||||
|
for(var i = 0; i < arguments.length; i++) {
|
||||||
|
reject(arguments[i]);
|
||||||
|
if(tokens.indexOf(arguments[i]) === -1) { tokens.push(arguments[i]); }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove: function() {
|
||||||
|
for(var i = 0; i < arguments.length; i++) {
|
||||||
|
reject(arguments[i]);
|
||||||
|
var idx = tokens.indexOf(arguments[i]);
|
||||||
|
if(idx !== -1) { tokens.splice(idx,1); }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggle: function(cls,status) {
|
||||||
|
reject(cls);
|
||||||
|
var has = tokens.indexOf(cls) !== -1;
|
||||||
|
var want = status === undefined ? !has : status;
|
||||||
|
if(want && !has) { tokens.push(cls); }
|
||||||
|
if(!want && has) { tokens.splice(tokens.indexOf(cls),1); }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_tokens: tokens
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
it("splits on every ASCII-whitespace flavour (space, tab, newline, CR, mixed runs, leading/trailing)",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
$tw.utils.addClass(el," a\tb\nc\r\nd \t e ");
|
||||||
|
expect(el._tokens).toEqual(["a","b","c","d","e"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("splits on Unicode whitespace too (U+00A0 non-breaking space, a common paste-in hazard)",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
$tw.utils.addClass(el,"a\u00A0b");
|
||||||
|
expect(el._tokens).toEqual(["a","b"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("de-duplicates tokens within one call and across calls",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
$tw.utils.addClass(el,"x x y");
|
||||||
|
$tw.utils.addClass(el,"y z");
|
||||||
|
expect(el._tokens).toEqual(["x","y","z"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("remove is a no-op for missing tokens and tolerates mixed-presence input",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
$tw.utils.addClass(el,"a b");
|
||||||
|
$tw.utils.removeClass(el,"b c d");
|
||||||
|
expect(el._tokens).toEqual(["a"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggle with no status flips each token independently",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
$tw.utils.addClass(el,"a");
|
||||||
|
$tw.utils.toggleClass(el,"a b");
|
||||||
|
expect(el._tokens).toEqual(["b"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggle with status=true/false forces state regardless of current",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
$tw.utils.addClass(el,"a");
|
||||||
|
$tw.utils.toggleClass(el,"a b",true);
|
||||||
|
expect(el._tokens).toEqual(["a","b"]);
|
||||||
|
$tw.utils.toggleClass(el,"a b",false);
|
||||||
|
expect(el._tokens).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is a silent no-op for whitespace-only / empty / non-string / null / undefined className",function() {
|
||||||
|
var el = makeEl();
|
||||||
|
var inputs = ["", " \t\n ", null, undefined, 42, {}, ["a"]];
|
||||||
|
inputs.forEach(function(v) {
|
||||||
|
expect(function() { $tw.utils.addClass(el,v); }).not.toThrow();
|
||||||
|
expect(function() { $tw.utils.removeClass(el,v); }).not.toThrow();
|
||||||
|
expect(function() { $tw.utils.toggleClass(el,v); }).not.toThrow();
|
||||||
|
});
|
||||||
|
expect(el._tokens).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is a silent no-op when element has no classList (SVG in old browsers, detached nodes, stubs)",function() {
|
||||||
|
var el = {};
|
||||||
|
expect(function() { $tw.utils.addClass(el,"a b"); }).not.toThrow();
|
||||||
|
expect(function() { $tw.utils.removeClass(el,"a b"); }).not.toThrow();
|
||||||
|
expect(function() { $tw.utils.toggleClass(el,"a b",true); }).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/tests/test-edit-widgets/helpers
|
||||||
|
type: application/javascript
|
||||||
|
module-type: library
|
||||||
|
|
||||||
|
Shared test helpers for the edit-text / edit-widget test suite. Every
|
||||||
|
helper here is usable in fakedom — nothing reaches for real DOM layout,
|
||||||
|
iframe documents, or synthetic events.
|
||||||
|
|
||||||
|
Import with:
|
||||||
|
|
||||||
|
var helpers = require("$:/tests/test-edit-widgets/helpers");
|
||||||
|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var widget = require("$:/core/modules/widgets/widget.js");
|
||||||
|
|
||||||
|
/*
|
||||||
|
makeWiki: build a fresh in-memory test wiki. Pass an array of tiddler
|
||||||
|
specs to seed. Use `seedTestTiddler: true` in the options to also add a
|
||||||
|
convenience "TestTiddler" (empty text) that many tests default to.
|
||||||
|
*/
|
||||||
|
exports.makeWiki = function(tiddlers,options) {
|
||||||
|
options = options || {};
|
||||||
|
var wiki = $tw.test.wiki();
|
||||||
|
if(options.seedTestTiddler) {
|
||||||
|
wiki.addTiddlers([{title: "TestTiddler", text: ""}]);
|
||||||
|
}
|
||||||
|
if(tiddlers) {
|
||||||
|
wiki.addTiddlers(tiddlers);
|
||||||
|
}
|
||||||
|
return wiki;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
parseAndRender: parse a wikitext snippet, build the widget tree against
|
||||||
|
the fakedom, and render into a detached wrapper <div>. Returns both the
|
||||||
|
top-level widgetNode (for refresh/teardown) and the wrapper (for DOM
|
||||||
|
inspection).
|
||||||
|
*/
|
||||||
|
exports.parseAndRender = function(widgetText,wiki) {
|
||||||
|
var parser = wiki.parseText("text/vnd.tiddlywiki",widgetText);
|
||||||
|
var widgetNode = new widget.widget(
|
||||||
|
{type: "widget", children: parser.tree},
|
||||||
|
{wiki: wiki, document: $tw.fakeDocument}
|
||||||
|
);
|
||||||
|
$tw.fakeDocument.setSequenceNumber(0);
|
||||||
|
var wrapper = $tw.fakeDocument.createElement("div");
|
||||||
|
widgetNode.render(wrapper,null);
|
||||||
|
return {widgetNode: widgetNode, wrapper: wrapper};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
findEditTextWidget: walk a widget tree and return the first
|
||||||
|
EditTextWidget instance (identified by having both an `engine` and an
|
||||||
|
`editTag` property). Needed because parseAndRender returns the
|
||||||
|
enclosing "widget" root, not the <$edit-text> itself.
|
||||||
|
*/
|
||||||
|
exports.findEditTextWidget = function findEditTextWidget(node) {
|
||||||
|
if(node.engine && node.editTag !== undefined) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if(node.children) {
|
||||||
|
for(var i = 0; i < node.children.length; i++) {
|
||||||
|
var found = findEditTextWidget(node.children[i]);
|
||||||
|
if(found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
renderFromWikitext: parse+render a wikitext snippet and return a bundle
|
||||||
|
{ widget, root, wrapper, wiki } exposing every layer. Used by the
|
||||||
|
attribute-propagation and refresh tests which need access to the wiki
|
||||||
|
and wrapper after render.
|
||||||
|
*/
|
||||||
|
exports.renderFromWikitext = function(widgetText,wiki) {
|
||||||
|
wiki = wiki || exports.makeWiki([{title: "TestTiddler", text: "hello"}]);
|
||||||
|
var result = exports.parseAndRender(widgetText,wiki);
|
||||||
|
return {
|
||||||
|
widget: exports.findEditTextWidget(result.widgetNode),
|
||||||
|
root: result.widgetNode,
|
||||||
|
wrapper: result.wrapper,
|
||||||
|
wiki: wiki
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
renderFromAttrs: build an <$edit-text> tag from an attrs object, render
|
||||||
|
it, and return just the EditTextWidget instance (not a full bundle).
|
||||||
|
Used by tests that only need the widget and its DOM node.
|
||||||
|
*/
|
||||||
|
exports.renderFromAttrs = function(attrs,wiki,tiddlerTitle) {
|
||||||
|
wiki = wiki || exports.makeWiki(null,{seedTestTiddler: true});
|
||||||
|
var attrStr = Object.keys(attrs).map(function(k) {
|
||||||
|
return k + "=\"" + attrs[k] + "\"";
|
||||||
|
}).join(" ");
|
||||||
|
var text = "<$edit-text tiddler=\"" + (tiddlerTitle || "TestTiddler") + "\" " + attrStr + "/>";
|
||||||
|
var rendered = exports.parseAndRender(text,wiki);
|
||||||
|
return exports.findEditTextWidget(rendered.widgetNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
refresh: drive a widget-tree refresh cycle for a given list of changed
|
||||||
|
tiddler titles, mimicking what the core does after wiki.addTiddler.
|
||||||
|
*/
|
||||||
|
exports.refresh = function(rootWidget,wrapper,changedTitles) {
|
||||||
|
var changed = {};
|
||||||
|
(changedTitles || []).forEach(function(t) { changed[t] = true; });
|
||||||
|
rootWidget.refresh(changed,wrapper,null);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
editorValue: read the editor's current text in a way that works with
|
||||||
|
fakedom. SimpleEngine seeds a textarea by appending a text-node child,
|
||||||
|
while inputs use the `value` attribute. Later updates go through
|
||||||
|
updateDomNodeText which sets `.value` on both.
|
||||||
|
*/
|
||||||
|
exports.editorValue = function(w) {
|
||||||
|
var dn = w.engine.domNode;
|
||||||
|
// If .value has been set explicitly, prefer it (covers refresh updates)
|
||||||
|
if(dn.attributes && dn.attributes.value !== undefined) {
|
||||||
|
return dn.value;
|
||||||
|
}
|
||||||
|
// Otherwise for textareas read the concatenated child text nodes
|
||||||
|
if(dn.tag === "textarea" && dn.children && dn.children.length) {
|
||||||
|
return dn.children.map(function(n) { return n.textContent || ""; }).join("");
|
||||||
|
}
|
||||||
|
return dn.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Note: jasmine-specific helpers (spies) belong in the test-spec files
|
||||||
|
// themselves. TW library modules run in a sandbox that does not expose
|
||||||
|
// `jasmine`, so anything that calls jasmine.createSpy must live in a
|
||||||
|
// file tagged with $:/tags/test-spec.
|
||||||
@@ -0,0 +1,353 @@
|
|||||||
|
/*\
|
||||||
|
title: test-edit-text-widget-attributes.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Covers edit-text widget behaviour that is observable in fakedom: value
|
||||||
|
resolution, tag/type selection, DOM attribute propagation, save-back
|
||||||
|
and refresh paths.
|
||||||
|
|
||||||
|
Each spec has a "manual:" comment with a by-hand recipe so a reviewer
|
||||||
|
can sanity-check the test against a live wiki.
|
||||||
|
|
||||||
|
NOT covered (need a real browser — Playwright territory):
|
||||||
|
* `focus`, `focusSelectFromStart`/`End`, `focusPopup`, `cancelPopups` — real DOM focus + selection APIs
|
||||||
|
* `inputActions`, `fileDrop` — synthetic events not dispatched by fakedom
|
||||||
|
* Pixel measurement / growth of auto-height textareas — `$tw.utils.resizeTextAreaToFit` needs real layout
|
||||||
|
* FramedEngine — requires a real iframe document
|
||||||
|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("Edit-text widget (attribute propagation and value handling)", function() {
|
||||||
|
|
||||||
|
// Shared helpers live in $:/tests/test-edit-widgets/helpers. See that
|
||||||
|
// file for what each helper prepares and where it is used across the suite.
|
||||||
|
var helpers = require("$:/tests/test-edit-widgets/helpers");
|
||||||
|
|
||||||
|
// Local aliases — `render`, `editorValue` etc. read better inline
|
||||||
|
// than `helpers.renderFromWikitext`. Each test supplies its own
|
||||||
|
// tiddlers, so makeWiki is NOT seeded with TestTiddler here.
|
||||||
|
var makeWiki = helpers.makeWiki;
|
||||||
|
var render = helpers.renderFromWikitext;
|
||||||
|
var refresh = helpers.refresh;
|
||||||
|
var editorValue = helpers.editorValue;
|
||||||
|
var findEditTextWidget = helpers.findEditTextWidget;
|
||||||
|
|
||||||
|
// spyOnSetText stays file-local because it depends on `jasmine`,
|
||||||
|
// which is only in scope for $:/tags/test-spec modules — NOT for
|
||||||
|
// library modules loaded via require().
|
||||||
|
function spyOnSetText(w) {
|
||||||
|
var spy = jasmine.createSpy("setText");
|
||||||
|
w.engine.setText = spy;
|
||||||
|
return spy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Value and type resolution (getEditInfo)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("value and type resolution", function() {
|
||||||
|
|
||||||
|
// manual: create tiddler "TestTiddler" with text "hello", then in any
|
||||||
|
// tiddler render <$edit-text tiddler='TestTiddler'/> — the
|
||||||
|
// textarea should show "hello".
|
||||||
|
it("reads the text field of an existing tiddler", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\"/>");
|
||||||
|
expect(editorValue(w.widget)).toBe("hello");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: give a tiddler "T" a caption="Hi" field, then render
|
||||||
|
// <$edit-text tiddler="T" field="caption"/> — input should show "Hi".
|
||||||
|
it("reads an arbitrary named field", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "body", caption: "Hi"}]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\" field=\"caption\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("Hi");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: create tiddler "T" without a caption field, then render
|
||||||
|
// <$edit-text tiddler="T" field="caption" default="fallback"/> —
|
||||||
|
// input should show "fallback".
|
||||||
|
it("falls back to the default attribute when the field is missing", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "body"}]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\" field=\"caption\" default=\"fallback\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("fallback");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: create tiddler "T" without a caption field, render
|
||||||
|
// <$edit-text tiddler="T" field="caption"/> — input should be empty.
|
||||||
|
it("falls back to an empty string when neither field nor default is present", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "body"}]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\" field=\"caption\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: ensure NO tiddler named "Missing" exists, then render
|
||||||
|
// <$edit-text tiddler="Missing" field="title"/> — input should show
|
||||||
|
// "Missing" (the title field of a not-yet-existing tiddler defaults
|
||||||
|
// to the tiddler title).
|
||||||
|
it("uses the tiddler title as the default for the title field on missing tiddlers", function() {
|
||||||
|
var wiki = makeWiki();
|
||||||
|
var w = render("<$edit-text tiddler=\"Missing\" field=\"title\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("Missing");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: ensure NO tiddler named "Missing" exists, then render
|
||||||
|
// <$edit-text tiddler="Missing" default="seed"/> — textarea should
|
||||||
|
// show "seed" until you type into it.
|
||||||
|
it("uses default= on a missing tiddler", function() {
|
||||||
|
var wiki = makeWiki();
|
||||||
|
var w = render("<$edit-text tiddler=\"Missing\" default=\"seed\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("seed");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: create a data tiddler "Data" with type
|
||||||
|
// "application/x-tiddler-dictionary" and text "one: 1\ntwo: 2",
|
||||||
|
// then render <$edit-text tiddler="Data" index="two"/> — input
|
||||||
|
// should show "2".
|
||||||
|
it("reads a value from a data tiddler index", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "Data", type: "application/x-tiddler-dictionary", text: "one: 1\ntwo: 2"}
|
||||||
|
]);
|
||||||
|
var w = render("<$edit-text tiddler=\"Data\" index=\"two\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("2");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: with the same "Data" dictionary but no "missing" key,
|
||||||
|
// render <$edit-text tiddler="Data" index="missing" default="none"/> —
|
||||||
|
// input should show "none".
|
||||||
|
it("uses default= for a missing index", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "Data", type: "application/x-tiddler-dictionary", text: "one: 1"}
|
||||||
|
]);
|
||||||
|
var w = render("<$edit-text tiddler=\"Data\" index=\"missing\" default=\"none\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("none");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Tag and type selection
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("tag and type selection", function() {
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="TestTiddler"/> and inspect the
|
||||||
|
// element — it should be a <textarea>.
|
||||||
|
it("defaults to a textarea for the text field", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\"/>");
|
||||||
|
expect(w.widget.engine.domNode.tag).toBe("textarea");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="TestTiddler" field="caption"/>
|
||||||
|
// and inspect the element — it should be a single-line <input>.
|
||||||
|
it("defaults to an input for non-text fields", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\" field=\"caption\"/>");
|
||||||
|
expect(w.widget.engine.domNode.tag).toBe("input");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="TestTiddler" tag="input"/> —
|
||||||
|
// even though the default for the text field is textarea, this
|
||||||
|
// should render as a single-line <input>.
|
||||||
|
it("tag=input forces an input even for the text field", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\" tag=\"input\"/>");
|
||||||
|
expect(w.widget.engine.domNode.tag).toBe("input");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" tag="input" type="password"/>
|
||||||
|
// — the element should be a password input (characters masked as dots).
|
||||||
|
it("type= sets the input type attribute", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\" tag=\"input\" type=\"password\"/>");
|
||||||
|
expect(w.widget.engine.domNode.getAttribute("type")).toBe("password");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" type="password"/> (no tag
|
||||||
|
// override) — the element should be a plain textarea; type is
|
||||||
|
// silently ignored because HTML textareas have no type attribute.
|
||||||
|
it("type= is ignored for textareas (no type attribute on textarea)", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\" type=\"password\"/>");
|
||||||
|
expect(w.widget.engine.domNode.getAttribute("type")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" tag="script"/> — must NOT
|
||||||
|
// inject a <script> element; tag should fall back to <input>. This
|
||||||
|
// is a defence-in-depth check against wiki-authored HTML injection.
|
||||||
|
it("unsafe tag names collapse to input", function() {
|
||||||
|
var w = render("<$edit-text tiddler=\"TestTiddler\" tag=\"script\"/>");
|
||||||
|
expect(w.widget.engine.domNode.tag).toBe("input");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// DOM attribute propagation
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("DOM attribute propagation", function() {
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="TestTiddler" tag="input"
|
||||||
|
// class="my-class" placeholder="Type here" size="40" tabindex="3"
|
||||||
|
// autocomplete="email" disabled="yes"/> — inspect the element in
|
||||||
|
// devtools; every attribute set on the widget should appear on
|
||||||
|
// the DOM element with the expected value. disabled="no" would
|
||||||
|
// omit the attribute entirely.
|
||||||
|
//
|
||||||
|
// INFO: View as "raw HTML" in preview panel
|
||||||
|
//
|
||||||
|
// SimpleEngine has one independent `if(this.widget.editXxx)
|
||||||
|
// setAttribute(...)` block per attribute. This combined test
|
||||||
|
// exercises every such block in one shot; if any copy breaks,
|
||||||
|
// this spec fails and the jasmine expectation report identifies
|
||||||
|
// the offending attribute.
|
||||||
|
it("copies class, placeholder, size, tabindex, autocomplete and disabled onto the DOM element", function() {
|
||||||
|
var w = render(
|
||||||
|
"<$edit-text tiddler=\"TestTiddler\" tag=\"input\" " +
|
||||||
|
"class=\"my-class\" placeholder=\"Type here\" size=\"40\" " +
|
||||||
|
"tabindex=\"3\" autocomplete=\"email\" disabled=\"yes\"/>"
|
||||||
|
);
|
||||||
|
var dn = w.widget.engine.domNode;
|
||||||
|
expect(dn.className).toBe("my-class");
|
||||||
|
expect(dn.getAttribute("placeholder")).toBe("Type here");
|
||||||
|
expect(dn.getAttribute("size")).toBe("40");
|
||||||
|
expect(dn.getAttribute("tabindex")).toBe("3");
|
||||||
|
expect(dn.getAttribute("autocomplete")).toBe("email");
|
||||||
|
expect(dn.getAttribute("disabled")).toBe("true");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Save-back via saveChanges
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("saving changes back to the store", function() {
|
||||||
|
|
||||||
|
// manual: with tiddler "T" text="old", render <$edit-text tiddler="T"/>
|
||||||
|
// and type "new text"; open T from the sidebar — its text field
|
||||||
|
// should update live.
|
||||||
|
it("writes a new value to the configured text field", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "old"}]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\"/>",wiki);
|
||||||
|
w.widget.saveChanges("new text");
|
||||||
|
expect(wiki.getTiddler("T").fields.text).toBe("new text");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: with tiddler "T" caption="old", render
|
||||||
|
// <$edit-text tiddler="T" field="caption"/> and type a new caption;
|
||||||
|
// {{T!!caption}} elsewhere should update live.
|
||||||
|
it("writes to a non-text field", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "body", caption: "old"}]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\" field=\"caption\"/>",wiki);
|
||||||
|
w.widget.saveChanges("new caption");
|
||||||
|
expect(wiki.getTiddler("T").fields.caption).toBe("new caption");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: ensure no tiddler "NewOne" exists, render
|
||||||
|
// <$edit-text tiddler="NewOne" default=""/> and type "created" —
|
||||||
|
// a new tiddler "NewOne" should appear in the sidebar with that
|
||||||
|
// text.
|
||||||
|
it("creates the tiddler if it does not exist", function() {
|
||||||
|
var wiki = makeWiki();
|
||||||
|
var w = render("<$edit-text tiddler=\"NewOne\" default=\"\"/>",wiki);
|
||||||
|
w.widget.saveChanges("created");
|
||||||
|
expect(wiki.tiddlerExists("NewOne")).toBe(true);
|
||||||
|
expect(wiki.getTiddler("NewOne").fields.text).toBe("created");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: with dictionary tiddler "Data" containing one:1, two:2,
|
||||||
|
// render <$edit-text tiddler="Data" index="one"/> and change the
|
||||||
|
// value to "uno"; view Data as text — it should now read
|
||||||
|
// "one: uno\ntwo: 2".
|
||||||
|
it("writes to a data tiddler index", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "Data", type: "application/x-tiddler-dictionary", text: "one: 1\ntwo: 2"}
|
||||||
|
]);
|
||||||
|
var w = render("<$edit-text tiddler=\"Data\" index=\"one\"/>",wiki);
|
||||||
|
w.widget.saveChanges("uno");
|
||||||
|
expect(wiki.extractTiddlerDataItem("Data","one")).toBe("uno");
|
||||||
|
// Other index entries preserved
|
||||||
|
expect(wiki.extractTiddlerDataItem("Data","two")).toBe("2");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: Select the text. Copy / Paste -> modified does not change
|
||||||
|
it("skips the store update when the value is unchanged", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "same"}]);
|
||||||
|
var before = wiki.getTiddler("T").fields.modified;
|
||||||
|
var w = render("<$edit-text tiddler=\"T\"/>",wiki);
|
||||||
|
w.widget.saveChanges("same");
|
||||||
|
expect(wiki.getTiddler("T").fields.modified).toBe(before);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Refresh behaviour
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("refresh behaviour", function() {
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T"/>, in New Tiddler and New Tiddler 1
|
||||||
|
// The textarea you are NOT focused on
|
||||||
|
// should live-update to reflect the new text field.
|
||||||
|
it("propagates an external change of the edited tiddler through engine.setText", function() {
|
||||||
|
var wiki = makeWiki([{title: "T", text: "first"}]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\"/>",wiki);
|
||||||
|
expect(editorValue(w.widget)).toBe("first");
|
||||||
|
var setTextSpy = spyOnSetText(w.widget);
|
||||||
|
wiki.addTiddler({title: "T", text: "second"});
|
||||||
|
refresh(w.root,w.wrapper,["T"]);
|
||||||
|
expect(setTextSpy).toHaveBeenCalled();
|
||||||
|
expect(setTextSpy.calls.mostRecent().args[0]).toBe("second");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual:
|
||||||
|
// 1. Create tiddlers T (text "initial") and Trigger (text "x").
|
||||||
|
// 2. Render <$edit-text tiddler="T" refreshTitle="Trigger"/>.
|
||||||
|
// 3. Click into the textarea (it now has focus) and type " typed".
|
||||||
|
// 4. Open F12, in console:
|
||||||
|
// $tw.wiki.addTiddler({title:"T",text:"external"})
|
||||||
|
// Focus stays on textarea. Store changes, but setText skips
|
||||||
|
// the DOM write because the node is focused — DOM diverges
|
||||||
|
// from store (textarea still shows "initial typed").
|
||||||
|
// 5. In console:
|
||||||
|
// $tw.wiki.addTiddler({title:"Trigger",text:"bump"})
|
||||||
|
// Textarea should VISIBLY flip to "external" while still
|
||||||
|
// focused. updateDomNodeText has no focus check.
|
||||||
|
// Without refreshTitle, step 5 does nothing — DOM stays diverged.
|
||||||
|
it("refreshTitle triggers updateDomNodeText even when a different tiddler changes", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "T", text: "first"},
|
||||||
|
{title: "Trigger", text: "x"}
|
||||||
|
]);
|
||||||
|
var w = render("<$edit-text tiddler=\"T\" refreshTitle=\"Trigger\"/>",wiki);
|
||||||
|
// refreshTitle goes through engine.updateDomNodeText, not setText
|
||||||
|
var updateSpy = jasmine.createSpy("updateDomNodeText");
|
||||||
|
w.widget.engine.updateDomNodeText = updateSpy;
|
||||||
|
wiki.addTiddler({title: "T", text: "updated"});
|
||||||
|
refresh(w.root,w.wrapper,["Trigger"]);
|
||||||
|
expect(updateSpy).toHaveBeenCalledWith("updated");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" class={{ClassSrc}}/> and
|
||||||
|
// change ClassSrc's text. Inspect the textarea element in devtools
|
||||||
|
// — the class attribute should flip to the new value. This covers
|
||||||
|
// a full re-render (refreshSelf), not just an in-place text update.
|
||||||
|
it("changes to attributes that require a full rerender trigger refreshSelf", function() {
|
||||||
|
// When an attribute like `class` changes via a transcluded variable,
|
||||||
|
// the widget should rerender itself. We simulate that by wrapping the
|
||||||
|
// edit-text inside a transclusion where the class is indirect.
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "T", text: "body"},
|
||||||
|
{title: "ClassSrc", text: "first-class"}
|
||||||
|
]);
|
||||||
|
var w = render(
|
||||||
|
"<$edit-text tiddler=\"T\" class={{ClassSrc}}/>",
|
||||||
|
wiki
|
||||||
|
);
|
||||||
|
expect(w.widget.engine.domNode.className).toBe("first-class");
|
||||||
|
wiki.addTiddler({title: "ClassSrc", text: "second-class"});
|
||||||
|
refresh(w.root,w.wrapper,["ClassSrc"]);
|
||||||
|
// Re-locate the widget because a full refresh creates a new instance
|
||||||
|
var fresh = findEditTextWidget(w.root);
|
||||||
|
expect(fresh.engine.domNode.className).toBe("second-class");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*\
|
||||||
|
title: test-edit-text-widget.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Covers the `rows` / `autoHeight` / `minHeight` interaction in the
|
||||||
|
edit-text widget. Regression guard for issue #9451 / PR #9454 follow-up
|
||||||
|
fix (rows=1 autoHeight=yes must still grow).
|
||||||
|
|
||||||
|
Assertions target the routing inside SimpleEngine.fixHeight (which
|
||||||
|
branch runs, called with which minHeight) — pixel measurement needs a
|
||||||
|
real browser.
|
||||||
|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("Edit-text widget", function() {
|
||||||
|
|
||||||
|
// Shared helpers live in $:/tests/test-edit-widgets/helpers. See that
|
||||||
|
// file for what each helper prepares and where it is used across the suite.
|
||||||
|
var helpers = require("$:/tests/test-edit-widgets/helpers");
|
||||||
|
|
||||||
|
// Local wrapper: every test in THIS file seeds an empty "TestTiddler"
|
||||||
|
// as its default binding target, so we inject that by default.
|
||||||
|
function makeWiki(extraTiddlers) {
|
||||||
|
return helpers.makeWiki(extraTiddlers,{seedTestTiddler: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local alias — `renderEditText` reads more naturally than
|
||||||
|
// `helpers.renderFromAttrs` for the routing tests.
|
||||||
|
function renderEditText(attrs,wiki,tiddlerTitle) {
|
||||||
|
return helpers.renderFromAttrs(attrs,wiki,tiddlerTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Attribute parsing
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("attribute parsing", function() {
|
||||||
|
|
||||||
|
// manual: on a bare <$edit-text tiddler="T"/>, open devtools and
|
||||||
|
// inspect the widget instance — the element should be a textarea,
|
||||||
|
// there should be no rows attribute, and auto-height should be on.
|
||||||
|
// Manual Preview: Also use the widget-tree
|
||||||
|
it("defaults: tag=textarea, autoHeight=yes, no rows, minHeight=100px", function() {
|
||||||
|
var et = renderEditText({});
|
||||||
|
expect(et.editTag).toBe("textarea");
|
||||||
|
expect(et.editAutoHeight).toBe(true);
|
||||||
|
expect(et.editRows).toBeUndefined();
|
||||||
|
expect(et.editMinHeight).toBe("100px");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" rows=5 autoHeight="no" minHeight="1em"/>
|
||||||
|
// — the widget's internal state should reflect all three explicit
|
||||||
|
// values, overriding the defaults verified in the previous test.
|
||||||
|
// Manual Preview: Also use the widget-tree
|
||||||
|
it("explicit attributes override defaults (rows, autoHeight, minHeight)", function() {
|
||||||
|
var et = renderEditText({rows: "5", autoHeight: "no", minHeight: "1em"});
|
||||||
|
expect(et.editRows).toBe("5");
|
||||||
|
expect(et.editAutoHeight).toBe(false);
|
||||||
|
expect(et.editMinHeight).toBe("1em");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" field="caption" tag="textarea"/>
|
||||||
|
// — the element should be a multi-line textarea even though the
|
||||||
|
// field is not `text`.
|
||||||
|
it("tag=textarea override applies to non-text fields", function() {
|
||||||
|
var et = renderEditText({field: "caption", tag: "textarea"});
|
||||||
|
expect(et.editTag).toBe("textarea");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: set $:/config/TextEditor/EditorHeight/Mode = "fixed"
|
||||||
|
// (via control panel → settings → editor toolbar, or edit the
|
||||||
|
// tiddler directly). <$edit-text tiddler="T"/> should now behave
|
||||||
|
// as fixed-height (scrollbar on overflow) by default.
|
||||||
|
it("$:/config/TextEditor/EditorHeight/Mode=fixed flips default autoHeight to false", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "$:/config/TextEditor/EditorHeight/Mode", text: "fixed"}
|
||||||
|
]);
|
||||||
|
var et = renderEditText({},wiki);
|
||||||
|
expect(et.editAutoHeight).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: with the same Mode=fixed config as above, an explicit
|
||||||
|
// <$edit-text tiddler="T" autoHeight="yes"/> should still auto-grow
|
||||||
|
// — the per-widget attribute wins over the global config.
|
||||||
|
it("explicit autoHeight=yes overrides Mode=fixed config", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "$:/config/TextEditor/EditorHeight/Mode", text: "fixed"}
|
||||||
|
]);
|
||||||
|
var et = renderEditText({autoHeight: "yes"},wiki);
|
||||||
|
expect(et.editAutoHeight).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// DOM node construction (SimpleEngine)
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("DOM node construction (non-toolbar SimpleEngine)", function() {
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" rows=1/> and inspect the
|
||||||
|
// element — it should carry rows="1". Proves SimpleEngine copies
|
||||||
|
// widget.editRows onto the DOM element.
|
||||||
|
it("sets the rows attribute on the DOM node when rows is specified", function() {
|
||||||
|
var et = renderEditText({rows: "1"});
|
||||||
|
expect(et.engine.domNode.getAttribute("rows")).toBe("1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// fixHeight routing — the core of the fix
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("fixHeight routing", function() {
|
||||||
|
|
||||||
|
var originalResize, resizeSpy;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
originalResize = $tw.utils.resizeTextAreaToFit;
|
||||||
|
resizeSpy = jasmine.createSpy("resizeTextAreaToFit").and.returnValue(0);
|
||||||
|
$tw.utils.resizeTextAreaToFit = resizeSpy;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
$tw.utils.resizeTextAreaToFit = originalResize;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make the SimpleEngine.fixHeight think we are in a real DOM so it
|
||||||
|
// actually reaches the $tw.utils.resizeTextAreaToFit call. Without
|
||||||
|
// this the engine short-circuits because fakedom nodes carry
|
||||||
|
// isTiddlyWikiFakeDom === true.
|
||||||
|
function callFixHeight(et) {
|
||||||
|
et.engine.domNode.isTiddlyWikiFakeDom = false;
|
||||||
|
et.engine.fixHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" rows=1 autoHeight=yes minHeight=1em/>
|
||||||
|
// (the TiddlyTools idiom). In 5.4.0 pre-fix, the textarea stayed
|
||||||
|
// a single row forever; after the fix, it starts at 1 row and
|
||||||
|
// grows as you type newlines. This covers BRANCH A (autoHeight=yes)
|
||||||
|
// of SimpleEngine.fixHeight: resize is called with minHeight even
|
||||||
|
// when rows is set. Strict superset of the no-rows baseline.
|
||||||
|
it("rows=1 autoHeight=yes minHeight=1em still calls resize (regression test for the PR #9454 follow-up fix)", function() {
|
||||||
|
var et = renderEditText({rows: "1", autoHeight: "yes", minHeight: "1em"});
|
||||||
|
callFixHeight(et);
|
||||||
|
expect(resizeSpy).toHaveBeenCalled();
|
||||||
|
expect(resizeSpy.calls.mostRecent().args[1]).toBe("1em");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" rows=5 autoHeight=no/> —
|
||||||
|
// the textarea must stay locked at exactly 5 rows tall, even if
|
||||||
|
// you paste in 50 lines (vertical scrollbar appears inside).
|
||||||
|
it("rows=5 autoHeight=no does NOT call resize and does NOT apply the fixed-height fallback", function() {
|
||||||
|
var et = renderEditText({rows: "5", autoHeight: "no"});
|
||||||
|
callFixHeight(et);
|
||||||
|
expect(resizeSpy).not.toHaveBeenCalled();
|
||||||
|
// rows attribute governs height — CSS height must remain unset
|
||||||
|
expect(et.engine.domNode.style.height).toBe("");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: set $:/config/TextEditor/EditorHeight/Height = "250px",
|
||||||
|
// then render <$edit-text tiddler="T" autoHeight="no"/> — the
|
||||||
|
// textarea should be exactly 250px tall regardless of content.
|
||||||
|
it("autoHeight=no with no rows applies the fixed-height fallback from config", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "$:/config/TextEditor/EditorHeight/Height", text: "250px"}
|
||||||
|
]);
|
||||||
|
var et = renderEditText({autoHeight: "no"},wiki);
|
||||||
|
callFixHeight(et);
|
||||||
|
expect(resizeSpy).not.toHaveBeenCalled();
|
||||||
|
expect(et.engine.domNode.style.height).toBe("250px");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: set $:/config/TextEditor/EditorHeight/Height = "5px",
|
||||||
|
// render <$edit-text tiddler="T" autoHeight="no"/> — the textarea
|
||||||
|
// should be 20px tall (clamped), not a sliver, so it stays usable.
|
||||||
|
it("autoHeight=no fallback height is clamped to at least 20px", function() {
|
||||||
|
var wiki = makeWiki([
|
||||||
|
{title: "$:/config/TextEditor/EditorHeight/Height", text: "5px"}
|
||||||
|
]);
|
||||||
|
var et = renderEditText({autoHeight: "no"},wiki);
|
||||||
|
callFixHeight(et);
|
||||||
|
expect(et.engine.domNode.style.height).toBe("20px");
|
||||||
|
});
|
||||||
|
|
||||||
|
// manual: render <$edit-text tiddler="T" field="caption" autoHeight="no"/>
|
||||||
|
// — the element is a single-line <input> and the CSS height should
|
||||||
|
// remain unset (no height fallback applies to inputs).
|
||||||
|
it("tag=input: neither resize nor fixed-height fallback runs", function() {
|
||||||
|
var et = renderEditText({field: "caption", autoHeight: "no"});
|
||||||
|
callFixHeight(et);
|
||||||
|
expect(resizeSpy).not.toHaveBeenCalled();
|
||||||
|
expect(et.engine.domNode.style.height).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -18,4 +18,15 @@ describe("fakedom tests", function() {
|
|||||||
expect($tw.fakeDocument.createTextNode("text").nodeType).toBe(3);
|
expect($tw.fakeDocument.createTextNode("text").nodeType).toBe(3);
|
||||||
expect($tw.fakeDocument.createTextNode("text").TEXT_NODE).toBe(3);
|
expect($tw.fakeDocument.createTextNode("text").TEXT_NODE).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Real CSSStyleDeclaration returns undefined for Symbol property keys.
|
||||||
|
// Without a guard, the TW_Style Proxy throws on Symbol access. This bites
|
||||||
|
// in practice when Jasmine pretty-prints fakedom elements on failure.
|
||||||
|
// See related TODO in test-select-widget.js
|
||||||
|
it("returns undefined for Symbol property access on element.style", function() {
|
||||||
|
var el = $tw.fakeDocument.createElement("div");
|
||||||
|
expect(function() { return el.style[Symbol.toPrimitive]; }).not.toThrow();
|
||||||
|
expect(el.style[Symbol.toPrimitive]).toBeUndefined();
|
||||||
|
expect(function() { el.style[Symbol.iterator] = "x"; }).not.toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*\
|
||||||
|
title: test-parsetree-positions.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Regression tests for #9882: wikitext parser rules must emit accurate
|
||||||
|
`start`/`end` source positions on their parse tree nodes. Tooling that maps
|
||||||
|
rendered output back to the source text relies on these offsets.
|
||||||
|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("Parse tree source position tests (#9882)", function() {
|
||||||
|
|
||||||
|
// Create a wiki
|
||||||
|
var wiki = $tw.test.wiki();
|
||||||
|
|
||||||
|
// Define a parsing shortcut
|
||||||
|
var parse = function(text) {
|
||||||
|
return wiki.parseText("text/vnd.tiddlywiki",text).tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should give inline code runs a text node that spans only the code, not the backticks", function() {
|
||||||
|
// codeinline.js: `code` gives a text node "code" spanning offsets 1 to 5. The closing backtick at 5 is excluded.
|
||||||
|
// Bug: `end` was set to `this.parser.pos`, which sits past the closing backtick, so end was 6 and the span swallowed the backtick.
|
||||||
|
expect(parse("`code`")).toEqual(
|
||||||
|
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 6, children: [ { type: "element", tag: "code", rule: "codeinline", start: 0, end: 6, children: [ { type: "text", text: "code", start: 1, end: 5 } ] } ] } ]
|
||||||
|
);
|
||||||
|
// ``a`b`` gives text "a`b" spanning 2 to 5. `end` must be the offset of the closing marker whatever its length.
|
||||||
|
// Bug: `end` was `this.parser.pos` (7), two characters past the code, so it also swallowed the closing ``.
|
||||||
|
expect(parse("``a`b``")).toEqual(
|
||||||
|
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 7, children: [ { type: "element", tag: "code", rule: "codeinline", start: 0, end: 7, children: [ { type: "text", text: "a`b", start: 2, end: 5 } ] } ] } ]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start the text node of a suppressed external link after the ~", function() {
|
||||||
|
// extlink.js: ~https://example.com/ emits the plain text "https://example.com/", which spans offsets 1 to 21.
|
||||||
|
// Bug: `start` was the offset of the ~ (0), so the span was one character too wide and began on the ~ that the text omits.
|
||||||
|
expect(parse("~https://example.com/")).toEqual(
|
||||||
|
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 21, children: [ { type: "text", text: "https://example.com/", start: 1, end: 21, rule: "extlink" } ] } ]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should give a suppressed wikilink's text node source positions", function() {
|
||||||
|
// wikilinkprefix.js: ~SuppressedLink emits the plain text "SuppressedLink", spanning offsets 1 to 15.
|
||||||
|
// Bug: the text node carried no `start`/`end` at all. The parser framework then defaulted `start` to the ~ offset (0).
|
||||||
|
expect(parse("~SuppressedLink")).toEqual(
|
||||||
|
[ { type: "element", tag: "p", rule: "parseblock", start: 0, end: 15, children: [ { type: "text", text: "SuppressedLink", start: 1, end: 15, rule: "wikilinkprefix" } ] } ]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should record the filter's start offset for an \\import pragma", function() {
|
||||||
|
// import.js: \import [tag[x]] records the filter value "[tag[x]]" starting at offset 8, right after "\import ".
|
||||||
|
// Bug: `filterStart` was assigned `this.parser.source` (the whole source string) instead of `this.parser.pos`, so `start` was a string, not an offset.
|
||||||
|
expect(parse("\\import [tag[x]]\n")).toEqual(
|
||||||
|
[ { type: "importvariables", rule: "import", start: 0, end: 16, attributes: { filter: { type: "string", value: "[tag[x]]", start: 8, end: 16 } }, children: [] } ]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
/*\
|
||||||
|
title: test-select-widget.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Tests the select widget, focused on multi-select refresh behaviour.
|
||||||
|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("Select widget", function() {
|
||||||
|
|
||||||
|
var widget = require("$:/core/modules/widgets/widget.js");
|
||||||
|
|
||||||
|
// Helpers reused from: test-widget.js and test-checkbox-widget.js
|
||||||
|
function createWidgetNode(parseTreeNode,wiki) {
|
||||||
|
return new widget.widget(parseTreeNode,{
|
||||||
|
wiki: wiki,
|
||||||
|
document: $tw.fakeDocument
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseText(text,wiki,options) {
|
||||||
|
var parser = wiki.parseText("text/vnd.tiddlywiki",text,options);
|
||||||
|
return parser ? {type: "widget", children: parser.tree} : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderWidgetNode(widgetNode) {
|
||||||
|
$tw.fakeDocument.setSequenceNumber(0);
|
||||||
|
var wrapper = $tw.fakeDocument.createElement("div");
|
||||||
|
widgetNode.render(wrapper,null);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshWidgetNode(widgetNode,wrapper,changes) {
|
||||||
|
var changedTiddlers = {};
|
||||||
|
if(changes) {
|
||||||
|
$tw.utils.each(changes,function(title) {
|
||||||
|
changedTiddlers[title] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
widgetNode.refresh(changedTiddlers,wrapper,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't pass fakedom elements to Jasmine matchers. The matchers pretty-print
|
||||||
|
// values on assertion which walks the element.style Proxy with Symbol keys and
|
||||||
|
// crashes inside convertStyleNameToPropertyName.
|
||||||
|
// TODO: once issue: "fakedom style Proxy guards against non-string property keys" lands
|
||||||
|
// revert these workarounds to idiomatic matchers (toBe(null), toBeUndefined()).
|
||||||
|
function findSelectDom(node) {
|
||||||
|
if(node.tag === "select") return node;
|
||||||
|
if(node.children) {
|
||||||
|
for(var i = 0; i < node.children.length; i++) {
|
||||||
|
var found = findSelectDom(node.children[i]);
|
||||||
|
if(found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedFlags(parent) {
|
||||||
|
var result = [];
|
||||||
|
for(var i = 0; i < parent.children.length; i++) {
|
||||||
|
result.push(!!parent.children[i].selected);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/TiddlyWiki/TiddlyWiki5/issues/9839
|
||||||
|
// PR #8093 added <optgroup> support but used `child.children.length === 0` to
|
||||||
|
// distinguish a plain <option> from an <optgroup>. That heuristic misfires for
|
||||||
|
// any <option> whose contents render to inline HTML elements (e.g. tc-tiddlylink
|
||||||
|
// auto-links generated for "$:/..." titles), so the option's `selected` state
|
||||||
|
// was never restored on refresh.
|
||||||
|
it("preserves multi-select state across refresh when options contain inline HTML children",function() {
|
||||||
|
var wiki = $tw.test.wiki();
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "Picks", mylist: "foo $:/mumble"}
|
||||||
|
]);
|
||||||
|
// Each option has element children to mimic the auto-link case from #9839.
|
||||||
|
// Explicit value attribute lets fakedom resolve option.value (real browsers
|
||||||
|
// fall back to text content).
|
||||||
|
var widgetText = "<$select tiddler='Picks' field='mylist' multiple>" +
|
||||||
|
"<option value='foo'><span>foo</span></option>" +
|
||||||
|
"<option value='bar'><span>bar</span></option>" +
|
||||||
|
"<option value='$:/mumble'><a class='tc-tiddlylink'>$:/mumble</a></option>" +
|
||||||
|
"</$select>";
|
||||||
|
var widgetNode = createWidgetNode(parseText(widgetText,wiki),wiki);
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
var select = findSelectDom(wrapper);
|
||||||
|
expect(select === null).toBe(false);
|
||||||
|
expect(select.children.length).toBe(3);
|
||||||
|
|
||||||
|
// After initial render, options matching the field value should be selected.
|
||||||
|
// foo (idx 0), bar (idx 1), $:/mumble (idx 2).
|
||||||
|
expect(selectedFlags(select)).toEqual([true,false,true]);
|
||||||
|
|
||||||
|
// Change the stored field value and refresh - this is where the bug surfaced:
|
||||||
|
// the "$:/mumble" option (with an <a> child) was wrongly skipped.
|
||||||
|
wiki.addTiddler({title: "Picks", mylist: "bar $:/mumble"});
|
||||||
|
refreshWidgetNode(widgetNode,wrapper,["Picks"]);
|
||||||
|
|
||||||
|
expect(selectedFlags(select)).toEqual([false,true,true]);
|
||||||
|
// The inner <a> must not be touched - .selected is meaningful only on <option>.
|
||||||
|
var innerLinkSelected = select.children[2].children[0].selected;
|
||||||
|
expect(innerLinkSelected === undefined).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still selects options inside <optgroup> across refresh, including $:/-prefixed entries with inline HTML",function() {
|
||||||
|
var wiki = $tw.test.wiki();
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "Picks", mylist: "1 $:/mumble"}
|
||||||
|
]);
|
||||||
|
// The "high" group mixes a plain option with a $:/-prefixed option whose
|
||||||
|
// content is wrapped in an auto-link <a> - same pattern that broke #9839
|
||||||
|
// for top-level options, exercised here inside an <optgroup>.
|
||||||
|
var widgetText = "<$select tiddler='Picks' field='mylist' multiple>" +
|
||||||
|
"<optgroup label='low'>" +
|
||||||
|
"<option value='1'>1</option>" +
|
||||||
|
"<option value='2'>2</option>" +
|
||||||
|
"</optgroup>" +
|
||||||
|
"<optgroup label='high'>" +
|
||||||
|
"<option value='4'>4</option>" +
|
||||||
|
"<option value='$:/mumble'><a class='tc-tiddlylink'>$:/mumble</a></option>" +
|
||||||
|
"</optgroup>" +
|
||||||
|
"</$select>";
|
||||||
|
var widgetNode = createWidgetNode(parseText(widgetText,wiki),wiki);
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
var select = findSelectDom(wrapper);
|
||||||
|
expect(select === null).toBe(false);
|
||||||
|
expect(select.children.length).toBe(2);
|
||||||
|
|
||||||
|
// Initial state: "1" and "$:/mumble" selected
|
||||||
|
expect(selectedFlags(select.children[0])).toEqual([true,false]);
|
||||||
|
expect(selectedFlags(select.children[1])).toEqual([false,true]);
|
||||||
|
|
||||||
|
wiki.addTiddler({title: "Picks", mylist: "2 4"});
|
||||||
|
refreshWidgetNode(widgetNode,wrapper,["Picks"]);
|
||||||
|
expect(selectedFlags(select.children[0])).toEqual([false,true]);
|
||||||
|
expect(selectedFlags(select.children[1])).toEqual([true,false]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/*\
|
||||||
|
title: test-utils-copyObjectPropertiesSafe.js
|
||||||
|
type: application/javascript
|
||||||
|
tags: [[$:/tags/test-spec]]
|
||||||
|
|
||||||
|
Tests $tw.utils.copyObjectPropertiesSafe, the root cause of #9869.
|
||||||
|
|
||||||
|
$eventcatcher serialises DOM events via JSON.stringify(copyObjectPropertiesSafe(event)).
|
||||||
|
|
||||||
|
The original bug was caused by instanceof Node/Window being realm-specific.
|
||||||
|
When an event originated from a different browser window, DOM objects from that
|
||||||
|
window were not detected and JSON.stringify() could throw "Illegal invocation".
|
||||||
|
|
||||||
|
The implementation must:
|
||||||
|
- skip DOM nodes and Window objects from other realms
|
||||||
|
- preserve normal event data
|
||||||
|
- preserve CustomEvent.detail payloads
|
||||||
|
- continue copying enumerable properties from non-DOM objects
|
||||||
|
- preserve arrays and break circular references
|
||||||
|
|
||||||
|
The tests use substitutes for foreign DOM objects because headless Node does not
|
||||||
|
provide a second browser realm. These objects model the important characteristics:
|
||||||
|
DOM nodes have nodeType/nodeName, and Window objects have window/self/document.
|
||||||
|
\*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("copyObjectPropertiesSafe (#9869)", function() {
|
||||||
|
|
||||||
|
var cops = $tw.utils.copyObjectPropertiesSafe;
|
||||||
|
|
||||||
|
// Simulates a DOM node from another realm.
|
||||||
|
// The important characteristics are:
|
||||||
|
// - nodeType/nodeName identify it as a DOM node
|
||||||
|
// - circular parentNode references resemble real DOM trees
|
||||||
|
function fakeElement(tagName,extra) {
|
||||||
|
var node = $tw.utils.extend({
|
||||||
|
nodeType: 1,
|
||||||
|
nodeName: tagName,
|
||||||
|
tagName: tagName
|
||||||
|
},extra || {});
|
||||||
|
|
||||||
|
node.parentNode = node;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulates a Window object from another realm.
|
||||||
|
function fakeWindow() {
|
||||||
|
var win = {
|
||||||
|
document: {},
|
||||||
|
location: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
win.window = win;
|
||||||
|
win.self = win;
|
||||||
|
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An event carrying foreign DOM nodes and a Window.
|
||||||
|
function fakeEvent(type,target,extra) {
|
||||||
|
return $tw.utils.extend({
|
||||||
|
type: type,
|
||||||
|
target: target,
|
||||||
|
currentTarget: target,
|
||||||
|
view: fakeWindow(),
|
||||||
|
detail: 0,
|
||||||
|
isTrusted: true
|
||||||
|
},extra || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
it("does not throw serialising any event type from a secondary window", function() {
|
||||||
|
var events = [
|
||||||
|
fakeEvent("focusin",fakeElement("INPUT")),
|
||||||
|
fakeEvent("change",fakeElement("SELECT")),
|
||||||
|
fakeEvent("click",fakeElement("BUTTON"),{
|
||||||
|
button: 0,
|
||||||
|
clientX: 5,
|
||||||
|
clientY: 9,
|
||||||
|
relatedTarget: null
|
||||||
|
}),
|
||||||
|
fakeEvent("mouseover",fakeElement("DIV"),{
|
||||||
|
relatedTarget: fakeElement("SPAN")
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
events.forEach(function(event) {
|
||||||
|
expect(function() {
|
||||||
|
JSON.stringify(cops(event));
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("drops DOM nodes and Window but keeps serialisable event data", function() {
|
||||||
|
var event = fakeEvent("click",fakeElement("BUTTON"),{
|
||||||
|
button: 0,
|
||||||
|
clientX: 5,
|
||||||
|
clientY: 9
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||||
|
|
||||||
|
expect(result.target).toBeUndefined();
|
||||||
|
expect(result.currentTarget).toBeUndefined();
|
||||||
|
expect(result.view).toBeUndefined();
|
||||||
|
|
||||||
|
expect(result.type).toBe("click");
|
||||||
|
expect(result.detail).toBe(0);
|
||||||
|
expect(result.isTrusted).toBe(true);
|
||||||
|
expect(result.button).toBe(0);
|
||||||
|
expect(result.clientX).toBe(5);
|
||||||
|
expect(result.clientY).toBe(9);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("preserves nested objects, arrays and circular reference handling", function() {
|
||||||
|
var event = fakeEvent("custom",fakeElement("DIV"),{
|
||||||
|
detail: {
|
||||||
|
nested: {
|
||||||
|
a: 1,
|
||||||
|
b: [2,3]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
path: [
|
||||||
|
fakeElement("DIV"),
|
||||||
|
fakeWindow(),
|
||||||
|
42
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
event.self = event;
|
||||||
|
|
||||||
|
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||||
|
|
||||||
|
expect(result.detail).toEqual({
|
||||||
|
nested: {
|
||||||
|
a: 1,
|
||||||
|
b: [2,3]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.path).toEqual([
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
42
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(result.self).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("preserves CustomEvent.detail objects including custom object instances", function() {
|
||||||
|
function DetailObject() {
|
||||||
|
this.name = "example";
|
||||||
|
this.values = [1,2,3];
|
||||||
|
}
|
||||||
|
|
||||||
|
var event = fakeEvent("custom",fakeElement("DIV"),{
|
||||||
|
detail: new DetailObject()
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||||
|
|
||||||
|
expect(result.detail).toEqual({
|
||||||
|
name: "example",
|
||||||
|
values: [1,2,3]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("preserves CustomEvent.detail objects while dropping DOM objects inside them", function() {
|
||||||
|
var event = fakeEvent("custom",fakeElement("DIV"),{
|
||||||
|
detail: {
|
||||||
|
value: 123,
|
||||||
|
nested: {
|
||||||
|
target: fakeElement("SPAN"),
|
||||||
|
kept: "yes"
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
fakeElement("BUTTON"),
|
||||||
|
42
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = JSON.parse(JSON.stringify(cops(event)));
|
||||||
|
|
||||||
|
expect(result.detail.value).toBe(123);
|
||||||
|
|
||||||
|
expect(result.detail.nested.target).toBeUndefined();
|
||||||
|
expect(result.detail.nested.kept).toBe("yes");
|
||||||
|
|
||||||
|
expect(result.detail.items).toEqual([
|
||||||
|
null,
|
||||||
|
42
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("accepts primitive, array and object arguments", function() {
|
||||||
|
var nullProto = Object.create(null);
|
||||||
|
nullProto.kept = true;
|
||||||
|
|
||||||
|
expect(cops(42)).toBe(42);
|
||||||
|
expect(cops(null)).toBe(null);
|
||||||
|
|
||||||
|
expect(cops([
|
||||||
|
1,
|
||||||
|
"two",
|
||||||
|
{three: 3}
|
||||||
|
])).toEqual([
|
||||||
|
1,
|
||||||
|
"two",
|
||||||
|
{three: 3}
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(cops({
|
||||||
|
a: 1,
|
||||||
|
b: {
|
||||||
|
c: 2
|
||||||
|
}
|
||||||
|
})).toEqual({
|
||||||
|
a: 1,
|
||||||
|
b: {
|
||||||
|
c: 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(JSON.parse(JSON.stringify(cops({
|
||||||
|
nullProto: nullProto
|
||||||
|
})))).toEqual({
|
||||||
|
nullProto: {
|
||||||
|
kept: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
created: 20231005205623086
|
created: 20231005205623086
|
||||||
modified: 20250807100434131
|
modified: 20260710090951727
|
||||||
tags: About
|
tags: About
|
||||||
title: TiddlyWiki Archive
|
title: TiddlyWiki Archive
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
\procedure versions()
|
\procedure versions()
|
||||||
5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9
|
5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9
|
||||||
@@ -9,7 +10,7 @@ title: TiddlyWiki Archive
|
|||||||
5.1.20 5.1.21 5.1.22 5.1.23
|
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.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.7 5.3.8
|
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
|
||||||
5.4.0
|
5.4.0 5.4.1
|
||||||
\end
|
\end
|
||||||
|
|
||||||
Older versions of TiddlyWiki are available in the [[archive|https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages/tree/master/archive]]:
|
Older versions of TiddlyWiki are available in the [[archive|https://github.com/TiddlyWiki/tiddlywiki.com-gh-pages/tree/master/archive]]:
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
title: Community Survey 2025
|
|
||||||
created: 20250708130030654
|
created: 20250708130030654
|
||||||
modified: 20250826162904085
|
modified: 20250826162904085
|
||||||
|
title: Community Survey 2025
|
||||||
|
|
||||||
<div style.float="right" style.padding-left="1em">
|
<div style.float="right" style.padding-left="1em">
|
||||||
<$image source="Community Survey 2025" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
|
<$image source="Community Survey 2025 Image" alt="Shaping the future of TiddlyWiki with the Community Survey 2025" width="280"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The core developers work hard year by year to continuously improve ~TiddlyWiki. Part of the satisfaction is that we are not just building software for ourselves, we’re serving the needs of a wider community of users.
|
The core developers work hard year by year to continuously improve ~TiddlyWiki. Part of the satisfaction is that we are not just building software for ourselves, we’re serving the needs of a wider community of users.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
created: 20130822170200000
|
created: 20130822170200000
|
||||||
icon: $:/core/icon
|
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?]]
|
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: 20260420192600833
|
modified: 20260710091122803
|
||||||
tags: Welcome
|
tags: Welcome
|
||||||
title: HelloThere
|
title: HelloThere
|
||||||
type: text/vnd.tiddlywiki
|
type: text/vnd.tiddlywiki
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 47 KiB |
-13
@@ -1,13 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"created": "20260125202948362",
|
|
||||||
"text": "",
|
|
||||||
"title": "$:/changenotes/5.4.0/#9609/impacts/event detail variables in eventcatcher",
|
|
||||||
"modified": "20260125203132149",
|
|
||||||
"tags": "$:/tags/ImpactNote",
|
|
||||||
"type": "text/vnd.tiddlywiki",
|
|
||||||
"changenote": "$:/changenotes/5.4.0/#9609",
|
|
||||||
"description": "`event-detail*` variables have been removed in favour of the new `eventJSON` variable",
|
|
||||||
"impact-type": "compatibility-break "
|
|
||||||
}
|
|
||||||
]
|
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
changenote: $:/changenotes/5.4.0/#9609
|
||||||
|
created: 20260125202948362
|
||||||
|
description: `event-detail*` variables have been removed in favour of the new `eventJSON` variable
|
||||||
|
impact-type: compatibility-break
|
||||||
|
modified: 20260428161250718
|
||||||
|
tags: $:/tags/ImpactNote
|
||||||
|
title: $:/changenotes/5.4.0/#9609/impacts/event detail variables in eventcatcher
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
change-category: internal
|
||||||
|
change-type: bugfix
|
||||||
|
created: 20260710092852681
|
||||||
|
description: Fixes an issue with the minheight of textareas.
|
||||||
|
github-contributors: pmario
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9828
|
||||||
|
modified: 20260710093223971
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
title: $:/changenotes/5.4.1/#9828
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
Fixes an issue with the minheight of textareas.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
title: $:/changenotes/5.4.0/#9829
|
||||||
|
description: Update Polish translation
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
change-type: enhancement
|
||||||
|
change-category: translation
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9829
|
||||||
|
github-contributors: EvidentlyCube
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
change-category: plugin
|
||||||
|
change-type: bugfix
|
||||||
|
created: 20260710092644515
|
||||||
|
description: Freelinks: Fix mid-text link cutoff, fix disabling freelinks toggle blanks view, and add configurable ~MaxLinks cap
|
||||||
|
github-contributors: s793016
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9836
|
||||||
|
modified: 20260710092700225
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
title: $:/changenotes/5.4.1/#9836
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
Fixes correctness and reliability issues in the freelinks plugin.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Disabling "Enable freelinking within tiddler view templates" in plugin
|
||||||
|
settings caused the entire tiddler view (including the Control Panel)
|
||||||
|
to blank out with no way to recover through the UI.
|
||||||
|
* Mid-text link cutoff caused by `search()` truncating results before the
|
||||||
|
render stage; the cap is moved to `$:/config/Freelinks/MaxLinks`
|
||||||
|
(default 500) so text beyond the limit is preserved as plain text.
|
||||||
|
* `$:/config/Freelinks/MaxLinks` render cap was applied before
|
||||||
|
longest-match deduplication, silently dropping valid matches within
|
||||||
|
the limit; cap now applied after the final non-overlapping set is resolved.
|
||||||
|
* Incorrect failure links for long patterns caused by a hard depth limit in
|
||||||
|
`buildFailureLinks()`; removed as chain depth is naturally bounded.
|
||||||
|
* Unnecessary cache rebuilds triggered by draft edits and system tiddler
|
||||||
|
changes such as tab clicks.
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
* `$:/config/Freelinks/MaxLinks` added to control rendered link count, with
|
||||||
|
a settings panel input field for convenience.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
change-category: widget
|
||||||
|
change-type: bugfix
|
||||||
|
created: 20260710093059614
|
||||||
|
description: Fixes a regression in the SelectWidget when selecting multiple values.
|
||||||
|
github-contributors: pmario
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9841
|
||||||
|
modified: 20260710093210171
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
title: $:/changenotes/5.4.1/#9841
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
Fixes a regression in the SelectWidget when selecting multiple values.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
title: $:/changenotes/5.4.0/#9870
|
||||||
|
description: Update Japanese translation
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
change-type: enhancement
|
||||||
|
change-category: translation
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9870
|
||||||
|
github-contributors: IchijikuIchigo
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
change-category: plugin
|
||||||
|
change-type: bugfix
|
||||||
|
created: 20260710093318232
|
||||||
|
description: Katex: restores a missing font
|
||||||
|
github-contributors: Leilei332
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9873
|
||||||
|
modified: 20260710093432151
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
title: $:/changenotes/5.4.1/#9873
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
Restores the missing KaTeX_Caligraphic-Regular.woff2
|
||||||
|
font in the Katex plugin
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
title: $:/changenotes/5.4.1/#9882
|
||||||
|
created: 20260701142414000
|
||||||
|
modified: 20260701142414000
|
||||||
|
description: Fix invalid source positions in parse tree nodes
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
change-type: bugfix
|
||||||
|
change-category: internal
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9882
|
||||||
|
github-contributors: pmario
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
Several wikitext parser rules emitted parse tree nodes with missing or incorrect `start` and `end` source positions. Accurate positions are needed by tooling that maps rendered output back to the source text.
|
||||||
|
|
||||||
|
* ''Inline code'' (`` `code` ``): the text node `end` no longer runs past the closing backtick, so the span covers only the code text.
|
||||||
|
|
||||||
|
* ''Suppressed external links'' (`~https://example.com`): the text node now starts after the suppressing `~`, matching the plain text that is emitted.
|
||||||
|
|
||||||
|
* ''Suppressed wikilinks'' (`~SuppressedLink`): the text node now carries `start` and `end` positions. Previously it had none.
|
||||||
|
|
||||||
|
* ''Import filter'' (`\import`): the filter start position was set to the whole source string instead of the current parse position. It now uses the correct offset.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
title: $:/changenotes/5.4.1/#9905
|
||||||
|
created: 20260709142414000
|
||||||
|
modified: 20260709142414000
|
||||||
|
description: Fix errors in eventcatcher while serializing event properties
|
||||||
|
release: 5.4.1
|
||||||
|
tags: $:/tags/ChangeNote
|
||||||
|
change-type: bugfix
|
||||||
|
change-category: internal
|
||||||
|
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9905
|
||||||
|
github-contributors: saqimtiaz
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
Fixes an issue in utils.copyObjectPropertiesSafe which did not correctly handle DOM objects from other windows resulting in an error.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
caption: 5.4.1
|
||||||
|
created: 20260508181012812
|
||||||
|
description: Release v5.4.1 addresses regressions and bugs introduced in the v5.4.0 release.
|
||||||
|
modified: 20260710091945414
|
||||||
|
released: 20260710090509444
|
||||||
|
tags: ReleaseNotes
|
||||||
|
title: Release 5.4.1
|
||||||
|
type: text/vnd.tiddlywiki
|
||||||
|
|
||||||
|
\procedure release-introduction()
|
||||||
|
Release v5.4.1 addresses regressions and bugs introduced in the v5.4.0 release.
|
||||||
|
\end release-introduction
|
||||||
|
|
||||||
|
\define banner-credit-user-name() Peter
|
||||||
|
\define banner-credit-user-link() https://talk.tiddlywiki.org/u/peter
|
||||||
|
\define banner-credit-discussion-link() https://talk.tiddlywiki.org/t/vote-for-the-banner-tiddlywiki-v5-4-0/15016
|
||||||
|
\define banner-credit-permalink() https://raw.githubusercontent.com/TiddlyWiki/TiddlyWiki5/92caa7312ebc51c59cd345cc81b4a326661a0650/editions/tw5.com/tiddlers/images/New%20Release%20Banner.webp
|
||||||
|
|
||||||
|
<<releasenote 5.4.1>>
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
caption: 5.5.0
|
|
||||||
created: 20260420195917090
|
|
||||||
modified: 20260420195917090
|
|
||||||
tags: ReleaseNotes
|
|
||||||
title: Release 5.5.0
|
|
||||||
type: text/vnd.tiddlywiki
|
|
||||||
description: Under development
|
|
||||||
|
|
||||||
\procedure release-introduction()
|
|
||||||
Release v5.5.0 is under development.
|
|
||||||
\end release-introduction
|
|
||||||
|
|
||||||
<<releasenote 5.5.0>>
|
|
||||||
@@ -35,7 +35,7 @@ The content of the `<$edit-text>` widget is ignored.
|
|||||||
|size |The size of the input field (in characters). This exact result depends on browser and font. Use the `class` attribute to style width for precise control |
|
|size |The size of the input field (in characters). This exact result depends on browser and font. Use the `class` attribute to style width for precise control |
|
||||||
|autoHeight |Either "yes" or "no" to specify whether to automatically resize `textarea` editors to fit their content (''defaults'' to "yes"). This setting can be changed globally with an editor toolbar button|
|
|autoHeight |Either "yes" or "no" to specify whether to automatically resize `textarea` editors to fit their content (''defaults'' to "yes"). This setting can be changed globally with an editor toolbar button|
|
||||||
|minHeight |Minimum height for automatically resized `textarea` editors, specified in CSS length units such as "px", "em" or "%". Has no effect if the [[CodeMirror Plugin]] is active |
|
|minHeight |Minimum height for automatically resized `textarea` editors, specified in CSS length units such as "px", "em" or "%". Has no effect if the [[CodeMirror Plugin]] is active |
|
||||||
|rows|Sets the rows attribute of a generated textarea. `rows` ''takes precedence'' over `autoHeight`, so text areas can be defined individually |
|
|rows|Sets the `rows` attribute of a generated textarea. When combined with `autoHeight="yes"` (the default), it sets the ''initial'' rendered height; the textarea still grows to fit content. To force a fixed number of rows, pair `rows` with `autoHeight="no"`. |
|
||||||
|cancelPopups |<<.from-version "5.1.23">> if set to "yes", cancels all popups when the input gets focus |
|
|cancelPopups |<<.from-version "5.1.23">> if set to "yes", cancels all popups when the input gets focus |
|
||||||
|inputActions |<<.from-version 5.1.23>> Optional actions that are triggered every time an input event occurs within the input field or textarea.<br><<.from-version "5.2.1">> The variable `actionValue` is available to the `inputActions` and contains the value of the input field. |
|
|inputActions |<<.from-version 5.1.23>> Optional actions that are triggered every time an input event occurs within the input field or textarea.<br><<.from-version "5.2.1">> The variable `actionValue` is available to the `inputActions` and contains the value of the input field. |
|
||||||
|refreshTitle |<<.from-version 5.1.23>> An optional tiddler title that makes the input field update whenever the specified tiddler changes |
|
|refreshTitle |<<.from-version 5.1.23>> An optional tiddler title that makes the input field update whenever the specified tiddler changes |
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ Advanced/Hint: Wewnętrzne informacje na temat TiddlyWiki
|
|||||||
Appearance/Caption: Wyświetlanie
|
Appearance/Caption: Wyświetlanie
|
||||||
Appearance/Hint: Dostosowywanie wyglądu tej TiddlyWiki
|
Appearance/Hint: Dostosowywanie wyglądu tej TiddlyWiki
|
||||||
Basics/AnimDuration/Prompt: Długość animacji
|
Basics/AnimDuration/Prompt: Długość animacji
|
||||||
Basics/AutoFocus/Prompt: Domyślne wybrane pole do edycji przy tworzeniu nowego tiddlera
|
Basics/AutoFocus/Prompt: Domyślnie zfocusowane pole przy tworzeniu nowego tiddlera
|
||||||
|
Basics/AutoFocusEdit/Prompt: Domyślnie zfocusowane pole przy edycji istniejącego tiddlera
|
||||||
Basics/Caption: Podstawowe
|
Basics/Caption: Podstawowe
|
||||||
Basics/DefaultTiddlers/BottomHint: Tiddlery, które mają spację w nazie otocz [[podwójnymi nawiasami kwadratowymi]]. Możesz też ustawić, by zawsze widzieć {{ostatnio otwarte tiddlery||$:/snippets/retain-story-ordering-button}}
|
Basics/DefaultTiddlers/BottomHint: Tiddlery, które mają spację w nazie otocz [[podwójnymi nawiasami kwadratowymi]]. Możesz też ustawić, by zawsze widzieć {{ostatnio otwarte tiddlery||$:/snippets/retain-story-ordering-button}}
|
||||||
Basics/DefaultTiddlers/Prompt: Domyślnie otwarte tiddlery
|
Basics/DefaultTiddlers/Prompt: Domyślnie otwarte tiddlery
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
title: $:/language/Draft/
|
||||||
|
|
||||||
|
Attribution: Szkic '<<draft-title>>' autorstwa {{$:/status/UserName}}
|
||||||
|
Title: Szkic '<<draft-title>>'
|
||||||
@@ -15,6 +15,8 @@ Listing/Preview/TextRaw: Tekst (oryginalny)
|
|||||||
Listing/Preview/Fields: Pola
|
Listing/Preview/Fields: Pola
|
||||||
Listing/Preview/Diff: Różnica
|
Listing/Preview/Diff: Różnica
|
||||||
Listing/Preview/DiffFields: Róznica (Pola)
|
Listing/Preview/DiffFields: Róznica (Pola)
|
||||||
|
Listing/ImportOptions/Caption: Opcje importu
|
||||||
|
Listing/ImportOptions/NoMatch: Żadna z opcji importu nie możę być zastosowana do tych plików.
|
||||||
Listing/Rename/Tooltip: Zmień nazwę tiddlera przed importem
|
Listing/Rename/Tooltip: Zmień nazwę tiddlera przed importem
|
||||||
Listing/Rename/Prompt: Zmień nazwę na:
|
Listing/Rename/Prompt: Zmień nazwę na:
|
||||||
Listing/Rename/ConfirmRename: Zmień nazwę tiddlera
|
Listing/Rename/ConfirmRename: Zmień nazwę tiddlera
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
title: $:/language/
|
title: $:/language/
|
||||||
|
|
||||||
|
Alerts: Powiadomienia
|
||||||
AboveStory/ClassicPlugin/Warning: Wygląda na to, że próbujesz wczytać wtyczkę zaprojektowaną dla ~TiddlyWiki Classic. Zwróc uwagę, że [[te wtyczki nie działają z TiddlyWiki 5|https://tiddlywiki.com/#TiddlyWikiClassic]]. Wykryte wtyczki ~TiddlyWiki Classic:
|
AboveStory/ClassicPlugin/Warning: Wygląda na to, że próbujesz wczytać wtyczkę zaprojektowaną dla ~TiddlyWiki Classic. Zwróc uwagę, że [[te wtyczki nie działają z TiddlyWiki 5|https://tiddlywiki.com/#TiddlyWikiClassic]]. Wykryte wtyczki ~TiddlyWiki Classic:
|
||||||
BinaryWarning/Prompt: Ten tiddler zawiera binarne dane
|
BinaryWarning/Prompt: Ten tiddler zawiera binarne dane
|
||||||
ClassicWarning/Hint: Ten tiddler został napisany w formacie dla TiddlyWiki Classic, który nie jest w pełni kompatybilny z TiddlyWiki 5. Więcej informacji dostępnych pod adresem https://tiddlywiki.com/static/Upgrading.html.
|
ClassicWarning/Hint: Ten tiddler został napisany w formacie dla TiddlyWiki Classic, który nie jest w pełni kompatybilny z TiddlyWiki 5. Więcej informacji dostępnych pod adresem https://tiddlywiki.com/static/Upgrading.html.
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ Advanced/ShadowInfo/NotShadow/Hint: Tiddler <$link to=<<infoTiddler>>><$text tex
|
|||||||
Advanced/ShadowInfo/Shadow/Hint: Tiddler <$link to=<<infoTiddler>>><$text text=<<infoTiddler>>/></$link> jest tiddlerem-cieniem
|
Advanced/ShadowInfo/Shadow/Hint: Tiddler <$link to=<<infoTiddler>>><$text text=<<infoTiddler>>/></$link> jest tiddlerem-cieniem
|
||||||
Advanced/ShadowInfo/Shadow/Source: Zdefiniiowany we wtyczce <$link to=<<pluginTiddler>>><$text text=<<pluginTiddler>>/></$link>
|
Advanced/ShadowInfo/Shadow/Source: Zdefiniiowany we wtyczce <$link to=<<pluginTiddler>>><$text text=<<pluginTiddler>>/></$link>
|
||||||
Advanced/ShadowInfo/OverriddenShadow/Hint: Nadpisany przez zwykłego tiddlera
|
Advanced/ShadowInfo/OverriddenShadow/Hint: Nadpisany przez zwykłego tiddlera
|
||||||
|
Advanced/CascadeInfo/Heading: Szczegóły Kaskady
|
||||||
|
Advanced/CascadeInfo/Hint: Lista elementów widoku, których kaskady określają szablony użyte do wyświetlenia tiddlera
|
||||||
|
Advanced/CascadeInfo/Detail/View: Widok
|
||||||
|
Advanced/CascadeInfo/Detail/ActiveCascadeFilter: Aktywny filtr kaskady
|
||||||
|
Advanced/CascadeInfo/Detail/Template: Szablon
|
||||||
Fields/Caption: Pola
|
Fields/Caption: Pola
|
||||||
List/Caption: Lista
|
List/Caption: Lista
|
||||||
List/Empty: Ten tiddler nie posiada żadnych list
|
List/Empty: Ten tiddler nie posiada żadnych list
|
||||||
|
|||||||
@@ -649,3 +649,5 @@ Rishu kumar, @rishu-7549, 2025/10/25
|
|||||||
@kjharcombe, 2026/03/16
|
@kjharcombe, 2026/03/16
|
||||||
|
|
||||||
Himmel, @NotHimmel, 2026/03/19
|
Himmel, @NotHimmel, 2026/03/19
|
||||||
|
|
||||||
|
@sean-clayton, 2026/05/16
|
||||||
Generated
+14
-6
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "tiddlywiki",
|
"name": "tiddlywiki",
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "tiddlywiki",
|
"name": "tiddlywiki",
|
||||||
"version": "5.4.0",
|
"version": "5.4.1",
|
||||||
"license": "BSD",
|
"license": "BSD",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tiddlywiki": "tiddlywiki.js"
|
"tiddlywiki": "tiddlywiki.js"
|
||||||
@@ -625,7 +625,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.3.3",
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -715,7 +717,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -780,7 +784,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -872,7 +878,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tiddlywiki",
|
"name": "tiddlywiki",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
"version": "5.5.0-prerelease",
|
"version": "5.4.1",
|
||||||
"author": "Jeremy Ruston <jeremy@jermolene.com>",
|
"author": "Jeremy Ruston <jeremy@jermolene.com>",
|
||||||
"description": "a non-linear personal web notebook",
|
"description": "a non-linear personal web notebook",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
|
|||||||
+11
-3
@@ -6,6 +6,8 @@ const { defineConfig, devices } = require('@playwright/test');
|
|||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
testDir: './editions/test/',
|
testDir: './editions/test/',
|
||||||
|
|
||||||
|
timeout: 60000,
|
||||||
|
|
||||||
// Allow parallel tests
|
// Allow parallel tests
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
|
|
||||||
@@ -23,11 +25,17 @@ module.exports = defineConfig({
|
|||||||
|
|
||||||
// Settings shared with all the tests
|
// Settings shared with all the tests
|
||||||
use: {
|
use: {
|
||||||
// Take a screenshot when the test fails
|
|
||||||
screenshot: {
|
screenshot: {
|
||||||
mode: 'only-on-failure',
|
mode: 'only-on-failure',
|
||||||
fullPage: true
|
fullPage: true
|
||||||
}
|
},
|
||||||
|
// Limit individual actions (like click/type) so they don't hang indefinitely
|
||||||
|
actionTimeout: 15000,
|
||||||
|
},
|
||||||
|
|
||||||
|
expect: {
|
||||||
|
// Give expect() assertions more time to find elements like '.jasmine-overall-result'
|
||||||
|
timeout: 20000,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
@@ -39,7 +47,7 @@ module.exports = defineConfig({
|
|||||||
|
|
||||||
{
|
{
|
||||||
name: 'firefox',
|
name: 'firefox',
|
||||||
use: { ...devices['Desktop Firefox'] },
|
use: { ...devices['Desktop Firefox'] }
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user