update TiddlyWiki tiddlers

This commit is contained in:
Soren I. Bjornstad 2024-07-25 16:24:16 -05:00
parent 81c46cc5e4
commit abc41c5a3c
276 changed files with 4389 additions and 2045 deletions

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@ type: text/vnd.tiddlywiki
<$set name="tr-rendering" value="yes"> <$set name="tr-rendering" value="yes">
<span id="tr-version">1.3.4</span> <span id="tr-version">1.3.5</span>
{{||$:/plugins/sobjornstad/TiddlyRemember/templates/AnkiDecks}} {{||$:/plugins/sobjornstad/TiddlyRemember/templates/AnkiDecks}}
{{||$:/plugins/sobjornstad/TiddlyRemember/templates/AnkiTags}} {{||$:/plugins/sobjornstad/TiddlyRemember/templates/AnkiTags}}

View File

@ -2,7 +2,7 @@
"title": "$:/plugins/sobjornstad/TiddlyRemember", "title": "$:/plugins/sobjornstad/TiddlyRemember",
"description": "TiddlyRemember: Embed Anki notes in your TiddlyWiki", "description": "TiddlyRemember: Embed Anki notes in your TiddlyWiki",
"author": "Soren Bjornstad", "author": "Soren Bjornstad",
"version": "1.3.4", "version": "1.3.5",
"core-version": ">=5.1.21", "core-version": ">=5.1.21",
"source": "https://github.com/sobjornstad/TiddlyRemember", "source": "https://github.com/sobjornstad/TiddlyRemember",
"list": "readme license", "list": "readme license",

View File

