mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-04-22 18:53:14 +00:00
Merge 1a8a62aa44e9d0302addeb96b53a1694a9d7cc82 into 961e74f73d230d0028efb586db07699120eac888
This commit is contained in:
commit
49cf4b8da1
core/modules
46
core/modules/filters/annotatedbacklinks.js
Normal file
46
core/modules/filters/annotatedbacklinks.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/annotatedbacklinks.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for returning all the back links from a tiddler with a given annotation
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
function parseAnnotations(annotationstring){
|
||||
var annotations = {}
|
||||
if (annotationstring.length > 0){
|
||||
annotationstring.split(',').forEach(function(part){
|
||||
if (part.includes('=')){
|
||||
let subparts = part.split('=', 2);
|
||||
let key = subparts[0],
|
||||
value = subparts[1];
|
||||
annotations[key] = value;
|
||||
}else{
|
||||
annotations[part] = "*";
|
||||
}
|
||||
});
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.annotatedbacklinks = function(source,operator,options) {
|
||||
var results = new $tw.utils.LinkedList();
|
||||
source(function(tiddler,title) {
|
||||
var annotations = parseAnnotations(operator.operands[0]);
|
||||
console.log(annotations);
|
||||
results.pushTop(options.wiki.getTiddlerAnnotatedBacklinks(title, annotations));
|
||||
});
|
||||
return results.makeTiddlerIterator(options.wiki);
|
||||
};
|
||||
|
||||
|
||||
})();
|
45
core/modules/filters/annotatedlinks.js
Normal file
45
core/modules/filters/annotatedlinks.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/annotatedlinks.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operator for returning all the links from a tiddler with a given annotation
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
function parseAnnotations(annotationstring){
|
||||
var annotations = {}
|
||||
if (annotationstring.length > 0){
|
||||
annotationstring.split(',').forEach(function(part){
|
||||
if (part.includes('=')){
|
||||
let subparts = part.split('=', 2);
|
||||
let key = subparts[0],
|
||||
value = subparts[1];
|
||||
annotations[key] = value;
|
||||
}else{
|
||||
annotations[part] = "*";
|
||||
}
|
||||
});
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/*
|
||||
Export our filter function
|
||||
*/
|
||||
exports.annotatedlinks = function(source,operator,options) {
|
||||
var results = new $tw.utils.LinkedList();
|
||||
source(function(tiddler,title) {
|
||||
var annotations = parseAnnotations(operator.operands[0]);
|
||||
results.pushTop(options.wiki.getTiddlerAnnotatedLinks(title, annotations));
|
||||
});
|
||||
return results.makeTiddlerIterator(options.wiki);
|
||||
};
|
||||
|
||||
|
||||
})();
|
147
core/modules/parsers/wikiparser/rules/prettyannotatedlink.js
Normal file
147
core/modules/parsers/wikiparser/rules/prettyannotatedlink.js
Normal file
@ -0,0 +1,147 @@
|
||||
/*\
|
||||
title: $:/core/modules/parsers/wikiparser/rules/prettyannotatedlink.js
|
||||
type: application/javascript
|
||||
module-type: wikirule
|
||||
|
||||
Wiki text inline rule for pretty links with annotations. For example:
|
||||
|
||||
```
|
||||
[link attribute="value" [Link description|TiddlerTitle]]
|
||||
[link attribute="value" [TiddlerTitle]]
|
||||
```
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.name = "prettyannotatedlink";
|
||||
exports.types = {inline: true};
|
||||
|
||||
exports.init = function(parser) {
|
||||
this.parser = parser;
|
||||
};
|
||||
|
||||
exports.findNextMatch = function(startPos) {
|
||||
// Find the next tag
|
||||
this.nextAnnotatedLink = this.findNextAnnotatedLink(this.parser.source,startPos);
|
||||
return this.nextAnnotatedLink ? this.nextAnnotatedLink.start : undefined;
|
||||
};
|
||||
|
||||
exports.parse = function() {
|
||||
// Move past the match
|
||||
this.parser.pos = this.nextAnnotatedLink.end;
|
||||
if($tw.utils.isLinkExternal(this.nextAnnotatedLink.attributes.to)) {
|
||||
return [{
|
||||
type: "element",
|
||||
tag: "a",
|
||||
attributes: {
|
||||
href: {type: "string", value: link},
|
||||
"class": {type: "string", value: "tc-tiddlylink-external"},
|
||||
target: {type: "string", value: "_blank"},
|
||||
rel: {type: "string", value: "noopener noreferrer"},
|
||||
...this.nextAnnotatedLink.attributes
|
||||
},
|
||||
children: [{
|
||||
type: "text", text: this.nextAnnotatedLink.text
|
||||
}]
|
||||
}];
|
||||
} else {
|
||||
return [{
|
||||
type: "link",
|
||||
attributes: this.nextAnnotatedLink.attributes,
|
||||
children: [{
|
||||
type: "text", text: this.nextAnnotatedLink.text
|
||||
}]
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Find the next link from the current position
|
||||
*/
|
||||
exports.findNextAnnotatedLink = function(source,pos) {
|
||||
// A regexp for finding candidate HTML tags
|
||||
var reLookahead = /(\[link)/g;
|
||||
// Find the next candidate
|
||||
reLookahead.lastIndex = pos;
|
||||
var match = reLookahead.exec(source);
|
||||
while(match) {
|
||||
// Try to parse the candidate as a tag
|
||||
var tag = this.parseAnnotatedLink(source,match.index);
|
||||
// Return success
|
||||
if(tag) {
|
||||
return tag;
|
||||
}
|
||||
// Look for the next match
|
||||
reLookahead.lastIndex = match.index + 1;
|
||||
match = reLookahead.exec(source);
|
||||
}
|
||||
// Failed
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
Look for an link at the specified position. Returns null if not found, otherwise returns {type: "link", attributes: [], isSelfClosing:, start:, end:,}
|
||||
*/
|
||||
exports.parseAnnotatedLink = function(source,pos) {
|
||||
var token,
|
||||
node = {
|
||||
type: "link",
|
||||
start: pos,
|
||||
attributes: {},
|
||||
text: ""
|
||||
};
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the `[img`
|
||||
token = $tw.utils.parseTokenString(source,pos,"[link");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Process attributes
|
||||
if(source.charAt(pos) !== "[") {
|
||||
var attribute = $tw.utils.parseAttribute(source,pos);
|
||||
while(attribute) {
|
||||
node.attributes[attribute.name] = attribute;
|
||||
pos = attribute.end;
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
if(source.charAt(pos) !== "[") {
|
||||
// Get the next attribute
|
||||
attribute = $tw.utils.parseAttribute(source,pos);
|
||||
} else {
|
||||
attribute = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Look for the `[` after the attributes
|
||||
token = $tw.utils.parseTokenString(source,pos,"[");
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
// Skip whitespace
|
||||
pos = $tw.utils.skipWhiteSpace(source,pos);
|
||||
// Get the source up to the terminating `]]`
|
||||
token = $tw.utils.parseTokenRegExp(source,pos,/(.*?)(?:\|(.*?))?\]\]/g);
|
||||
if(!token) {
|
||||
return null;
|
||||
}
|
||||
pos = token.end;
|
||||
var text = token.match[1],
|
||||
link = token.match[2] || text;
|
||||
node.attributes.to = {type: "string", value: link};
|
||||
node.text = text;
|
||||
// Update the end position
|
||||
node.end = pos;
|
||||
return node;
|
||||
};
|
||||
|
||||
})();
|
@ -635,6 +635,101 @@ exports.getTiddlerBacktranscludes = function(targetTitle) {
|
||||
return backtranscludes;
|
||||
};
|
||||
|
||||
/*
|
||||
Return an array of tiddler titles that are directly linked within the given parse tree
|
||||
annotations: This could either be:
|
||||
* a string: to look for the availability of a single annotation
|
||||
* an array: for any of the listed annotations
|
||||
* an object: defining a number of key value pairs, where either
|
||||
* the value is function which must return true
|
||||
* the value is '*' which allows any value
|
||||
* the value given matches exactly the annotation
|
||||
*/
|
||||
exports.extractAnnotatedLinks = function(parseTreeRoot, annotations) {
|
||||
var annotation_list = {};
|
||||
switch(typeof(annotations)){
|
||||
case "string":
|
||||
if (annotations.length==0){
|
||||
return [];
|
||||
}
|
||||
let key = annotations;
|
||||
annotation_list[key] = '*';
|
||||
break;
|
||||
case "object":
|
||||
let original_annotations = annotations;
|
||||
if (Array.isArray(annotations)){
|
||||
annotations.forEach((key) => {
|
||||
annotation_list[key] = '*';
|
||||
});
|
||||
}else{
|
||||
annotation_list = annotations;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Count up the links
|
||||
var links = [],
|
||||
checkParseTree = function(parseTree) {
|
||||
for(var t=0; t<parseTree.length; t++) {
|
||||
var parseTreeNode = parseTree[t];
|
||||
console.log(parseTreeNode);
|
||||
if(parseTreeNode.type === "link" && parseTreeNode.attributes.to && parseTreeNode.attributes.to.type === "string") {
|
||||
var add = false;
|
||||
for (const [key, value] of Object.entries(annotation_list)) {
|
||||
console.log(key, value, parseTreeNode.attributes);
|
||||
add = add || ((
|
||||
key in parseTreeNode.attributes
|
||||
) && (
|
||||
value == '*'
|
||||
|| value == parseTreeNode.attributes[key].value
|
||||
|| (
|
||||
typeof(value) == "function"
|
||||
&& value(parseTreeNode.attributes[key].value)
|
||||
)
|
||||
));
|
||||
}
|
||||
if (add){
|
||||
var value = parseTreeNode.attributes.to.value;
|
||||
if(links.indexOf(value) === -1) {
|
||||
links.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(parseTreeNode.children) {
|
||||
checkParseTree(parseTreeNode.children);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkParseTree(parseTreeRoot);
|
||||
return links;
|
||||
};
|
||||
|
||||
/*
|
||||
Return an array of tiddler titles that are directly linked from the specified tiddler
|
||||
*/
|
||||
exports.getTiddlerAnnotatedLinks = function(title, annotations) {
|
||||
var self = this;
|
||||
var parser = self.parseTiddler(title);
|
||||
if(parser) {
|
||||
return self.extractAnnotatedLinks(parser.tree, annotations);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/*
|
||||
Return an array of tiddler titles that link to the specified tiddler
|
||||
*/
|
||||
exports.getTiddlerAnnotatedBacklinks = function(targetTitle, annotations) {
|
||||
var self = this;
|
||||
var backlinks = [];
|
||||
this.forEachTiddler(function(title,tiddler) {
|
||||
var links = self.getTiddlerAnnotatedLinks(title, annotations);
|
||||
if(links.indexOf(targetTitle) !== -1) {
|
||||
backlinks.push(title);
|
||||
}
|
||||
});
|
||||
return backlinks;
|
||||
};
|
||||
|
||||
/*
|
||||
Return a hashmap of tiddler titles that are referenced but not defined. Each value is the number of times the missing tiddler is referenced
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user