1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-16 10:29:54 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bram Chen 2015-01-07 00:04:51 +08:00
commit d03f4585f6
24 changed files with 463 additions and 118 deletions

View File

@ -27,18 +27,13 @@ exports.init = function(parser) {
this.matchRegExp = /\[\[(.*?)(?:\|(.*?))?\]\]/mg;
};
var isLinkExternal = function(to) {
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data|skype):[^\s<>{}\[\]`|'"\\^~]+(?:\/|\b)/i;
return externalRegExp.test(to);
};
exports.parse = function() {
// Move past the match
this.parser.pos = this.matchRegExp.lastIndex;
// Process the link
var text = this.match[1],
link = this.match[2] || text;
if(isLinkExternal(link)) {
if($tw.utils.isLinkExternal(link)) {
return [{
type: "element",
tag: "a",

View File

@ -439,6 +439,12 @@ exports.escapeRegExp = function(s) {
return s.replace(/[\-\/\\\^\$\*\+\?\.\(\)\|\[\]\{\}]/g, '\\$&');
};
// Checks whether a link target is external, i.e. not a tiddler title
exports.isLinkExternal = function(to) {
var externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data|skype):[^\s<>{}\[\]`|'"\\^~]+(?:\/|\b)/i;
return externalRegExp.test(to);
};
exports.nextTick = function(fn) {
/*global window: false */
if(typeof process === "undefined") {

View File

@ -106,8 +106,8 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
this.domNodes.push(domNode);
};
LinkWidget.prototype.handleClickEvent = function (event) {
// Send the click on it's way as a navigate event
LinkWidget.prototype.handleClickEvent = function(event) {
// Send the click on its way as a navigate event
var bounds = this.domNodes[0].getBoundingClientRect();
this.dispatchEvent({
type: "tm-navigate",

View File

@ -60,16 +60,18 @@ ListWidget.prototype.execute = function() {
this.variableName = this.getAttribute("variable","currentTiddler");
this.storyViewName = this.getAttribute("storyview");
this.historyTitle = this.getAttribute("history");
this.iterator = this.getAttribute("iterator","iterator");
// Compose the list elements
this.list = this.getTiddlerList();
var members = [],
self = this;
self = this,
count = self.list.length;
// Check for an empty list
if(this.list.length === 0) {
if(0 === count) {
members = this.getEmptyMessage();
} else {
$tw.utils.each(this.list,function(title,index) {
members.push(self.makeItemTemplate(title));
members.push(self.makeItemTemplate(title,index,count,self.iterator));
});
}
// Construct the child widgets
@ -96,7 +98,7 @@ ListWidget.prototype.getEmptyMessage = function() {
/*
Compose the template for a list item
*/
ListWidget.prototype.makeItemTemplate = function(title) {
ListWidget.prototype.makeItemTemplate = function(title,index,count,iterator) {
// Check if the tiddler is a draft
var tiddler = this.wiki.getTiddler(title),
isDraft = tiddler && tiddler.hasField("draft.of"),
@ -119,7 +121,15 @@ ListWidget.prototype.makeItemTemplate = function(title) {
}
}
// Return the list item
return {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree};
return {
type: "listitem",
itemTitle: title,
variableName: this.variableName,
children: templateTree,
index: index,
count: count,
iterator: iterator
};
};
/*
@ -291,8 +301,12 @@ ListItemWidget.prototype.render = function(parent,nextSibling) {
Compute the internal state of the widget
*/
ListItemWidget.prototype.execute = function() {
var item = this.parseTreeNode;
// Set the current list item title
this.setVariable(this.parseTreeNode.variableName,this.parseTreeNode.itemTitle);
this.setVariable(item.variableName,item.itemTitle);
this.setVariable(item.iterator,(item.index + 1).toString());
this.setVariable(item.iterator + "-even",item.index % 2 == 1 ? "true" : "false");
this.setVariable(item.iterator + "-last",item.index + 1 == item.count ? "true" : "false");
// Construct the child widgets
this.makeChildWidgets();
};

View File

@ -167,7 +167,7 @@ tags: $:/tags/Macro
<div class="tc-tabbed-table-of-contents-content">
<$reveal state="""$selectedTiddler$""" type="nomatch" text="">
<$transclude mode="block" tiddler="$template$">
<h1><$transclude field="caption"><$view field="title"/></$transclude></h1>
<h1><<toc-caption>></h1>
<$transclude mode="block">$missingText$</$transclude>
</$transclude>
</$reveal>

View File

@ -0,0 +1,14 @@
caption: Développable
created: 20150104182842728
modified: 20150104183013132
tags: table-of-contents-example
title: TableOfContentsMacro Expandable Example
type: text/vnd.tiddlywiki
!! Table des matières développable
<$macrocall $name='wikitext-example-without-html'
src='<div class="tc-table-of-contents">
<<toc-expandable "Contents">>
</div>
'/>

View File

@ -0,0 +1,14 @@
caption: Développable sélectivement
created: 20150104182559211
modified: 20150104183032161
tags: table-of-contents-example
title: TableOfContentsMacro Selective Expandable Example
type: text/vnd.tiddlywiki
!! Table des matières développable sélectivement
<$macrocall $name='wikitext-example-without-html'
src='<div class="tc-table-of-contents">
<<toc-selective-expandable "Contents">>
</div>
'/>

View File

@ -0,0 +1,14 @@
caption: Simple
created: 20150104182258371
modified: 20150104182323524
tags: table-of-contents-example
title: TableOfContentsMacro Simple Example
type: text/vnd.tiddlywiki
!! Table des matières simple
<$macrocall $name='wikitext-example-without-html'
src='<div class="tc-table-of-contents">
<<toc "Contents">>
</div>
'/>

View File

@ -0,0 +1,14 @@
caption: Développable triée
created: 20150104182748334
modified: 20150104182956846
tags: table-of-contents-example
title: TableOfContentsMacro Sorted Expandable Example
type: text/vnd.tiddlywiki
!! Table des matières développable triée
<$macrocall $name='wikitext-example-without-html'
src='<div class="tc-table-of-contents">
<<toc-expandable "Contents" "sort[title]">>
</div>
'/>

View File

@ -0,0 +1,45 @@
caption: Tabbed
created: 20150104183128274
modified: 20150104222928663
tags: table-of-contents-example
title: TableOfContentsMacro Tabbed Example
type: text/vnd.tiddlywiki
!! Table des matières tabulée
La variante tabulée de la macro table des matières affiche une table des matières développable sélectivement à côté d'un panneau qui affiche le tiddler en cours de sélection.
!!! Paramètres
|!Position |!Nom |!Description |!Défaut |
|1^^re^^ |tag |Tag à utiliser pour construire la table des matières | |
|2^^e^^ |sort |Sous-filtre de tri optionnel (par exemple `sort[title]`) | |
|3^^e^^ |selectedTiddler |Titre du tiddler contenant le titre du tiddler en cours d'affichage |"$:/temp/toc/selectedTiddler" |
|4^^e^^ |unselectedText |Texte à afficher lorsqu'aucun tiddler n'est sélectionné | |
|5^^e^^ |missingText |Texte à afficher quand le tiddler sélectionné est manquant | |
|6^^e^^ |template |Titre optionnel d'un tiddler à utiliser comme template pour le rendu du tiddler sélectionné | |
!!! Navigation interne
Cet exemple montre comment construire une table des matières tabulée avec navigation interne, de sorte que cliquer sur les liens du tiddler en cours d'affichage remplacera le tiddler en question.
```
<<toc-tabbed-internal-nav tag:"TableOfContents" selectedTiddler:"$:/temp/toc/selectedTiddler" unselectedText:"Choisissez un sujet dans la table des matières. Cliquez sur la flèche pour développer un sujet.">>
```
!!! Navigation externe
Cet exemple montre comment construire une table des matières tabulée avec navigation externe, de sorte que cliquer sur les liens du tiddler en cours d'affichage ouvrira les tiddlers référencés dans le déroulé principal, selon la manière habituelle.
```
<<toc-tabbed-external-nav tag:"TableOfContents" selectedTiddler:"$:/temp/toc/selectedTiddler" unselectedText:"Choisissez un sujet dans la table des matières. Cliquez sur la flèche pour développer un sujet.">>
```
!! Exemple
Type<<:>> <$select tiddler="TabbedExampleType">
<option value="toc-tabbed-internal-nav">Ouvre les liens du tiddler affiché dans le même espace (toc-tabbed-internal-nav)</option>
<option value="toc-tabbed-external-nav">Ouvre les liens du tiddler affiché hors de son espace (toc-tabbed-external-nav)</option>
</$select>
<$macrocall $name={{TabbedExampleType}} tag="TableOfContents" selectedTiddler="$:/temp/toc/selectedTiddler" unselectedText="<p>Choisissez un sujet dans la table des matières. Cliquez sur la flèche pour développer un sujet.</p>" missingText="<p>Tiddler manquant.</p>"/>

View File

@ -0,0 +1,51 @@
caption: toc
created: 20140919155729620
modified: 20150105102807522
tags: Macros
title: TableOfContentsMacro
type: text/vnd.tiddlywiki
La macro //~TableOfContents// (Table des Matières) produit une arborescence hiérarchique de tiddlers, en se basant sur leurs tags.
Les entrées de premier niveau de la table des matières sont définies par un tag racine. Les sous-entrées de chacune de ces entrées sont taguées avec le titre de l'entrée. Les entrées peuvent être triées à l'aide du champ `list` du tiddler de tag correspondant, comme décrit dans [[Tagging]].
Le libellé utilisé pour chaque entrée est tiré du champ ''caption'' s'il est présent<<;>> dans le cas contraire, c'est le titre (champ ''title'') qui est utilisé.
Les entrées sont affichées sous forme de liens vers le tiddler correspondant, à moins que le tiddler contienne un champ ''toc-link'' avec la valeur ''no''. Dans les exemples ci-dessous, l'entrée SecondThree est configurée ainsi, de manière à ne pas apparaître comme un lien.
Il existe plusieurs variantes de cette macro<<:>>
* `<<toc>>` produit une arborescence hiérarchique de liens simple
* `<<toc-expandable>>` produit une arborescence de liens développable
* `<<toc-selective-expandable>>` produit une arborescence de liens développable où les boutons développer / contracter ne sont affichés que pour les entrées qui possèdent des nœuds fils
Les macros génèrent des listes HTML ordonnées. Les éléments `<ol>` bénéficient de la classe `tc-toc`, ceux de la variante //expandable// bénéficiant également de la classe `tc-toc-expandable` tandis que ceux de la variante //selective expandable// bénéficient de la classe `tc-toc-selective-expandable`.
! Paramètres
|!Position |!Nom |!Description |!Défaut |
|1^^re^^ |tag |Le tag racine qui identifie le premier niveau de la hiérachie | |
|2^^e^^ |sort |Sous-fitre optionnel de tri (par exemple `sort[title]`) | |
Les paramètres ''tag'' et ''sort'' sont combinés pour construire une expression de filtre de la forme<<:>>
```
[tag[$tag$]$sort$]
```
! Exemples
Dans les exemples suivants, les entrées de premier niveau de la table des matières sont définies par leur tag racine ''Contents''. Les sous-entrées sous chacune de ces entrées sont taguées avec le titre de leur parent, ici ''First'', ''Second'', ''Third'', et ''Fourth''. Au niveau du dessous, seul ''~SecondThree'' comporte des sous-entrées.
Voici la structure des tags, affichée à l'aide de pastilles de tag cliquables<<:>>
{{Contents||$:/core/ui/TagTemplate}}
*{{First||$:/core/ui/TagTemplate}}
*{{Second||$:/core/ui/TagTemplate}}
**{{SecondThree||$:/core/ui/TagTemplate}}
*{{Third||$:/core/ui/TagTemplate}}
*{{Fourtth||$:/core/ui/TagTemplate}}
Pour des instructions sur la manière d'ajouter une table des matières dans la barre latérale, voyez<<:>> [[Comment ajouter un nouvel onglet dans la barre latérale|How to add a new tab to the sidebar]].
<<tabs "[tag[table-of-contents-example]]" "TableOfContentsMacro Simple Example">>

View File

@ -4,7 +4,7 @@ tags: documenting
Each of the main words of a tiddler title begins with a capital letter, but minor words such as "and", "or", "the", "to" and "with" do not. Avoid starting a tiddler with the word "the".
[[Reference Tiddlers]] have ~CamelCase nouns as their titles, e.g. ''~RevealWidget'', ''~CamelCase'', ''CSS''. The title is plural if it denotes a category, e.g. ''~KeyboardShorcuts'', ''~TiddlerFields''. Such categories are used to tag more specific tiddlers within the category.
[[Reference Tiddlers]] have ~CamelCase nouns as their titles, e.g. ''~RevealWidget'', ''~CamelCase'', ''CSS''. The title is plural if it denotes a category, e.g. ''~KeyboardShortcuts'', ''~TiddlerFields''. Such categories are used to tag more specific tiddlers within the category.
Other tags usually consist of a single lowercase word. Avoid spaces in tags.

View File

@ -1,16 +1,14 @@
created: 20141122093837330
modified: 20141122093837330
tags: Resources
title: "Taskgraph Plugin" by Felix Küppers
title: TiddlyMap Plugin by Felix Küppers
type: text/vnd.tiddlywiki
url: http://wkpr.de/hosting/tmp/tw5/taskgraph/
url: http://bit.ly/tiddlymap
An interactive network visualisation plugin based on [[Vis.js|http://visjs.org]].
{{!!url}}
An interactive network visualisation plugin based on [[Vis.js|http://visjs.org]]. A demo can be found here: {{!!url}}.
<<<
TW-Taskgraph is a TiddlyWiki plugin that allows you to link your wiki-topics (tiddlers) in order to create clickable graphs. By creating relations between your topics you can easily do the following:
~TiddlyMap is a TiddlyWiki plugin that allows you to link your wiki-topics (tiddlers) in order to create clickable graphs. By creating relations between your topics you can easily do the following:
* Create mindmaps and quickly manifest your ideas in tiddlers (wiki entries).
* Create task-dependency graphs to organize and describe your tasks.

View File

@ -0,0 +1,15 @@
created: 20150105133800000
modified: 20150105134300000
title: RailroadDiagrams
Railroad diagrams, sometimes called syntax diagrams, are a visual way of explaining the syntax rules of a computer language. Reading one is like reading a public transport map.
Each diagram starts on the left and ends on the right. Simply follow any line from the startpoint to the endpoint. All the alternative lines are equally valid. A line will sometimes jump over an item that is optional, or loop back to indicate that an item can be repeated.
<$railroad text="""
start [:optional] {repeated +","} end
"""/>
Characters in round boxes are literal, i.e. they denote themselves. A name in a rectangular box denotes a further railroad diagram.
The railroad diagrams on this site are generated using the [[Railroad Plugin]].

View File

@ -4,21 +4,21 @@ tags: Learning
title: Introduction to Filters
type: text/vnd.tiddlywiki
A step by step introduction to how [[Filters]] are used.
This is a step-by-step introduction to how [[Filters]] are used. [[Filter Syntax]] presents a more technical summary of this information.
! Using Filters
Filters are a special language within WikiText for expressing lists of tiddlers.
Filters are a special notation for expressing lists of tiddlers within WikiText.
Filters are used in the ListMacro, TabsMacro, ListWidget, CountWidget, and many other areas of TiddlyWiki.
For example, this is how the ListMacro would be used to display the first example below:
For example, this is how the ListMacro would be used to display the first example below:
```
<<list-links "HelloThere Introduction [[Title with Spaces]]">>
```
The easiest way to experiment with tiddler filters is by typing them into the "Filter" tab of the [[advanced search panel|$:/AdvancedSearch]].
The easiest way to experiment with tiddler filters is by typing them into the ''Filter'' tab of the [[advanced search panel|$:/AdvancedSearch]].
! Simple Filters
@ -32,13 +32,13 @@ The titles must be separated by one or more spaces and/or linebreaks.
! Filter Operators
Filter operators are used to select tiddlers based on some criteria. For example, this filter consists of a single operation that selects all tiddlers tagged "introduction":
Filter operators are used to select tiddlers in particular ways. For example, this filter consists of a single step that selects all tiddlers tagged ''introduction'':
```
[tag[introduction]]
```
The word "tag" is the ''operator'' and "introduction" is the ''operand''.
The word `tag` is the ''operator'' and `introduction` is the ''parameter'' (sometimes called the ''operand'').
See [[Filters]] for a complete list of the available operators.
@ -46,9 +46,9 @@ See [[Filters]] for a complete list of the available operators.
The operator defaults to `title` if omitted, so `[[HelloThere]]` is equivalent to `[title[HelloThere]]`. If there are no spaces in the title, then the double square brackets can also be omitted: `HelloThere`.
! Negating Filter Operators
! Negating Filter Steps
Filter operations can be negated by preceding the operator with an exclamation mark (!). This example selects all tiddlers that are not tagged "introduction":
A filter step can be negated by preceding the operator with an exclamation mark (`!`). This example selects all tiddlers that are not tagged ''introduction'':
```
[!tag[introduction]]
@ -56,7 +56,7 @@ Filter operations can be negated by preceding the operator with an exclamation m
! Operator Suffixes
Some filter operators can take an optional suffix that provides further information for the operation. For example, the "field" operator takes a suffix indicating the field to be compared. The following filter returns all tiddlers that have "JeremyRuston" in the "modifier" field:
Some filter operators can take an optional suffix that provides further information. For example, the `field` operator takes a suffix indicating the field to be compared. The following filter returns all tiddlers that have ''JeremyRuston'' in the `modifier` field:
```
[field:modifier[JeremyRuston]]
@ -64,70 +64,72 @@ Some filter operators can take an optional suffix that provides further informat
! Field Operator Shortcut
If an unknown operator is used then it is instead interpreted as the suffix of the "field" operator. Thus, these two filters both return all the tiddlers that contain the string "create" in their "caption" field:
If an operator is not recognised, then it is instead interpreted as the suffix of the `field` operator. Thus, these two filters both return all the tiddlers that contain the string ''create'' in their `caption` field:
```
[caption[create]]
[field:caption[create]]
```
! Indirect Operands
! Indirect Parameters
If a filter operator is written with curly brackets around the operand then it is taken to be a TextReference to the actual value. For example, this filter selects all tiddlers containing the string contained in the tiddler titled "$:/temp/search"
If a filter step has curly brackets around its parameter, then it is taken to be a TextReference to the actual value. For example, this filter selects all tiddlers containing the string contained in the ''$:/temp/search'' tiddler:
```
[search{$:/temp/search}]
```
! Variable Operands
! Variable Parameters
If a filter operator is written with angle brackets around the operand then it is taken to be the name of a variable containing the actual value. For example, this filter selects all tiddlers containing the title of the current tiddler:
If a filter step has angle brackets around its parameter, then it is taken to be the name of a variable containing the actual value. For example, this filter selects all tiddlers containing the title of the current tiddler:
```
[search<currentTiddler>]
```
(Note that the `currentTiddler` variable is used to track the current tiddler).
(The built-in `currentTiddler` variable keeps track of which tiddler is the current one.)
! ORing Multiple Filter Operators
! ORing Multiple Filter Steps
You can use multiple filter operations at once. This example selects all tiddlers that are either tagged "introduction" or "demo":
You can use multiple filter steps at once. If you write the steps separately, the overall result is the set of tiddlers that match //any// of the steps. Each step is processed separately, adding its tiddlers to the overall result.
This example selects all tiddlers that are either tagged ''introduction'' or ''demo'':
```
[tag[introduction]] [tag[demo]]
```
Each separate operator is processed in turn, accumulating the tiddlers that they select.
Here's an example that returns tiddlers tagged ''alpha'' or ''beta'' that are also tagged ''task'' and not tagged ''done'':
Here's an example that returns tiddlers tagged ''alpha'' or ''beta'' that are also tagged ''task'' but not tagged ''done'':
```
[tag[alpha]] [tag[beta]] +[tag[task]!tag[done]]
```
! ANDing Multiple Filter Operators
! ANDing Multiple Filter Steps
A sequence of operators can be logically ANDed together by bashing them together and merging the outer square brackets. This is called a "run" of operations. For example, here we select tiddlers that are tagged "introduction" and also tagged "demo":
A sequence of steps can also be combined by bashing them together and merging the outer square brackets. This is called a "run". The result is the set of tiddlers that match //all// of the steps in the run.
For example, here we select tiddlers that are tagged ''introduction'' and also tagged ''demo'':
```
[tag[introduction]tag[demo]]
```
Here's another example that selects all tiddlers tagged "introduction" that are not tagged "demo":
Here's another example that selects all tiddlers tagged ''introduction'' that are //not// tagged ''demo'':
```
[tag[introduction]!tag[demo]]
```
! Negating Runs of Filter Operators
! Negating Runs
Ordinarily, each run of filter operations adds to the accumulated results. Prefixing a run with `-` causes the list of tiddlers to instead be removed from the results. For example, this example returns all the tiddlers tagged "introduction" apart from `HelloThere` and `Title with Spaces`:
Ordinarily, each run //adds// to the accumulated results. Prefixing a run with `-` instead causes the list of tiddlers selected to be //removed// from the results. For example, this example returns all the tiddlers tagged ''introduction'', apart from `HelloThere` and `Title with Spaces`:
```
[tag[introduction]] -HelloThere -[[Title with Spaces]]
```
This example returns all tiddlers tagged "introduction" that are not also tagged "demo":
This example returns all tiddlers tagged ''introduction'' that are not also tagged ''demo'':
```
[tag[introduction]] -[tag[demo]]
@ -135,9 +137,9 @@ This example returns all tiddlers tagged "introduction" that are not also tagged
! Working with Filter Results
Usually, each run of filter operations takes as its source the entire store of available tiddlers. Prefixing a run with `+` causes the accumulated results to be used as the source instead.
Usually, each run takes the entire store of available tiddlers as its source. Prefixing a run with `+` causes the results so far accumulated to be used as the source instead.
For example, this filter selects tiddlers tagged "introduction" or "demo" and then sorts the resulting list by the "title" field:
For example, this filter selects tiddlers tagged ''introduction'' or ''demo'', and then sorts the resulting list by the `title` field:
```
[tag[introduction]] [tag[demo]] +[sort[title]]

View File

@ -0,0 +1,7 @@
title: Railroad Plugin
modified: 20150105134500000
tags: Plugins
{{$:/plugins/tiddlywiki/railroad/readme}}
{{$:/plugins/tiddlywiki/railroad/syntax}}

View File

@ -89,13 +89,13 @@ Component.prototype.debug = function(output,indent) {
Component.prototype.debugArray = function(array,output,indent) {
for(var i=0; i<array.length; i++) {
var item = array[i];
// Choice content is a special case: an array of arrays
// Choice content is a special case: we number the branches
if(item.isChoiceBranch) {
output.push(indent);
output.push("(");
output.push(i);
output.push(")\n");
item.debug(output," " +indent);
item.debug(output," "+indent);
} else {
item.debug(output,indent);
}
@ -205,6 +205,27 @@ Repeated.prototype.toSvg = function() {
return railroad.OneOrMore(this.child.toSvg(),separatorSvg);
}
var Link = function(content,options) {
this.initialiseWithChild("Link",content);
this.options = options;
};
Link.prototype = new Component();
Link.prototype.toSvg = function() {
return railroad.Link(this.child.toSvg(),this.options);
}
var Transclusion = function(content) {
this.initialiseWithChild("Transclusion",content);
};
Transclusion.prototype = new Component();
Transclusion.prototype.toSvg = function() {
return this.child.toSvg();
}
/////////////////////////// Components with an array of children
var Root = function(content) {
@ -252,13 +273,15 @@ exports.components = {
Choice: Choice,
Comment: Comment,
Dummy: Dummy,
Link: Link,
Nonterminal: Nonterminal,
Optional: Optional,
OptionalRepeated: OptionalRepeated,
Repeated: Repeated,
Root: Root,
Sequence: Sequence,
Terminal: Terminal
Terminal: Terminal,
Transclusion: Transclusion
};
})();

View File

@ -5,5 +5,5 @@ title: $:/plugins/tiddlywiki/railroad/example-source
type: text/plain
["+"]
({digit} | "#" <'escape sequence'>)
({ [[digit|GettingStarted]] } | "#" <'escape sequence'>)
[{("@" name-char | :"--" )}]

View File

@ -8,7 +8,7 @@ title: $:/plugins/tiddlywiki/railroad/example
```
<$railroad text="""
["+"]
({digit} | "#" <'escape sequence'>)
({ [[digit|GettingStarted]] } | "#" <'escape sequence'>)
[{("@" name-char | :"--" )}]
"""/>
```

View File

@ -2,7 +2,7 @@ created: 20150102163222184
modified: 20150102172016663
title: $:/plugins/tiddlywiki/railroad/readme
This plugin provides a `<$railroad>` widget for generating railroad syntax diagrams as SVG images. It is based on [[a library by Tab Atkins|https://github.com/tabatkins/railroad-diagrams]].
This plugin provides a `<$railroad>` widget for generating railroad syntax diagrams as SVG images. It is based on [[a library by Tab Atkins|https://github.com/tabatkins/railroad-diagrams]], and has been extended to allow components of a diagram to function as links.
The content of the `<$railroad>` widget is ignored.
@ -10,8 +10,10 @@ The content of the `<$railroad>` widget is ignored.
|text |Text in a special syntax that defines the diagram's layout |
|mode |If set to `debug`, the diagram will display its internal tree structure. The default mode is `svg` |
The `text` can be transcluded from another tiddler:
The entire `text` can be transcluded from another tiddler:
```
<$railroad tiddler={{diagram}}>
```
```
Alternatively, the diagram syntax allows specific parts of the `text` to be transcluded from other tiddlers.

View File

@ -2,10 +2,12 @@ created: 20150103184022184
modified: 20150103184022184
title: $:/plugins/tiddlywiki/railroad/syntax
The railroad widget constructs a diagram from the components defined below.
The railroad widget uses a special ''diagram syntax'' to construct the components defined below.
`x` and `y` here stand for any component.
Names (as opposed to quoted strings) are available when a value starts with a letter and contains only letters, digits, underscores, dots and hyphens.
---
; sequence
@ -52,7 +54,6 @@ The railroad widget constructs a diagram from the components defined below.
; nonterminal
: <$railroad text=""" (name | "<" string ">") """/>
* A nonterminal component, i.e. the name of another diagram
* The simple `name` option is available when the text starts with a letter and contains only letters, digits, underscores, dots and hyphens
---
@ -64,4 +65,16 @@ The railroad widget constructs a diagram from the components defined below.
; dummy
: <$railroad text=""" "-" """/>
* The absence of a component
* The absence of a component
---
; link
: <$railroad text=""" "[[" x "|" (name|string) "]]" """/>
* A link to the tiddler title or URI given by the string or name
---
; transclusion
: <$railroad text=""" "{{" (name|string) "}}" """/>
* Treats the content of another tiddler as diagram syntax and transcludes it into the current diagram

View File

@ -467,15 +467,16 @@ var temp = (function(options) {
}
/* TiddlyWiki: added linking ability */
function Link(target, item) {
if(!(this instanceof Link)) return new Link(target, item);
FakeSVG.call(this, 'a', {'xlink:href': target});
function Link(item,options) {
if(!(this instanceof Link)) return new Link(item,options);
FakeSVG.call(this,'a',options);
this.item = item;
this.width = item.width;
this.up = item.up;
this.down = item.down;
}
subclassOf(Link, FakeSVG);
Link.prototype.needsSpace = true;
Link.prototype.format = function(x, y, width) {
this.item.format(x,y,width).addTo(this);
return this;

View File

@ -21,13 +21,11 @@ x y z sequence
<"x"> nonterminal
/"blah"/ comment
- dummy
[[x|"tiddler"]] link
{{"tiddler"}} transclusion
"x" can also be written 'x' or """x"""
Future extensions:
[[x|tiddler]] link
{{tiddler}} transclusion
\*/
(function(){
@ -37,12 +35,14 @@ Future extensions:
var components = require("$:/plugins/tiddlywiki/railroad/components.js").components;
var Parser = function(source) {
var Parser = function(widget,source) {
this.widget = widget;
this.source = source;
this.tokens = this.tokenise(source);
this.tokenPos = 0;
this.advance();
this.root = new components.Root(this.parseContent());
this.content = this.parseContent();
this.root = new components.Root(this.content);
this.checkFinished();
};
@ -66,8 +66,8 @@ Parser.prototype.parseComponent = function() {
if(this.token) {
if(this.at("string")) {
component = this.parseTerminal();
} else if(this.at("identifier")) {
component = this.parseIdentifier();
} else if(this.at("name")) {
component = this.parseName();
} else {
switch(this.token.value) {
case "[":
@ -85,6 +85,12 @@ Parser.prototype.parseComponent = function() {
case "/":
component = this.parseComment();
break;
case "[[":
component = this.parseLink();
break;
case "{{":
component = this.parseTransclusion();
break;
case "<-":
component = this.parseSequence();
break;
@ -112,25 +118,21 @@ Parser.prototype.parseChoice = function() {
// Parse the next branch
content.push(this.parseContent());
} while(this.eat("|"));
// Create a component
var component = new components.Choice(content,colon === -1 ? 0 : colon);
// Consume the closing bracket
this.close(")");
return component;
// Create a component
return new components.Choice(content,colon === -1 ? 0 : colon);
};
Parser.prototype.parseComment = function() {
// Consume the /
this.advance();
// The comment's content should be in a string literal
this.expectStringLiteral("/");
// Create a component
var component = new components.Comment(this.token.value);
// Consume the string literal
this.advance();
var content = this.expectString("after /");
// Consume the closing /
this.close("/");
return component;
// Create a component
return new components.Comment(content);
};
Parser.prototype.parseDummy = function() {
@ -140,27 +142,43 @@ Parser.prototype.parseDummy = function() {
return new components.Dummy();
};
Parser.prototype.parseIdentifier = function() {
Parser.prototype.parseLink = function() {
// Consume the [[
this.advance();
// Parse the content
var content = this.parseContent();
// Consume the |
this.expect("|");
// Consume the target
var target = this.expectNameOrString("as link target");
// Prepare some attributes for the SVG "a" element to carry
var options = {"data-tw-target": target};
if($tw.utils.isLinkExternal(target)) {
options["data-tw-external"] = true;
}
// Consume the closing ]]
this.close("]]");
// Create a component
return new components.Link(content,options);
};
Parser.prototype.parseName = function() {
// Create a component
var component = new components.Nonterminal(this.token.value);
// Consume the identifier
// Consume the name
this.advance();
return component;
};
Parser.prototype.parseNonterminal = function() {
// Consume the <
this.advance();
// The nonterminal's name should be in a string literal
this.expectStringLiteral("<");
// Create a component
var component = new components.Nonterminal(this.token.value);
// Consume the string literal
this.advance();
var content = this.expectString("after <");
// Consume the closing bracket
this.close(">");
return component;
// Create a component
return new components.Nonterminal(content);
};
Parser.prototype.parseOptional = function() {
@ -177,14 +195,13 @@ Parser.prototype.parseOptional = function() {
if(repeated && this.eat("+")) {
separator = this.parseContent();
}
// Create a component
var component = repeated ? new components.OptionalRepeated(content,separator,normal) : new components.Optional(content,normal);
// Consume the closing brackets
if(repeated) {
this.close("}");
}
this.close("]");
return component;
// Create a component
return repeated ? new components.OptionalRepeated(content,separator,normal) : new components.Optional(content,normal);
};
Parser.prototype.parseRepeated = function() {
@ -197,23 +214,21 @@ Parser.prototype.parseRepeated = function() {
if(this.eat("+")) {
separator = this.parseContent();
}
// Create a component
var component = new components.Repeated(content,separator);
// Consume the closing bracket
this.close("}");
return component;
// Create a component
return new components.Repeated(content,separator);
};
Parser.prototype.parseSequence = function() {
// Consume the ~
// Consume the <-
this.advance();
// Parse the content
var content = this.parseContent();
// Create a component
var component = new components.Sequence(content);
// Consume the closing ~
// Consume the closing ->
this.close("->");
return component;
// Create a component
return new components.Sequence(content);
};
Parser.prototype.parseTerminal = function() {
@ -223,6 +238,21 @@ Parser.prototype.parseTerminal = function() {
return component;
};
Parser.prototype.parseTransclusion = function() {
// Consume the {{
this.advance();
// Consume the text reference
var textRef = this.expectNameOrString("as transclusion source");
// Consume the closing }}
this.close("}}");
// Retrieve the content of the text reference
var source = this.widget.wiki.getTextReference(textRef,"",this.widget.getVariable("currentTiddler"));
// Parse the content
var content = new Parser(this.widget,source).content;
// Create a component
return new components.Transclusion(content);
};
/////////////////////////// Token manipulation
Parser.prototype.advance = function() {
@ -244,10 +274,10 @@ Parser.prototype.eat = function(token) {
return at;
};
Parser.prototype.expectStringLiteral = function(preamble) {
if(!this.at("string")) {
throw "String expected after " + preamble;
}
Parser.prototype.tokenValue = function() {
var output = this.token.value;
this.advance();
return output;
};
Parser.prototype.close = function(token) {
@ -262,6 +292,27 @@ Parser.prototype.checkFinished = function() {
}
};
Parser.prototype.expect = function(token) {
if(!this.eat(token)) {
throw token + " expected";
}
};
Parser.prototype.expectString = function(context,token) {
if(!this.at("string")) {
token = token || "String";
throw token + " expected " + context;
}
return this.tokenValue();
};
Parser.prototype.expectNameOrString = function(context) {
if(this.at("name")) {
return this.tokenValue();
}
return this.expectString(context,"Name or string");
};
/////////////////////////// Tokenisation
Parser.prototype.tokenise = function(source) {
@ -294,12 +345,12 @@ Parser.prototype.tokenise = function(source) {
} else if(c === "-") {
// - or ->
s = source.charAt(pos+1) === ">" ? "->" : "-";
} else if("()>+|/:".indexOf(c) !== -1) {
} else if("()>+/:|".indexOf(c) !== -1) {
// Single character
s = c;
} else if(c.match(/[a-zA-Z]/)) {
// Identifier
token = this.readIdentifier(source,pos);
// Name
token = this.readName(source,pos);
} else {
throw "Syntax error at " + c;
}
@ -316,14 +367,14 @@ Parser.prototype.tokenise = function(source) {
return tokens;
};
Parser.prototype.readIdentifier = function(source,pos) {
Parser.prototype.readName = function(source,pos) {
var re = /([a-zA-Z0-9_.-]+)/g;
re.lastIndex = pos;
var match = re.exec(source);
if(match && match.index === pos) {
return {type: "identifier", value: match[1], start: pos, end: pos + match[1].length};
return {type: "name", value: match[1], start: pos, end: pos+match[1].length};
} else {
throw "Invalid identifier";
throw "Invalid name";
}
};

View File

@ -38,24 +38,73 @@ RailroadWidget.prototype.render = function(parent,nextSibling) {
var div = this.document.createElement("div");
try {
// Parse the source
var parser = new Parser(source);
var parser = new Parser(this,source);
// Generate content into the div
if(this.getAttribute("mode","svg") === "debug") {
var output = ["<pre>"];
parser.root.debug(output, "");
output.push("</pre>");
div.innerHTML = output.join("");
this.renderDebug(parser,div);
} else {
div.innerHTML = parser.root.toSvg();
this.renderSvg(parser,div);
}
} catch(ex) {
div.className = "tc-error";
div.textContent = ex;
}
// Insert it into the DOM
// Insert the div into the DOM
parent.insertBefore(div,nextSibling);
this.domNodes.push(div);
};
RailroadWidget.prototype.renderDebug = function(parser,div) {
var output = ["<pre>"];
parser.root.debug(output, "");
output.push("</pre>");
div.innerHTML = output.join("");
};
RailroadWidget.prototype.renderSvg = function(parser,div) {
// Generate a model of the diagram
var fakeSvg = parser.root.toSvg();
// Render the model into a tree of SVG DOM nodes
var svg = fakeSvg.toSVG();
// Fill in the remaining attributes of any link nodes
this.patchLinks(svg);
// Insert the SVG tree into the div
div.appendChild(svg);
};
RailroadWidget.prototype.patchLinks = function(node) {
var self = this;
if(node.hasChildNodes()) {
var children = node.childNodes;
for(var i=0; i<children.length; i++) {
var child = children[i];
var attributes = child.attributes;
if(attributes) {
// Find each element that has a data-tw-target attribute
var target = child.attributes["data-tw-target"];
if(target !== undefined) {
target = target.value;
if(child.attributes["data-tw-external"]) {
// External links are straightforward
child.setAttribute("target","_blank");
} else {
// Each internal link gets its own onclick handler, capturing its own copy of target
(function(myTarget) {
child.onclick = function(event) {
self.dispatchLink(myTarget,event);
return false;
}
})(target);
target = "#" + target;
}
child.setAttributeNS("http://www.w3.org/1999/xlink","href",target);
}
}
this.patchLinks(child);
}
}
};
RailroadWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.text) {
@ -65,6 +114,23 @@ RailroadWidget.prototype.refresh = function(changedTiddlers) {
return false;
};
RailroadWidget.prototype.dispatchLink = function(to,event) {
// Send the click on its way as a navigate event
var bounds = this.domNodes[0].getBoundingClientRect();
this.dispatchEvent({
type: "tm-navigate",
navigateTo: to,
navigateFromTitle: this.getVariable("storyTiddler"),
navigateFromNode: this,
navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height
},
navigateSuppressNavigation: event.metaKey || event.ctrlKey || (event.button === 1)
});
event.preventDefault();
event.stopPropagation();
return false;
};
exports.railroad = RailroadWidget;
})();