mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-24 11:54:41 +00:00
Compare commits
5 Commits
v5.2.2
...
dynannotat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a79644bff3 | ||
|
|
9e8e93cbb7 | ||
|
|
495c42c7cf | ||
|
|
ffea61b345 | ||
|
|
5f4dc2a5fe |
File diff suppressed because one or more lines are too long
167
core/modules/filters/json-ops.js
Normal file
167
core/modules/filters/json-ops.js
Normal file
@@ -0,0 +1,167 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/json-ops.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operators for JSON operations
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports["getjson"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = options.wiki.getTiddlerDataCached(title);
|
||||
if(data) {
|
||||
var item = getDataItemValueAsStrings(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push.apply(results,item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["indexesjson"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = options.wiki.getTiddlerDataCached(title);
|
||||
if(data) {
|
||||
var item = getDataItemKeysAsStrings(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push.apply(results,item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["typejson"] = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var data = options.wiki.getTiddlerDataCached(title);
|
||||
if(data) {
|
||||
var item = getDataItemType(data,operator.operands);
|
||||
if(item !== undefined) {
|
||||
results.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItemValueAsStrings(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item as a string
|
||||
return convertDataItemValueToStrings(item);
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the keys of the item at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItemKeysAsStrings(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item keys as a string
|
||||
return convertDataItemKeysToStrings(item);
|
||||
}
|
||||
|
||||
/*
|
||||
Return an array of the string representation of the values of a data item, or "undefined" if the item is undefined
|
||||
*/
|
||||
function convertDataItemValueToStrings(item) {
|
||||
// Return the item as a string
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
}
|
||||
if(typeof item === "object") {
|
||||
if(item === null) {
|
||||
return ["null"];
|
||||
}
|
||||
var results = [];
|
||||
if($tw.utils.isArray(item)) {
|
||||
$tw.utils.each(item,function(value) {
|
||||
results.push.apply(results,convertDataItemValueToStrings(value));
|
||||
});
|
||||
return results;
|
||||
} else {
|
||||
$tw.utils.each(Object.keys(item).sort(),function(key) {
|
||||
results.push.apply(results,convertDataItemValueToStrings(item[key]));
|
||||
});
|
||||
return results;
|
||||
}
|
||||
}
|
||||
return [item.toString()];
|
||||
}
|
||||
|
||||
/*
|
||||
Return an array of the string representation of the keys of a data item, or "undefined" if the item is undefined
|
||||
*/
|
||||
function convertDataItemKeysToStrings(item) {
|
||||
// Return the item as a string
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
} else if(typeof item === "object") {
|
||||
if(item === null) {
|
||||
return [];
|
||||
}
|
||||
var results = [];
|
||||
if($tw.utils.isArray(item)) {
|
||||
for(var i=0; i<item.length; i++) {
|
||||
results.push(i.toString());
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
$tw.utils.each(Object.keys(item).sort(),function(key) {
|
||||
results.push(key);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getDataItemType(data,indexes) {
|
||||
// Get the item
|
||||
var item = getDataItem(data,indexes);
|
||||
// Return the item type
|
||||
if(item === undefined) {
|
||||
return item;
|
||||
} else if(item === null) {
|
||||
return "null";
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
return "array";
|
||||
} else if(typeof item === "object") {
|
||||
return "object";
|
||||
} else {
|
||||
return typeof item;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
function getDataItem(data,indexes) {
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return data;
|
||||
}
|
||||
// Get the item
|
||||
var item = data;
|
||||
for(var i=0; i<indexes.length; i++) {
|
||||
if(item !== undefined) {
|
||||
item = item[indexes[i]];
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -15,7 +15,7 @@ caption: {{$:/language/SideBar/Open/Caption}}
|
||||
|
||||
\define droppable-item(button)
|
||||
\whitespace trim
|
||||
<$droppable actions=<<drop-actions>> enable=<<tv-allow-drag-and-drop>> tag="div">
|
||||
<$droppable actions=<<drop-actions>> enable=<<tv-allow-drag-and-drop>>>
|
||||
<<placeholder>>
|
||||
<div>
|
||||
$button$
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
caption: 5.2.2
|
||||
created: 20220325130817150
|
||||
modified: 20220325130817150
|
||||
released: 20220325130817150
|
||||
created: 20220208152620527
|
||||
modified: 20220208152620527
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.2.2
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -10,21 +9,15 @@ type: text/vnd.tiddlywiki
|
||||
<a href="https://github.com/$username$" style="text-decoration:none;font-size:24px;" class="tc-tiddlylink-external" target="_blank" rel="noopener noreferrer"><img src="https://github.com/$username$.png?size=32" width="32" height="32"/> @<$text text=<<__username__>>/></a>
|
||||
\end
|
||||
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.2.1...v5.2.2]]//
|
||||
|
||||
<<.banner-credits
|
||||
credit:"""Congratulations to [[Elise Springer|https://www.wesleyan.edu/academics/faculty/espringer/profile.html]] for her winning design for the banner for this release (here is the [[competition thread|https://groups.google.com/d/msgid/tiddlywiki/ad868177-3432-4f8c-bec5-5f6639d1aeddn%40googlegroups.com]]).
|
||||
"""
|
||||
url:"https://raw.githubusercontent.com/Jermolene/TiddlyWiki5/e26a4d8cb54fa60b8a47e3cef2c3a962535ca619/editions/tw5.com/tiddlers/images/New%20Release%20Banner.png"
|
||||
>>
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.2.1...master]]//
|
||||
|
||||
! Highlights
|
||||
|
||||
!! <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/pull/6427">> [[Highlight Plugin]] to use highlight.js v11.4.0
|
||||
!! <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6553">> incorrectly nested `<span>` and `<div>` elements
|
||||
|
||||
This is a major upgrade to the latest version of highlight.js. The new version has many improvements, including better support for Fortran.
|
||||
Until now, TiddlyWiki's core page layout included several instances of inline `<span>` elements containing block `<div>` elements, something that is technically invalid HTML. In practice, browsers have always handled these cases leniently to make the page work but it has also been unhelpful for developers to encountering invalid HTML constructions in their first interactions with TiddlyWiki.
|
||||
|
||||
<<.warning """The new version of the [[Highlight Plugin]] requires a modern browser that fully supports JavaScript ES6 (released in 2015). The older version is still available as the ''highlight-legacy'' plugin for users who need to use an older browser.""">>
|
||||
The immediate prompt for fixing the issue now is that Chrome v100 [[includes a change|https://chromium.googlesource.com/chromium/src/+/4a76c96d2a4021257e691058c1fe1fb60d9d8327]] that causes these misnested elements to be rendered correctly visually but to be unclickable in some situations. This change is [[reverted|https://chromium.googlesource.com/chromium/src/+/abadd1181ca70cf3d1ac7e7878d46b1d3173a2e0]] in Chrome v102.
|
||||
|
||||
!! <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/6498">> support for secondary windows
|
||||
|
||||
@@ -54,11 +47,11 @@ For example, adding a new tiddler with a given tag previously caused the new tid
|
||||
|
||||
The fix ensures that the enumeration order remains consistent.
|
||||
|
||||
!! <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6553">> some incorrectly nested `<span>` and `<div>` elements
|
||||
!! <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/pull/6427">> [[Highlight Plugin]] to use highlight.js v11.4.0
|
||||
|
||||
TiddlyWiki's core page layout has historically included several instances of inline `<span>` elements containing block `<div>` elements, something that is technically invalid HTML. In practice, browsers have always handled these cases leniently to make the page work but it has also been unhelpful for developers to encountering invalid HTML constructions in their first interactions with TiddlyWiki.
|
||||
This is a major upgrade to the latest version of highlight.js. The new version has many improvements, including better support for Fortran.
|
||||
|
||||
The immediate prompt for starting to fix these issue now is that Chrome v100 [[includes a change|https://chromium.googlesource.com/chromium/src/+/4a76c96d2a4021257e691058c1fe1fb60d9d8327]] that causes some of these misnested elements to be rendered correctly visually but to be unclickable in some situations. (The change is [[reverted|https://chromium.googlesource.com/chromium/src/+/abadd1181ca70cf3d1ac7e7878d46b1d3173a2e0]] in Chrome v102, but it still makes sense to fix it)
|
||||
<<.warning """The new version of the [[Highlight Plugin]] requires a modern browser that fully supports JavaScript ES6 (released in 2015). The older version is still available as the ''highlight-legacy'' plugin for users who need to use an older browser.""">>
|
||||
|
||||
! Plugin Improvements
|
||||
|
||||
@@ -141,7 +134,6 @@ The immediate prompt for starting to fix these issue now is that Chrome v100 [[i
|
||||
* <<contributor btheado>>
|
||||
* <<contributor CodaCodr>>
|
||||
* <<contributor cdruan>>
|
||||
* <<contributor damscal>>
|
||||
* <<contributor davout1806>>
|
||||
* <<contributor EvidentlyCube>>
|
||||
* <<contributor FlashSystems>>
|
||||
@@ -1,58 +0,0 @@
|
||||
caption: 5.2.3
|
||||
created: 20220325131459084
|
||||
modified: 20220325131459084
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.2.3
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
\define contributor(username)
|
||||
<a href="https://github.com/$username$" style="text-decoration:none;font-size:24px;" class="tc-tiddlylink-external" target="_blank" rel="noopener noreferrer"><img src="https://github.com/$username$.png?size=32" width="32" height="32"/> @<$text text=<<__username__>>/></a>
|
||||
\end
|
||||
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.2.2...master]]//
|
||||
|
||||
! Plugin Improvements
|
||||
|
||||
*
|
||||
|
||||
! Translation improvements
|
||||
|
||||
*
|
||||
|
||||
! Usability Improvements
|
||||
|
||||
*
|
||||
|
||||
! Widget Improvements
|
||||
|
||||
*
|
||||
|
||||
! Filter improvements
|
||||
|
||||
*
|
||||
|
||||
! Hackability Improvements
|
||||
|
||||
*
|
||||
|
||||
! Developer Improvements
|
||||
|
||||
*
|
||||
|
||||
! Node.js Improvements
|
||||
|
||||
*
|
||||
|
||||
! Performance Improvements
|
||||
|
||||
*
|
||||
|
||||
! Bug Fixes
|
||||
|
||||
*
|
||||
|
||||
! Acknowledgements
|
||||
|
||||
[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki:
|
||||
|
||||
*
|
||||
82
editions/test/tiddlers/tests/test-json-filters.js
Normal file
82
editions/test/tiddlers/tests/test-json-filters.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/*\
|
||||
title: test-json-filters.js
|
||||
type: application/javascript
|
||||
tags: [[$:/tags/test-spec]]
|
||||
|
||||
Tests the JSON filters.
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/* jslint node: true, browser: true */
|
||||
/* eslint-env node, browser, jasmine */
|
||||
/* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
|
||||
/* global $tw, require */
|
||||
"use strict";
|
||||
|
||||
describe("json filter tests", function() {
|
||||
|
||||
var wiki = new $tw.Wiki();
|
||||
var tiddlers = [{
|
||||
title: "First",
|
||||
text: '{"a":"one","b":"","c":1.618,"d": {"e": "four","f": ["five","six",true,false,null]}}',
|
||||
type: "application/json"
|
||||
},{
|
||||
title: "Second",
|
||||
text: '["une","deux","trois"]',
|
||||
type: "application/json"
|
||||
}];
|
||||
wiki.addTiddlers(tiddlers);
|
||||
|
||||
it("should support the getindex operator", function() {
|
||||
expect(wiki.filterTiddlers("[[First]getindex[b]]")).toEqual([]);
|
||||
});
|
||||
|
||||
it("should support the getjson operator", function() {
|
||||
expect(wiki.filterTiddlers("[[First]getjson[]]")).toEqual(["one","","1.618","four","five","six","true","false","null"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[a]]")).toEqual(["one"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[b]]")).toEqual([""]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d]]")).toEqual(["four","five","six","true","false","null"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[e]]")).toEqual(["four"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[f]]")).toEqual(["five","six","true","false","null"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[f],[0]]")).toEqual(["five"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[f],[1]]")).toEqual(["six"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[f],[2]]")).toEqual(["true"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[f],[3]]")).toEqual(["false"]);
|
||||
expect(wiki.filterTiddlers("[[First]getjson[d],[f],[4]]")).toEqual(["null"]);
|
||||
});
|
||||
|
||||
it("should support the indexesjson operator", function() {
|
||||
expect(wiki.filterTiddlers("[[Second]indexesjson[]]")).toEqual(["0","1","2"]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[]]")).toEqual(["a","b","c","d"]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[a]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[b]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d]]")).toEqual(["e","f"]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[e]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[f]]")).toEqual(["0","1","2","3","4"]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[f],[0]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[f],[1]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[f],[2]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[f],[3]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[[First]indexesjson[d],[f],[4]]")).toEqual([]);
|
||||
});
|
||||
|
||||
it("should support the typejson operator", function() {
|
||||
expect(wiki.filterTiddlers("[[First]typejson[]]")).toEqual(["object"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[a]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[b]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[c]]")).toEqual(["number"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d]]")).toEqual(["object"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[e]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[f]]")).toEqual(["array"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[f],[0]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[f],[1]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[f],[2]]")).toEqual(["boolean"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[f],[3]]")).toEqual(["boolean"]);
|
||||
expect(wiki.filterTiddlers("[[First]typejson[d],[f],[4]]")).toEqual(["null"]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -7,4 +7,4 @@ title: Named Filter Run Prefix
|
||||
<$railroad text=<<prefixlist>>/>
|
||||
</$set>
|
||||
|
||||
A named filter run prefix can precede any [[run|Filter Run]] of a [[filter expression|Filter Expression]] in place of a single-character prefix (`+`, `-` and so on). To create a new filter run prefix, create a [[Javascript module|Modules]] with a [[module-type|ModuleType]] of `filterrunprefix`.
|
||||
A named filter run prefix can precede any [[run|Filter Run]] of a [[filter expression|Filter Expression]] in place of a single-character prefix (`+`, `-` and so on). To create a new filter run prefix, create a [[Javascript module|Modules]] with a [[module-type|ModuleType]] of `filerrunprefix`.
|
||||
@@ -1,6 +1,6 @@
|
||||
created: 20130822170200000
|
||||
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?]] [[HelloThumbnail - TWEUM2017]]
|
||||
modified: 20220325130817150
|
||||
modified: 20220131164555580
|
||||
tags: TableOfContents
|
||||
title: HelloThere
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 144 KiB |
@@ -27,17 +27,10 @@ Basics/Tiddlers/Prompt: Anzahl Tiddler
|
||||
Basics/Title/Prompt: Titel dieses ~TiddlyWikis
|
||||
Basics/Username/Prompt: Benutzersignatur zum Editieren
|
||||
Basics/Version/Prompt: ~TiddlyWiki Version
|
||||
Cascades/Caption: Kaskaden
|
||||
Cascades/Hint: Diese Regeln werden dynamisch auf verschiedene Templates angewendet. Das Ergebnis einer Filter-Kaskade wird von der Filter Sequenz bestimmt, die als Erste ein Resultat zurück gibt.
|
||||
Cascades/TagPrompt: Filter Tags: <$macrocall $name="tag" tag=<<currentTiddler>>/>
|
||||
EditorTypes/Caption: Editor Typen
|
||||
EditorTypes/Editor/Caption: Editor
|
||||
EditorTypes/Hint: Diese Tiddler definieren, welcher Editor für bestimmte Tiddler Typen (MIME-Type) verwendet werden soll.
|
||||
EditorTypes/Type/Caption: MIME-Type
|
||||
EditTemplateBody/Caption: Edit Template "Körper"
|
||||
EditTemplateBody/Hint: Diese Filter-Kaskade wird vom Standard "Edit Template" verwendet. Es wählt die Vorlage für den Textbereich aus.
|
||||
FieldEditor/Caption: Feld Editor
|
||||
FieldEditor/Hint: Diese Filter-Kaskade wird vom "Edit Template" zur Bestimmung der Vorlage für die "Feld Namen" verwendet.
|
||||
Info/Caption: Info
|
||||
Info/Hint: Informationen über dieses TiddlyWiki
|
||||
KeyboardShortcuts/Add/Prompt: Tastenkürzel hier eingeben
|
||||
@@ -198,8 +191,6 @@ Settings/TitleLinks/Yes/Description: Tiddler Titel als Link anzeigen.
|
||||
Settings/MissingLinks/Caption: Wiki-Links
|
||||
Settings/MissingLinks/Hint: Aktiviere Links zu fehlenden Tiddlern. zB: FehlenderTiddler [[Einführung]]
|
||||
Settings/MissingLinks/Description: Aktiviere Links zu fehlenden Tiddlern.
|
||||
StoryTiddler/Caption: Story Tiddler
|
||||
StoryTiddler/Hint: Diese Filter-Kaskade wird verwendet, um die Vorlage für den Tiddler im "Story River" auszuwählen.
|
||||
StoryView/Caption: Anzeige
|
||||
StoryView/Prompt: Ausgewählte Anzeige:
|
||||
Stylesheets/Caption: Stylesheets
|
||||
@@ -210,10 +201,6 @@ Theme/Caption: Theme
|
||||
Theme/Prompt: Ausgewähltes Theme:
|
||||
TiddlerFields/Caption: Tiddler Felder
|
||||
TiddlerFields/Hint: Hier finden Sie alle [[Felder|TiddlerFields]], die in diesem Wiki verwendet werden. Inklusive der Felder aus System-, exklusive Schatten-Tiddler.
|
||||
TiddlerColour/Caption: Tiddler Farbe
|
||||
TiddlerColour/Hint: Diese Filter-Kaskade wird verwendet, um die Farbe für das "Icon" und den "Tag" auszuwählen.
|
||||
TiddlerIcon/Caption: Tiddler Icon
|
||||
TiddlerIcon/Hint: Diese Filter-Kaskade wird verwendet, um die das Tiddler "Icon" auszuwählen.
|
||||
Toolbars/Caption: Toolbar
|
||||
Toolbars/EditToolbar/Caption: Edit Toolbar
|
||||
Toolbars/EditToolbar/Hint: Auswählen, welche Buttons im "Edit Modus" angezeigt werden. Verwenden Sie "Drag and Drop", um die Reihenfolge zu ändern
|
||||
@@ -225,7 +212,3 @@ Toolbars/EditorToolbar/Hint: Auswählen, welche Editorbuttons angezeigt werden s
|
||||
Toolbars/ViewToolbar/Caption: View Toolbar
|
||||
Toolbars/ViewToolbar/Hint: Auswählen, welche Buttons im "View Modus" angezeigt werden. Verwenden Sie "Drag and Drop", um die Reihenfolge zu ändern
|
||||
Tools/Download/Full/Caption: Herunterladen des ''gesamten Wikis''
|
||||
ViewTemplateBody/Caption: View Template Text
|
||||
ViewTemplateBody/Hint: Diese Filter-Kaskade wird vom "View Template" dazu verwendet, um die Vorlage für den Tiddler Textbereich auszuwählen.
|
||||
ViewTemplateTitle/Caption: View Template Titel
|
||||
ViewTemplateTitle/Hint: Diese Filter-Kaskade wird vom "View Template" dazu verwendet, um die Vorlage für den Tiddler Titel auszuwählen.
|
||||
|
||||
@@ -2,27 +2,26 @@ title: $:/language/Docs/Fields/
|
||||
|
||||
_canonical_uri: Die komplette URI eines externen Foto Tiddlers. URI = Uniform Resource Identifier, Identifikator für Ressourcen im Internet.
|
||||
bag: Der Name eines ~TiddlyWeb "bags" von dem der Tiddler kam.
|
||||
code-body: Das "View Template" wird den Tiddler Text als "Code" anzeigen, wenn dieses Feld auf: ''"yes"'' gesetzt wird.
|
||||
caption: Der Text, der auf "Tab-Buttons" angezeigt wird.
|
||||
color: Der CSS Farbwert, der mit einem Tiddler assoziiert wird.
|
||||
color: Der CSS Farbwert, der mit einem Tiddler assoziiert wird.
|
||||
component: Der Name einer Komponente, die für eine [[Alarm Anzeige|AlertMechanism]] verantwortlich ist.
|
||||
current-tiddler: Wird verwendet um den "obersten" Tiddler in der [[Tiddler Historie|HistoryMechanism]] zwischen zu speichern.
|
||||
current-tiddler: Wird verwendet um den "obersten" Tiddler in der [[Tiddler Historie|HistoryMechanism]] zwischen zu speichern.
|
||||
created: Datum an dem der Tiddler erstellt wurde.
|
||||
creator: Name des Erstellers dieses Tiddlers.
|
||||
dependents: Listet die Abhängigkeiten bei "plugins" auf.
|
||||
description: Die Beschreibung für ein "plugin" oder einen "modalen" Dialog.
|
||||
draft.of: Entwurf von - enthält den Titel des Tiddlers, zu dem dieser Entwurf-Tiddler gehört.
|
||||
draft.title: Entwurf Titel - enthält den neuen Titel, wenn der Entwurf-Tiddler gespeichert wird.
|
||||
draft.title: Entwurf Titel - enthält den neuen Titel, wenn der Entwurf-Tiddler gespeichert wird.
|
||||
footer: Der Fußnoten Text bei einem "~Wizard-Dialog"
|
||||
hide-body: Der Textbereich eines Tiddlers wird verborgen, wenn dieses Feld auf ''"yes"'' gesetzt wird
|
||||
icon: Der Titel eines ~Icon-Tiddlers, der mit diesem Tiddler verbunden ist.
|
||||
library: Wenn dieses Feld="yes" ist, dann soll der Tiddler als JavaScript Bibliothek gespeichert werden.
|
||||
list: Eine geordnete Tiddler Liste, die mit diesem Tiddler verbunden ist.
|
||||
list-before: Dient zum Einfügen von Tiddler Titeln in das "list" Feld. Wenn gesetzt, wird der neue Tiddler ''vor'' dem hier definierten Tiddler in die Liste eingefügt. Wenn vorhanden, aber leer, dann wird der neue Tiddler an den Anfang der Liste gesetzt.
|
||||
list-after: Dient zum Einfügen von Tiddler Titeln in das "list" Feld. Wenn gesetzt, wird der neue Tiddler ''nach'' dem hier definierten Tiddler in die Liste eingefügt.
|
||||
list-after: Dient zum Einfügen von Tiddler Titeln in das "list" Feld. Wenn gesetzt, wird der neue Tiddler ''nach'' dem hier definierten Tiddler in die Liste eingefügt.
|
||||
modified: Datum, an dem der Tiddler zuletzt verändert wurde.
|
||||
modifier: Name der Person, die den Tiddler zuletzt verändert hat.
|
||||
name: Ein Menschen lesbarer Name für einen "plugin" Tiddler.
|
||||
name: Ein Menschen lesbarer Name für einen "plugin" Tiddler.
|
||||
plugin-priority: Ein numerischer Wert, der die Priorität eines "plugins" festlegt.
|
||||
plugin-type: Der Typ eines "plugins".
|
||||
revision: Die Revisionsnummer eines Tiddlers. Wird von einem Server vergeben.
|
||||
|
||||
@@ -3,7 +3,6 @@ title: $:/language/SideBar/
|
||||
All/Caption: Alle
|
||||
Contents/Caption: Inhalt
|
||||
Drafts/Caption: Entwurf
|
||||
Explorer/Caption: Explorer
|
||||
Missing/Caption: Fehlend
|
||||
More/Caption: Mehr
|
||||
Open/Caption: Offen
|
||||
|
||||
2
license
2
license
@@ -1,7 +1,7 @@
|
||||
TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com)
|
||||
|
||||
Copyright (c) 2004-2007, Jeremy Ruston
|
||||
Copyright (c) 2007-2022, UnaMesa Association
|
||||
Copyright (c) 2007-2021, UnaMesa Association
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -485,5 +485,3 @@ Joseph Cosentino, @jc-ose, 2021-12-14
|
||||
@FSpark, 2022/03/08
|
||||
|
||||
Guang Li, @oflg, 2022/03/12
|
||||
|
||||
Dam S., @damscal, 2022/03/24
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tiddlywiki",
|
||||
"preferGlobal": "true",
|
||||
"version": "5.2.2",
|
||||
"version": "5.2.2-prerelease",
|
||||
"author": "Jeremy Ruston <jeremy@jermolene.com>",
|
||||
"description": "a non-linear personal web notebook",
|
||||
"contributors": [
|
||||
@@ -34,4 +34,5 @@
|
||||
"scripts": {
|
||||
"lint": "eslint ."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
title: $:/plugins/tiddlywiki/dynannotate/readme
|
||||
|
||||
The ''Dynannotate'' plugin allows annotations on textual content to be created and displayed. It has three components:
|
||||
The ''Dynannotate'' plugin allows annotations on textual content to be created and displayed. It has several components:
|
||||
|
||||
* The dynannotate widget overlays clickable textual annotations, search highlights and search snippets on the content that it contains
|
||||
* The selection tracker displays a popup that tracks the selection, and keeps track of the selected text. It also tracks a prefix and suffix that can be used to disambiguate the selected text within the container
|
||||
* The `<$action-popup>` widget is used for some specialised popup switching in the demo
|
||||
|
||||
''Note that the TiddlyWiki core plugin __Dynaview__ is required for correct operation of __Dynannotate__''
|
||||
* The dynannotate widget draws clickable textual annotations, search highlights and search snippets as overlays over the top of the content that it contains
|
||||
* The selection tracker keeps track of changes to the selected text in the main browser window. It triggers an action string when the selection changes, passing it the details of the selection. It can be used to display a popup menu
|
||||
** The original legacy selection tracker is also provided for backwards compatibility. It is much more limited, and not recommended for new projects
|
||||
|
||||
!! Dynannotate Widget
|
||||
|
||||
@@ -32,6 +30,10 @@ The `<$dynannotate>` widget uses the selection tracker to support a popup that d
|
||||
|searchCaseSensitive |"no" (default) for a case insensitive search, or "yes" for a case sensitive search |
|
||||
|searchClass |Optional CSS class to be added to search overlays |
|
||||
|snippetContextLength |Optional length of search result contextual prefix/suffix |
|
||||
|
||||
The following attributes are only used with the legacy selection tracker:
|
||||
|
||||
|!Attribute |!Description |
|
||||
|selection |Tiddler to which the currently selected text should be dynamically saved |
|
||||
|selectionPrefix |Tiddler to which up to 50 characters preceding the currently selected text should be dynamically saved |
|
||||
|selectionSuffix |Tiddler to which up to 50 characters succeeding the currently selected text should be dynamically saved |
|
||||
@@ -91,9 +93,71 @@ An annotation tiddler is a tiddler describing an annotation to be overlaid over
|
||||
|
||||
Note that using the `annotate-tiddler` field to associate an annotation with the annotated tiddler is a lightweight convention employed by the examples; it isn't actually required by any of the JavaScript code. Thus authors can experiment with other techniques for recording the association.
|
||||
|
||||
!! Selection Tracker
|
||||
!! Selection Trackers
|
||||
|
||||
The selection tracker is incorporated within the `<$dynannotate>` widget, but it can be used independently for specialised applications.
|
||||
The following configuration tiddlers can be used to control whether the selection trackers are enabled when the following configuration tiddlers are set to ''yes'' (the default).
|
||||
|
||||
* $:/config/Dynannotate/SelectionTracker/Enable for the main selection tracker
|
||||
* $:/config/Dynannotate/LegacySelectionTracker/Enable for the legacy selection tracker
|
||||
|
||||
Both selection trackers are enabled by default.
|
||||
|
||||
!!! Main Selection Tracker
|
||||
|
||||
The selection tracker triggers an action string whenever the browser text selection changes. The actions are delayed until the selection has not changed for 500ms (this means that the selection tracker is only triggered when the user pauses after completing a selection, and is not continuously invoked as the user drags the selection).
|
||||
|
||||
The selection tracker works within DOM subtrees that have the following structure:
|
||||
|
||||
* The outer wrapper element has the attribute `data-selection-action-title` containing the title of the tiddler containing the action string to be invoked when the selection changes
|
||||
* Each child element of the outer element must have a unique `id` attribute to identify it
|
||||
|
||||
```
|
||||
<div data-selection-action-title="{tiddler title}">
|
||||
<div id="{title}">
|
||||
Content text
|
||||
</div>
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
The action string is invoked with the following variables:
|
||||
|
||||
|!Variable |!Description |
|
||||
|`selection` |A JSON object representing the selection (see below) |
|
||||
|`dom-*` |All DOM attributes of the outer wrapper node are made available as variables, with the prefix `dom-` |
|
||||
|`tv-selection-posx` |X position of the selection in pixels |
|
||||
|`tv-selection-posy` |Y position of the selection in pixels |
|
||||
|`tv-selection-width` |Width of the selection in pixels |
|
||||
|`tv-selection-height` |Height of the selection in pixels |
|
||||
|`tv-selection-coords` |A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the selection |
|
||||
|
||||
The JSON representation of the selection is as follows:
|
||||
|
||||
```
|
||||
{
|
||||
"chunks": [
|
||||
{
|
||||
"id": "id-of-first-chunk-of-selection",
|
||||
"text": "text-of-first-chunk-of-selection",
|
||||
"prefix": "optional-prefix-before-first-chunk-of-selection",
|
||||
"suffix": "optional-suffix-after-last-chunk-of-selection"
|
||||
}
|
||||
...
|
||||
],
|
||||
"variables": {
|
||||
<variables listed above>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
* Only the first chunk of the selection may have a "prefix" field which will contain any text at the start of the chunk preceding the selection
|
||||
* Only the last chunk of the selection may have a "suffix" field which will contain any text at the end of the chunk following the selection
|
||||
|
||||
!!! Legacy Selection Tracker
|
||||
|
||||
The selection tracker is incorporated within the `<$dynannotate>` widget via the ''selection'', ''selectionPrefix'' ''selectionSuffix'' and ''selectionPopup'' attributes. It can be used independently for specialised applications.
|
||||
|
||||
Each selection container is marked with the class `tc-dynannotate-selection-container`, and should contain the following attributes:
|
||||
|
||||
|
||||
@@ -183,7 +183,16 @@ DynannotateWidget.prototype.applyAnnotations = function() {
|
||||
// We'll dynamically build the click event handler so that we can reuse it
|
||||
var clickHandlerFn = function(title) {
|
||||
return function(event,domOverlay,modifierKey) {
|
||||
self.invokeActionString(self.getAttribute("actions"),self,event,{annotationTiddler: title, modifier: modifierKey});
|
||||
var bounds = domOverlay.getBoundingClientRect();
|
||||
self.invokeActionString(self.getAttribute("actions"),self,event,{
|
||||
annotationTiddler: title,
|
||||
modifier: modifierKey,
|
||||
"tv-selection-posx": (bounds.left).toString(),
|
||||
"tv-selection-posy": (bounds.top).toString(),
|
||||
"tv-selection-width": (bounds.width).toString(),
|
||||
"tv-selection-height": (bounds.height).toString(),
|
||||
"tv-selection-coords": "(" + bounds.left + "," + bounds.top + "," + bounds.width + "," + bounds.height + ")"
|
||||
});
|
||||
if(self.hasAttribute("popup")) {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: domOverlay,
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/dynannotate/legacy-selection-tracker.js
|
||||
type: application/javascript
|
||||
module-type: library
|
||||
|
||||
Legacy version of the dyannotate background daemon to track the selection
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var TextMap = require("$:/plugins/tiddlywiki/dynannotate/textmap.js").TextMap;
|
||||
|
||||
function LegacySelectionTracker(wiki,options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this.wiki = wiki;
|
||||
this.allowBlankSelectionPopup = options.allowBlankSelectionPopup;
|
||||
this.selectionPopupTitle = null;
|
||||
document.addEventListener("selectionchange",function(event) {
|
||||
var selection = document.getSelection();
|
||||
if(selection && (selection.type === "Range" || (self.allowBlankSelectionPopup && !self.selectionPopupTitle))) {
|
||||
// Look for the selection containers for each of the two ends of the selection
|
||||
var anchorContainer = self.findSelectionContainer(selection.anchorNode),
|
||||
focusContainer = self.findSelectionContainer(selection.focusNode);
|
||||
// If either end of the selection then we ignore it
|
||||
if(!!anchorContainer || !!focusContainer) {
|
||||
var selectionRange = selection.getRangeAt(0);
|
||||
// Check for the selection spilling outside the starting container
|
||||
if((anchorContainer !== focusContainer) || (selectionRange.startContainer.nodeType !== Node.TEXT_NODE && selectionRange.endContainer.nodeType !== Node.TEXT_NODE)) {
|
||||
if(self.selectionPopupTitle) {
|
||||
self.wiki.deleteTiddler(self.selectionPopupTitle);
|
||||
self.selectionPopupTitle = null;
|
||||
}
|
||||
} else {
|
||||
self.selectionSaveTitle = anchorContainer.getAttribute("data-annotation-selection-save");
|
||||
self.selectionPrefixSaveTitle = anchorContainer.getAttribute("data-annotation-selection-prefix-save");
|
||||
self.selectionSuffixSaveTitle = anchorContainer.getAttribute("data-annotation-selection-suffix-save");
|
||||
self.selectionPopupTitle = anchorContainer.getAttribute("data-annotation-selection-popup");
|
||||
// The selection is a range so we trigger the popup
|
||||
if(self.selectionPopupTitle) {
|
||||
var selectionRectangle = selectionRange.getBoundingClientRect(),
|
||||
trackingRectangle = anchorContainer.getBoundingClientRect();
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: null,
|
||||
domNodeRect: {
|
||||
left: selectionRectangle.left - trackingRectangle.left,
|
||||
top: selectionRectangle.top - trackingRectangle.top,
|
||||
width: selectionRectangle.width,
|
||||
height: selectionRectangle.height
|
||||
},
|
||||
force: true,
|
||||
floating: true,
|
||||
title: self.selectionPopupTitle,
|
||||
wiki: self.wiki
|
||||
});
|
||||
}
|
||||
// Write the selection text to the specified tiddler
|
||||
if(self.selectionSaveTitle) {
|
||||
// Note that selection.toString() normalizes whitespace but selection.getRangeAt(0).toString() does not
|
||||
var text = selectionRange.toString();
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSaveTitle, text: text}));
|
||||
// Build a textmap of the container so that we can find the prefix and suffix
|
||||
var textMap = new TextMap(anchorContainer);
|
||||
// Find the selection start in the text map and hence extract the prefix and suffix
|
||||
var context = textMap.extractContext(selectionRange.startContainer,selectionRange.startOffset,text);
|
||||
// Save the prefix and suffix
|
||||
if(context) {
|
||||
if(self.selectionPrefixSaveTitle) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionPrefixSaveTitle, text: context.prefix}));
|
||||
}
|
||||
if(self.selectionSuffixSaveTitle) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSuffixSaveTitle, text: context.suffix}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the selection is a caret we clear any active popup
|
||||
if(self.selectionPopupTitle) {
|
||||
self.wiki.deleteTiddler(self.selectionPopupTitle);
|
||||
self.selectionPopupTitle = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LegacySelectionTracker.prototype.findSelectionContainer = function findSelectionContainer(domNode) {
|
||||
if(domNode && domNode.nodeType === Node.ELEMENT_NODE && domNode.classList.contains("tc-dynannotation-selection-container")) {
|
||||
return domNode;
|
||||
}
|
||||
if(domNode && domNode.parentNode) {
|
||||
return findSelectionContainer(domNode.parentNode);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.LegacySelectionTracker = LegacySelectionTracker;
|
||||
|
||||
})();
|
||||
@@ -3,7 +3,7 @@ title: $:/plugins/tiddlywiki/dynannotate/selection-tracker.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Dyannotate background daemon to track the selection
|
||||
Background daemon to track the selection
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
@@ -12,105 +12,160 @@ Dyannotate background daemon to track the selection
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "dyannotate-startup";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["render"];
|
||||
exports.synchronous = true;
|
||||
|
||||
var TextMap = require("$:/plugins/tiddlywiki/dynannotate/textmap.js").TextMap;
|
||||
|
||||
exports.startup = function() {
|
||||
$tw.dynannotate = {
|
||||
selectionTracker: new SelectionTracker($tw.wiki,{
|
||||
allowBlankSelectionPopup: true
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
function SelectionTracker(wiki,options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this.wiki = wiki;
|
||||
this.allowBlankSelectionPopup = options.allowBlankSelectionPopup;
|
||||
this.selectionPopupTitle = null;
|
||||
var timerId = null;
|
||||
document.addEventListener("selectionchange",function(event) {
|
||||
var selection = document.getSelection();
|
||||
if(selection && (selection.type === "Range" || (self.allowBlankSelectionPopup && !self.selectionPopupTitle))) {
|
||||
// Look for the selection containers for each of the two ends of the selection
|
||||
var anchorContainer = self.findSelectionContainer(selection.anchorNode),
|
||||
focusContainer = self.findSelectionContainer(selection.focusNode);
|
||||
// If either end of the selection then we ignore it
|
||||
if(!!anchorContainer || !!focusContainer) {
|
||||
var selectionRange = selection.getRangeAt(0);
|
||||
// Check for the selection spilling outside the starting container
|
||||
if((anchorContainer !== focusContainer) || (selectionRange.startContainer.nodeType !== Node.TEXT_NODE && selectionRange.endContainer.nodeType !== Node.TEXT_NODE)) {
|
||||
if(self.selectionPopupTitle) {
|
||||
self.wiki.deleteTiddler(self.selectionPopupTitle);
|
||||
self.selectionPopupTitle = null;
|
||||
}
|
||||
} else {
|
||||
self.selectionSaveTitle = anchorContainer.getAttribute("data-annotation-selection-save");
|
||||
self.selectionPrefixSaveTitle = anchorContainer.getAttribute("data-annotation-selection-prefix-save");
|
||||
self.selectionSuffixSaveTitle = anchorContainer.getAttribute("data-annotation-selection-suffix-save");
|
||||
self.selectionPopupTitle = anchorContainer.getAttribute("data-annotation-selection-popup");
|
||||
// The selection is a range so we trigger the popup
|
||||
if(self.selectionPopupTitle) {
|
||||
var selectionRectangle = selectionRange.getBoundingClientRect(),
|
||||
trackingRectangle = anchorContainer.getBoundingClientRect();
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: null,
|
||||
domNodeRect: {
|
||||
left: selectionRectangle.left - trackingRectangle.left,
|
||||
top: selectionRectangle.top - trackingRectangle.top,
|
||||
width: selectionRectangle.width,
|
||||
height: selectionRectangle.height
|
||||
},
|
||||
force: true,
|
||||
floating: true,
|
||||
title: self.selectionPopupTitle,
|
||||
wiki: self.wiki
|
||||
});
|
||||
}
|
||||
// Write the selection text to the specified tiddler
|
||||
if(self.selectionSaveTitle) {
|
||||
// Note that selection.toString() normalizes whitespace but selection.getRangeAt(0).toString() does not
|
||||
var text = selectionRange.toString();
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSaveTitle, text: text}));
|
||||
// Build a textmap of the container so that we can find the prefix and suffix
|
||||
var textMap = new TextMap(anchorContainer);
|
||||
// Find the selection start in the text map and hence extract the prefix and suffix
|
||||
var context = textMap.extractContext(selectionRange.startContainer,selectionRange.startOffset,text);
|
||||
// Save the prefix and suffix
|
||||
if(context) {
|
||||
if(self.selectionPrefixSaveTitle) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionPrefixSaveTitle, text: context.prefix}));
|
||||
}
|
||||
if(self.selectionSuffixSaveTitle) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler({title: self.selectionSuffixSaveTitle, text: context.suffix}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the selection is a caret we clear any active popup
|
||||
if(self.selectionPopupTitle) {
|
||||
self.wiki.deleteTiddler(self.selectionPopupTitle);
|
||||
self.selectionPopupTitle = null;
|
||||
}
|
||||
if(timerId) {
|
||||
clearTimeout(timerId);
|
||||
}
|
||||
timerId = setTimeout(function() {
|
||||
timerId = null;
|
||||
self.handleSelectionChange();
|
||||
},500);
|
||||
});
|
||||
}
|
||||
|
||||
SelectionTracker.prototype.findSelectionContainer = function findSelectionContainer(domNode) {
|
||||
if(domNode && domNode.nodeType === Node.ELEMENT_NODE && domNode.classList.contains("tc-dynannotation-selection-container")) {
|
||||
return domNode;
|
||||
SelectionTracker.prototype.handleSelectionChange = function() {
|
||||
var selection = document.getSelection();
|
||||
if(selection && selection.type === "Range") {
|
||||
// Helper to get the tiddler title corresponding to a chunk container
|
||||
var getIdOfContainer = function(domNode) {
|
||||
return domNode.id;
|
||||
}
|
||||
// Get information about the selection anchor and focus
|
||||
var getSelectionInfo = function(targetDomNode,targetOffset) {
|
||||
// Find the chunk container node
|
||||
var domNode = targetDomNode;
|
||||
if(domNode.nodeType === Node.TEXT_NODE) {
|
||||
domNode = domNode.parentNode;
|
||||
}
|
||||
var container = domNode.closest(".dynannotate-chunk");
|
||||
if(!container) {
|
||||
return null;
|
||||
}
|
||||
// Find the index of the container within the child nodes of its parent
|
||||
var childNodeIndex = Array.prototype.indexOf.call(container.parentNode.childNodes,container);
|
||||
// Walk through the chunk collecting the text before and after the specified domNode and offset
|
||||
var beforeText = null, afterText = [];
|
||||
var splitTextResult = function() {
|
||||
beforeText = afterText;
|
||||
afterText = [];
|
||||
},
|
||||
processNode = function(domNode) {
|
||||
// Check for a text node
|
||||
if(domNode.nodeType === Node.TEXT_NODE) {
|
||||
// If this is the target node then perform the split
|
||||
if(domNode === targetDomNode) {
|
||||
afterText.push(domNode.textContent.substring(0,targetOffset));
|
||||
splitTextResult();
|
||||
afterText.push(domNode.textContent.substring(targetOffset));
|
||||
} else {
|
||||
afterText.push(domNode.textContent);
|
||||
}
|
||||
} else {
|
||||
// Process the child nodes
|
||||
$tw.utils.each(domNode.childNodes,function(childNode,childNodeIndex) {
|
||||
// Check whether we need to split on this child node
|
||||
if(domNode === targetDomNode && childNodeIndex === targetOffset) {
|
||||
splitTextResult();
|
||||
}
|
||||
processNode(childNode);
|
||||
});
|
||||
}
|
||||
};
|
||||
processNode(container);
|
||||
if(beforeText === null) {
|
||||
splitTextResult();
|
||||
}
|
||||
// Return results
|
||||
return {
|
||||
container: container,
|
||||
childNodeIndex: childNodeIndex,
|
||||
beforeText: beforeText.join(""),
|
||||
afterText: afterText.join("")
|
||||
}
|
||||
|
||||
}
|
||||
var anchor = getSelectionInfo(selection.anchorNode,selection.anchorOffset),
|
||||
focus = getSelectionInfo(selection.focusNode,selection.focusOffset);
|
||||
// Check that the containers share a parent
|
||||
if(anchor && focus && anchor.container.parentNode === focus.container.parentNode) {
|
||||
// Make sure that the anchor is before the focus
|
||||
if((anchor.childNodeIndex > focus.childNodeIndex) || (anchor.container === focus.container && anchor.beforeText.length > focus.beforeText.length)) {
|
||||
var temp = anchor;
|
||||
anchor = focus;
|
||||
focus = temp;
|
||||
}
|
||||
var chunks = [];
|
||||
// Check for the selection being all in one chunk
|
||||
if(anchor.container === focus.container) {
|
||||
chunks.push({
|
||||
id: getIdOfContainer(anchor.container),
|
||||
prefix: anchor.beforeText,
|
||||
text: anchor.afterText.substring(0,anchor.afterText.length - focus.afterText.length),
|
||||
suffix: focus.afterText
|
||||
});
|
||||
} else {
|
||||
// We span two or more chunks
|
||||
chunks.push({
|
||||
id: getIdOfContainer(anchor.container),
|
||||
prefix: anchor.beforeText,
|
||||
text: anchor.afterText
|
||||
});
|
||||
// Get the titles and text of the intervening tiddlers
|
||||
var domNode;
|
||||
if(anchor.container !== focus.container) {
|
||||
domNode = anchor.container.nextElementSibling;
|
||||
while(domNode && domNode !== focus.container) {
|
||||
chunks.push({
|
||||
id: getIdOfContainer(domNode),
|
||||
text: domNode.textContent
|
||||
});
|
||||
domNode = domNode.nextElementSibling;
|
||||
}
|
||||
}
|
||||
chunks.push({
|
||||
id: getIdOfContainer(focus.container),
|
||||
text: focus.beforeText,
|
||||
suffix: focus.afterText
|
||||
});
|
||||
}
|
||||
// Get the title of the tiddler containing the actions to be executed
|
||||
var actionsTiddler = anchor.container.parentNode.getAttribute("data-selection-actions-title");
|
||||
// Assemble the variables to be passed to the action
|
||||
var variables = {};
|
||||
// Get the bounds of the container and the selection
|
||||
var selectionRectangle = selection.getRangeAt(0).getBoundingClientRect(),
|
||||
offsetParentRectangle = anchor.container.offsetParent.getBoundingClientRect();
|
||||
variables["tv-selection-posx"] = (selectionRectangle.left - offsetParentRectangle.left).toString();
|
||||
variables["tv-selection-posy"] = (selectionRectangle.top - offsetParentRectangle.top).toString();
|
||||
variables["tv-selection-width"] = (selectionRectangle.width).toString();
|
||||
variables["tv-selection-height"] = (selectionRectangle.height).toString();
|
||||
variables["tv-selection-coords"] = "(" + variables["tv-selection-posx"] + "," + variables["tv-selection-posy"] + "," + variables["tv-selection-width"] + "," + variables["tv-selection-height"] + ")";
|
||||
// Collect the attributes from the container
|
||||
$tw.utils.each(anchor.container.parentNode.attributes,function(attribute) {
|
||||
variables["dom-" + attribute.name] = attribute.value.toString();
|
||||
});
|
||||
// Action the selection
|
||||
this.performSelectionActions(chunks,variables,actionsTiddler);
|
||||
}
|
||||
}
|
||||
if(domNode && domNode.parentNode) {
|
||||
return findSelectionContainer(domNode.parentNode);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
SelectionTracker.prototype.performSelectionActions = function(chunks,variables,actionsTiddler) {
|
||||
// Invoke the actions, passing the extract tiddler title as a variable
|
||||
if(actionsTiddler) {
|
||||
var actions = $tw.wiki.getTiddlerText(actionsTiddler)
|
||||
if(actions) {
|
||||
var selection = JSON.stringify({chunks: chunks,variables: variables});
|
||||
$tw.rootWidget.invokeActionString(actions,undefined,undefined,$tw.utils.extend({},variables,{selection: selection}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.SelectionTracker = SelectionTracker;
|
||||
|
||||
})();
|
||||
|
||||
40
plugins/tiddlywiki/dynannotate/modules/startup.js
Normal file
40
plugins/tiddlywiki/dynannotate/modules/startup.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/dynannotate/startup.js
|
||||
type: application/javascript
|
||||
module-type: startup
|
||||
|
||||
Startup the dyannotate background daemon to track the selection
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
// Export name and synchronous status
|
||||
exports.name = "dyannotate-startup";
|
||||
exports.platforms = ["browser"];
|
||||
exports.after = ["render"];
|
||||
exports.synchronous = true;
|
||||
|
||||
var CONFIG_SELECTION_TRACKER_TITLE = "$:/config/Dynannotate/SelectionTracker/Enable",
|
||||
CONFIG_LEGACY_SELECTION_TRACKER_TITLE = "$:/config/Dynannotate/LegacySelectionTracker/Enable";
|
||||
|
||||
var SelectionTracker = require("$:/plugins/tiddlywiki/dynannotate/selection-tracker.js").SelectionTracker,
|
||||
LegacySelectionTracker = require("$:/plugins/tiddlywiki/dynannotate/legacy-selection-tracker.js").LegacySelectionTracker;
|
||||
|
||||
exports.startup = function() {
|
||||
$tw.dynannotate = {};
|
||||
if($tw.wiki.getTiddlerText(CONFIG_SELECTION_TRACKER_TITLE,"yes") === "yes") {
|
||||
$tw.dynannotate.selectionTracker = new SelectionTracker($tw.wiki);
|
||||
}
|
||||
if($tw.wiki.getTiddlerText(CONFIG_LEGACY_SELECTION_TRACKER_TITLE,"yes") === "yes") {
|
||||
$tw.dynannotate.legacySelectionTracker = new LegacySelectionTracker($tw.wiki,{
|
||||
allowBlankSelectionPopup: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user