@ -4,13 +4,13 @@ type: text/vnd.tiddlywiki
!! 2.0.1 !! 2.0.1
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v2.0.0...v2.0.1]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v2.0.0...v2.0.1]]
* Add MIT license * Add MIT license
!! 2.0.0 !! 2.0.0
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.5.0...v2.0.0]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.5.0...v2.0.0]]
''ALMOST COMPLETE REWRITING, BACKWARDS INCOMPATIBLE CHANGES ARE INTRODUCED'' ''ALMOST COMPLETE REWRITING, BACKWARDS INCOMPATIBLE CHANGES ARE INTRODUCED''
@ -28,7 +28,7 @@ type: text/vnd.tiddlywiki
!! 1.5.0 !! 1.5.0
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.4.0...v1.5.0]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.4.0...v1.5.0]]
* Move tiddler from current context to higher- or subtopic by drag-n-dropping in the Locator sidebar * Move tiddler from current context to higher- or subtopic by drag-n-dropping in the Locator sidebar
* Assign any tiddler to any of the titles in the Locator sidebar by drag-n-dropping * Assign any tiddler to any of the titles in the Locator sidebar by drag-n-dropping
@ -42,7 +42,7 @@ type: text/vnd.tiddlywiki
!! 1.4.0 !! 1.4.0
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.3.1...v1.4.0]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.3.1...v1.4.0]]
* When there was a space in the search string and "new tiddler" button is used from search, the tiddler title was encapsulated (`[[title with spaces]]`) * When there was a space in the search string and "new tiddler" button is used from search, the tiddler title was encapsulated (`[[title with spaces]]`)
* When there was a space in any of the breadcrumbs in sidebar and "new tiddler" button is used from it, the tiddler got all of the breadcrumb tags, the title with spaces was encapsulated (literally `[[title with spaces]]` tag) * When there was a space in any of the breadcrumbs in sidebar and "new tiddler" button is used from it, the tiddler got all of the breadcrumb tags, the title with spaces was encapsulated (literally `[[title with spaces]]` tag)
@ -51,27 +51,27 @@ type: text/vnd.tiddlywiki
!! 1.3.1 !! 1.3.1
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.3.0...v1.3.1]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.3.0...v1.3.1]]
* Add base tag if there is no breadcrumbs when creating new tiddler in current context * Add base tag if there is no breadcrumbs when creating new tiddler in current context
* Reword documentation * Reword documentation
!! 1.3.0 !! 1.3.0
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.2.0...v1.3.0]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.2.0...v1.3.0]]
* Create new tiddler from search text * Create new tiddler from search text
!! 1.2.0 !! 1.2.0
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.1.0...v1.2.0]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.1.0...v1.2.0]]
* Add button to create new tiddler in current context * Add button to create new tiddler in current context
* Ability to use additional filter on tags * Ability to use additional filter on tags
!! 1.1.0 !! 1.1.0
[[See GitLab for detailed change history of this release|https://gitlab.com/bimlas/tw5-locator/compare/v1.0.0...v1.1.0]] [[See GitHub for detailed change history of this release|https://github.com/bimlas/tw5-locator/compare/v1.0.0...v1.1.0]]
* ''I realized that most functions work without the `kin` filter'' * ''I realized that most functions work without the `kin` filter''
* Use the same "style" for breadcrumb- and list items * Use the same "style" for breadcrumb- and list items

View File

@ -2,7 +2,7 @@ title: $:/plugins/bimlas/locator/README/LICENSE
MIT License MIT License
Copyright (c) since 2020 BimbaLaszlo <bimbalaszlo@gmail.com> (https://bimlas.gitlab.io/) Copyright (c) since 2020 BimbaLaszlo <bimbalaszlo@gmail.com> (https://bimlas.github.io/)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -6,9 +6,8 @@ type: text/vnd.tiddlywiki
Locator provides ''a universal interface for navigating between your tiddlers and searching, filtering them'' without the need to write [[filter expressions|https://tiddlywiki.com/#Filters]]. The plugin contains ''a table of contents widget and an enhanced search engine'' that contains a ''visual filter builder to filter results by tags and field values''. You can ''organize the tiddlers to any number of trees, even by custom fields or links in text''. Unlike [[classic Table of Contents|https://tiddlywiki.com/#Table-of-Contents%20Macros%20(Examples)]], [[standard search|$:/core/ui/AdvancedSearch/Standard]], [[filter search|$:/core/ui/AdvancedSearch/Filter]] and [[list of tags|$:/core/ui/MoreSideBar/Tags]], this plugin handles these functions organically. Locator provides ''a universal interface for navigating between your tiddlers and searching, filtering them'' without the need to write [[filter expressions|https://tiddlywiki.com/#Filters]]. The plugin contains ''a table of contents widget and an enhanced search engine'' that contains a ''visual filter builder to filter results by tags and field values''. You can ''organize the tiddlers to any number of trees, even by custom fields or links in text''. Unlike [[classic Table of Contents|https://tiddlywiki.com/#Table-of-Contents%20Macros%20(Examples)]], [[standard search|$:/core/ui/AdvancedSearch/Standard]], [[filter search|$:/core/ui/AdvancedSearch/Filter]] and [[list of tags|$:/core/ui/MoreSideBar/Tags]], this plugin handles these functions organically.
* https://bimlas.gitlab.io/tw5-locator (demo + install) * https://bimlas.github.io/tw5-locator (demo + install)
* https://gitlab.com/bimlas/tw5-locator (main repository) * https://github.com/bimlas/tw5-locator (''please star if you like the plugin'')
* https://github.com/bimlas/tw5-locator (mirror, ''please star if you like the plugin'')
! Benefits compared to built-in solutions ! Benefits compared to built-in solutions
@ -23,7 +22,7 @@ Locator provides ''a universal interface for navigating between your tiddlers an
! Usage ! Usage
See the [[demo|https://bimlas.gitlab.io/tw5-locator]] for a complete tutorial See the [[demo|https://bimlas.github.io/tw5-locator]] for a complete tutorial
! Installation instructions ! Installation instructions

View File

@ -4,7 +4,7 @@
"author": "bimlas", "author": "bimlas",
"version": "2.0.1", "version": "2.0.1",
"core-version": ">=5.1.18", "core-version": ">=5.1.18",
"source": "https://gitlab.com/bimlas/tw5-locator", "source": "https://github.com/bimlas/tw5-locator",
"plugin-type": "plugin", "plugin-type": "plugin",
"list": "README/README README/macros README/TIPS README/CHANGELOG README/LICENSE", "list": "README/README README/macros README/TIPS README/CHANGELOG README/LICENSE",
"dependents": "" "dependents": ""

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/attributes/$transclude/$subtiddler
title

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/attributes/$transclude/$tiddler
title

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/exceptions/$:/DefaultTiddlers
application/x-tiddler-filter

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/exceptions/$:/config/FileSystemExtensions
application/x-tiddler-filter

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/exceptions/$:/config/FileSystemPaths
application/x-tiddler-filter

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/macros/translink/title
title

View File

@ -0,0 +1,3 @@
title: $:/config/flibbles/relink/messages/tm-new-tiddler
(.*)

View File

@ -2,15 +2,13 @@ tags: $:/tags/EditTemplate
title: $:/core/ui/EditTemplate/title title: $:/core/ui/EditTemplate/title
\whitespace trim \whitespace trim
<$edit-text field="draft.title" class="tc-titlebar tc-edit-texteditor" focus="true" tabindex={{$:/config/EditTabIndex}}/> <$edit-text field="draft.title" class="tc-titlebar tc-edit-texteditor" focus={{{ [{$:/config/AutoFocus}match[title]then[true]] ~[[false]] }}} tabindex={{$:/config/EditTabIndex}} cancelPopups="yes" dir={{$:/config/DefaultTextDirection}}/>
<$reveal state="!!draft.title" type="nomatch" text={{!!draft.of}} tag="div">
<$vars pattern="""[\|\[\]{}]""" bad-chars="""`| [ ] { }`"""> <$vars pattern="""[\|\[\]{}]""" bad-chars="""`| [ ] { }`""">
<$list filter="[all[current]regexp:draft.title<pattern>]" variable="listItem"> <$list filter="[all[current]regexp:draft.title<pattern>]" variable="listItem">
<div class="tc-message-box"> <div class="tc-message-box" dir={{$:/config/DefaultTextDirection}}>
{{$:/core/images/warning}}&#32;{{$:/language/EditTemplate/Title/BadCharacterWarning}} {{$:/core/images/warning}}&#32;{{$:/language/EditTemplate/Title/BadCharacterWarning}}
@ -20,11 +18,13 @@ title: $:/core/ui/EditTemplate/title
</$vars> </$vars>
<$reveal state="!!draft.title" type="nomatch" text={{!!draft.of}} tag="div" dir={{$:/config/DefaultTextDirection}}>
<$list filter="[{!!draft.title}!is[missing]]" variable="listItem"> <$list filter="[{!!draft.title}!is[missing]]" variable="listItem">
<div class="tc-message-box"> <div class="tc-message-box">
{{$:/core/images/warning}}&#32;{{$:/language/EditTemplate/Title/Exists/Prompt}} {{$:/core/images/warning}}&#32;{{$:/language/EditTemplate/Title/Exists/Prompt}}:&#32;<$link to={{!!draft.title}} />
</div> </div>

View File

@ -1,4 +1,4 @@
description: {{$:/plugins/flibbles/relink/language/Filters/Missing}} description: {{$:/plugins/flibbles/relink/language/Filters/Missing}}
filter: [all[]!is[system]relink:references[]!is[tiddler]!is[shadow]!prefix[$:/tags/]sort[title]] -[[]] filter: [all[]!is[system]relink:references:hard[]!is[tiddler]!is[shadow]!prefix[$:/tags/]sort[title]] -[[]]
tags: $:/tags/Filter tags: $:/tags/Filter
title: $:/plugins/flibbles/relink/Filters/Missing title: $:/plugins/flibbles/relink/Filters/Missing

View File

@ -36,6 +36,7 @@ function relinkTiddler(fromTitle, toTitle, options) {
var failures = []; var failures = [];
var indexer = utils.getIndexer(this); var indexer = utils.getIndexer(this);
var records = indexer.relinkLookup(fromTitle, toTitle, options); var records = indexer.relinkLookup(fromTitle, toTitle, options);
var changedTitles = Object.create(null);
for (var title in records) { for (var title in records) {
var entries = records[title], var entries = records[title],
changes = Object.create(null), changes = Object.create(null),
@ -63,11 +64,16 @@ function relinkTiddler(fromTitle, toTitle, options) {
this.addTiddler(newTiddler); this.addTiddler(newTiddler);
// If the title changed, we need to perform a nested rename // If the title changed, we need to perform a nested rename
if (newTiddler.fields.title !== title) { if (newTiddler.fields.title !== title) {
this.deleteTiddler(title); changedTitles[title] = newTiddler.fields.title;
this.relinkTiddler(title, newTiddler.fields.title,options);
} }
} }
}; };
// Now that the rename is complete, we must now rename any tiddlers that
// changed their titles, and thus repeat the process.
for (var title in changedTitles) {
this.deleteTiddler(title);
this.relinkTiddler(title, changedTitles[title], options);
}
if (failures.length > 0) { if (failures.length > 0) {
var options = $tw.utils.extend( var options = $tw.utils.extend(
{ variables: {to: toTitle, from: fromTitle}, { variables: {to: toTitle, from: fromTitle},

View File

@ -43,6 +43,13 @@ Context.prototype.getMacros = function() {
return this.parent.getMacros(); return this.parent.getMacros();
}; };
/* Exceptions are tiddlers that have text/vnd.tiddlywiki type, but should
* not be treated as such.
*/
Context.prototype.getException = function(tiddlerTitle) {
return this.parent.getException(tiddlerTitle);
};
Context.prototype.allowPrettylinks = function() { Context.prototype.allowPrettylinks = function() {
return this.parent.allowPrettylinks(); return this.parent.allowPrettylinks();
}; };
@ -54,3 +61,22 @@ Context.prototype.allowWidgets = function() {
Context.prototype.hasImports = function(value) { Context.prototype.hasImports = function(value) {
return this.parent.hasImports(value); return this.parent.hasImports(value);
}; };
Context.prototype.getFocus = function() {
if (this.parent) {
return this.parent.getFocus();
}
};
Context.prototype.getPlaceholderList = function() {
if (this.parent) {
return this.parent.getPlaceholderList();
}
return null;
};
Context.prototype.addParameter = function(parameter) {
if(this.parent) {
return this.parent.addParameter(parameter);
}
};

View File

@ -10,9 +10,12 @@ function ImportContext(wiki, parent, filter) {
this.parent = parent; this.parent = parent;
this.wiki = wiki; this.wiki = wiki;
var importWidget = createImportWidget(filter, this.wiki, this.parent.widget); var importWidget = createImportWidget(filter, this.wiki, this.parent.widget);
this._compileList(importWidget.tiddlerList); this._compileList(importWidget.tiddlerList, importWidget.variables);
// this.widget is where we ask for macro definitions.
// This only works if only one filter is imported // This only works if only one filter is imported
this.widget = this.getBottom(importWidget); this.widget = this.getBottom(importWidget);
// We keep this one because it's where we need to test for changes from.
this.importWidget = importWidget
// Trickle this up, so that any containing tiddlercontext knows that this // Trickle this up, so that any containing tiddlercontext knows that this
// tiddler does some importing, and must be checked regularly. // tiddler does some importing, and must be checked regularly.
parent.hasImports(true); parent.hasImports(true);
@ -23,7 +26,7 @@ exports.import = ImportContext;
ImportContext.prototype = new WidgetContext(); ImportContext.prototype = new WidgetContext();
ImportContext.prototype.changed = function(changes) { ImportContext.prototype.changed = function(changes) {
return this.widget && this.widget.refresh(changes) return this.importWidget && this.importWidget.refresh(changes)
}; };
function createImportWidget(filter, wiki, parent) { function createImportWidget(filter, wiki, parent) {
@ -45,16 +48,20 @@ function createImportWidget(filter, wiki, parent) {
return importWidget; return importWidget;
}; };
ImportContext.prototype._compileList = function(titleList) { ImportContext.prototype._compileList = function(titleList, variables) {
for (var i = 0; i < titleList.length; i++) { for (var i = 0; i < titleList.length; i++) {
var parser = this.wiki.parseTiddler(titleList[i]); var parser = this.wiki.parseTiddler(titleList[i]);
if (parser) { if (parser) {
var parseTreeNode = parser.tree[0]; var parseTreeNode = parser.tree[0];
while (parseTreeNode && parseTreeNode.type === "set") { while (parseTreeNode && parseTreeNode.type === "set") {
var variable = variables[parseTreeNode.attributes.name.value];
if(variable) {
variable.tiddler = titleList[i];
}
if (parseTreeNode.relink) { if (parseTreeNode.relink) {
for (var macroName in parseTreeNode.relink) { for (var macroName in parseTreeNode.relink) {
var parameters = parseTreeNode.relink[macroName]; var parameters = parseTreeNode.relink[macroName];
for (paramName in parameters) { for (var paramName in parameters) {
this.addSetting(this.wiki, macroName, paramName, parameters[paramName], titleList[i]); this.addSetting(this.wiki, macroName, paramName, parameters[paramName], titleList[i]);
} }
} }

View File

@ -20,6 +20,11 @@ exports.tiddler = TiddlerContext;
TiddlerContext.prototype = new WidgetContext(); TiddlerContext.prototype = new WidgetContext();
TiddlerContext.prototype.getFocus = function() {
// Tiddler Contexts are the last possible focus when not embedded in contexts.
return this;
};
TiddlerContext.prototype.changed = function(changes) { TiddlerContext.prototype.changed = function(changes) {
return this.widget && this.widget.refresh(changes); return this.widget && this.widget.refresh(changes);
}; };

View File

@ -7,17 +7,55 @@ This handles the context for variables. Either from $set, $vars, or \define
var WidgetContext = require('./widget').widget; var WidgetContext = require('./widget').widget;
function VariableContext(parent, setParseTreeNode) { function VariableContext(parent, setParseTreeNode) {
var name = setParseTreeNode.attributes.name.value;
this.parent = parent; this.parent = parent;
// Now create a new widget and attach it. // Now create a new widget and attach it.
var attachPoint = parent.widget; var attachPoint = parent.widget;
var setWidget = attachPoint.makeChildWidget(setParseTreeNode); this.setWidget = attachPoint.makeChildWidget(setParseTreeNode);
attachPoint.children.push(setWidget); attachPoint.children.push(this.setWidget);
setWidget.computeAttributes(); this.setWidget.computeAttributes();
setWidget.execute(); this.setWidget.execute();
// We get the title of our current parameter focus
// (i.e. what \param would affect)
// If it's another definition, then title will be null.
this.setWidget.variables[name].tiddler = parent.getFocus().title;
// point our widget to bottom, where any other contexts would attach to // point our widget to bottom, where any other contexts would attach to
this.widget = this.getBottom(setWidget); this.widget = this.getBottom(this.setWidget);
this.parameterFocus = true;
if (setParseTreeNode.isMacroDefinition) {
this.placeholderList = Object.create(parent.getPlaceholderList());
for (var i = 0; i < setParseTreeNode.params.length; i++) {
this.placeholderList[setParseTreeNode.params[i].name] = true;
}
}
}; };
exports.variable = VariableContext; exports.variable = VariableContext;
VariableContext.prototype = new WidgetContext(); VariableContext.prototype = new WidgetContext();
VariableContext.prototype.getFocus = function() {
if(this.parameterFocus) {
return this;
} else {
return this.parent.getFocus();
}
};
VariableContext.prototype.getPlaceholderList = function() {
if (this.placeholderList !== undefined) {
return this.placeholderList;
} else {
return this.parent.getPlaceholderList();
}
};
VariableContext.prototype.addParameter = function(parameter) {
if(this.parameterFocus) {
var name = this.setWidget.setName;
var data = this.setWidget.variables[name];
data.params.push({name: parameter});
} else if (this.parent) {
this.parent.addParameter(parameter);
}
};

View File

@ -80,6 +80,10 @@ WhitelistContext.prototype.getMacros = function() {
return flatten(this.macros); return flatten(this.macros);
}; };
WhitelistContext.prototype.getException = function(tiddlerTitle) {
return this.exceptions[tiddlerTitle];
};
WhitelistContext.prototype.changed = function(changedTiddlers) { WhitelistContext.prototype.changed = function(changedTiddlers) {
for (var i = 0; i < WhitelistContext.hotDirectories.length; i++) { for (var i = 0; i < WhitelistContext.hotDirectories.length; i++) {
var dir = WhitelistContext.hotDirectories[i]; var dir = WhitelistContext.hotDirectories[i];

View File

@ -18,7 +18,23 @@ WidgetContext.prototype = new Context();
WidgetContext.prototype.getMacroDefinition = function(variableName) { WidgetContext.prototype.getMacroDefinition = function(variableName) {
// widget.variables is prototyped, so it looks up into all its parents too // widget.variables is prototyped, so it looks up into all its parents too
return this.widget.variables[variableName] || $tw.macros[variableName]; var def = this.widget.variables[variableName];
if (!def) {
// It might be a javascript macro
def = $tw.macros[variableName];
if (def && !def.tiddler) {
// We haven't assigned associated tiddlers to these macros yet.
// That may be important for some installed supplemental plugins.
$tw.modules.forEachModuleOfType('macro', function(title, module) {
if (module.name) {
// For now, we just attach it directly to the definition
// It's easier, albeit a little sloppy.
$tw.macros[module.name].tiddler = title;
}
});
}
}
return def;
}; };
WidgetContext.prototype.addSetting = function(wiki, macroName, parameter, type, sourceTitle) { WidgetContext.prototype.addSetting = function(wiki, macroName, parameter, type, sourceTitle) {
@ -31,10 +47,6 @@ WidgetContext.prototype.addSetting = function(wiki, macroName, parameter, type,
var handler = utils.getType(type); var handler = utils.getType(type);
if (handler) { if (handler) {
handler.source = sourceTitle; handler.source = sourceTitle;
// We attach the fields of the defining tiddler for the benefit
// of any 3rd party field types that want access to them.
var tiddler = wiki.getTiddler(sourceTitle);
handler.fields = tiddler.fields;
macro[parameter] = handler; macro[parameter] = handler;
} }
}; };
@ -71,6 +83,44 @@ WidgetContext.prototype.getMacro = function(macroName) {
return theseSettings || parentSettings; return theseSettings || parentSettings;
}; };
WidgetContext.prototype.getAttribute = function(elementName) {
if (elementName.charAt(0) == '$' && elementName.indexOf('.') >= 0) {
// This is potentially a \widget, look in macros for it.
var macroSettings = this.getMacro(elementName);
if (macroSettings) {
// Make sure that it's actually a widget definition
var def = this.getMacroDefinition(elementName);
if (def) {
// We found a definition, but if it's not a widget, abort all.
return (def.isWidgetDefinition)? macroSettings: undefined;
}
}
}
return this.parent.getAttribute(elementName);
};
WidgetContext.prototype.getOperator = function(name, index) {
if (name.indexOf('.') >= 0) {
// This is potentially a \function, look in macros for it.
var macroSettings = this.getMacro(name);
if (macroSettings) {
//Make sure that it's actually a macro definition
var def = this.getMacroDefinition(name);
if (def) {
if (def.isFunctionDefinition) {
// Minus one because operator indices are 1 indexed,
// but parameters as we store them are not.
var param = def.params[index - 1];
return param && macroSettings[param.name];
}
// If it's not a filter, abort all.
return undefined;
}
}
}
return this.parent.getOperator(name, index);
};
/**Returns the deepest descendant of the given widget. /**Returns the deepest descendant of the given widget.
*/ */
WidgetContext.prototype.getBottom = function(widget) { WidgetContext.prototype.getBottom = function(widget) {

View File

@ -51,7 +51,7 @@ exports.relink = function(filter, fromTitle, toTitle, options) {
} }
} }
if (changed) { if (changed) {
builder = assembleFilterString(parseTree, filter, options); var builder = assembleFilterString(parseTree, filter, options);
results.output = builder.results(); results.output = builder.results();
results.impossible = results.impossible || builder.impossible; results.impossible = results.impossible || builder.impossible;
return results; return results;
@ -116,9 +116,6 @@ function assembleFilterString(parseTree, oldFilter, options) {
} }
relinker.add(wrapped, p, end); relinker.add(wrapped, p, end);
wordBarrierRequired = wrapped === text; wordBarrierRequired = wrapped === text;
} else if (options.placeholder) {
var ph = options.placeholder.getPlaceholderFor(text);
relinker.add('[<' + ph + '>]', p, end);
} else { } else {
relinker.impossible = true; relinker.impossible = true;
} }
@ -156,13 +153,8 @@ function assembleFilterString(parseTree, oldFilter, options) {
p = oldFilter.indexOf('[', p); p = oldFilter.indexOf('[', p);
end = oldFilter.indexOf(']', p+1); end = oldFilter.indexOf(']', p+1);
if (!canBePrettyOperand(operand.text) || (options.inBraces && operand.text.indexOf('}}}') >= 0)) { if (!canBePrettyOperand(operand.text) || (options.inBraces && operand.text.indexOf('}}}') >= 0)) {
if (options.placeholder) {
var ph = options.placeholder.getPlaceholderFor(operand.text, operand.handler);
wrapped = '<' + ph + '>';
} else {
skip = true; skip = true;
relinker.impossible = true; relinker.impossible = true;
}
} else { } else {
wrapped = '[' + operand.text + ']'; wrapped = '[' + operand.text + ']';
} }

View File

@ -0,0 +1,65 @@
/*\
Handles reporting of [function[]] operators.
\*/
exports.name = "function";
exports.report = function(filterParseTree, callback, options) {
forEachOperand(filterParseTree, options, function(name, operand, handler, index) {
handler.report(operand.text, function(title, blurb, style) {
callback(title, '[function[' + name + ']' + ','.repeat(index) + '[' + (blurb || '') + ']]', style);
}, options);
});
};
exports.relink = function(filterParseTree, fromTitle, toTitle, options) {
var output = {};
forEachOperand(filterParseTree, options, function(name, operand, handler, index) {
var entry = handler.relink(operand.text, fromTitle, toTitle, options);
if (entry) {
if (entry.output) {
output.changed = true;
operand.text = entry.output;
}
if (entry.impossible) {
output.impossible = true;
}
}
});
return output;
};
// Calls the callback for every applicable operand of a function operator
function forEachOperand(filterParseTree, options, callback) {
for (var i = 0; i < filterParseTree.length; i++) {
var run = filterParseTree[i];
for (var j = 0; j < run.operators.length; j++) {
var operator = run.operators[j];
var titleOp = operator.operands[0];
if (operator.operator === "function"
&& !titleOp.variable && !titleOp.indirect
&& titleOp.text) {
var funcName = titleOp.text;
var managedMacro = options.settings.getMacro(funcName);
if (managedMacro) {
var def = options.settings.getMacroDefinition(funcName);
if (def && def.isFunctionDefinition) {
for (var index = 1; index < operator.operands.length; index++) {
var operand = operator.operands[index];
if (!operand.variable && !operand.indirect
&& def.params.length >= index) {
var paramName = def.params[index-1].name;
var handler = managedMacro[paramName];
if (handler) {
callback(funcName, operand, handler, index);
}
}
}
}
}
}
}
}
};

View File

@ -0,0 +1,3 @@
module-type: relinkfilter
title: $:/plugins/flibbles/relink/js/fieldtypes/filter/function.js
type: application/javascript

View File

@ -17,32 +17,38 @@ exports.report = function(filterParseTree, callback, options) {
var operator = run.operators[j]; var operator = run.operators[j];
for (var index = 1; index <= operator.operands.length; index++) { for (var index = 1; index <= operator.operands.length; index++) {
var operand = operator.operands[index-1]; var operand = operator.operands[index-1];
var display = operator.operator === 'title'? '': operator.operator; var display = makeDisplay(operator);
if (operator.suffix) {
display += ':' + operator.suffix;
}
// Now add any commas if this is a later operand // Now add any commas if this is a later operand
for (var x = 1; x < index; x++) { for (var x = 1; x < index; x++) {
display += ','; display += ',';
} }
if (operand.indirect) { if (operand.indirect) {
refHandler.report(operand.text, function(title, blurb) { refHandler.report(operand.text, function(title, blurb, style) {
callback(title, (run.prefix || '') + '[' + (operator.prefix || '') + display + '{' + (blurb || '') + '}]'); callback(title, (run.prefix || '') + '[' + display + '{' + (blurb || '') + '}]', style);
}, options); }, options);
} else if (operand.variable) { } else if (operand.variable) {
var macro = $tw.utils.parseMacroInvocation("<<"+operand.text+">>", 0); var macro = $tw.utils.parseMacroInvocation("<<"+operand.text+">>", 0);
macrocall.report(options.settings, macro, function(title, blurb) { if (macro) {
callback(title, (run.prefix || '') + '[' + (operator.prefix || '') + display + '<' + blurb + '>]'); macrocall.report(options.settings, macro, function(title, blurb, style) {
callback(title, (run.prefix || '') + '[' + display + '<' + blurb + '>]', style);
}, options); }, options);
}
continue; continue;
} else if (operand.text) { } else if (operand.text) {
var handler = fieldType(options.settings, operator, index, options) var handler = fieldType(options.settings, operator, index, options)
if (handler) { if (handler) {
handler.report(operand.text, function(title, blurb) { handler.report(operand.text, function(title, blurb, style) {
if (blurb || !standaloneTitleRun(run)) { if (!isTitleRun(operator) || blurb) {
callback(title, (run.prefix || '') + '[' + (operator.prefix || '') + display + '[' + (blurb || '') + ']]'); callback(title, (run.prefix || '') + '[' + display + '[' + (blurb || '') + ']]', style);
} else if (j === run.operators.length-1) {
// index will always be 1, meaning single operator run,
// unless the user is weird. [title[]] ignores
// input, so why would it ever not be 1?
callback(title, run.prefix, style);
} else { } else {
callback(title, run.prefix); // Special case: It's a title operator that's
// leading a run
callback(title, (run.prefix || '') + '[[]' + makeDisplay(run.operators[j+1]) + '...]', style);
} }
}, options); }, options);
} }
@ -69,9 +75,6 @@ exports.relink = function(filterParseTree, fromTitle, toTitle, options) {
var handler = fieldType(options.settings, operator, index, options) var handler = fieldType(options.settings, operator, index, options)
if (handler) { if (handler) {
entry = handler.relink(operand.text, fromTitle, toTitle, options); entry = handler.relink(operand.text, fromTitle, toTitle, options);
if (entry && entry.output) {
operand.handler = handler.name;
}
} }
} }
if (entry) { if (entry) {
@ -115,32 +118,37 @@ function fieldType(context, operator, index, options) {
return rtn; return rtn;
}; };
function standaloneTitleRun(run) { function makeDisplay(operator) {
if (run.operators.length == 1) { return (operator.prefix || '') + (operator.operator === 'title'? '': operator.operator) + (operator.suffix? ':' + operator.suffix: '');
var op = run.operators[0]; };
return op.operator === 'title'
&& !op.prefix function isTitleRun(operator) {
&& !op.suffix; return operator.operator === 'title'
} && !operator.prefix
return false; && !operator.suffix;
}; };
// Takes care of relinking a macro, as well as putting it back together. // Takes care of relinking a macro, as well as putting it back together.
function relinkMacro(context, text, fromTitle, toTitle, options) { function relinkMacro(context, text, fromTitle, toTitle, options) {
text = "<<" + text + ">>"; text = "<<" + text + ">>";
var macro = $tw.utils.parseMacroInvocation(text, 0); var macro = $tw.utils.parseMacroInvocation(text, 0);
var entry = macrocall.relink(context, macro, text, fromTitle, toTitle, false, options); var entry;
if (macro) {
entry = macrocall.relink(context, macro, text, fromTitle, toTitle, false, options);
}
if (entry && entry.output) { if (entry && entry.output) {
var string = macrocall.reassemble(entry.output, text, options); var string = macrocall.reassemble(entry, text, options);
if (string !== undefined) {
// We remove the surrounding brackets. // We remove the surrounding brackets.
string = string.substring(2, string.length-2); string = string.substring(2, string.length-2);
// And we make sure that no brackets remain // And we make sure that no brackets remain
if (string.indexOf(">") >= 0) { if (string.indexOf(">") < 0) {
entry.output = string;
return entry;
}
}
delete entry.output; delete entry.output;
entry.impossible = true; entry.impossible = true;
} else {
entry.output = string;
}
} }
return entry; return entry;
}; };

View File

@ -4,12 +4,14 @@ This manages replacing titles that occur within stringLists, like,
TiddlerA [[Tiddler with spaces]] [[Another Title]] TiddlerA [[Tiddler with spaces]] [[Another Title]]
\*/ \*/
var titleRelinker = require('./title.js');
exports.name = "list"; exports.name = "list";
exports.report = function(value, callback, options) { exports.report = function(value, callback, options) {
var list = $tw.utils.parseStringArray(value); var list = $tw.utils.parseStringArray(value);
for (var i = 0; i < list.length; i++) { for (var i = 0; i < list.length; i++) {
callback(list[i]); titleRelinker.report(list[i], callback, options);
} }
}; };
@ -19,7 +21,9 @@ exports.report = function(value, callback, options) {
*/ */
exports.relink = function(value, fromTitle, toTitle, options) { exports.relink = function(value, fromTitle, toTitle, options) {
var isModified = false, var isModified = false,
impossible = false,
actualList = false, actualList = false,
entry,
list; list;
if (typeof value !== "string") { if (typeof value !== "string") {
// Not a string. Must be a list. // Not a string. Must be a list.
@ -31,25 +35,30 @@ exports.relink = function(value, fromTitle, toTitle, options) {
list = $tw.utils.parseStringArray(value || ""); list = $tw.utils.parseStringArray(value || "");
} }
$tw.utils.each(list,function (title,index) { $tw.utils.each(list,function (title,index) {
if(title === fromTitle) { var titleEntry = titleRelinker.relink(title, fromTitle, toTitle, options);
list[index] = toTitle; if (titleEntry) {
if (titleEntry.output) {
list[index] = titleEntry.output;
isModified = true; isModified = true;
} }
if (titleEntry.impossible) {
impossible = true;
}
}
}); });
if (isModified) { if (isModified || impossible) {
var entry = {name: "list"}; entry = {name: "list", impossible: impossible};
// It doesn't parse correctly alone, it won't // It doesn't parse correctly alone, it won't
// parse correctly in any list. // parse correctly in any list.
if (!canBeListItem(toTitle)) { if (!canBeListItem(toTitle)) {
entry.impossible = true; entry.impossible = true;
} else if (actualList) { } else if (actualList) {
entry.output = list; entry.output = list;
} else { } else if (isModified) {
entry.output = $tw.utils.stringifyList(list); entry.output = $tw.utils.stringifyList(list);
} }
return entry;
} }
return undefined; return entry;
}; };
function canBeListItem(value) { function canBeListItem(value) {

View File

@ -4,11 +4,13 @@ This handles the title inside of references.
\*/ \*/
var titleRelinker = require('../title.js');
exports.name = 'title'; exports.name = 'title';
exports.report = function(reference, callback, options) { exports.report = function(reference, callback, options) {
var title = reference.title; var title = reference.title;
if (title) { titleRelinker.report(reference.title, function(title, blurb, style) {
if (reference.field) { if (reference.field) {
callback(title, '!!' + reference.field); callback(title, '!!' + reference.field);
} else if (reference.index) { } else if (reference.index) {
@ -16,13 +18,15 @@ exports.report = function(reference, callback, options) {
} else { } else {
callback(title); callback(title);
} }
} }, options);
}; };
exports.relink = function(reference, fromTitle, toTitle, options) { exports.relink = function(reference, fromTitle, toTitle, options) {
if ($tw.utils.trim(reference.title) === fromTitle) { var entry = titleRelinker.relink($tw.utils.trim(reference.title), fromTitle, toTitle, options);
if (entry && entry.output) {
// preserve user's whitespace // preserve user's whitespace
reference.title = reference.title.replace(fromTitle, toTitle); reference.title = reference.title.replace(fromTitle, entry.output);
return {output: reference}; entry.output = reference;
} }
return entry;
}; };

View File

@ -8,16 +8,43 @@ simplest kind of field type. One title swaps out for the other.
exports.name = 'title'; exports.name = 'title';
exports.report = function(value, callback, options) { exports.report = function(value, callback, options) {
if (value && !containsPlaceholder(value, options)) {
callback(value); callback(value);
}
}; };
/**Returns undefined if no change was made. /**Returns undefined if no change was made.
*/ */
exports.relink = function(value, fromTitle, toTitle, options) { exports.relink = function(value, fromTitle, toTitle, options) {
if (value === fromTitle) { if (value !== fromTitle || containsPlaceholder(value, options)) {
return undefined;
} else if (containsPlaceholder(toTitle, options)) {
return {impossible: true};
} else {
return {output: toTitle}; return {output: toTitle};
} }
return undefined; };
function containsPlaceholder(value, options) {
var dollar = value.indexOf('$');
// Quick test. If no dollar signs. No placeholders.
if (dollar >= 0 && value.indexOf('$', dollar+1)) {
// We potentially have a placeholder
var placeholders = options.settings.getPlaceholderList();
if (placeholders) {
if (value.search(/\$\([^$\)]+\)\$/) >= 0) {
// A global placeholder exists
return true;
}
for (var name in placeholders) {
if (value.indexOf('$' + name + '$') >= 0) {
// Oops. This contains a placeholder.
return true;
}
}
}
}
return false;
}; };
// This is legacy support for when 'title' was known as 'field' // This is legacy support for when 'title' was known as 'field'

View File

@ -78,6 +78,10 @@ WikiWalker.prototype.parseInlineRunUnterminated = function(options) {
}; };
WikiWalker.prototype.parseInlineRunTerminated = function(terminatorRegExp,options) { WikiWalker.prototype.parseInlineRunTerminated = function(terminatorRegExp,options) {
return this.parseInlineRunTerminatedExtended(terminatorRegExp,options).tree;
};
WikiWalker.prototype.parseInlineRunTerminatedExtended = function(terminatorRegExp,options) {
var entries = []; var entries = [];
options = options || {}; options = options || {};
terminatorRegExp.lastIndex = this.pos; terminatorRegExp.lastIndex = this.pos;
@ -90,7 +94,10 @@ WikiWalker.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
if (options.eatTerminator) { if (options.eatTerminator) {
this.pos += terminatorMatch[0].length; this.pos += terminatorMatch[0].length;
} }
return entries; return {
match: terminatorMatch,
tree: entries
};
} }
} }
if (inlineRuleMatch) { if (inlineRuleMatch) {
@ -104,7 +111,7 @@ WikiWalker.prototype.parseInlineRunTerminated = function(terminatorRegExp,option
} }
} }
this.pos = this.sourceLength; this.pos = this.sourceLength;
return entries; return {tree: entries};
}; };
@ -131,9 +138,6 @@ WikiWalker.prototype.amendRules = function(type, names) {
} else { } else {
return; return;
} }
if (only !== (names.indexOf("macrodef") >= 0) && this.options.macrodefCanBeDisabled) {
this.options.placeholder = undefined
}
if (only !== (names.indexOf("html") >= 0)) { if (only !== (names.indexOf("html") >= 0)) {
this.context.allowWidgets = disabled; this.context.allowWidgets = disabled;
} }
@ -179,10 +183,6 @@ exports.report = function(wikitext, callback, options) {
function WikiRelinker(type, text, fromTitle, toTitle, options) { function WikiRelinker(type, text, fromTitle, toTitle, options) {
this.fromTitle = fromTitle; this.fromTitle = fromTitle;
this.toTitle = toTitle; this.toTitle = toTitle;
this.placeholder = options.placeholder;
if (this.placeholder) {
this.placeholder.parser = this;
}
WikiWalker.call(this, type, text, options); WikiWalker.call(this, type, text, options);
}; };

View File

@ -13,7 +13,7 @@ Returns all tiddlers which are not referenced in any way
"use strict"; "use strict";
exports.orphans = function(source,prefix,options) { exports.orphans = function(source,prefix,options) {
return options.wiki.getRelinkOrphans(); return options.wiki.getRelinkOrphans({ignore: ['$:/StoryList']});
}; };
})(); })();

View File

@ -37,6 +37,10 @@ exports.backreferences = function(source,operator,options) {
exports.references = function(source,operator,options) { exports.references = function(source,operator,options) {
var results = new LinkedList(); var results = new LinkedList();
if (operator.suffix === 'hard') {
options = Object.create(options);
options.hard = true;
}
source(function(tiddler,title) { source(function(tiddler,title) {
var refs = options.wiki.getTiddlerRelinkReferences(title,options); var refs = options.wiki.getTiddlerRelinkReferences(title,options);
if (refs) { if (refs) {

View File

@ -17,16 +17,21 @@ var utils = require("$:/plugins/flibbles/relink/js/utils.js");
exports.wouldchange = function(source,operator,options) { exports.wouldchange = function(source,operator,options) {
var from = options.widget && options.widget.getVariable("currentTiddler"), var from = options.widget && options.widget.getVariable("currentTiddler"),
to = operator.operand, to = operator.operand,
indexer = utils.getIndexer(options.wiki), indexer = utils.getIndexer(options.wiki);
records = indexer.relinkLookup(from, to, options); if (from !== to) {
var records = indexer.relinkLookup(from, to, options);
return Object.keys(records); return Object.keys(records);
} else {
return [];
}
}; };
exports.impossible = function(source,operator,options) { exports.impossible = function(source,operator,options) {
var from = options.widget && options.widget.getVariable("currentTiddler"), var from = options.widget && options.widget.getVariable("currentTiddler"),
to = operator.operand, to = operator.operand,
results = [], results = [];
indexer = utils.getIndexer(options.wiki), if (to && from !== to) {
var indexer = utils.getIndexer(options.wiki),
records = indexer.relinkLookup(from, to, options); records = indexer.relinkLookup(from, to, options);
source(function(tiddler, title) { source(function(tiddler, title) {
var fields = records[title]; var fields = records[title];
@ -38,5 +43,6 @@ exports.impossible = function(source,operator,options) {
} }
} }
}); });
}
return results; return results;
}; };

View File

@ -85,12 +85,26 @@ Indexer.prototype.relinkLookup = function(fromTitle, toTitle, options) {
}; };
// Returns all tiddlers that don't have anything referencing it. // Returns all tiddlers that don't have anything referencing it.
Indexer.prototype.orphans = function() { Indexer.prototype.orphans = function(options) {
this._upkeep(); this._upkeep();
var results = []; var results = [];
var ignoreList = (options && options.ignore) || [];
var ignoreMap = Object.create(null);
for (var i = 0; i < ignoreList.length; i++) {
ignoreMap[ignoreList[i]] = true;
}
for (var title in this.index) { for (var title in this.index) {
if (!this.backIndex[title] var index = this.backIndex[title];
|| Object.keys(this.backIndex[title]).length === 0) { var owned = false;
if (index) {
for (var key in index) {
if (!ignoreMap[key]) {
owned = true;
break;
}
}
}
if (!owned) {
results.push(title); results.push(title);
} }
} }

View File

@ -22,11 +22,11 @@ exports.report = function(tiddler, callback, options) {
// they actually point to, but let's not bother with that now // they actually point to, but let's not bother with that now
return; return;
} }
handler.report(input, function(title, blurb) { handler.report(input, function(title, blurb, style) {
if (blurb) { if (blurb) {
callback(title, field + ': ' + blurb); callback(title, field + ': ' + blurb, style);
} else { } else {
callback(title, field); callback(title, field, style);
} }
}, options); }, options);
} }

View File

@ -9,28 +9,28 @@ relink titles within the body.
/*global $tw: false */ /*global $tw: false */
"use strict"; "use strict";
var defaultOperator = "text/vnd.tiddlywiki";
var utils = require('$:/plugins/flibbles/relink/js/utils.js'); var utils = require('$:/plugins/flibbles/relink/js/utils.js');
exports.name = 'text'; exports.name = 'text';
var textOperators = utils.getModulesByTypeAsHashmap('relinktext', 'type'); var textOperators = utils.getModulesByTypeAsHashmap('relinktext', 'type');
// Set up any aliases, mostly for backward-compatibility
$tw.utils.each(Object.keys(textOperators), function(type) {
var operator = textOperators[type];
if(operator.aliases) {
for(var index = 0; index < operator.aliases.length; index++) {
textOperators[operator.aliases[index]] = operator;
}
}
});
// These are deprecated. Don't use them. // These are deprecated. Don't use them.
var oldTextOperators = utils.getModulesByTypeAsHashmap('relinktextoperator', 'type'); var oldTextOperators = utils.getModulesByTypeAsHashmap('relinktextoperator', 'type');
// $:/DefaultTiddlers is a tiddler which has type "text/vnd.tiddlywiki",
// but it lies. It doesn't contain wikitext. It contains a filter, so
// we pretend it has a filter type.
// If you want to be able to add more exceptions for your plugin, let me know.
var exceptions = {
"$:/DefaultTiddlers": "text/x-tiddler-filter"
};
exports.report = function(tiddler, callback, options) { exports.report = function(tiddler, callback, options) {
var fields = tiddler.fields; if (tiddler.fields.text) {
if (fields.text) { var type = getType(tiddler, options);
var type = exceptions[fields.title] || fields.type || defaultOperator;
if (textOperators[type]) { if (textOperators[type]) {
textOperators[type].report(tiddler.fields.text, callback, options); textOperators[type].report(tiddler.fields.text, callback, options);
} else if (oldTextOperators[type]) { } else if (oldTextOperators[type]) {
@ -41,9 +41,8 @@ exports.report = function(tiddler, callback, options) {
}; };
exports.relink = function(tiddler, fromTitle, toTitle, changes, options) { exports.relink = function(tiddler, fromTitle, toTitle, changes, options) {
var fields = tiddler.fields; if (tiddler.fields.text) {
if (fields.text) { var type = getType(tiddler, options),
var type = exceptions[fields.title] || fields.type || defaultOperator,
entry; entry;
if (textOperators[type]) { if (textOperators[type]) {
entry = textOperators[type].relink(tiddler.fields.text, fromTitle, toTitle, options); entry = textOperators[type].relink(tiddler.fields.text, fromTitle, toTitle, options);
@ -56,3 +55,14 @@ exports.relink = function(tiddler, fromTitle, toTitle, changes, options) {
} }
} }
}; };
/* The type of the tiddler is determined based on:
* 1. Whether there's an exception specified on it.
* 2. The type the tiddler says it is.
* 3. Or the default vnd.tiddlywiki type if not specified.
*/
function getType(tiddler, options) {
return options.settings.getException(tiddler.fields.title)
|| tiddler.fields.type
|| "text/vnd.tiddlywiki";
};

View File

@ -11,7 +11,9 @@ wikitext.
var filterHandler = require("$:/plugins/flibbles/relink/js/utils").getType('filter'); var filterHandler = require("$:/plugins/flibbles/relink/js/utils").getType('filter');
exports.type = 'text/x-tiddler-filter'; exports.type = 'application/x-tiddler-filter';
exports.aliases = ['text/x-tiddler-filter'];
exports.report = filterHandler.report; exports.report = filterHandler.report;
exports.relink = filterHandler.relink; exports.relink = filterHandler.relink;

View File

@ -10,7 +10,9 @@ This relinks tiddlers which contain a tiddler list as their body.
var listHandler = require("$:/plugins/flibbles/relink/js/utils").getType('list'); var listHandler = require("$:/plugins/flibbles/relink/js/utils").getType('list');
exports.type = 'text/x-tiddler-list'; exports.type = 'application/x-tiddler-list';
exports.aliases = ['text/x-tiddler-list'];
exports.report = listHandler.report; exports.report = listHandler.report;
exports.relink = listHandler.relink; exports.relink = listHandler.relink;

View File

@ -10,7 +10,9 @@ This relinks tiddlers which contain a tiddler reference as their body.
var refHandler = require("$:/plugins/flibbles/relink/js/utils").getType('reference'); var refHandler = require("$:/plugins/flibbles/relink/js/utils").getType('reference');
exports.type = 'text/x-tiddler-reference'; exports.type = 'application/x-tiddler-reference';
exports.aliases = ['text/x-tiddler-reference'];
exports.report = refHandler.report; exports.report = refHandler.report;
exports.relink = refHandler.relink; exports.relink = refHandler.relink;

View File

@ -10,7 +10,9 @@ This relinks tiddlers which contain a single title as their body.
var titleHandler = require("$:/plugins/flibbles/relink/js/utils").getType('title'); var titleHandler = require("$:/plugins/flibbles/relink/js/utils").getType('title');
exports.type = 'text/x-tiddler-title'; exports.type = 'application/x-tiddler-title';
exports.aliases = ['text/x-tiddler-title'];
exports.report = titleHandler.report; exports.report = titleHandler.report;
exports.relink = titleHandler.relink; exports.relink = titleHandler.relink;

View File

@ -9,23 +9,9 @@ and tries to swap it out if it is.
/*global $tw: false */ /*global $tw: false */
"use strict"; "use strict";
var Placeholder = require("$:/plugins/flibbles/relink/js/utils/placeholder.js");
var wikitextHandler = require('$:/plugins/flibbles/relink/js/utils.js').getType('wikitext'); var wikitextHandler = require('$:/plugins/flibbles/relink/js/utils.js').getType('wikitext');
exports.type = 'text/vnd.tiddlywiki'; exports.type = 'text/vnd.tiddlywiki';
exports.report = wikitextHandler.report; exports.report = wikitextHandler.report;
exports.relink = wikitextHandler.relink;
exports.relink = function(text, fromTitle, toTitle, options) {
var placeholder = new Placeholder();
var currentOptions = Object.create(options);
currentOptions.placeholder = placeholder;
var entry = wikitextHandler.relink(text, fromTitle, toTitle, currentOptions);
if (entry && entry.output) {
// If there's output, we've also got to prepend any macros
// that the placeholder defined.
var preamble = placeholder.getPreamble();
entry.output = preamble + entry.output;
}
return entry;
};

View File

@ -0,0 +1,124 @@
/*\
module-type: relinkwikitextrule
Handles replacement of conditionals
<%if Tiddler %>
<%elseif TiddlerB %>
\*/
var utils = require("$:/plugins/flibbles/relink/js/utils.js");
var Rebuilder = require("$:/plugins/flibbles/relink/js/utils/rebuilder");
var filterRelinker = utils.getType('filter');
exports.name = "conditional";
exports.report = function(text, callback, options) {
var match = this.match;
var keyword = '<%if ';
var reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>|\\<\\%\\s*(else)\\s*\\%\\>|\\<\\%\\s*(elseif)\\s+([\\s\\S]+?)\\%\\>";
this.parser.pos = this.terminateIfMatch.index + this.terminateIfMatch[0].length;
var ex;
var filter = this.parser.source.substring(match.index + match[0].length, this.terminateIfMatch.index);
while (true) {
if (filter) {
filterRelinker.report(filter, function(title, blurb, style) {
if (blurb) {
blurb = keyword + blurb + ' %>';
} else {
blurb = keyword + '%>';
}
callback(title, blurb, style);
}, options);
}
var hasLineBreak = doubleLineBreakAtPos(this.parser);
// Parse the body looking for else or endif
if (hasLineBreak) {
ex = this.parser.parseBlocksTerminatedExtended(reEndString);
} else {
var reEnd = new RegExp(reEndString,"mg");
ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
}
if (ex.match) {
match = ex.match;
if (ex.match[3] === "elseif") {
keyword = '<%elseif ';
filter = ex.match[4];
continue;
} else if (ex.match[2] === "else") {
reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>";
filter = null;
continue;
}
}
break;
}
};
exports.relink = function(text, fromTitle, toTitle, options) {
var conditionEntry = {};
var builder = new Rebuilder(text, this.match.index);
var reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>|\\<\\%\\s*(else)\\s*\\%\\>|\\<\\%\\s*(elseif)\\s+([\\s\\S]+?)\\%\\>";
this.parser.pos = this.terminateIfMatch.index + this.terminateIfMatch[0].length;
var ex;
var filter = this.parser.source.substring(this.match.index + this.match[0].length, this.terminateIfMatch.index);
var endOfFilter = this.terminateIfMatch.index;
while (true) {
if (filter) {
var entry = filterRelinker.relink(filter, fromTitle, toTitle, options);
if (entry) {
if (entry.output) {
if (entry.output.indexOf('%>') > 0) {
builder.impossible = true;
} else {
builder.add(entry.output, endOfFilter - filter.length, endOfFilter);
}
}
if (entry.impossible) {
builder.impossible = true;
}
}
}
var hasLineBreak = doubleLineBreakAtPos(this.parser);
// Parse the body looking for else or endif
if (hasLineBreak) {
ex = this.parser.parseBlocksTerminatedExtended(reEndString);
} else {
var reEnd = new RegExp(reEndString,"mg");
ex = this.parser.parseInlineRunTerminatedExtended(reEnd,{eatTerminator: true});
}
for (var i = 0; i < ex.tree.length; i++) {
var child = ex.tree[i];
if (child.output) {
builder.add(child.output, child.start, child.end);
}
if (child.impossible) {
builder.impossible = true;
}
}
if (ex.match) {
if (ex.match[3] === "elseif") {
filter = ex.match[4];
endOfFilter = ex.match.index + ex.match[0].length - 2;
continue;
} else if (ex.match[2] === "else") {
filter = null;
reEndString = "\\<\\%\\s*(endif)\\s*\\%\\>";
continue;
}
}
break;
}
if (builder.changed() || builder.impossible) {
conditionEntry = {
output: builder.results(this.parser.pos),
impossible: builder.impossible };
}
return conditionEntry;
};
function doubleLineBreakAtPos(parser) {
return !!$tw.utils.parseTokenRegExp(parser.source, parser.pos, /([^\S\n\r]*\r?\n(?:[^\S\n\r]*\r?\n|$))/g);
};

View File

@ -0,0 +1,3 @@
module-type: relinkwikitextrule
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/conditional.js
type: application/javascript

View File

@ -0,0 +1,109 @@
/*\
module-type: library
This is a generic def rule that manages both fnprocdef and macrodef.
\*/
var utils = require("$:/plugins/flibbles/relink/js/utils");
var VariableContext = utils.getContext('variable');
var Rebuilder = require("$:/plugins/flibbles/relink/js/utils/rebuilder");
var defOperators = utils.getModulesByTypeAsHashmap('relinkdef', 'name');
exports.report = function(text, callback, options) {
// fnprocdef and macrodef have their own implementations of createDefinition
// They create a modifiable object from the respective rule matches.
var definition = this.createDefinition();
var setParseTreeNode = this.parse();
var context = this.parser.context = new VariableContext(this.parser.context, setParseTreeNode[0]);
// Parse set the pos pointer, but we don't want to skip the macro body.
this.parser.pos = this.matchRegExp.lastIndex;
var endMatch = getBodyMatch(this.parser.source, this.parser.pos, definition);
if (endMatch) {
definition.body = endMatch[2];
options.settings = context
for (var operator in defOperators) {
defOperators[operator].report(definition, callback, options);
}
this.parser.pos = endMatch.index + endMatch[0].length;
}
context.parameterFocus = false;
context.placeholderList = undefined;
};
exports.relink = function(text, fromTitle, toTitle, options) {
// fnprocdef and macrodef have their own implementations of createDefinition
// They create a modifiable object from the respective rule matches.
var definition = this.createDefinition();
var setParseTreeNode = this.parse(),
entry,
context = this.parser.context = new VariableContext(this.parser.context, setParseTreeNode[0]);
// Parse set the pos pointer, but we don't want to skip the macro body.
this.parser.pos = this.matchRegExp.lastIndex;
var endMatch = getBodyMatch(this.parser.source, this.parser.pos, definition);
if (endMatch) {
definition.body = endMatch[2];
options.settings = context;
for (var operator in defOperators) {
var result = defOperators[operator].relink(definition, fromTitle, toTitle, options);
if (result) {
entry = entry || {};
if (result.output) {
entry.output = true;
}
if (result.impossible) {
entry.impossible = true;
}
}
}
this.parser.pos = endMatch.index + endMatch[0].length;
if (entry && entry.output) {
entry.output = reassembleSignature(definition, this.match[0]) + endMatch[1] + definition.body + endMatch[0];
}
}
context.parameterFocus = false;
context.placeholderList = undefined;
return entry;
};
function reassembleSignature(definition, text) {
// Reconstruct the definition. Might be tricky because we need to preserve whitespace within the parameters.
var builder = new Rebuilder(text);
builder.add(definition.type, 1, text.search(/[^\w\\]/));
var pos = builder.index;
builder.add(definition.name, skipWhitespace(text, pos), text.indexOf('(', pos));
pos = builder.index;
if (definition.parameters) {
builder.add(definition.parameters, skipWhitespace(text, pos+1), text.indexOf(')'));
}
return builder.results();
};
function skipWhitespace(text, pos) {
return text.substr(pos).search(/\S/)+pos;
};
// Return another match for the body, but tooled uniquely
// m[1] = whitespace before body
// m[2] = body
// m.index + m[0].length -> end of match
function getBodyMatch(text, pos, definition) {
var whitespace,
valueRegExp;
if (definition.multiline) {
valueRegExp = new RegExp("\\r?\\n[^\\S\\n\\r]*\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(definition.name) + ")?(?:\\r?\\n|$)", "mg");
whitespace = '';
} else {
valueRegExp = /(?:\r?\n|$)/mg;
var newPos = $tw.utils.skipWhiteSpace(text, pos);
whitespace = text.substring(pos, newPos);
pos = newPos;
}
valueRegExp.lastIndex = pos;
var match = valueRegExp.exec(text);
if (match) {
match[1] = whitespace;
match[2] = text.substring(pos, match.index);
}
return match;
};

View File

@ -0,0 +1,3 @@
module-type: library
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/def.js
type: application/javascript

View File

@ -0,0 +1,60 @@
/*\
Takes care of relinking the bodies of definitions.
\*/
var utils = require("$:/plugins/flibbles/relink/js/utils");
exports.name = "body";
exports.report = function(definition, callback, options) {
var handler = getHandler(definition.type, definition.name);
if (handler) {
var newOptions = Object.create(options);
var entry = handler.report(definition.body, function(title, blurb, style) {
var macroStr = '\\' + definition.type + ' ' + definition.name + '()';
if (blurb) {
macroStr += ' ' + blurb;
}
callback(title, macroStr, style);
}, newOptions);
}
};
exports.relink = function(definition, fromTitle, toTitle, options) {
var handler = getHandler(definition.type, definition.name);
var results;
if (handler) {
var newOptions = Object.create(options);
results = handler.relink(definition.body, fromTitle, toTitle, newOptions);
if (results && results.output) {
definition.body = results.output;
}
}
return results;
};
function getHandler(macroType, macroName) {
var type;
switch (macroType) {
case "function":
type = "filter";
break;
case "define":
/**This returns the handler to use for a macro
* By default, we treat them like wikitext, but Relink used to make
* little macros as placeholders. If we find one, we must return
* the correct handler for what that placeholder represented.
*/
var placeholder = /^relink-(?:(\w+)-)?\d+$/.exec(macroName);
// normal macro or special placeholder?
if (placeholder) {
type = placeholder[1] || 'title';
break;
}
default:
type = 'wikitext';
}
return utils.getType(type);
};

View File

@ -0,0 +1,3 @@
module-type: relinkdef
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/def/body.js
type: application/javascript

View File

@ -0,0 +1,25 @@
exports.name = 'substitution';
var subHandler = require("$:/plugins/flibbles/relink/js/utils/substitution.js");
exports.report = function(definition, callback, options) {
if (definition.type === 'define') {
var options = Object.create(options);
options.noFilterSubstitution = true;
subHandler.report(definition.body, function(title, blurb, style) {
callback(title, '\\define ' + definition.name + '() ' + (blurb || ''), style);
}, options);
}
};
exports.relink = function(definition, fromTitle, toTitle, options) {
var results;
if (definition.type === "define") {
var options = Object.create(options);
options.noFilterSubstitution = true;
results = subHandler.relink(definition.body, fromTitle, toTitle, options);
if (results && results.output) {
definition.body = results.output;
}
}
return results;
};

View File

@ -0,0 +1,3 @@
module-type: relinkdef
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/def/substitution.js
type: application/javascript

View File

@ -23,9 +23,11 @@ exports.report = function(text, callback, options) {
filter = m[1], filter = m[1],
template = $tw.utils.trim(m[3]), template = $tw.utils.trim(m[3]),
append = template ? '||' + template + '}}}' : '}}}'; append = template ? '||' + template + '}}}' : '}}}';
filterHandler.report(filter, function(title, blurb) { var nestedOptions = Object.create(options);
callback(title, '{{{' + blurb + append); nestedOptions.settings = this.parser.context;
}, options); filterHandler.report(filter, function(title, blurb, style) {
callback(title, '{{{' + blurb + append, style);
}, nestedOptions);
if (template) { if (template) {
callback(template, '{{{' + $tw.utils.trim(filter).replace(/\r?\n/mg, ' ') + '||}}}'); callback(template, '{{{' + $tw.utils.trim(filter).replace(/\r?\n/mg, ' ') + '||}}}');
} }
@ -43,8 +45,9 @@ exports.relink = function(text, fromTitle, toTitle, options) {
entry = {}; entry = {};
parser.pos = this.matchRegExp.lastIndex; parser.pos = this.matchRegExp.lastIndex;
var modified = false; var modified = false;
var nestedOptions = Object.create(options);
var filterEntry = filterHandler.relink(filter, fromTitle, toTitle, options); nestedOptions.settings = this.parser.context;
var filterEntry = filterHandler.relink(filter, fromTitle, toTitle, nestedOptions);
if (filterEntry !== undefined) { if (filterEntry !== undefined) {
if (filterEntry.output) { if (filterEntry.output) {
filter = filterEntry.output; filter = filterEntry.output;

View File

@ -0,0 +1,21 @@
/*\
module-type: relinkwikitextrule
Handles pragma function/procedure/widget definitions.
\*/
// We inherit from DefRule
var DefRule = require('./def.js');
$tw.utils.extend(exports, DefRule);
exports.name = "fnprocdef";
exports.createDefinition = function() {
var m = this.match;
return {
type: m[1],
name: m[2],
parameters: m[4],
multiline: m[5]};
};

View File

@ -0,0 +1,3 @@
module-type: relinkwikitextrule
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/fnprocdef.js
type: application/javascript

View File

@ -21,8 +21,8 @@ exports.report = function(text, callback, options) {
var nestedOptions = Object.create(options); var nestedOptions = Object.create(options);
nestedOptions.settings = this.parser.context; nestedOptions.settings = this.parser.context;
for (var operator in htmlOperators) { for (var operator in htmlOperators) {
htmlOperators[operator].report(this.nextTag, this.parser, function(title, blurb) { htmlOperators[operator].report(this.nextTag, this.parser, function(title, blurb, style) {
callback(title, '<' + blurb + ' />'); callback(title, '<' + blurb + ' />', style);
}, nestedOptions); }, nestedOptions);
} }
this.parse(); this.parse();
@ -33,11 +33,12 @@ exports.relink = function(text, fromTitle, toTitle, options) {
widgetEntry.attributes = Object.create(null); widgetEntry.attributes = Object.create(null);
widgetEntry.element = this.nextTag.tag; widgetEntry.element = this.nextTag.tag;
var elem = this.nextTag; var elem = this.nextTag;
var originalTag = elem.tag;
var changed = false; var changed = false;
var nestedOptions = Object.create(options); var nestedOptions = Object.create(options);
nestedOptions.settings = this.parser.context; nestedOptions.settings = this.parser.context;
for (var operator in htmlOperators) { for (var operator in htmlOperators) {
var entry = htmlOperators[operator].relink(this.nextTag, this.parser, fromTitle, toTitle, nestedOptions); var entry = htmlOperators[operator].relink(elem, this.parser, fromTitle, toTitle, nestedOptions);
if (entry) { if (entry) {
if (entry.output) { if (entry.output) {
changed = true; changed = true;
@ -47,6 +48,11 @@ exports.relink = function(text, fromTitle, toTitle, options) {
} }
} }
} }
// We swap in the original tag in case it changed. We need the old tag
// to find the proper closing tag. Parsing must come after the htmlmodules
// because those might change the context for the inner body.
var newTag = elem.tag;
elem.tag = originalTag;
var tag = this.parse()[0]; var tag = this.parse()[0];
if (tag.children) { if (tag.children) {
for (var i = 0; i < tag.children.length; i++) { for (var i = 0; i < tag.children.length; i++) {
@ -61,6 +67,7 @@ exports.relink = function(text, fromTitle, toTitle, options) {
} }
if (changed) { if (changed) {
var builder = new Rebuilder(text, elem.start); var builder = new Rebuilder(text, elem.start);
builder.add(newTag, elem.start+1, getEndOfTag(elem, text));
for (var attributeName in elem.attributes) { for (var attributeName in elem.attributes) {
var attr = elem.attributes[attributeName]; var attr = elem.attributes[attributeName];
var quotedValue; var quotedValue;
@ -72,16 +79,8 @@ exports.relink = function(text, fromTitle, toTitle, options) {
var quote = relinkUtils.determineQuote(text, attr); var quote = relinkUtils.determineQuote(text, attr);
quotedValue = utils.wrapAttributeValue(attr.value, quote) quotedValue = utils.wrapAttributeValue(attr.value, quote)
if (quotedValue === undefined) { if (quotedValue === undefined) {
// The value was unquotable. We need to make
// a macro in order to replace it.
if (!options.placeholder) {
// but we can't...
widgetEntry.impossible = true; widgetEntry.impossible = true;
continue; continue;
} else {
var value = options.placeholder.getPlaceholderFor(attr.value,attr.handler)
quotedValue = "<<"+value+">>";
}
} }
break; break;
case 'indirect': case 'indirect':
@ -98,6 +97,18 @@ exports.relink = function(text, fromTitle, toTitle, options) {
} }
// Else If output isn't set, this wasn't ever changed // Else If output isn't set, this wasn't ever changed
break; break;
case 'substituted':
var ticIndex = attr.rawValue.lastIndexOf("`");
if (ticIndex < 0) {
quotedValue = "`" + attr.rawValue + "`";
} else if (ticIndex < attr.rawValue.length-1
&& attr.rawValue.indexOf("```") < 0) {
quotedValue = "```" + attr.rawValue + "```";
} else {
// We can't have a tic at the end; can't have triple tic.
widgetEntry.impossible = true;
}
break;
} }
var ptr = attr.start; var ptr = attr.start;
ptr = $tw.utils.skipWhiteSpace(text, ptr); ptr = $tw.utils.skipWhiteSpace(text, ptr);
@ -122,6 +133,12 @@ exports.relink = function(text, fromTitle, toTitle, options) {
} }
} }
} }
var closingTag = '</' + elem.tag + '>';
var startClosingTag = this.parser.pos - closingTag.length;
if (text.substring(startClosingTag, this.parser.pos) === closingTag) {
// Replace the closing tag in case the tag changed.
builder.add(newTag, startClosingTag + 2, this.parser.pos-1);
}
widgetEntry.output = builder.results(this.parser.pos); widgetEntry.output = builder.results(this.parser.pos);
} }
if (widgetEntry.output || widgetEntry.impossible) { if (widgetEntry.output || widgetEntry.impossible) {
@ -129,3 +146,10 @@ exports.relink = function(text, fromTitle, toTitle, options) {
} }
return undefined; return undefined;
}; };
function getEndOfTag(element, text) {
var regExp = /[^a-zA-Z\-\$\.]/g;
regExp.lastIndex = element.start+1;
var match = regExp.exec(text);
return match.index;
};

View File

@ -4,10 +4,14 @@ Handles all element attribute values. Most widget relinking happens here.
\*/ \*/
'use strict';
var relinkUtils = require('$:/plugins/flibbles/relink/js/utils.js'); var relinkUtils = require('$:/plugins/flibbles/relink/js/utils.js');
var utils = require('../utils.js');
var refHandler = relinkUtils.getType('reference'); var refHandler = relinkUtils.getType('reference');
var filterHandler = relinkUtils.getType('filter'); var filterHandler = relinkUtils.getType('filter');
var macrocall = require("$:/plugins/flibbles/relink/js/utils/macrocall.js"); var macrocall = require("$:/plugins/flibbles/relink/js/utils/macrocall.js");
var substitution = require("$:/plugins/flibbles/relink/js/utils/substitution.js");
var attributeOperators = relinkUtils.getModulesByTypeAsHashmap('relinkhtmlattributes', 'name'); var attributeOperators = relinkUtils.getModulesByTypeAsHashmap('relinkhtmlattributes', 'name');
exports.name = "attributes"; exports.name = "attributes";
@ -22,20 +26,22 @@ exports.report = function(element, parser, callback, options) {
if (nextEql < 0 || nextEql > attr.end) { if (nextEql < 0 || nextEql > attr.end) {
continue; continue;
} }
var entry;
switch (attr.type) { switch (attr.type) {
case "string": case "string":
for (var operatorName in attributeOperators) { for (var operatorName in attributeOperators) {
var operator = attributeOperators[operatorName]; var operator = attributeOperators[operatorName];
var handler = operator.getHandler(element, attr, options); var handler = operator.getHandler(element, attr, options);
if (handler) { if (handler) {
handler.report(attr.value, function(title, blurb) { handler.report(attr.value, function(title, blurb, style) {
if (operator.formBlurb) { if (operator.formBlurb) {
callback(title, operator.formBlurb(element, attr, blurb, options)); if (blurb) {
blurb = '"' + blurb + '"';
}
callback(title, operator.formBlurb(element, attr, blurb, options), style);
} else if (blurb) { } else if (blurb) {
callback(title, element.tag + ' ' + attributeName + '="' + blurb + '"'); callback(title, element.tag + ' ' + attributeName + '="' + blurb + '"', style);
} else { } else {
callback(title, element.tag + ' ' + attributeName); callback(title, element.tag + ' ' + attributeName, style);
} }
}, options); }, options);
break; break;
@ -43,21 +49,48 @@ exports.report = function(element, parser, callback, options) {
} }
break; break;
case "indirect": case "indirect":
entry = refHandler.report(attr.textReference, function(title, blurb) { refHandler.report(attr.textReference, function(title, blurb, style) {
callback(title, element.tag + ' ' + attributeName + '={{' + (blurb || '') + '}}'); callback(title, element.tag + ' ' + attributeName + '={{' + (blurb || '') + '}}', style);
}, options); }, options);
break; break;
case "filtered": case "filtered":
entry = filterHandler.report(attr.filter, function(title, blurb) { filterHandler.report(attr.filter, function(title, blurb, style) {
callback(title, element.tag + ' ' + attributeName + '={{{' + blurb + '}}}'); callback(title, element.tag + ' ' + attributeName + '={{{' + blurb + '}}}', style);
}, options); }, options);
break; break;
case "macro": case "macro":
var macro = attr.value; var macro = attr.value;
entry = macrocall.report(options.settings, macro, function(title, blurb) { macrocall.report(options.settings, macro, function(title, blurb, style) {
callback(title, element.tag + ' ' + attributeName + '=<<' + blurb + '>>'); callback(title, element.tag + ' ' + attributeName + '=<<' + blurb + '>>', style);
}, options); }, options);
break; break;
case "substituted":
substitution.report(attr.rawValue, function(title, blurb, style) {
callback(title, element.tag + ' ' + attributeName + '=`' + blurb + '`', style);
}, options);
for (var operatorName in attributeOperators) {
var operator = attributeOperators[operatorName];
var handler = operator.getHandler(element, attr, options);
if (handler) {
handler.report(attr.rawValue, function(title, blurb, style) {
// Only consider titles without substitutions.
if (!utils.containsPlaceholders(title)) {
blurb = (utils.containsPlaceholders(attr.rawValue) || blurb)? '`' + blurb + '`': '';
if (operator.formBlurb) {
blurb = operator.formBlurb(element, attr, blurb, options);
} else {
if (blurb) {
blurb = '=' + blurb;
}
blurb = element.tag + ' ' + attributeName + blurb;
}
callback(title, blurb, style);
}
}, options);
break;
}
}
break;
} }
} }
}; };
@ -74,8 +107,44 @@ exports.relink = function(element, parser, fromTitle, toTitle, options) {
attr.valueless = true; attr.valueless = true;
continue; continue;
} }
var entry; var entry = undefined;
switch (attr.type) { switch (attr.type) {
case 'substituted':
if (utils.containsPlaceholders(attr.rawValue)) {
var subEntry = substitution.relink(attr.rawValue, fromTitle, toTitle, options);
if (subEntry) {
if (subEntry.output) {
attr.rawValue = subEntry.output;
changed = true;
}
if (subEntry.impossible) {
impossible = true;
}
}
if (!utils.containsPlaceholders(fromTitle)) {
for (var operatorName in attributeOperators) {
var operator = attributeOperators[operatorName];
var handler = operator.getHandler(element, attr, options);
if (handler) {
entry = handler.relink(attr.rawValue, fromTitle, toTitle, options);
if (entry && entry.output) {
if (utils.containsPlaceholders(toTitle)) {
// If we relinked, but the toTitle can't be in
// a substitution, then we must fail instead.
entry.impossible = true;
} else {
attr.rawValue = entry.output;
attr.handler = handler.name;
changed = true;
}
}
}
}
}
break;
}
// no break. turn it into a string and try to work with it
attr.value = attr.rawValue;
case 'string': case 'string':
for (var operatorName in attributeOperators) { for (var operatorName in attributeOperators) {
var operator = attributeOperators[operatorName]; var operator = attributeOperators[operatorName];
@ -83,11 +152,13 @@ exports.relink = function(element, parser, fromTitle, toTitle, options) {
if (handler) { if (handler) {
entry = handler.relink(attr.value, fromTitle, toTitle, options); entry = handler.relink(attr.value, fromTitle, toTitle, options);
if (entry && entry.output) { if (entry && entry.output) {
attr.oldValue = attr.value;
attr.value = entry.output; attr.value = entry.output;
attr.handler = handler.name; attr.handler = handler.name;
changed = true; changed = true;
// Change it into a string if this was a substitution that had no substitutions
attr.type = 'string';
} }
break;
} }
} }
break; break;
@ -109,10 +180,11 @@ exports.relink = function(element, parser, fromTitle, toTitle, options) {
var macro = attr.value; var macro = attr.value;
entry = macrocall.relink(options.settings, macro, parser.source, fromTitle, toTitle, false, options); entry = macrocall.relink(options.settings, macro, parser.source, fromTitle, toTitle, false, options);
if (entry && entry.output) { if (entry && entry.output) {
attr.output = macrocall.reassemble(entry.output, parser.source, options); attr.output = macrocall.reassemble(entry, parser.source, options);
attr.value = entry.output; attr.value = entry.output;
changed = true; changed = true;
} }
break;
} }
if (entry && entry.impossible) { if (entry && entry.impossible) {
impossible = true; impossible = true;

View File

@ -20,7 +20,7 @@ exports.formBlurb = function(element, attribute, blurb, options) {
var nameAttr = element.attributes["$name"]; var nameAttr = element.attributes["$name"];
var newBlurb = '<' + nameAttr.value + ' ' + attribute.name; var newBlurb = '<' + nameAttr.value + ' ' + attribute.name;
if (blurb) { if (blurb) {
newBlurb += '="' + blurb + '"'; newBlurb += '=' + blurb;
} }
return newBlurb; return newBlurb;
}; };

View File

@ -0,0 +1,31 @@
/*\
Handles replacement in $action-sendmessage widgets
\*/
exports.name = "sendmessage";
exports.getHandler = function(element, attribute, options) {
if (element.tag === "$action-sendmessage"
&& attribute.name[0] !== "$") {
var messageAttr = element.attributes['$message'];
if (messageAttr) {
var regexp = options.settings.getConfig("messages")[messageAttr.value];
if (regexp) {
var results = regexp.exec(attribute.name);
if (results && results[0] === attribute.name) {
return options.settings.getFields()[results[1]];
}
}
}
}
};
exports.formBlurb = function(element, attribute, blurb, options) {
var messageAttr = element.attributes['$message'];
var newBlurb = '$action-sendmessage ' + messageAttr.value + ' ' + attribute.name;
if (blurb) {
newBlurb += '=' + blurb;
}
return newBlurb;
};

View File

@ -0,0 +1,3 @@
module-type: relinkhtmlattributes
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/html/attributes/sendmessage.js
type: application/javascript

View File

@ -0,0 +1,39 @@
/*\
Handles replacement in $transclude widgets
\*/
exports.name = "transclude";
exports.getHandler = function(element, attribute, options) {
if (element.tag === "$transclude") {
var name = attribute.name;
if (name[0] === '$') {
if (name[1] === '$') {
name = name.substr(1);
} else {
// This is a reserved attribute
return;
}
}
var nameAttr = element.attributes["$variable"];
if (nameAttr) {
var setting = options.settings.getMacro(nameAttr.oldValue || nameAttr.value);
return setting && setting[name];
}
}
};
exports.formBlurb = function(element, attribute, blurb, options) {
var nameAttr = element.attributes["$variable"];
var name = attribute.name;
if (name[0] === '$') {
name = name.substr(1);
}
var newBlurb = '<' + nameAttr.value + ' ' + name;
if (blurb) {
newBlurb += '=' + blurb;
}
return newBlurb;
};

View File

@ -0,0 +1,3 @@
module-type: relinkhtmlattributes
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/html/attributes/transclude.js
type: application/javascript

View File

@ -0,0 +1,36 @@
/*\
Handles replacement in $macrocall widgets
\*/
exports.name = "parameters";
exports.report = function(element, parser, callback, options) {
if (element.tag === "$parameters") {
processParameters(element, parser, options);
}
};
exports.relink = function(element, parser, fromTitle, toTitle, options) {
if (element.tag === "$parameters") {
processParameters(element, parser, options);
}
};
function processParameters(element, parser, options) {
var attributes = element.orderedAttributes;
var index = 0;
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i].name;
if (attribute[0] == '$') {
if (attribute[1] == '$') {
attribute = attribute.substr(1);
} else {
continue;
}
}
parser.context.addParameter(attribute);
++index;
}
};

View File

@ -0,0 +1,3 @@
module-type: relinkhtml
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/html/parameters.js
type: application/javascript

View File

@ -52,11 +52,11 @@ exports.relink = function(text, fromTitle, toTitle, options) {
skipSource = false, skipSource = false,
imageEntry; imageEntry;
if (this.nextImage.attributes.source.value === fromTitle && !canBePretty(toTitle, this.nextImage.attributes.tooltip)) { if (this.nextImage.attributes.source.value === fromTitle && !canBePretty(toTitle, this.nextImage.attributes.tooltip)) {
if (this.parser.context.allowWidgets() && (utils.wrapAttributeValue(toTitle) || options.placeholder)) { if (this.parser.context.allowWidgets() && utils.wrapAttributeValue(toTitle)) {
makeWidget = true; makeWidget = true;
builder.add("<$image", ptr, ptr+4); builder.add("<$image", ptr, ptr+4);
} else { } else {
// We won't be able to make a placeholder to replace // We won't be able to make a wdget to replace
// the source attribute. We check now so we don't // the source attribute. We check now so we don't
// prematurely convert into a widget. // prematurely convert into a widget.
// Keep going in case other attributes need replacing. // Keep going in case other attributes need replacing.
@ -89,8 +89,7 @@ exports.relink = function(text, fromTitle, toTitle, options) {
if (makeWidget) { if (makeWidget) {
var quotedValue = utils.wrapAttributeValue(toTitle); var quotedValue = utils.wrapAttributeValue(toTitle);
if (quotedValue === undefined) { if (quotedValue === undefined) {
var key = options.placeholder.getPlaceholderFor(toTitle); builder.impossible = true;
builder.add("source=<<"+key+">>", ptr, ptr+fromTitle.length);
} else { } else {
builder.add("source="+quotedValue, ptr, ptr+fromTitle.length); builder.add("source="+quotedValue, ptr, ptr+fromTitle.length);
} }
@ -138,22 +137,22 @@ function reportAttribute(parser, attribute, callback, options) {
} else if (attribute.type === "indirect") { } else if (attribute.type === "indirect") {
ptr = text.indexOf('{{', ptr); ptr = text.indexOf('{{', ptr);
var end = ptr + attribute.textReference.length + 4; var end = ptr + attribute.textReference.length + 4;
refHandler.report(attribute.textReference, function(title, blurb) { refHandler.report(attribute.textReference, function(title, blurb, style) {
callback(title, '[img ' + attribute.name + '={{' + (blurb || '') + '}}]'); callback(title, '[img ' + attribute.name + '={{' + (blurb || '') + '}}]', style);
}, options); }, options);
} else if (attribute.type === "filtered") { } else if (attribute.type === "filtered") {
ptr = text.indexOf('{{{', ptr); ptr = text.indexOf('{{{', ptr);
var end = ptr + attribute.filter.length + 6; var end = ptr + attribute.filter.length + 6;
filterHandler.report(attribute.filter, function(title, blurb) { filterHandler.report(attribute.filter, function(title, blurb, style) {
callback(title, '[img ' + attribute.name + '={{{' + blurb + '}}}]'); callback(title, '[img ' + attribute.name + '={{{' + blurb + '}}}]', style);
}, options); }, options);
} else if (attribute.type === "macro") { } else if (attribute.type === "macro") {
ptr = text.indexOf("<<", ptr); ptr = text.indexOf("<<", ptr);
var end = attribute.value.end; var end = attribute.value.end;
var macro = attribute.value; var macro = attribute.value;
oldValue = attribute.value; var oldValue = attribute.value;
macrocall.reportAttribute(parser, macro, function(title, blurb) { macrocall.reportAttribute(parser, macro, function(title, blurb, style) {
callback(title, '[img ' + attribute.name + '=' + blurb + ']'); callback(title, '[img ' + attribute.name + '=' + blurb + ']', style);
}, options); }, options);
} }
return end; return end;
@ -199,7 +198,7 @@ function relinkAttribute(parser, attribute, builder, fromTitle, toTitle, options
ptr = text.indexOf("<<", ptr); ptr = text.indexOf("<<", ptr);
var end = attribute.value.end; var end = attribute.value.end;
var macro = attribute.value; var macro = attribute.value;
oldValue = attribute.value; var oldValue = attribute.value;
var macroEntry = macrocall.relinkAttribute(parser, macro, text, fromTitle, toTitle, options); var macroEntry = macrocall.relinkAttribute(parser, macro, text, fromTitle, toTitle, options);
if (macroEntry !== undefined) { if (macroEntry !== undefined) {
if (macroEntry.impossible) { if (macroEntry.impossible) {

View File

@ -16,17 +16,17 @@ exports.report = function(text, callback, options) {
// This moves the pos for us // This moves the pos for us
var parseTree = this.parse(); var parseTree = this.parse();
var filter = parseTree[0].attributes.filter.value || ''; var filter = parseTree[0].attributes.filter.value || '';
filterRelinker.report(filter, function(title, blurb) { filterRelinker.report(filter, function(title, blurb, style) {
if (blurb) { if (blurb) {
blurb = '\\import ' + blurb; blurb = '\\import ' + blurb;
} else { } else {
blurb = '\\import'; blurb = '\\import';
} }
callback(title, blurb); callback(title, blurb, style);
}, options); }, options);
// Before we go, we need to actually import the variables // Before we go, we need to actually import the variables
// it's calling for, and any /relink pragma // it's calling for, and any /relink pragma
this.parser.context = new ImportContext(options.wiki, this.parser.context, filter); options.settings = this.parser.context = new ImportContext(options.wiki, this.parser.context, filter);
}; };
exports.relink = function(text, fromTitle, toTitle, options) { exports.relink = function(text, fromTitle, toTitle, options) {
@ -44,7 +44,7 @@ exports.relink = function(text, fromTitle, toTitle, options) {
// Before we go, we need to actually import the variables // Before we go, we need to actually import the variables
// it's calling for, and any /relink pragma // it's calling for, and any /relink pragma
this.parser.context = new ImportContext(options.wiki, this.parser.context, filter); options.settings = this.parser.context = new ImportContext(options.wiki, this.parser.context, filter);
return entry; return entry;
}; };

View File

@ -23,22 +23,20 @@ exports.report = function(text, callback, options) {
exports.relink = function(text, fromTitle, toTitle, options) { exports.relink = function(text, fromTitle, toTitle, options) {
var macroInfo = getInfoFromRule(this); var macroInfo = getInfoFromRule(this);
var managedMacro = this.parser.context.getMacro(macroInfo.name);
this.parser.pos = macroInfo.end; this.parser.pos = macroInfo.end;
if (!managedMacro) {
// We don't manage this macro. Bye.
return undefined;
}
var mayBeWidget = this.parser.context.allowWidgets(); var mayBeWidget = this.parser.context.allowWidgets();
var names = getParamNames(this.parser, macroInfo.name, macroInfo.params, options); var names = getParamNames(this.parser, macroInfo.name, macroInfo.params, options);
if (names === undefined) { if (names === undefined) {
// Needed the definition, and couldn't find it. So if a single // Needed the definition, and couldn't find it. So if a single
// parameter needs to placeholder, just fail. // parameter doesn't work, just fail.
mayBeWidget = false; mayBeWidget = false;
} }
var entry = macrocall.relink(this.parser.context, macroInfo, text, fromTitle, toTitle, mayBeWidget, options); var entry = macrocall.relink(this.parser.context, macroInfo, text, fromTitle, toTitle, mayBeWidget, options);
if (entry && entry.output) { if (entry && entry.output) {
entry.output = macroToString(entry.output, text, names, options); entry.output = macroToString(entry, text, names, this.parser, options);
if (entry.output === undefined) {
entry.impossible = true;
}
} }
return entry; return entry;
}; };
@ -51,7 +49,7 @@ exports.relink = function(text, fromTitle, toTitle, options) {
exports.relinkAttribute = function(parser, macro, text, fromTitle, toTitle, options) { exports.relinkAttribute = function(parser, macro, text, fromTitle, toTitle, options) {
var entry = macrocall.relink(parser.context, macro, text, fromTitle, toTitle, false, options); var entry = macrocall.relink(parser.context, macro, text, fromTitle, toTitle, false, options);
if (entry && entry.output) { if (entry && entry.output) {
entry.output = macrocall.reassemble(entry.output, text, options); entry.output = macrocall.reassemble(entry, text, options);
} }
return entry; return entry;
}; };
@ -60,8 +58,8 @@ exports.relinkAttribute = function(parser, macro, text, fromTitle, toTitle, opti
* Kept for backward compatibility reasons * Kept for backward compatibility reasons
*/ */
exports.reportAttribute = function(parser, macro, callback, options) { exports.reportAttribute = function(parser, macro, callback, options) {
macrocall.report(parser.context, macro, function(title, blurb) { macrocall.report(parser.context, macro, function(title, blurb, style) {
callback(title, "<<" + blurb + ">>"); callback(title, "<<" + blurb + ">>", style);
}, options); }, options);
}; };
@ -79,12 +77,33 @@ function getInfoFromRule(rule) {
}; };
macroInfo.params = parseParams(match[2], offset+macroInfo.start); macroInfo.params = parseParams(match[2], offset+macroInfo.start);
} }
// post v5.3.0 changed it so name and param aren't used, but we still use
// them. Maybe I should migrate so that I don't either, and that it's
// orderedAttributes and $variable that I use.
if (macroInfo.name === undefined) {
macroInfo.name = macroInfo.attributes["$variable"].value;
macroInfo.params = macroInfo.orderedAttributes.slice(1);
var index = 0;
for (var i = 0; i < macroInfo.params.length; i++) {
var param = macroInfo.params[i];
if (param.name === index.toString()) {
// Swap out the param with one that doesn't have a name.
macroInfo.params[i] = {
start: param.start,
end: param.end,
type: param.type,
value: param.value
};
index++;
}
}
}
return macroInfo; return macroInfo;
}; };
function mustBeAWidget(macro) { function mustBeAWidget(macro) {
for (var i = 0; i < macro.params.length; i++) { for (var i = 0; i < macro.params.length; i++) {
if (macro.params[i].type === "macro") { if (macrocall.wrapParameterValue(macro.params[i].value) === undefined) {
return true; return true;
} }
} }
@ -95,8 +114,21 @@ function mustBeAWidget(macro) {
* it was parsed from, returns a new macro that maintains any syntactic * it was parsed from, returns a new macro that maintains any syntactic
* structuring. * structuring.
*/ */
function macroToString(macro, text, names, options) { function macroToString(entry, text, names, parser, options) {
if (mustBeAWidget(macro)) { var macro = entry.output;
if (mustBeAWidget(macro) && parser.context.allowWidgets()) {
var widgetString = macroToWidgetString(macro, names);
if (widgetString) {
// It worked! return it.
return widgetString;
}
entry.impossible = true;
// Otherwise continue on and try macrocall anyways, despite failutes.
}
return macrocall.reassemble(entry, text, options);
};
function macroToWidgetString(macro, names) {
var attrs = []; var attrs = [];
for (var i = 0; i < macro.params.length; i++) { for (var i = 0; i < macro.params.length; i++) {
var p = macro.params[i]; var p = macro.params[i];
@ -106,12 +138,25 @@ function macroToString(macro, text, names, options) {
} else { } else {
val = utils.wrapAttributeValue(p.value); val = utils.wrapAttributeValue(p.value);
} }
attrs.push(" "+names[i]+"="+val); if (val !== undefined) {
var name = p.name;
if (name === undefined) {
if (names === undefined) {
// Oops. We've got to give up here. We can't resolve
// the name of one of the parameters.
return undefined;
} else {
name = names[i];
}
}
attrs.push(" "+name+"="+val);
} else {
// Oops. There's an attribute that can't be quoted. We need
// to abort.
return undefined;
}
} }
return "<$macrocall $name="+utils.wrapAttributeValue(macro.name)+attrs.join('')+"/>"; return "<$macrocall $name="+utils.wrapAttributeValue(macro.name)+attrs.join('')+"/>";
} else {
return macrocall.reassemble(macro, text, options);
}
}; };
function getParamNames(parser, macroName, params, options) { function getParamNames(parser, macroName, params, options) {

View File

@ -1,103 +1,24 @@
/*\ /*\
module-type: relinkwikitextrule module-type: relinkwikitextrule
Handles pragma macro definitions. Except we only update placeholder macros Handles pragma macro definitions.
that we may have previously install. We may also update placeholder macros that we may have previously installed.
\define relink-?() Tough title \define relink-?() Tough title
\*/ \*/
var utils = require("$:/plugins/flibbles/relink/js/utils"); // We inherit from DefRule
var VariableContext = utils.getContext('variable'); var DefRule = require('./def.js');
$tw.utils.extend(exports, DefRule);
exports.name = "macrodef"; exports.name = "macrodef";
exports.report = function(text, callback, options) { exports.createDefinition = function() {
var setParseTreeNode = this.parse(), var m = this.match;
m = this.match, return {
name = m[1]; type: "define",
this.parser.context = new VariableContext(this.parser.context, setParseTreeNode[0]); name: m[1],
// Parse set the pos pointer, but we don't want to skip the macro body. parameters: m[2],
this.parser.pos = this.matchRegExp.lastIndex; multiline: m[3]};
var endMatch = getBodyMatch(text, this.parser.pos, m[3]);
if (endMatch) {
var value = endMatch[2],
handler = utils.getType(getActiveType(name, m[2]) || 'wikitext');
if (handler) {
var entry = handler.report(value, function(title, blurb) {
var macroStr = '\\define ' + name + '()';
if (blurb) {
macroStr += ' ' + blurb;
}
callback(title, macroStr);
}, options);
}
this.parser.pos = endMatch.index + endMatch[0].length;
}
};
exports.relink = function(text, fromTitle, toTitle, options) {
var setParseTreeNode = this.parse(),
entry,
m = this.match,
name = m[1],
params = m[2],
multiline = m[3];
this.parser.context = new VariableContext(this.parser.context, setParseTreeNode[0]);
// Parse set the pos pointer, but we don't want to skip the macro body.
this.parser.pos = this.matchRegExp.lastIndex;
var endMatch = getBodyMatch(text, this.parser.pos, multiline);
if (endMatch) {
var value = endMatch[2],
type = getActiveType(name, params),
handler = utils.getType(type || 'wikitext');
if (handler) {
// If this is an active relink placeholder, then let's remember it
if (type && options.placeholder) {
options.placeholder.registerExisting(name, value);
}
// Relink the contents
entry = handler.relink(value, fromTitle, toTitle, options);
if (entry && entry.output) {
entry.output = m[0] + endMatch[1] + entry.output + endMatch[0];
}
}
this.parser.pos = endMatch.index + endMatch[0].length;
}
return entry;
};
// Return another match for the body, but tooled uniquely
// m[1] = whitespace before body
// m[2] = body
// m.index + m[0].length -> end of match
function getBodyMatch(text, pos, isMultiline) {
var whitespace,
valueRegExp;
if (isMultiline) {
valueRegExp = /\r?\n\\end[^\S\n\r]*(?:\r?\n|$)/mg;
whitespace = '';
} else {
valueRegExp = /(?:\r?\n|$)/mg;
var newPos = $tw.utils.skipWhiteSpace(text, pos);
whitespace = text.substring(pos, newPos);
pos = newPos;
}
valueRegExp.lastIndex = pos;
var match = valueRegExp.exec(text);
if (match) {
match[1] = whitespace;
match[2] = text.substring(pos, match.index);
}
return match;
};
function getActiveType(macroName, parameters) {
var placeholder = /^relink-(?:(\w+)-)?\d+$/.exec(macroName);
// normal macro or special placeholder?
if (placeholder && parameters === '') {
return placeholder[1] || 'title';
}
return undefined;
}; };

View File

@ -0,0 +1,28 @@
/*\
module-type: relinkwikitextrule
Handles parameters pragma.
\parameters(...)
\*/
exports.name = "parameters";
exports.report = function(text, callback, options) {
operate(this, options);
};
exports.relink = function(text, fromTitle, toTitle, options) {
operate(this, options);
};
function operate(rule, options) {
var parser = rule.parser;
var parseTreeNode = rule.parse();
var attributes = parseTreeNode[0].orderedAttributes;
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i].name;
parser.context.addParameter(attribute);
}
};

View File

@ -0,0 +1,3 @@
module-type: relinkwikitextrule
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/parameters.js
type: application/javascript

View File

@ -61,8 +61,8 @@ exports.relink = function(text, fromTitle, toTitle, options) {
function reportCite(parser, delimeter) { function reportCite(parser, delimeter) {
var callback = parser.callback; var callback = parser.callback;
try { try {
parser.callback = function(title, blurb) { parser.callback = function(title, blurb, style) {
return callback(title, delimeter + " " + blurb); return callback(title, delimeter + " " + blurb, style);
}; };
parser.parseInlineRun(/(\r?\n)/mg); parser.parseInlineRun(/(\r?\n)/mg);
} finally { } finally {

View File

@ -10,13 +10,14 @@ It takes care of providing its own relink and report rules.
var utils = require('$:/plugins/flibbles/relink/js/utils.js'); var utils = require('$:/plugins/flibbles/relink/js/utils.js');
var language = require('$:/plugins/flibbles/relink/js/language.js'); var language = require('$:/plugins/flibbles/relink/js/language.js');
var pragmaOperators = utils.getModulesByTypeAsHashmap('relinkpragma', 'name');
exports.name = "relink"; exports.name = "relink";
exports.types = {pragma: true}; exports.types = {pragma: true};
exports.init = function(parser) { exports.init = function(parser) {
this.parser = parser; this.parser = parser;
this.matchRegExp = /^\\relink[^\S\n]+([^(\s]+)([^\r\n]*)(\r?\n)?/mg; this.matchRegExp = /^\\relink[^\S\r\n]+([^(\s]+)([^\r\n]*)(\r?\n)?/mg;
}; };
/**This makes the widget that the macro library will later parse to determine /**This makes the widget that the macro library will later parse to determine
@ -32,7 +33,7 @@ exports.parse = function() {
var error = undefined; var error = undefined;
var rtn = []; var rtn = [];
var self = this; var self = this;
this.interpretSettings(function(macro, parameter, type) { interpretSettings(this, function(macro, parameter, type) {
macroName = macro; macroName = macro;
if (type && !utils.getType(type)) { if (type && !utils.getType(type)) {
error = language.getString("text/plain", "Error/UnrecognizedType", error = language.getString("text/plain", "Error/UnrecognizedType",
@ -50,6 +51,7 @@ exports.parse = function() {
name: {type: "string", value: ""} name: {type: "string", value: ""}
}, },
children: [], children: [],
isRelinkDefinition: true,
isMacroDefinition: true, isMacroDefinition: true,
relink: relink}); relink: relink});
} }
@ -67,22 +69,40 @@ exports.parse = function() {
return rtn; return rtn;
}; };
exports.report = function(text, callback, options) {
operate(this, options);
for (var operator in pragmaOperators) {
pragmaOperators[operator].report(this, callback, options);
}
};
exports.relink = function(text, fromTitle, toTitle, options) { exports.relink = function(text, fromTitle, toTitle, options) {
var parser = this.parser; operate(this, options);
var entry;
for (var operator in pragmaOperators) {
// Yes, this only handles one thing for now. I haven't bothered
// breaking up \relink into a modifiable type.
entry = pragmaOperators[operator].relink(this, fromTitle, toTitle, options);
}
return entry;
};
function operate(rule, options) {
var parser = rule.parser;
var currentTiddler = parser.context.widget.variables.currentTiddler.value; var currentTiddler = parser.context.widget.variables.currentTiddler.value;
parser.pos = this.matchRegExp.lastIndex; parser.pos = rule.matchRegExp.lastIndex;
this.interpretSettings(function(macro, parameter, type) { interpretSettings(rule, function(macro, parameter, type) {
options.settings.addSetting(parser.wiki, macro, parameter, type, currentTiddler); options.settings.addSetting(parser.wiki, macro, parameter, type, currentTiddler);
}); });
// Return nothing, because this rule is ignored by the parser // Return nothing, because this rule is ignored by the parser
return undefined; return undefined;
}; };
exports.interpretSettings = function(block) { function interpretSettings(rule, block) {
var paramString = this.match[2]; var paramString = rule.match[2];
if (paramString !== "") { if (paramString !== "") {
var macro = this.match[1]; var macro = rule.match[1];
var reParam = /\s*([A-Za-z0-9\-_]+)(?:\s*:\s*([^\s]+))?/mg; var reParam = /\s*([$A-Za-z0-9\-_]+)(?:\s*:\s*([^\s]+))?/mg;
var paramMatch = reParam.exec(paramString); var paramMatch = reParam.exec(paramString);
while (paramMatch) { while (paramMatch) {
var parameter = paramMatch[1]; var parameter = paramMatch[1];

View File

@ -29,8 +29,8 @@ exports.report = function(text, callback, options) {
this.parser.pos++; this.parser.pos++;
// Parse the caption // Parse the caption
var oldCallback = this.parser.callback; var oldCallback = this.parser.callback;
this.parser.callback = function(title, blurb) { this.parser.callback = function(title, blurb, style) {
callback(title, '|' + blurb + '|c'); callback(title, '|' + blurb + '|c', style);
}; };
try { try {
this.parser.parseInlineRun(rowTermRegExp,{eatTerminator: true}); this.parser.parseInlineRun(rowTermRegExp,{eatTerminator: true});
@ -148,8 +148,8 @@ var processRow = function(rowType, callback) {
// Parse the cell // Parse the cell
var oldCallback = this.parser.callback; var oldCallback = this.parser.callback;
var reports = []; var reports = [];
this.parser.callback = function(title, blurb) { this.parser.callback = function(title, blurb, style) {
reports.push(title, blurb); reports.push(title, blurb, style);
}; };
try { try {
var output = this.parser.parseInlineRun(cellTermRegExp,{eatTerminator: true}); var output = this.parser.parseInlineRun(cellTermRegExp,{eatTerminator: true});
@ -159,8 +159,8 @@ var processRow = function(rowType, callback) {
if(this.parser.source.substr(this.parser.pos - 2,1) === " ") { // spaceRight if(this.parser.source.substr(this.parser.pos - 2,1) === " ") { // spaceRight
suffix = ' |'; suffix = ' |';
} }
for (var i = 0; i < reports.length; i += 2) { for (var i = 0; i < reports.length; i += 3) {
callback(reports[i], prefix + reports[i+1] + suffix + rowType); callback(reports[i], prefix + reports[i+1] + suffix + rowType, reports[i+2]);
} }
} finally { } finally {
this.parser.callback = oldCallback; this.parser.callback = oldCallback;

View File

@ -11,6 +11,7 @@ This renames both the tiddler and the template field.
\*/ \*/
var refHandler = require("$:/plugins/flibbles/relink/js/fieldtypes/reference"); var refHandler = require("$:/plugins/flibbles/relink/js/fieldtypes/reference");
var titleHandler = require("$:/plugins/flibbles/relink/js/fieldtypes/title");
var utils = require("./utils.js"); var utils = require("./utils.js");
var relinkUtils = require('$:/plugins/flibbles/relink/js/utils.js'); var relinkUtils = require('$:/plugins/flibbles/relink/js/utils.js');
var referenceOperators = relinkUtils.getModulesByTypeAsHashmap('relinkreference', 'name'); var referenceOperators = relinkUtils.getModulesByTypeAsHashmap('relinkreference', 'name');
@ -20,20 +21,28 @@ exports.name = ['transcludeinline', 'transcludeblock'];
exports.report = function(text, callback, options) { exports.report = function(text, callback, options) {
var m = this.match, var m = this.match,
refString = $tw.utils.trim(m[1]), refString = $tw.utils.trim(m[1]),
ref = parseTextReference(refString); ref = parseTextReference(refString),
template = $tw.utils.trim(m[2]); template = $tw.utils.trim(m[2]),
params = m[3];
for (var operator in referenceOperators) { for (var operator in referenceOperators) {
referenceOperators[operator].report(ref, function(title, blurb) { referenceOperators[operator].report(ref, function(title, blurb, style) {
blurb = blurb || ""; blurb = blurb || "";
if (template) { if (template) {
blurb += '||' + template; blurb += '||' + template;
} }
callback(title, "{{" + blurb + "}}"); if (params) {
blurb += '|' + params;
}
callback(title, "{{" + blurb + "}}", style);
}, options); }, options);
} }
if (template) { titleHandler.report(template, function(title, blurb, style) {
callback(template, '{{' + refString + '||}}'); var templateBlurb = refString + '||';
if (params) {
templateBlurb += '|' + params;
} }
callback(template, '{{' + templateBlurb + '}}', style);
}, options);
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
}; };
@ -41,6 +50,7 @@ exports.relink = function(text, fromTitle, toTitle, options) {
var m = this.match, var m = this.match,
reference = parseTextReference(m[1]), reference = parseTextReference(m[1]),
template = m[2], template = m[2],
params = m[3],
entry = undefined, entry = undefined,
impossible = false, impossible = false,
modified = false; modified = false;
@ -57,12 +67,18 @@ exports.relink = function(text, fromTitle, toTitle, options) {
} }
} }
} }
if ($tw.utils.trim(template) === fromTitle) { var templateEntry = titleHandler.relink($tw.utils.trim(template), fromTitle, toTitle, options);
if (templateEntry) {
if (templateEntry.impossible) {
impossible = true;
}
if (templateEntry.output) {
template = template.replace(fromTitle, toTitle); template = template.replace(fromTitle, toTitle);
modified = true; modified = true;
} }
}
if (modified) { if (modified) {
var output = this.makeTransclude(this.parser, reference, template); var output = this.makeTransclude(this.parser, reference, template, params);
if (output) { if (output) {
// Adding any newline that might have existed is // Adding any newline that might have existed is
// what allows this relink method to work for both // what allows this relink method to work for both
@ -82,7 +98,7 @@ exports.relink = function(text, fromTitle, toTitle, options) {
// I have my own because the core one is deficient for my needs. // I have my own because the core one is deficient for my needs.
function parseTextReference(textRef) { function parseTextReference(textRef) {
// Separate out the title, field name and/or JSON indices // Separate out the title, field name and/or JSON indices
var reTextRef = /^([\w\W]*?)(?:!!(\S[\w\W]*)|##(\S[\w\W]*))?$/g; var reTextRef = /^([\w\W]*?)(?:!!(\S[\w\W]*)|##(\S[\w\W]*))?$/g,
match = reTextRef.exec(textRef), match = reTextRef.exec(textRef),
result = {}; result = {};
if(match) { if(match) {
@ -100,7 +116,7 @@ function parseTextReference(textRef) {
/** This converts a reference and a template into a string representation /** This converts a reference and a template into a string representation
* of a transclude. * of a transclude.
*/ */
exports.makeTransclude = function(parser, reference, template) { exports.makeTransclude = function(parser, reference, template, params) {
var rtn; var rtn;
if (!canBePrettyTemplate(template)) { if (!canBePrettyTemplate(template)) {
var widget = utils.makeWidget(parser, '$transclude', { var widget = utils.makeWidget(parser, '$transclude', {
@ -117,14 +133,14 @@ exports.makeTransclude = function(parser, reference, template) {
var transclude; var transclude;
if (canBePrettyField(reference.field)) { if (canBePrettyField(reference.field)) {
var reducedRef = {field: reference.field, index: reference.index}; var reducedRef = {field: reference.field, index: reference.index};
transclude = prettyTransclude(reducedRef, template); transclude = prettyTransclude(reducedRef, template, params);
} else { } else {
transclude = utils.makeWidget(parser, "$transclude", {tiddler: $tw.utils.trim(reference.title), field: reference.field}); transclude = utils.makeWidget(parser, "$transclude", {tiddler: $tw.utils.trim(reference.title), field: reference.field});
} }
rtn = utils.makeWidget(parser, '$tiddler', {tiddler: $tw.utils.trim(reference.title)}, transclude); rtn = utils.makeWidget(parser, '$tiddler', {tiddler: $tw.utils.trim(reference.title)}, transclude);
} else { } else {
// This block takes care of 99% of all cases // This block takes care of 99% of all cases
rtn = prettyTransclude(reference, template); rtn = prettyTransclude(reference, template, params);
} }
return rtn; return rtn;
}; };
@ -141,7 +157,7 @@ function canBePrettyTemplate(value) {
return !value || (value.indexOf('}') < 0 && value.indexOf('{') < 0 && value.indexOf('|') < 0); return !value || (value.indexOf('}') < 0 && value.indexOf('{') < 0 && value.indexOf('|') < 0);
}; };
function prettyTransclude(textReference, template) { function prettyTransclude(textReference, template, params) {
if (typeof textReference !== "string") { if (typeof textReference !== "string") {
textReference = refHandler.toString(textReference); textReference = refHandler.toString(textReference);
} }
@ -149,8 +165,10 @@ function prettyTransclude(textReference, template) {
textReference = ''; textReference = '';
} }
if (template !== undefined) { if (template !== undefined) {
return "{{"+textReference+"||"+template+"}}"; textReference += "||" + template;
} else {
return "{{"+textReference+"}}";
} }
if (params) {
textReference += "|" + params;
}
return "{{"+textReference+"}}";
}; };

View File

@ -15,13 +15,9 @@ exports.makeWidget = function(parser, tag, attributes, body) {
if (value !== undefined) { if (value !== undefined) {
var quoted = exports.wrapAttributeValue(value); var quoted = exports.wrapAttributeValue(value);
if (!quoted) { if (!quoted) {
if (!parser.options.placeholder) {
// It's not possible to make this widget // It's not possible to make this widget
return undefined; return undefined;
} }
var category = getPlaceholderCategory(parser.context, tag, attr);
quoted = '<<' + parser.placeholder.getPlaceholderFor(value, category) + '>>';
}
string += ' ' + attr + '=' + quoted; string += ' ' + attr + '=' + quoted;
} }
} }
@ -33,23 +29,6 @@ exports.makeWidget = function(parser, tag, attributes, body) {
return string; return string;
}; };
function getPlaceholderCategory(context, tag, attribute) {
var element = context.getAttribute(tag);
var rule = element && element[attribute];
// titles go to relink-\d
// plaintext goes to relink-plaintext-\d
// because titles are way more common, also legacy
if (rule === undefined) {
return 'plaintext';
} else {
rule = rule.fields.text;
if (rule === 'title') {
rule = undefined;
}
return rule;
}
};
exports.makePrettylink = function(parser, title, caption) { exports.makePrettylink = function(parser, title, caption) {
var output; var output;
if (parser.context.allowPrettylinks() && canBePrettylink(title, caption)) { if (parser.context.allowPrettylinks() && canBePrettylink(title, caption)) {
@ -65,13 +44,6 @@ exports.makePrettylink = function(parser, title, caption) {
} }
} else if (exports.shorthandPrettylinksSupported(parser.wiki)) { } else if (exports.shorthandPrettylinksSupported(parser.wiki)) {
output = exports.makeWidget(parser, '$link', {to: title}); output = exports.makeWidget(parser, '$link', {to: title});
} else if (parser.context.allowWidgets() && parser.placeholder) {
// If we don't have a caption, we must resort to
// placeholders anyway to prevent link/caption desync
// from later relinks.
// It doesn't matter whether the tiddler is quotable.
var ph = parser.placeholder.getPlaceholderFor(title);
output = "<$link to=<<"+ph+">>><$text text=<<"+ph+">>/></$link>";
} }
return output; return output;
}; };
@ -105,6 +77,29 @@ function sanitizeCaption(parser, caption) {
} }
}; };
exports.containsPlaceholders = function(string) {
// Does it contain a variable placeholder?
if (/\$\(([^\)\$]+)\)\$/.test(string)) {
return true;
}
// Does it contain a filter placeholder?
var filterStart = string.indexOf("${");
if (filterStart >= 0 && string.indexOf("}$", filterStart+3) >= 0) {
return true;
}
// If no, then it's just a string.
return false;
};
var whitelist = ["", "'", '"', '"""'];
var choices = {
"": function(v) {return !/([\/\s<>"'`=])/.test(v) && v.length > 0; },
"'": function(v) {return v.indexOf("'") < 0; },
'"': function(v) {return v.indexOf('"') < 0; },
'"""': function(v) {return v.indexOf('"""') < 0 && v[v.length-1] != '"';},
};
var _backticksSupported;
/**Finds an appropriate quote mark for a given value. /**Finds an appropriate quote mark for a given value.
* *
*Tiddlywiki doesn't have escape characters for attribute values. Instead, *Tiddlywiki doesn't have escape characters for attribute values. Instead,
@ -115,13 +110,16 @@ function sanitizeCaption(parser, caption) {
* return: Returns the wrapped value, or undefined if it's impossible to wrap * return: Returns the wrapped value, or undefined if it's impossible to wrap
*/ */
exports.wrapAttributeValue = function(value, preference) { exports.wrapAttributeValue = function(value, preference) {
var whitelist = ["", "'", '"', '"""']; if (_backticksSupported === undefined) {
var choices = { var test = $tw.wiki.renderText("text/plain", "text/vnd.tiddlywiki", "<$link to=`test`/>");
"": function(v) {return !/([\/\s<>"'=])/.test(v) && v.length > 0; }, _backticksSupported = (test === "test");
"'": function(v) {return v.indexOf("'") < 0; }, if (_backticksSupported) {
'"': function(v) {return v.indexOf('"') < 0; }, // add in support for the backtick to the lists
'"""': function(v) {return v.indexOf('"""') < 0 && v[v.length-1] != '"';} whitelist.push('`', '```');
}; choices['`'] = function(v) {return v.indexOf('`') < 0 && !exports.containsPlaceholders(v); };
choices['```'] = function(v) {return v.indexOf('```') < 0 && v[v.length-1] != '`' && !exports.containsPlaceholders(v);};
}
}
if (choices[preference] && choices[preference](value)) { if (choices[preference] && choices[preference](value)) {
return wrap(value, preference); return wrap(value, preference);
} }
@ -141,7 +139,9 @@ function wrap(value, wrapper) {
"'": function(v) {return "'"+v+"'"; }, "'": function(v) {return "'"+v+"'"; },
'"': function(v) {return '"'+v+'"'; }, '"': function(v) {return '"'+v+'"'; },
'"""': function(v) {return '"""'+v+'"""'; }, '"""': function(v) {return '"""'+v+'"""'; },
"[[": function(v) {return "[["+v+"]]"; } "[[": function(v) {return "[["+v+"]]"; },
"`": function(v) {return '`'+v+'`'; },
'```': function(v) {return '```'+v+'```'; }
}; };
var chosen = wrappers[wrapper]; var chosen = wrappers[wrapper];
if (chosen) { if (chosen) {

View File

@ -0,0 +1,13 @@
/*\
module-type: relinkwikitextrule
Handles whitespace pragma
\*/
exports.name = "whitespace";
// We don't actually do anything, but we can't rely on
// the default behavior of moving to parser.pos.
// we have to forward past all the whitespace tokens.
exports.relink = exports.report = function() { this.parse(); }

View File

@ -0,0 +1,3 @@
module-type: relinkwikitextrule
title: $:/plugins/flibbles/relink/js/relinkoperations/text/wikitext/whitespace.js
type: application/javascript

View File

@ -12,10 +12,6 @@ exports.generate = function(attributes, tiddler, key) {
var data = utils.getType(tiddler.fields.text.trim()); var data = utils.getType(tiddler.fields.text.trim());
if (data) { if (data) {
data.source = tiddler.fields.title; data.source = tiddler.fields.title;
// Secret feature. You can access a config tiddler's
// fields from inside the fieldtype handler. Cool
// tricks can be done with this.
data.fields = tiddler.fields;
var elem = root(key); var elem = root(key);
var attr = key.substr(elem.length+1); var attr = key.substr(elem.length+1);
attributes[elem] = attributes[elem] || Object.create(null); attributes[elem] = attributes[elem] || Object.create(null);

View File

@ -0,0 +1,17 @@
/*\
Factory method for specifying system tiddlers which should not be treated
as text/vnd.tiddlywiki types, but for whatever reason, they don't specify
the type they really are. Sort of like how $:/DefaultTiddlers is actually
a filter.
\*/
exports.name = "exceptions";
exports.generate = function(exceptions, tiddler, title) {
var tiddlerType = tiddler.fields.text.trim();
if (tiddlerType) {
exceptions[title] = tiddlerType;
}
};

View File

@ -0,0 +1,4 @@
module-type: relinksetting
the type they really are. Sort of like how $: /DefaultTiddlers is actually
title: $:/plugins/flibbles/relink/js/settings/exceptions.js
type: application/javascript

View File

@ -12,10 +12,6 @@ exports.generate = function(fields, tiddler, name) {
var data = utils.getType(tiddler.fields.text.trim()); var data = utils.getType(tiddler.fields.text.trim());
if (data) { if (data) {
data.source = tiddler.fields.title; data.source = tiddler.fields.title;
// Secret feature. You can access a config tiddler's
// fields from inside the fieldtype handler. Cool
// tricks can be done with this.
data.fields = tiddler.fields;
fields[name] = data; fields[name] = data;
} }
}; };

View File

@ -12,10 +12,6 @@ exports.generate = function(macros, tiddler, key) {
var data = utils.getType(tiddler.fields.text.trim()); var data = utils.getType(tiddler.fields.text.trim());
if (data) { if (data) {
data.source = tiddler.fields.title; data.source = tiddler.fields.title;
// Secret feature. You can access a config tiddler's
// fields from inside the fieldtype handler. Cool
// tricks can be done with this.
data.fields = tiddler.fields;
// We take the last index, not the first, because macro // We take the last index, not the first, because macro
// parameters can't have slashes, but macroNames can. // parameters can't have slashes, but macroNames can.
var name = dir(key); var name = dir(key);
@ -33,4 +29,3 @@ function dir(string) {
return string.substr(0, index); return string.substr(0, index);
} }
} }

View File

@ -0,0 +1,11 @@
/*\
Factory method for creating the message regexp cache.
\*/
exports.name = "messages";
exports.generate = function(messages, tiddler, key) {
messages[key] = new RegExp(tiddler.fields.text.trim());
};

View File

@ -0,0 +1,3 @@
module-type: relinksetting
title: $:/plugins/flibbles/relink/js/settings/messages.js
type: application/javascript

View File

@ -12,10 +12,6 @@ exports.generate = function(operators, tiddler, key) {
var data = utils.getType(tiddler.fields.text.trim()); var data = utils.getType(tiddler.fields.text.trim());
if (data) { if (data) {
data.source = tiddler.fields.title; data.source = tiddler.fields.title;
// Secret feature. You can access a config tiddler's
// fields from inside the fieldtype handler. Cool
// tricks can be done with this.
data.fields = tiddler.fields;
var pair = key.split('/'); var pair = key.split('/');
var name = pair[0]; var name = pair[0];
data.key = key; data.key = key;

View File

@ -5,7 +5,7 @@ Utility methods for relink.
\*/ \*/
var macroFilter = "[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"; var macroFilter = "[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]] [all[shadows+tiddlers]tag[$:/tags/Global]!has[draft.of]]";
/**This works nearly identically to $tw.modules.getModulesByTypeAsHashmap /**This works nearly identically to $tw.modules.getModulesByTypeAsHashmap
* except that this also takes care of migrating V1 relink modules. * except that this also takes care of migrating V1 relink modules.
@ -25,7 +25,27 @@ exports.getModulesByTypeAsHashmap = function(moduleType, nameField) {
} }
} }
}); });
return results; // We've got to sort these so that behavior is consistent across different
// versions of TiddlyMap, whose module return order depends on version...
return sortModules(results);
};
function sortModules(moduleMap) {
var keys = Object.keys(moduleMap);
var sortedResults = Object.create(null);
keys.sort();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (moduleMap[key].after
&& moduleMap[key].after.some(function(requirement) { return !sortedResults[requirement]})) {
// Not all requirements have been met yet.
$tw.utils.pushTop(keys, key);
i--;
} else {
sortedResults[key] = moduleMap[key];
}
}
return sortedResults;
}; };
exports.getTiddlerRelinkReferences = function(wiki, title, context) { exports.getTiddlerRelinkReferences = function(wiki, title, context) {
@ -35,9 +55,9 @@ exports.getTiddlerRelinkReferences = function(wiki, title, context) {
if (tiddler) { if (tiddler) {
try { try {
for (var relinker in getRelinkOperators()) { for (var relinker in getRelinkOperators()) {
getRelinkOperators()[relinker].report(tiddler, function(title, blurb) { getRelinkOperators()[relinker].report(tiddler, function(title, blurb, style) {
references[title] = references[title] || []; references[title] = references[title] || [];
references[title].push(blurb || ''); references[title].push($tw.utils.extend({blurb: blurb || ''}, style));
}, options); }, options);
} }
} catch (e) { } catch (e) {
@ -160,6 +180,18 @@ exports.getDefaultType = function(wiki) {
return fieldTypes[defaultType] ? defaultType : "title"; return fieldTypes[defaultType] ? defaultType : "title";
}; };
exports.abridgeString = function(string, maxLength, truncLength) {
if (typeof string === "string") {
maxLength = maxLength || 3;
if (truncLength === undefined) {
truncLength = maxLength-3;
}
string = string.replace(/\s+/g, " ").trim();
return (string.length > maxLength)? string.substr(0, truncLength) + "..." : string;
}
return string;
};
exports.touchModifyField = function(wiki) { exports.touchModifyField = function(wiki) {
var tiddler = wiki.getTiddler("$:/config/flibbles/relink/touch-modify"); var tiddler = wiki.getTiddler("$:/config/flibbles/relink/touch-modify");
return tiddler && tiddler.fields.text.trim() === "yes"; return tiddler && tiddler.fields.text.trim() === "yes";

View File

@ -6,45 +6,13 @@ Methods for reporting and relinking macros. Behaves much like a fieldtype, excep
var utils = require('$:/plugins/flibbles/relink/js/utils.js'); var utils = require('$:/plugins/flibbles/relink/js/utils.js');
var Rebuilder = require("$:/plugins/flibbles/relink/js/utils/rebuilder"); var Rebuilder = require("$:/plugins/flibbles/relink/js/utils/rebuilder");
var macrocallOperators = utils.getModulesByTypeAsHashmap('relinkmacrocall', 'name');
// Error thrown when a macro's definition is needed, but can't be found.
function CannotFindMacroDef() {};
CannotFindMacroDef.prototype.impossible = true;
CannotFindMacroDef.prototype.name = "macroparam";
// Failed relinks due to missing definitions aren't reported for now.
// I may want to do something special later on.
CannotFindMacroDef.prototype.report = function() { return []; };
/** As in, report a macrocall invocation that is an html attribute. /** As in, report a macrocall invocation that is an html attribute.
* macro: must be a macro object.*/ * macro: must be a macro object.*/
exports.report = function(context, macro, callback, options) { exports.report = function(context, macro, callback, options) {
var managedMacro = context.getMacro(macro.name); for (var operator in macrocallOperators) {
if (!managedMacro) { macrocallOperators[operator].report(context, macro, callback, options);
// We don't manage this macro. Bye.
return undefined;
}
for (var managedArg in managedMacro) {
var index;
try {
index = getParamIndexWithinMacrocall(context, macro.name, managedArg, macro.params, options);
} catch (e) {
continue;
}
if (index < 0) {
// The argument was not supplied. Move on to next.
continue;
}
var param = macro.params[index];
var handler = managedMacro[managedArg];
var nestedOptions = Object.create(options);
nestedOptions.settings = context;
var entry = handler.report(param.value, function(title, blurb) {
var rtn = managedArg;
if (blurb) {
rtn += ': "' + blurb + '"';
}
callback(title, macro.name + ' ' + rtn);
}, nestedOptions);
} }
}; };
@ -57,171 +25,59 @@ exports.report = function(context, macro, callback, options) {
* to be converted. * to be converted.
*/ */
exports.relink = function(context, macro, text, fromTitle, toTitle, mayBeWidget, options) { exports.relink = function(context, macro, text, fromTitle, toTitle, mayBeWidget, options) {
var managedMacro = context.getMacro(macro.name); var entry;
var modified = false; for (var operator in macrocallOperators) {
if (!managedMacro) { var results = macrocallOperators[operator].relink(context, macro, text, fromTitle, toTitle, options);
// We don't manage this macro. Bye. if (results) {
return undefined; entry = entry || {};
if (results.impossible) {
entry.impossible = true;
} }
var outMacro = $tw.utils.extend({}, macro); if (results.output) {
var macroEntry = {}; macro = results.output;
outMacro.params = macro.params.slice(); entry.output = macro;
for (var managedArg in managedMacro) {
var index;
try {
index = getParamIndexWithinMacrocall(context, macro.name, managedArg, macro.params, options);
} catch (e) {
if (e instanceof CannotFindMacroDef) {
macroEntry.impossible = true;
continue;
} }
} }
if (index < 0) {
// this arg either was not supplied, or we can't find
// the definition, so we can't tie it to an anonymous
// argument. Either way, move on to the next.
continue;
} }
var param = macro.params[index]; return entry;
var handler = managedMacro[managedArg];
var nestedOptions = Object.create(options);
nestedOptions.settings = context;
var entry = handler.relink(param.value, fromTitle, toTitle, nestedOptions);
if (entry === undefined) {
continue;
}
// Macro parameters can only be string parameters, not
// indirect, or macro, or filtered
if (entry.impossible) {
macroEntry.impossible = true;
}
if (!entry.output) {
continue;
}
var quote = utils.determineQuote(text, param);
var quoted = wrapParameterValue(entry.output, quote);
var newParam = $tw.utils.extend({}, param);
if (quoted === undefined) {
if (!mayBeWidget || !options.placeholder) {
macroEntry.impossible = true;
continue;
}
var ph = options.placeholder.getPlaceholderFor(entry.output,handler.name);
newParam.newValue = "<<"+ph+">>";
newParam.type = "macro";
} else {
newParam.start = newParam.end - (newParam.value.length + (quote.length*2));
newParam.value = entry.output;
newParam.newValue = quoted;
}
outMacro.params[index] = newParam;
modified = true;
}
if (modified || macroEntry.impossible) {
if (modified) {
macroEntry.output = outMacro;
}
return macroEntry;
}
return undefined;
}; };
/**Converts the macro object into a string, includes the <<..>>. /**Converts the macro object into a string, includes the <<..>>.
* The text is the old text the macro was formed from. It's used to preserve * The text is the old text the macro was formed from. It's used to preserve
* whitespace. * whitespace.
*/ */
exports.reassemble = function(macro, text, options) { exports.reassemble = function(entry, text, options) {
var macro = entry.output;
var builder = new Rebuilder(text, macro.start); var builder = new Rebuilder(text, macro.start);
var varAttribute = macro.attributes && macro.attributes['$variable'];
if (varAttribute && varAttribute.value !== macro.name) {
// The name of the macro changed. Update it.
builder.add(varAttribute.value, macro.start + 2, macro.start + 2 + macro.name.length);
}
for (var i = 0; i < macro.params.length; i++) { for (var i = 0; i < macro.params.length; i++) {
var param = macro.params[i]; var param = macro.params[i];
if (param.newValue) { if (param.modified) {
builder.add(param.newValue, param.start, param.end); var newValue = exports.wrapParameterValue(param.value, param.quote);
if (newValue === undefined) {
entry.impossible = true;
} else {
builder.add(newValue, param.start, param.end);
}
} }
} }
return builder.results(macro.end); return builder.results(macro.end);
}; };
/** Returns -1 if param definitely isn't in macrocall.
*/
function getParamIndexWithinMacrocall(context, macroName, param, params, options) {
var index, i, anonsExist = false;
for (i = 0; i < params.length; i++) {
var name = params[i].name;
if (name === param) {
return i;
}
if (name === undefined) {
anonsExist = true;
}
}
if (!anonsExist) {
// If no anonymous parameters are present, and we didn't find
// it among the named ones, it must not be there.
return -1;
}
var expectedIndex = indexOfParameterDef(context, macroName, param, options);
// We've got to skip over all the named parameter instances.
if (expectedIndex >= 0) {
var anonI = 0;
for (i = 0; i < params.length; i++) {
if (params[i].name === undefined) {
if (anonI === expectedIndex) {
return i;
}
anonI++;
} else {
var indexOfOther = indexOfParameterDef(context, macroName, params[i].name, options);
if (indexOfOther < expectedIndex) {
anonI++;
}
}
}
}
return -1;
};
// Looks up the definition of a macro, and figures out what the expected index
// is for the given parameter.
function indexOfParameterDef(context, macroName, paramName, options) {
var def = context.getMacroDefinition(macroName);
if (def === undefined) {
throw new CannotFindMacroDef();
}
var params = def.params || [];
for (var i = 0; i < params.length; i++) {
if (params[i].name === paramName) {
return i;
}
}
return -1;
};
// Looks up the definition of a macro, and figures out what the expected index
// is for the given parameter.
function indexOfParameterDef(context, macroName, paramName, options) {
var def = context.getMacroDefinition(macroName);
if (def === undefined) {
throw new CannotFindMacroDef();
}
var params = def.params || [];
for (var i = 0; i < params.length; i++) {
if (params[i].name === paramName) {
return i;
}
}
return -1;
};
/**Like wrapAttribute value, except for macro parameters, not attributes. /**Like wrapAttribute value, except for macro parameters, not attributes.
* *
* These are more permissive. Allows brackets, * These are more permissive. Allows brackets,
* and slashes and '<' in unquoted values. * and slashes and '<' in unquoted values.
*/ */
function wrapParameterValue(value, preference) { exports.wrapParameterValue = function(value, preference) {
var whitelist = ["", "'", '"', '[[', '"""']; var whitelist = ["", "'", '"', '[[', '"""'];
var choices = { var choices = {
"": function(v) {return !/([\s>"'=])/.test(v); }, "": function(v) {return !/([\s>"':])/.test(v); },
"'": function(v) {return v.indexOf("'") < 0; }, "'": function(v) {return v.indexOf("'") < 0; },
'"': function(v) {return v.indexOf('"') < 0; }, '"': function(v) {return v.indexOf('"') < 0; },
"[[": canBePrettyOperand, "[[": canBePrettyOperand,

View File

@ -0,0 +1,158 @@
var utils = require('$:/plugins/flibbles/relink/js/utils.js');
exports.name = "parameters";
// Error thrown when a macro's definition is needed, but can't be found.
function CannotFindMacroDef() {};
CannotFindMacroDef.prototype.impossible = true;
CannotFindMacroDef.prototype.name = "macroparam";
// Failed relinks due to missing definitions aren't reported for now.
// I may want to do something special later on.
CannotFindMacroDef.prototype.report = function() { return []; };
exports.report = function(context, macro, callback, options) {
var managedMacro = context.getMacro(macro.name);
if (!managedMacro) {
// We don't manage this macro. Bye.
return undefined;
}
for (var managedArg in managedMacro) {
var index;
try {
index = getParamIndexWithinMacrocall(context, macro.name, managedArg, macro.params, options);
} catch (e) {
continue;
}
if (index < 0) {
// The argument was not supplied. Move on to next.
continue;
}
var param = macro.params[index];
var handler = managedMacro[managedArg];
var nestedOptions = Object.create(options);
nestedOptions.settings = context;
var entry = handler.report(param.value, function(title, blurb, style) {
var rtn = managedArg;
if (blurb) {
rtn += ': "' + blurb + '"';
}
callback(title, macro.name + ' ' + rtn, style);
}, nestedOptions);
}
};
exports.relink = function(context, macro, text, fromTitle, toTitle, options) {
var managedMacro = context.getMacro(macro.name);
var modified = false;
if (!managedMacro) {
// We don't manage this macro. Bye.
return undefined;
}
var outMacro = $tw.utils.extend({}, macro);
var macroEntry = {};
outMacro.params = macro.params.slice();
for (var managedArg in managedMacro) {
var index;
try {
index = getParamIndexWithinMacrocall(context, macro.name, managedArg, macro.params, options);
} catch (e) {
if (e instanceof CannotFindMacroDef) {
macroEntry.impossible = true;
continue;
}
}
if (index < 0) {
// this arg either was not supplied, or we can't find
// the definition, so we can't tie it to an anonymous
// argument. Either way, move on to the next.
continue;
}
var param = macro.params[index];
var handler = managedMacro[managedArg];
var nestedOptions = Object.create(options);
nestedOptions.settings = context;
var entry = handler.relink(param.value, fromTitle, toTitle, nestedOptions);
if (entry === undefined) {
continue;
}
// Macro parameters can only be string parameters, not
// indirect, or macro, or filtered
if (entry.impossible) {
macroEntry.impossible = true;
}
if (!entry.output) {
continue;
}
var quote = utils.determineQuote(text, param);
var newParam = $tw.utils.extend({}, param);
newParam.start = newParam.end - (newParam.value.length + (quote.length*2));
newParam.value = entry.output;
newParam.quote = quote;
newParam.modified = true;
outMacro.params[index] = newParam;
modified = true;
}
if (modified || macroEntry.impossible) {
if (modified) {
macroEntry.output = outMacro;
}
return macroEntry;
}
return undefined;
};
/** Returns -1 if param definitely isn't in macrocall.
*/
function getParamIndexWithinMacrocall(context, macroName, param, params, options) {
var index, i, anonsExist = false;
for (i = 0; i < params.length; i++) {
var name = params[i].name;
if (name === param) {
return i;
}
if (name === undefined) {
anonsExist = true;
}
}
if (!anonsExist) {
// If no anonymous parameters are present, and we didn't find
// it among the named ones, it must not be there.
return -1;
}
var expectedIndex = indexOfParameterDef(context, macroName, param, options);
// We've got to skip over all the named parameter instances.
if (expectedIndex >= 0) {
var anonI = 0;
for (i = 0; i < params.length; i++) {
if (params[i].name === undefined) {
if (anonI === expectedIndex) {
return i;
}
anonI++;
} else {
var indexOfOther = indexOfParameterDef(context, macroName, params[i].name, options);
if (indexOfOther < expectedIndex) {
anonI++;
}
}
}
}
return -1;
};
// Looks up the definition of a macro, and figures out what the expected index
// is for the given parameter.
function indexOfParameterDef(context, macroName, paramName, options) {
var def = context.getMacroDefinition(macroName);
if (def === undefined) {
throw new CannotFindMacroDef();
}
var params = def.params || [];
for (var i = 0; i < params.length; i++) {
if (params[i].name === paramName) {
return i;
}
}
return -1;
};

View File

@ -0,0 +1,3 @@
module-type: relinkmacrocall
title: $:/plugins/flibbles/relink/js/utils/macrocall/parameters.js
type: application/javascript

View File

@ -1,61 +0,0 @@
/*\
A method which doles out placeholders when requested, and constructs
the necessary supporting pragma when requested.
\*/
var utils = require('../utils');
function Placeholder() {
this.placeholders = Object.create(null);
this.reverseMap = {};
this.used = Object.create(null);
};
module.exports = Placeholder;
Placeholder.prototype.getPlaceholderFor = function(value, category) {
this.reverseMap[category] = this.reverseMap[category] || Object.create(null);
var placeholder = this.reverseMap[category][value];
if (placeholder) {
return placeholder;
}
var config = (this.parser && this.parser.context) || utils.getWikiContext(this.parser.wiki);
var number = 0;
var prefix = "relink-"
if (category && category !== "title") {
// I don't like "relink-title-1". "relink-1" should be for
// titles. lists, and filters can have descriptors though.
prefix += category + "-";
}
do {
number += 1;
placeholder = prefix + number;
} while (config.getMacroDefinition(placeholder) || this.used[placeholder]);
this.placeholders[placeholder] = value;
this.reverseMap[category][value] = placeholder;
this.used[placeholder] = true;
return placeholder;
};
// For registering placeholders that already existed
Placeholder.prototype.registerExisting = function(key, value) {
this.reverseMap[value] = key;
this.used[key] = true;
};
Placeholder.prototype.getPreamble = function() {
var results = [];
var keys = Object.keys(this.placeholders);
if (keys.length > 0) {
keys.sort();
for (var i = 0; i < keys.length; i++) {
var name = keys[i];
var val = this.placeholders[name];
results.push("\\define "+name+"() "+val+"\n");
}
}
return results.join('');
};

View File

@ -1,3 +0,0 @@
module-type: library
title: $:/plugins/flibbles/relink/js/utils/placeholder.js
type: application/javascript

View File

@ -0,0 +1,76 @@
/*\
Handles relinking substitution text, like strings containing $(this)$.
\*/
var utils = require('$:/plugins/flibbles/relink/js/utils.js');
var filterHandler = utils.getType('filter');
var macrocallHandler = require("./macrocall.js");
exports.report = function(string, callback, options) {
if (!options.noFilterSubstitution) {
var filterRegex = /\$\{([\S\s]+?)\}\$/g, filter;
while (filter = filterRegex.exec(string)) {
filterHandler.report(filter[1], function(title, blurb, style) {
callback(title, '${' + blurb + '}$', style);
}, options);
}
}
var varRegex = /\$\(([^\)\$]+)\)\$/g, varMatch;
while (varMatch = varRegex.exec(string)) {
macrocallHandler.report(options.settings, {name: varMatch[1], params: []}, function(title, blurb, style) {
callback(title, '$(' + blurb + ')$', style);
}, options);
}
};
exports.relink = function(string, fromTitle, toTitle, options) {
var entry;
var changed = false;
var newValue = string;
if (!options.noFilterSubstitution) {
newValue = newValue.replace(/\$\{([\S\s]+?)\}\$/g, function(match, filter) {
var filterEntry = filterHandler.relink(filter, fromTitle, toTitle, options);
if (filterEntry) {
entry = entry || {};
if (filterEntry.output) {
// The only }$ should be the one at the very end
if (filterEntry.output.indexOf("}$") < 0) {
changed = true;
match = '${' + filterEntry.output + '}$';
} else {
entry.impossible = true;
}
}
if (filterEntry.impossible) {
entry.impossible = true;
}
}
return match;
});
}
newValue = newValue.replace(/\$\(([^\)\$]+)\)\$/g, function(match, varname) {
var macroEntry = macrocallHandler.relink(options.settings, {name: varname, params: []}, string, fromTitle, toTitle, false, options);
if (macroEntry) {
entry = entry || {};
if (macroEntry.output) {
var newTitle = macroEntry.output.attributes.$variable.value;
if (newTitle.indexOf('$') >= 0 || newTitle.indexOf(')') >= 0) {
entry.impossible = true;
} else {
changed = true;
match = '$(' + newTitle + ')$';
}
}
if (macroEntry.impossible) {
entry.impossible = true;
}
}
return match;
});
if (changed) {
entry.output = newValue;
}
return entry;
};

View File

@ -0,0 +1,3 @@
module-type: library
title: $:/plugins/flibbles/relink/js/utils/substitution.js
type: application/javascript

View File

@ -7,12 +7,16 @@ Introduces some utility methods used by Relink.
var utils = require("./utils.js"); var utils = require("./utils.js");
exports.getTiddlerRelinkReferences = function(title) { exports.getTiddlerRelinkReferences = function(title, options) {
return utils.getIndexer(this).lookup(title); var refs = utils.getIndexer(this).lookup(title);
return refs && blurbs(refs, options && options.hard);
}; };
exports.getTiddlerRelinkBackreferences = function(title) { exports.getTiddlerRelinkBackreferences = function(title) {
return utils.getIndexer(this).reverseLookup(title); var refs = utils.getIndexer(this).reverseLookup(title);
// For now, I don't have the equivalent "hard" option because I'm not
// sure it has any value. May change this later.
return blurbs(refs);
}; };
exports.getRelinkableTitles = function() { exports.getRelinkableTitles = function() {
@ -28,6 +32,20 @@ exports.getRelinkableTitles = function() {
})(); })();
}; };
exports.getRelinkOrphans = function() { exports.getRelinkOrphans = function(options) {
return utils.getIndexer(this).orphans(); return utils.getIndexer(this).orphans(options);
};
function blurbs(refs, hardOnly) {
var blurbsOnly = Object.create(null);
for (var title in refs) {
for (var i = 0; i < refs[title].length; i++) {
var record = refs[title][i];
if (!hardOnly || !record.soft) {
blurbsOnly[title] = blurbsOnly[title] || [];
blurbsOnly[title].push(record.blurb);
}
}
}
return blurbsOnly;
}; };

View File

@ -10,6 +10,8 @@ title: $:/plugins/flibbles/relink/ui/TiddlerInfo/References
</$list> </$list>
<table class="tc-relink-references-table"> <table class="tc-relink-references-table">
<tbody> <tbody>
<$vars toTitle='' >
<$list filter=<<filter>> emptyMessage=<<lingo References/Empty>> variable="listItem" template="$:/plugins/flibbles/relink/ui/TiddlerInfo/ReferencesTemplate" /> <$list filter=<<filter>> emptyMessage=<<lingo References/Empty>> variable="listItem" template="$:/plugins/flibbles/relink/ui/TiddlerInfo/ReferencesTemplate" />
</$vars>
</tbody> </tbody>
</table> </table>

View File

@ -2,14 +2,19 @@ title: $:/plugins/flibbles/relink/ui/TiddlerInfo/ReferencesTemplate
\whitespace trim \whitespace trim
<tr class="tc-relink-references"> <tr class="tc-relink-references">
<td class="tc-relink-references-title"> <td class={{{ tc-relink-references-title [<toTitle>!match[]then<listItem>relink:impossible<toTitle>then[tc-relink-impossible]] +[join[ ]] }}} >
<$link to=<<listItem>>/> <$list
variable=whitelist
filter="[<listItem>removeprefix[$:/config/flibbles/relink/]split[/]first[]titlecase[]addprefix[$:/plugins/flibbles/relink/ui/configuration/]]"
emptyMessage="<$link to=<<listItem>>/>" >
<$link to=<<whitelist>>>
''//Relink//&#32;whitelist''
</$link>
</$list>
</td> </td>
<td class="tc-relink-references-report"> <td class="tc-relink-references-report">
<$list filter="[<listItem>relink:report<currentTiddler>]"> <$list filter="[<listItem>relink:report<currentTiddler>]">
<$text text=<<currentTiddler>> /> <$text text=<<currentTiddler>> />
</$list> </$list>
</td></tr>
</td>
</tr>

View File

@ -4,7 +4,7 @@ title: $:/plugins/flibbles/relink/ui/components/tables
\whitespace trim \whitespace trim
<$list variable="render" filter="[relink:signatures<__plugin__>prefix<__category__>first[]]"> <$list variable="render" filter="[relink:signatures<__plugin__>prefix<__category__>first[]]">
<$set name="table-state" value=<<qualify """$:/state/flibbles/relink/tables/$title$""">>> <$vars table-state=<<qualify """$:/state/flibbles/relink/tables/$plugin$""">>>
<tr><th class="tc-relink-header-plugin" colspan=<<column-count>> > <tr><th class="tc-relink-header-plugin" colspan=<<column-count>> >
<$reveal type="nomatch" state=<<table-state>> text="yes" default="""$default-table-state$"""> <$reveal type="nomatch" state=<<table-state>> text="yes" default="""$default-table-state$""">
<$button class="tc-btn-invisible tc-btn-dropdown" set=<<table-state>> setTo="yes"> <$button class="tc-btn-invisible tc-btn-dropdown" set=<<table-state>> setTo="yes">
@ -30,7 +30,7 @@ title: $:/plugins/flibbles/relink/ui/components/tables
</$tiddler> </$tiddler>
</$vars> </$vars>
</$list> </$list>
</$set> </$vars>
</$list> </$list>
\end \end
@ -48,7 +48,10 @@ title: $:/plugins/flibbles/relink/ui/components/tables
<$list <$list
filter="[plugin-type[plugin]![$:/core]![$:/plugins/flibbles/relink]]"> filter="[plugin-type[plugin]![$:/core]![$:/plugins/flibbles/relink]]">
<$set name="subtitle" filter="[all[current]has[description]]" value="''{{!!name}}'':&#32;<$text text={{!!description}}/>" emptyValue="''{{!!name}}''" > <$set name="subtitle"
filter="[all[current]has[description]]"
value="''{{!!name}}'':&#32;<$text text={{!!description}}/>"
emptyValue="''{{!!name}}''" >
<$macrocall $name=".make-table" title=<<subtitle>> plugin=<<currentTiddler>> /> <$macrocall $name=".make-table" title=<<subtitle>> plugin=<<currentTiddler>> />
</$set> </$set>
</$list> </$list>

View File

@ -62,11 +62,14 @@ title: $:/plugins/flibbles/relink/ui/configuration/Attributes
<$text text={{$(lingo-base)$NewAttribute/Caption}}/> <$text text={{$(lingo-base)$NewAttribute/Caption}}/>
</$button> </$button>
</$reveal> </$reveal>
@@.tc-control-panel
<$macrocall <$macrocall
$name=tables $name=tables
category="attributes" category="attributes"
header-list="[[Widget/HTML Element]] Attribute Type" header-list="[[Widget/HTML Element]] Attribute Type"
list-row-macro="row" /> list-row-macro="row" />
@@
\end \end
{{$:/plugins/flibbles/relink/language/Help/Attributes}} {{$:/plugins/flibbles/relink/language/Help/Attributes}}

View File

@ -39,11 +39,14 @@ title: $:/plugins/flibbles/relink/ui/configuration/Fields
<$text text={{$(lingo-base)$NewField/Caption}}/> <$text text={{$(lingo-base)$NewField/Caption}}/>
</$button> </$button>
</$reveal> </$reveal>
@@.tc-control-panel
<$macrocall <$macrocall
$name=tables $name=tables
category="fields" category="fields"
header-list="[[Field Name]] [[Field Type]]" header-list="[[Field Name]] [[Field Type]]"
list-row-macro="row" /> list-row-macro="row" />
@@
\end \end
{{$:/plugins/flibbles/relink/language/Help/Fields}} {{$:/plugins/flibbles/relink/language/Help/Fields}}

View File

@ -63,11 +63,14 @@ title: $:/plugins/flibbles/relink/ui/configuration/Macros
<$text text={{$(lingo-base)$NewParameter/Caption}}/> <$text text={{$(lingo-base)$NewParameter/Caption}}/>
</$button> </$button>
</$reveal> </$reveal>
@@.tc-control-panel
<$macrocall <$macrocall
$name=tables $name=tables
category="macros" category="macros"
header-list="Macro Parameter Type" header-list="Macro Parameter Type"
list-row-macro="row" /> list-row-macro="row" />
@@
\end \end
{{$:/plugins/flibbles/relink/language/Help/Macros}} {{$:/plugins/flibbles/relink/language/Help/Macros}}

View File

@ -39,11 +39,14 @@ title: $:/plugins/flibbles/relink/ui/configuration/Operators
<$text text={{$(lingo-base)$NewOperator/Caption}}/> <$text text={{$(lingo-base)$NewOperator/Caption}}/>
</$button> </$button>
</$reveal> </$reveal>
@@.tc-control-panel
<$macrocall <$macrocall
$name=tables $name=tables
category="operators" category="operators"
header-list="[[Filter Operator]] [[Operand Type]]" header-list="[[Filter Operator]] [[Operand Type]]"
list-row-macro="row" /> list-row-macro="row" />
@@
\end \end
{{$:/plugins/flibbles/relink/language/Help/Operators}} {{$:/plugins/flibbles/relink/language/Help/Operators}}

View File

@ -20,6 +20,8 @@ title: $:/plugins/flibbles/relink/ui/stylesheet.css
.tc-relink-references-title { .tc-relink-references-title {
text-align: left; text-align: left;
vertical-align: top; vertical-align: top;
padding-top: 3px;
padding-bottom: 3px;
} }
.tc-relink-references-occurrence { .tc-relink-references-occurrence {

Some files were not shown because too many files have changed in this diff Show More