1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-01-24 03:44:41 +00:00

Compare commits

...

15 Commits

Author SHA1 Message Date
yaisog
3605543078 Add docs and examples for the new operators (#7328)
* Create makepatches Operator.tid

* Improve wording

* Doc and examples for the new operators
2023-03-04 21:15:40 +00:00
yaisog
b548b29b2b Prevent infinite loop for single-word texts (#7327) 2023-03-04 13:37:16 +00:00
yaisog
0eb6569674 Add words and lines options to makepatches (#7326) 2023-03-04 10:40:25 +00:00
jeremy@jermolene.com
0c7e06a10c Fix crash with invalid patches
See https://github.com/Jermolene/TiddlyWiki5/pull/7290#issuecomment-1453155311

Thanks @yaisog
2023-03-03 18:22:35 +00:00
jeremy@jermolene.com
d5f0d834bc Initial Commit 2023-02-25 17:09:43 +00:00
jeremy@jermolene.com
8c378e0d24 Let widget should specify a default value for variables
Fixes #7270
2023-02-25 13:41:13 +00:00
andrigamerita
925ce2b505 Add meta viewport to StaticRiver (#6953)
StaticRiver HTML didn't feature a <meta name="viewport" line, which made single-page full wiki static exports look bad on mobile.
2023-02-24 16:51:30 +00:00
cdruan
eb8f4d66b9 Markdown Plugin: Handle non-string attr values in tw_image() (#7284) 2023-02-24 15:41:29 +00:00
jeremy@jermolene.com
ef03a4a5df Markdown Plugin: Default to recognising TiddlyWiki-format links
Fixes #7275
2023-02-24 09:51:45 +00:00
jeremy@jermolene.com
51a4d39c19 Refresh text editors when the palette changes
Fixes #7276
2023-02-23 10:13:13 +00:00
btheado
95dc56d850 Fix importTitle and autoOpenOnImport in the tm-import-tiddler message (#7243)
* Add initial tm-import-tiddler test

* Add failing test for tm-import-tiddlers importTitle #7234

* Add failing test for tm-import-tiddlers autoOpenOnImport #7234

* Use event.paramObject instead of event to access tm-import-tiddlers options. Fixes #7234.

* Added a clarifying comment

* Allow mixing tm-import-tiddler params in both event and event.paramObject

* Added import test using tv-auto-open-on-import variable

* Removed stray punctuation
2023-02-11 09:15:44 +00:00
Jeremy Ruston
77b418004a Zoomin: Avoid using broken tc-storyview-zoomin-tiddler class (#7252)
* First commit

Fixes #7247

* Revert to setting the faulty tc-storyview-zoomin-tiddler class for backwards compatibility
2023-02-10 21:27:20 +00:00
Cameron Fischer
028dfe39b7 Fixed issue where $genesis didn't pass isBlock (#7230)
* Fixed issue where $genesis didn't pass isBlock

* Added $mode attribute for genesis

* Added documentation for $mode attribute
2023-02-10 10:17:32 +00:00
jeremy@jermolene.com
81f5141166 Dynannotate: Don't crash when used in the fake DOM
Fixes #7258
2023-02-10 09:00:52 +00:00
jeremy@jermolene.com
3da3318396 Node.js: Prefer .tid format when a _canonical_uri field present
Fixes #7238
2023-02-03 19:22:43 +00:00
28 changed files with 562 additions and 31 deletions

View File

@@ -218,7 +218,7 @@ function editTextWidgetFactory(toolbarEngine,nonToolbarEngine) {
EditTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Completely rerender if any of our attributes have changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedAttributes.disabled || changedAttributes.fileDrop) {
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || changedTiddlers[HEIGHT_MODE_TITLE] || changedTiddlers[ENABLE_TOOLBAR_TITLE] || changedTiddlers["$:/palette"] || changedAttributes.disabled || changedAttributes.fileDrop) {
this.refreshSelf();
return true;
} else if (changedTiddlers[this.editRefreshTitle]) {

View File

@@ -74,6 +74,113 @@ exports.join = makeStringReducingOperator(
},null
);
var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
exports.levenshtein = makeStringBinaryOperator(
function(a,b) {
var dmpObject = new dmp.diff_match_patch(),
diffs = dmpObject.diff_main(a,b);
return [dmpObject.diff_levenshtein(diffs) + ""];
}
);
// these two functions are adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
function diffLineWordMode(text1,text2,mode) {
var dmpObject = new dmp.diff_match_patch();
var a = diffPartsToChars(text1,text2,mode);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmpObject.diff_main(lineText1,lineText2,false);
dmpObject.diff_charsToLines_(diffs,lineArray);
return diffs;
}
function diffPartsToChars(text1,text2,mode) {
var lineArray = [];
var lineHash = {};
lineArray[0] = '';
function diff_linesToPartsMunge_(text,mode) {
var chars = '';
var lineStart = 0;
var lineEnd = -1;
var lineArrayLength = lineArray.length,
regexpResult;
const searchRegexp = /\W+/g;
while(lineEnd < text.length - 1) {
if(mode === "words") {
regexpResult = searchRegexp.exec(text);
lineEnd = searchRegexp.lastIndex;
if(regexpResult === null) {
lineEnd = text.length;
}
lineEnd = --lineEnd;
} else {
lineEnd = text.indexOf('\n', lineStart);
if(lineEnd == -1) {
lineEnd = text.length - 1;
}
}
var line = text.substring(lineStart, lineEnd + 1);
if(lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : (lineHash[line] !== undefined)) {
chars += String.fromCharCode(lineHash[line]);
} else {
if (lineArrayLength == maxLines) {
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
var maxLines = 40000;
var chars1 = diff_linesToPartsMunge_(text1,mode);
maxLines = 65535;
var chars2 = diff_linesToPartsMunge_(text2,mode);
return {chars1: chars1, chars2: chars2, lineArray: lineArray};
};
exports.makepatches = function(source,operator,options) {
var dmpObject = new dmp.diff_match_patch(),
suffix = operator.suffix || "",
result = [];
source(function(tiddler,title) {
var diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmpObject.patch_make(title,diffs);
} else {
patches = dmpObject.patch_make(title,operator.operand);
}
Array.prototype.push.apply(result,[dmpObject.patch_toText(patches)]);
});
return result;
};
exports.applypatches = makeStringBinaryOperator(
function(a,b) {
var dmpObject = new dmp.diff_match_patch(),
patches;
try {
patches = dmpObject.patch_fromText(b);
} catch(e) {
}
if(patches) {
return [dmpObject.patch_apply(patches,a)[0]];
} else {
return [a];
}
}
);
function makeStringBinaryOperator(fnCalc) {
return function(source,operator,options) {
var result = [];
@@ -184,4 +291,4 @@ exports.charcode = function(source,operator,options) {
return [chars.join("")];
};
})();
})();

View File

@@ -238,7 +238,7 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
} else {
// Save as a .tid or a text/binary file plus a .meta file
var tiddlerType = tiddler.fields.type || "text/vnd.tiddlywiki";
if(tiddlerType === "text/vnd.tiddlywiki") {
if(tiddlerType === "text/vnd.tiddlywiki" || tiddler.hasField("_canonical_uri")) {
// Save as a .tid file
fileInfo.type = "application/x-tiddler";
fileInfo.hasMetaFile = false;

View File

@@ -46,6 +46,7 @@ GenesisWidget.prototype.execute = function() {
this.genesisRemappable = this.getAttribute("$remappable","yes") === "yes";
this.genesisNames = this.getAttribute("$names","");
this.genesisValues = this.getAttribute("$values","");
this.genesisIsBlock = this.getAttribute("$mode",this.parseTreeNode.isBlock && "block") === "block";
// Do not create a child widget if the $type attribute is missing or blank
if(!this.genesisType) {
this.makeChildWidgets(this.parseTreeNode.children);
@@ -60,6 +61,7 @@ GenesisWidget.prototype.execute = function() {
tag: nodeTag,
attributes: {},
orderedAttributes: [],
isBlock: this.genesisIsBlock,
children: this.parseTreeNode.children || [],
isNotRemappable: !this.genesisRemappable
}];

View File

@@ -74,7 +74,9 @@ LetWidget.prototype.getVariableInfo = function(name,options) {
text: this.currentValueFor[name]
};
}
return Widget.prototype.getVariableInfo.call(this,name,options);
return Widget.prototype.getVariableInfo.call(this,name,$tw.utils.extend(Object.create(null),options,{
defaultValue: ""
}));
};
/*

View File

@@ -499,7 +499,8 @@ NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
// Get the tiddlers
var tiddlers = $tw.utils.parseJSONSafe(event.param,[]);
// Get the current $:/Import tiddler
var importTitle = event.importTitle ? event.importTitle : IMPORT_TITLE,
var paramObject = event.paramObject || {},
importTitle = event.importTitle || paramObject.importTitle || IMPORT_TITLE,
importTiddler = this.wiki.getTiddler(importTitle),
importData = this.wiki.getTiddlerData(importTitle,{}),
newFields = new Object({
@@ -540,7 +541,7 @@ NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
newFields.text = JSON.stringify(importData,null,$tw.config.preferences.jsonSpaces);
this.wiki.addTiddler(new $tw.Tiddler(importTiddler,newFields));
// Update the story and history details
var autoOpenOnImport = event.autoOpenOnImport ? event.autoOpenOnImport : this.getVariable("tv-auto-open-on-import");
var autoOpenOnImport = event.autoOpenOnImport || paramObject.autoOpenOnImport || this.getVariable("tv-auto-open-on-import");
if(autoOpenOnImport !== "no") {
var storyList = this.getStoryList(),
history = [];

View File

@@ -14,6 +14,7 @@ extension: .html
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="generator" content="TiddlyWiki" />
<meta name="tiddlywiki-version" content="{{$:/core/templates/version}}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="format-detection" content="telephone=no">
<link id="faviconLink" rel="shortcut icon" href="favicon.ico">
<title>{{$:/core/wiki/title}}</title>

View File

@@ -0,0 +1,28 @@
title: Filters/DiffMergePatch1
description: Tests for diff-merge-patch derived operators
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\define text1()
the cat sat on the mat
\end
\define text2()
the hat saw in every category
\end
<$text text={{{ [<text1>makepatches<text2>] }}}/>
+
title: ExpectedResult
<p>@@ -1,22 +1,29 @@
the
-c
+h
at sa
-t on the mat
+w in every category
</p>

View File

@@ -0,0 +1,25 @@
title: Filters/DiffMergePatch2
description: Tests for diff-merge-patch derived operators
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\define text1()
the cat sat on the mat
\end
\define text2()
the hat saw in every category
\end
<$let patches={{{ [<text1>makepatches<text2>] }}}>
<$text text={{{ [<text1>applypatches<patches>] }}}/>
</$let>
+
title: ExpectedResult
the hat saw in every category

View File

@@ -0,0 +1,22 @@
title: Filters/DiffMergePatch3
description: Tests for diff-merge-patch derived operators
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\define text1()
the cat sat on the mat
\end
\define patches()
**NOT A VALID PATCH**
\end
<$text text={{{ [<text1>applypatches<patches>] }}}/>
+
title: ExpectedResult
the cat sat on the mat

View File

@@ -0,0 +1,30 @@
title: Genesis/Block
description: genesis widget distinguishes between block and inline
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
<$genesis $type="$reveal" type=nomatch>
Block
</$genesis>
<$genesis $type="$reveal" type=nomatch $mode=block>
Block forced block
</$genesis>
<$genesis $type="$reveal" type=nomatch $mode=inline>
Block forced inline
</$genesis>
<$genesis $type=$reveal type=nomatch>Inline</$genesis>
<$genesis $type=$reveal type=nomatch $mode=block>Inline forced block</$genesis>
<$genesis $type=$reveal type=nomatch $mode=inline>Inline forced inline</$genesis>
+
title: ExpectedResult
<div class=" tc-reveal"><p>Block</p></div><div class=" tc-reveal"><p>Block forced block</p></div><span class=" tc-reveal"><p>Block forced inline</p></span><p><span class=" tc-reveal">Inline</span><div class=" tc-reveal">Inline forced block</div><span class=" tc-reveal">Inline forced inline</span></p>

View File

@@ -0,0 +1,35 @@
title: Message/tm-import-tiddlers/CustomTitle
description: tm-import-tiddlers message can import to a tiddler with a custom title
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
text: <$text text={{MyCustomTitle}}/>
plugin-type: <$text text={{MyCustomTitle!!plugin-type}}/>
~$:/StoryList: <$text text={{$:/StoryList!!list}}/>
+
title: Actions
<$navigator story="$:/StoryList">
<$action-sendmessage
$message="tm-import-tiddlers"
$param='[{"title": "Elephants"}, {"title": "Eagles"}]'
importTitle=MyCustomTitle/>
</$navigator>
+
title: ExpectedResult
<p>text: {
"tiddlers": {
"Elephants": {
"title": "Elephants"
},
"Eagles": {
"title": "Eagles"
}
}
}
plugin-type: import
$:/StoryList: MyCustomTitle</p>

View File

@@ -0,0 +1,35 @@
title: Message/tm-import-tiddlers/NoAutoOpen
description: tm-import-tiddlers can import without automatically opening the import tiddler
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
text: <$text text={{$:/Import}}/>
plugin-type: <$text text={{$:/Import!!plugin-type}}/>
~$:/StoryList: <$text text={{$:/StoryList!!list}}/>
+
title: Actions
<$navigator story="$:/StoryList">
<$action-sendmessage
$message="tm-import-tiddlers"
$param='[{"title": "Elephants"}, {"title": "Eagles"}]'
autoOpenOnImport=no/>
</$navigator>
+
title: ExpectedResult
<p>text: {
"tiddlers": {
"Elephants": {
"title": "Elephants"
},
"Eagles": {
"title": "Eagles"
}
}
}
plugin-type: import
$:/StoryList: </p>

View File

@@ -0,0 +1,36 @@
title: Message/tm-import-tiddlers/NoAutoOpenViaVar
description: tm-import-tiddlers can import and open based on tv-auto-open-on-import
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
text: <$text text={{$:/Import}}/>
plugin-type: <$text text={{$:/Import!!plugin-type}}/>
~$:/StoryList: <$text text={{$:/StoryList!!list}}/>
+
title: Actions
<$let tv-auto-open-on-import="no">
<$navigator story="$:/StoryList">
<$action-sendmessage
$message="tm-import-tiddlers"
$param='[{"title": "Elephants"}, {"title": "Eagles"}]'/>
</$navigator>
</$let>
+
title: ExpectedResult
<p>text: {
"tiddlers": {
"Elephants": {
"title": "Elephants"
},
"Eagles": {
"title": "Eagles"
}
}
}
plugin-type: import
$:/StoryList: </p>

View File

@@ -0,0 +1,34 @@
title: Message/tm-import-tiddlers/default
description: tm-import-tiddlers message by default should import to $:/Import and open the tiddler
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
text: <$text text={{$:/Import}}/>
plugin-type: <$text text={{$:/Import!!plugin-type}}/>
~$:/StoryList: <$text text={{$:/StoryList!!list}}/>
+
title: Actions
<$navigator story="$:/StoryList">
<$action-sendmessage
$message="tm-import-tiddlers"
$param='[{"title": "Elephants"}, {"title": "Eagles"}]'/>
</$navigator>
+
title: ExpectedResult
<p>text: {
"tiddlers": {
"Elephants": {
"title": "Elephants"
},
"Eagles": {
"title": "Eagles"
}
}
}
plugin-type: import
$:/StoryList: $:/Import</p>

View File

@@ -1071,6 +1071,20 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("[charcode[9],[10]]").join(" ")).toBe(String.fromCharCode(9) + String.fromCharCode(10));
expect(wiki.filterTiddlers("[charcode[]]").join(" ")).toBe("");
});
it("should handle the levenshtein operator", function() {
expect(wiki.filterTiddlers("[[apple]levenshtein[apple]]").join(" ")).toBe("0");
expect(wiki.filterTiddlers("[[apple]levenshtein[banana]]").join(" ")).toBe("9");
expect(wiki.filterTiddlers("[[representation]levenshtein[misreprehensionisation]]").join(" ")).toBe("10");
expect(wiki.filterTiddlers("[[the cat sat on the mat]levenshtein[the hat saw in every category]]").join(" ")).toBe("13");
});
it("should handle the makepatches operator", function() {
expect(wiki.filterTiddlers("[[apple]makepatches[apple]]").join(" ")).toBe("");
expect(wiki.filterTiddlers("[[apple]makepatches[banana]]").join(" ")).toBe("@@ -1,5 +1,6 @@\n-apple\n+banana\n");
expect(wiki.filterTiddlers("[[representation]makepatches[misreprehensionisation]]").join(" ")).toBe("@@ -1,13 +1,21 @@\n+mis\n repre\n-sent\n+hensionis\n atio\n");
expect(wiki.filterTiddlers("[[the cat sat on the mat]makepatches[the hat saw in every category]]").join(" ")).toBe("@@ -1,22 +1,29 @@\n the \n-c\n+h\n at sa\n-t on the mat\n+w in every category\n");
});
it("should parse filter variable parameters", function(){
expect($tw.utils.parseFilterVariable("currentTiddler")).toEqual(

View File

@@ -0,0 +1,15 @@
caption: applypatches
created: 20230304154824762
modified: 20230304154826621
op-purpose: applies a set of patches to transform the input
op-input: a [[selection of titles|Title Selection]]
op-parameter: a string containing patches from the [[makepatches Operator]]
op-parameter-name: P
op-output: the transformed input to which the patches <<.place P>> have been applied
tags: [[Filter Operators]] [[String Operators]]
title: applypatches Operator
type: text/vnd.tiddlywiki
<<.from-version "5.2.6">>
<<.operator-examples "makepatches and applypatches">>

View File

@@ -0,0 +1,11 @@
created: 20230304161453213
modified: 20230304162156826
tags: [[Operator Examples]]
title: Hamlet
type: application/json
{
"Shakespeare-old": "Hamlet: Do you see yonder cloud that's almost in shape of a camel?\nPolonius: By the mass, and 'tis like a camel, indeed.\nHamlet: Methinks it is like a weasel.\nPolonius: It is backed like a weasel.\nHamlet: Or like a whale?\nPolonius: Very like a whale.\n-- Shakespeare",
"Shakespeare-new": "Hamlet: Do you see the cloud over there that's almost the shape of a camel?\nPolonius: By golly, it is like a camel, indeed.\nHamlet: I think it looks like a weasel.\nPolonius: It is shaped like a weasel.\nHamlet: Or like a whale?\nPolonius: It's totally like a whale.\n-- Shakespeare",
"Trekkie-old": "Kirk: Do you see yonder cloud that's almost in shape of a Klingon?\nSpock: By the mass, and 'tis like a Klingon, indeed.\nKirk: Methinks it is like a Vulcan.\nSpock: It is backed like a Vulcan.\nKirk: Or like a Romulan?\nSpock: Very like a Romulan.\n-- Trekkie"
}

View File

@@ -0,0 +1,21 @@
created: 20230304183158728
modified: 20230304183159654
tags: [[levenshtein Operator]] [[Operator Examples]]
title: levenshtein Operator (Examples)
type: text/vnd.tiddlywiki
Determine the Levenshtein distance between two words:
<<.operator-example 1 "[[motel]levenshtein[money]]">>
List the 10 tiddler titles with the smallest Levenstein distance to "~TiddlyWiki":
<$macrocall $name='wikitext-example-without-html'
src="""<ul>
<$list filter="[all[tiddlers]!is[system]] :sort:number[levenshtein[TiddlyWiki]] :and[first[10]]">
<li>
<$link /> (<$text text={{{ [all[current]levenshtein[TiddlyWiki]] }}} />)
</li>
</$list>
</ul>
"""/>

View File

@@ -0,0 +1,43 @@
created: 20230304160331362
modified: 20230304160332927
tags: [[makepatches Operator]] [[applypatches Operator]] [[Operator Examples]]
title: makepatches and applypatches Operator (Examples)
type: text/vnd.tiddlywiki
These examples use the example texts in [[Hamlet]], taken from [[https://neil.fraser.name/software/diff_match_patch/demos/patch.html]]
|^!Shakespeare's original |@@white-space: pre-wrap;{{Hamlet##Shakespeare-old}}@@ |
|^!Modern English |@@white-space: pre-wrap;{{Hamlet##Shakespeare-new}}@@ |
|^!Trekkie's Copy |@@white-space: pre-wrap;{{Hamlet##Trekkie-old}}@@ |
<div class="doc-examples-hard-breaks">
Use `makepatches` to generate the set of patches to transform Shakepeare's original into Modern English:
<<.operator-example 1 "[{Hamlet##Shakespeare-old}makepatches{Hamlet##Shakespeare-new}]">>
Use `applypatches` to apply the patches to Shakespeare's original text:
<<.operator-example 2 "[{Hamlet##Shakespeare-old}makepatches{Hamlet##Shakespeare-new}] :map[{Hamlet##Shakespeare-old}applypatches<currentTiddler>]">>
In the above example, the [[Map Filter Run Prefix]] is used to pass the patches information as a parameter to `applypatches`. Inside `:map`, <<.value currentTiddler>> is set to the input title (i.e. the previously generated patches).
The patch information from the Shakepeare texts can also be used to transform the //Trekkie's Copy// to a Modern English version:
<<.operator-example 3 "[{Hamlet##Shakespeare-old}makepatches{Hamlet##Shakespeare-new}] :map[{Hamlet##Trekkie-old}applypatches<currentTiddler>]">>
The above examples used the character mode of `makepatches`. The `word` mode yields very similar results in this case, even when applied to the //Trekkie's Copy//.
<<.operator-example 4 "[{Hamlet##Shakespeare-old}makepatches:words{Hamlet##Shakespeare-new}]">>
<<.operator-example 5 "[{Hamlet##Shakespeare-old}makepatches:words{Hamlet##Shakespeare-new}] :map[{Hamlet##Trekkie-old}applypatches<currentTiddler>]">>
The `lines` mode doesn't work as well in this application:
<<.operator-example 6 "[{Hamlet##Shakespeare-old}makepatches:lines{Hamlet##Shakespeare-new}]">>
<<.operator-example 7 "[{Hamlet##Shakespeare-old}makepatches:lines{Hamlet##Shakespeare-new}] :map[{Hamlet##Trekkie-old}applypatches<currentTiddler>]">>
It is better suited as a very fast algorithm to detect line-wise incremental changes to texts and store only the changes instead of multiple versions of the whole texts.
</div>

View File

@@ -0,0 +1,17 @@
caption: levenshtein
created: 20230304181639768
modified: 20230304181642365
op-purpose: determine the Levenshtein distance of the input title(s) and a given string
op-input: a [[selection of titles|Title Selection]]
op-parameter: a string
op-parameter-name: S
op-output: the Levenshtein distance between the input title(s) and <<.place S>>
tags: [[Filter Operators]] [[String Operators]]
title: levenshtein Operator
type: text/vnd.tiddlywiki
<<.from-version "5.2.6">>
The Levenshtein distance is a metric for measuring the difference between two strings. Informally, the Levenshtein distance between two strings is the //minimum// number of single-character edits required to change one string into the other.
<<.operator-examples "levenshtein">>

View File

@@ -0,0 +1,23 @@
caption: makepatches
created: 20230304122354967
modified: 20230304122400128
op-purpose: returns a set of patches that transform the input to a given string
op-input: a [[selection of titles|Title Selection]]
op-parameter: a string of characters
op-parameter-name: S
op-output: a set of patch instructions per input title to be used by the [[applypatches Operator]] to transform the input title(s) into the string <<.place S>>
op-suffix: `lines` to operate in line mode, `words` to operate in word mode. If omitted (default), the algorithm operates in character mode. See notes below.
op-suffix-name: T
tags: [[Filter Operators]] [[String Operators]]
title: makepatches Operator
type: text/vnd.tiddlywiki
<<.from-version "5.2.6">>
The difference algorithm operates in character mode by default. This produces the most detailed diff possible. In `words` mode, each word in the input text is transformed into a meta-character, upon which the algorithm then operates. In the default character mode, the filter would find two patches between "ActionWidget" and "Action-Widgets" (the hyphen and the plural s), while in `words` mode, the whole word is found to be changed. In `lines` mode, the meta-character is formed from the whole line, delimited by newline characters, and is found to be changed independent of the number of changes within the line.
The different modes influence the result when the patches are applied to texts other than the original, as well as the runtime.
<<.tip "The calculation in `words` mode is roughly 10 times faster than the default character mode, while `lines` mode can be more than 100 times faster than the default.">>
<<.operator-examples "makepatches and applypatches">>

View File

@@ -133,6 +133,10 @@ td svg {
padding-left: 20px;
}
.doc-examples-hard-breaks .doc-example-result li {
white-space: pre-wrap;
}
.doc-bad-example code, .doc-bad-example pre, table.doc-bad-example {
background-color:#ffff80;
}

View File

@@ -17,6 +17,7 @@ The content of the <<.wid genesis>> widget is used as the content of the dynamic
|$type |The type of widget or element to create (an initial `$` indicates a widget, otherwise an HTML element will be created) |
|$names |An optional filter evaluating to the names of a list of attributes to be applied to the widget |
|$values |An optional filter evaluating to the values corresponding to the list of names specified in <<.attr $names>> |
|$mode |An optional override of the parsing mode. May be "inline" or "block" |
|//{other attributes starting with $}// |Other attributes starting with a single dollar sign are reserved for future use |
|//{attributes starting with $$}// |Attributes starting with two dollar signs are applied as attributes to the output widget, but with the attribute name changed to use a single dollar sign |
|//{attributes not starting with $}// |Any other attributes that do not start with a dollar are applied as attributes to the output widget |

View File

@@ -38,23 +38,28 @@ DynannotateWidget.prototype.render = function(parent,nextSibling) {
// Create our DOM nodes
var isSnippetMode = this.isSnippetMode();
this.domContent = $tw.utils.domMaker("div",{
"class": "tc-dynannotation-selection-container"
"class": "tc-dynannotation-selection-container",
document: this.document
});
if(isSnippetMode) {
this.domContent.setAttribute("hidden","hidden");
this.domContent.setAttribute("hidden","hidden");
}
this.domAnnotations = $tw.utils.domMaker("div",{
"class": "tc-dynannotation-annotation-wrapper"
"class": "tc-dynannotation-annotation-wrapper",
document: this.document
});
this.domSnippets = $tw.utils.domMaker("div",{
"class": "tc-dynannotation-snippet-wrapper"
"class": "tc-dynannotation-snippet-wrapper",
document: this.document
});
this.domSearches = $tw.utils.domMaker("div",{
"class": "tc-dynannotation-search-wrapper"
"class": "tc-dynannotation-search-wrapper",
document: this.document
});
this.domWrapper = $tw.utils.domMaker("div",{
"class": "tc-dynannotation-wrapper",
children: [this.domContent,this.domAnnotations,this.domSnippets,this.domSearches]
children: [this.domContent,this.domAnnotations,this.domSnippets,this.domSearches],
document: this.document
})
parent.insertBefore(this.domWrapper,nextSibling);
this.domNodes.push(this.domWrapper);
@@ -64,16 +69,18 @@ DynannotateWidget.prototype.render = function(parent,nextSibling) {
}
// Render our child widgets
this.renderChildren(this.domContent,null);
if(isSnippetMode) {
// Apply search snippets
this.applySnippets();
} else {
// Get the list of annotation tiddlers
this.getAnnotationTiddlers();
// Apply annotations
this.applyAnnotations();
// Apply search overlays
this.applySearch();
if(!this.document.isTiddlyWikiFakeDom) {
if(isSnippetMode) {
// Apply search snippets
this.applySnippets();
} else {
// Get the list of annotation tiddlers
this.getAnnotationTiddlers();
// Apply annotations
this.applyAnnotations();
// Apply search overlays
this.applySearch();
}
}
// Save the width of the wrapper so that we can tell when it changes
this.wrapperWidth = this.domWrapper.offsetWidth;

View File

@@ -7,5 +7,5 @@ markdown/breaks: false
markdown/linkify: false
markdown/quotes: “”‘’
markdown/renderWikiText: true
markdown/renderWikiTextPragma: \rules only html entity syslink wikilink commentblock commentinline macrocallblock macrocallinline transcludeblock transcludeinline filteredtranscludeblock filteredtranscludeinline
markdown/renderWikiTextPragma: \rules only html entity syslink prettylink image prettyextlink wikilink commentblock commentinline macrocallblock macrocallinline transcludeblock transcludeinline filteredtranscludeblock filteredtranscludeinline
markdown/typographer: false

View File

@@ -103,8 +103,9 @@ function render_tw_expr(tokens,idx) {
return tokens[idx].content;
}
// Overwrite default: render attribute strings in e"..." format instead,
// so HTML entities can be decoded. See parseStringLiteralExt() below.
// Overwrite default: attribute values can be either a string or {type;, value:}.
// 1) string attr val: render in e"..." format so HTML entities can be decoded.
// 2) object attr val: render value as is.
function render_token_attrs(token) {
var i, l, result;
@@ -113,7 +114,11 @@ function render_token_attrs(token) {
result = '';
for(i=0, l=token.attrs.length; i<l; i++) {
result += ' ' + md.utils.escapeHtml(token.attrs[i][0]) + '=e"' + md.utils.escapeHtml(token.attrs[i][1]) + '"';
if(typeof token.attrs[i][1] === "object" && token.attrs[i][1] !== null) {
result += ' ' + md.utils.escapeHtml(token.attrs[i][0]) + '=' + token.attrs[i][1].value;
} else {
result += ' ' + md.utils.escapeHtml(token.attrs[i][0]) + '=e"' + md.utils.escapeHtml(token.attrs[i][1]) + '"';
}
}
return result;
@@ -264,7 +269,19 @@ function tw_image(state,silent) {
var twNode = ruleinfo.rule.parse()[0];
var token = state.push('$image','$image',0);
$tw.utils.each(twNode.attributes,function(attr,id) {
token.attrSet(id,attr.value);
switch(attr.type) {
case "filtered":
token.attrSet(id,{ type: "filtered", value: "{{{" + attr.filter + "}}}" });
break;
case "indirect":
token.attrSet(id,{ type: "indirect", value: "{{" + attr.textReference + "}}" });
break;
case "macro":
token.attrSet(id,{ type: "macro", value: ruleinfo.rule.parser.source.substring(attr.value.start,attr.value.end) });
break;
default:
token.attrSet(id,attr.value);
}
});
token.markup = 'tw_image';
}

View File

@@ -1445,7 +1445,7 @@ html body.tc-body.tc-single-tiddler-window {
width: {{$:/themes/tiddlywiki/vanilla/metrics/sidebarwidth}};
}
body.tc-body .tc-storyview-zoomin-tiddler {
body.tc-body .tc-page-container.tc-page-view-zoomin .tc-tiddler-frame {
width: 100%;
width: calc(100% - 42px);
}
@@ -1457,7 +1457,7 @@ html body.tc-body.tc-single-tiddler-window {
margin-right: 0;
}
body.tc-body .tc-storyview-zoomin-tiddler {
body.tc-body .tc-page-container.tc-page-view-zoomin .tc-tiddler-frame {
width: 100%;
width: calc(100% - 84px);
}
@@ -1716,7 +1716,7 @@ html body.tc-body.tc-single-tiddler-window {
margin-right: .3em;
}
.tc-storyview-zoomin-tiddler {
.tc-page-container.tc-page-view-zoomin .tc-tiddler-frame {
position: absolute;
display: block;
width: 100%;
@@ -1724,7 +1724,7 @@ html body.tc-body.tc-single-tiddler-window {
@media (min-width: <<sidebarbreakpoint>>) {
.tc-storyview-zoomin-tiddler {
.tc-page-container.tc-page-view-zoomin .tc-tiddler-frame {
width: calc(100% - 84px);
}