1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-07-07 12:32:51 +00:00

Text-slicer: Add support for hyperlinks

This commit is contained in:
Jermolene 2018-01-30 11:06:50 +00:00
parent ab2ac78620
commit 7534a97518
3 changed files with 89 additions and 82 deletions

View File

@ -21,6 +21,12 @@ tags: $:/tags/text-slicer/slicer-rules
"dontRenderTag": true "dontRenderTag": true
} }
}, },
{
"selector": "a",
"actions": {
"isAnchor": true
}
},
{ {
"selector": "address,blockquote,center,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,iframe,isindex,noframes,noscript,ol,pre,table,ul", "selector": "address,blockquote,center,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,iframe,isindex,noframes,noscript,ol,pre,table,ul",
"actions": { "actions": {
@ -30,7 +36,7 @@ tags: $:/tags/text-slicer/slicer-rules
} }
}, },
{ {
"selector": "code,em,i,dd,li,strike,strong,b,sub,sup,u", "selector": "br,caption,code,col,colgroup,em,i,dd,li,strike,strong,b,sub,sup,u,tbody,td,tfoot,th,thead,tr",
"actions": { "actions": {
} }
}, },

View File

@ -52,10 +52,11 @@ function Slicer(options) {
this.currentChunk = null; // Index of the chunk currently being written to this.currentChunk = null; // Index of the chunk currently being written to
this.parentStack = []; // Stack of parent chunks {chunk: chunk index,actions:} this.parentStack = []; // Stack of parent chunks {chunk: chunk index,actions:}
this.elementStack = []; // Stack of {tag:,isSelfClosing:,actions:} this.elementStack = []; // Stack of {tag:,isSelfClosing:,actions:}
this.titleCounts = {}; // Hashmap of counts of prefixed titles that have been issued
// Set up the document tiddler as top level heading // Set up the document tiddler as top level heading
this.chunks.push({ this.chunks.push({
"toc-type": "document", "toc-type": "document",
title: "", // makeUniqueTitle will later initialise it to baseTiddlerTitle title: this.baseTiddlerTitle,
text: "<div class='tc-table-of-contents'><<toc-selective-expandable \"\"\"" + this.baseTiddlerTitle + "document\"\"\">></div>", text: "<div class='tc-table-of-contents'><<toc-selective-expandable \"\"\"" + this.baseTiddlerTitle + "document\"\"\">></div>",
list: [], list: [],
tags: [], tags: [],
@ -64,6 +65,10 @@ function Slicer(options) {
"slicer-output-mode": this.outputMode "slicer-output-mode": this.outputMode
}); });
this.parentStack.push({chunk: 0, actions: this.getMatchingSlicerRuleActions("(document)")}); this.parentStack.push({chunk: 0, actions: this.getMatchingSlicerRuleActions("(document)")});
this.insertPrecedingChunk({
"toc-type": "anchor",
"title": this.baseTiddlerTitle + "-anchor-"
});
// Set up the parser // Set up the parser
var sax = require("$:/plugins/tiddlywiki/sax/sax.js"); var sax = require("$:/plugins/tiddlywiki/sax/sax.js");
this.sax = sax.parser(false,{ this.sax = sax.parser(false,{
@ -261,6 +266,13 @@ Slicer.prototype.onCloseNamespace = function(info) {
Slicer.prototype.onOpenTag = function(node) { Slicer.prototype.onOpenTag = function(node) {
var actions = this.getMatchingSlicerRuleActions(node.name); var actions = this.getMatchingSlicerRuleActions(node.name);
// Create an anchor if we encounter an ID
if(node.attributes.id) {
this.insertPrecedingChunk({
"toc-type": "anchor",
"title": this.baseTiddlerTitle + "-anchor-" + node.attributes.id.value
});
}
// Check for an element that should start a new chunk // Check for an element that should start a new chunk
if(actions.startNewChunk) { if(actions.startNewChunk) {
// If this is a heading, pop off any higher or equal level headings first // If this is a heading, pop off any higher or equal level headings first
@ -281,7 +293,9 @@ Slicer.prototype.onOpenTag = function(node) {
// Render the tag inline in the current chunk unless we should ignore it // Render the tag inline in the current chunk unless we should ignore it
if(!actions.dontRenderTag) { if(!actions.dontRenderTag) {
if(actions.isImage) { if(actions.isImage) {
this.onImage(node); this.onOpenImage(node);
} else if(actions.isAnchor) {
this.onOpenAnchor(node);
} else { } else {
var markupInfo = actions.markup && actions.markup[this.outputMode]; var markupInfo = actions.markup && actions.markup[this.outputMode];
if(markupInfo) { if(markupInfo) {
@ -292,10 +306,26 @@ Slicer.prototype.onOpenTag = function(node) {
} }
} }
// Remember whether this tag is self closing // Remember whether this tag is self closing
this.elementStack.push({tag: node.name,isSelfClosing: node.isSelfClosing, actions: actions}); this.elementStack.push({tag: node.name,isSelfClosing: node.isSelfClosing, actions: actions, node: node});
}; };
Slicer.prototype.onImage = function(node) { Slicer.prototype.onOpenAnchor = function(node) {
if(node.attributes.href) {
var parts = node.attributes.href.value.split("#"),
base = parts[0],
hash = parts[1] || "",
title = $tw.utils.resolvePath(base,this.baseTiddlerTitle) + "-anchor-" + hash;
this.addTextToCurrentChunk("<$link to=\"" + title + "\">");
}
};
Slicer.prototype.onCloseAnchor = function(elementInfo) {
if(elementInfo.node.attributes.href) {
this.addTextToCurrentChunk("</$link>");
}
};
Slicer.prototype.onOpenImage = function(node) {
var url = node.attributes.src.value; var url = node.attributes.src.value;
if(url.slice(0,5) === "data:") { if(url.slice(0,5) === "data:") {
// var parts = url.slice(5).split(","); // var parts = url.slice(5).split(",");
@ -314,11 +344,14 @@ Slicer.prototype.onCloseTag = function(name) {
actions = e.actions, actions = e.actions,
selfClosing = e.isSelfClosing; selfClosing = e.isSelfClosing;
// Set the caption if required // Set the caption if required
if(actions.setCaption) { // TODO
this.chunks[this.currentChunk].caption = this.chunks[this.currentChunk].title; // if(actions.setCaption) {
} // this.chunks[this.currentChunk].caption = this.chunks[this.currentChunk].title;
// }
// Render the tag // Render the tag
if (!actions.dontRenderTag && !selfClosing) { if(actions.isAnchor) {
this.onCloseAnchor(e);
} else if (!actions.dontRenderTag && !selfClosing) {
var markupInfo = actions.markup && actions.markup[this.outputMode]; var markupInfo = actions.markup && actions.markup[this.outputMode];
if(markupInfo) { if(markupInfo) {
this.addTextToCurrentChunk(markupInfo.suffix); this.addTextToCurrentChunk(markupInfo.suffix);
@ -355,11 +388,10 @@ Slicer.prototype.onText = function(text) {
}); });
} }
this.addTextToCurrentChunk(text); this.addTextToCurrentChunk(text);
this.addTextToCurrentChunk(text,"title"); this.addTextToCurrentChunk(text,"caption");
}; };
Slicer.prototype.onEnd = function() { Slicer.prototype.onEnd = function() {
this.assignTitlesToChunks();
this.callback(null,this.chunks); this.callback(null,this.chunks);
}; };
@ -367,6 +399,7 @@ Slicer.prototype.addTextToCurrentChunk = function(str,field) {
field = field || "text"; field = field || "text";
if(this.currentChunk === null && str.trim() !== "") { if(this.currentChunk === null && str.trim() !== "") {
this.startNewChunk({ this.startNewChunk({
title: this.makeTitle("paragraph"),
"toc-type": "paragraph" "toc-type": "paragraph"
}); });
} }
@ -376,86 +409,54 @@ Slicer.prototype.addTextToCurrentChunk = function(str,field) {
}; };
Slicer.prototype.startNewChunk = function(fields) { Slicer.prototype.startNewChunk = function(fields) {
var parentIndex = this.getImmediateParent().chunk; var title = fields.title || this.makeTitle(fields["toc-type"]);
var parentChunk = this.chunks[this.getImmediateParent().chunk];
this.chunks.push($tw.utils.extend({},{ this.chunks.push($tw.utils.extend({},{
title: "", title: title,
text: "", text: "",
tags: [parentIndex], caption: "",
tags: [parentChunk.title],
list: [], list: [],
role: this.role role: this.role
},fields)); },fields));
this.currentChunk = this.chunks.length - 1; this.currentChunk = this.chunks.length - 1;
this.chunks[parentIndex].list.push(this.currentChunk); parentChunk.list.push(title);
};
Slicer.prototype.insertPrecedingChunk = function(fields) {
if(!fields.title) {
throw "Chunks need a title"
}
if(!this.currentChunk) {
this.startNewChunk(fields);
this.currentChunk = null;
} else {
var parentChunk = this.chunks[this.getImmediateParent().chunk],
index = this.chunks.length - 1;
// Insert the new chunk
this.chunks.splice(index,0,$tw.utils.extend({},{
text: "",
caption: "",
tags: [parentChunk.title],
list: [],
role: this.role
},fields));
// Adjust the current chunk pointer
this.currentChunk += 1;
// Insert a pointer to the new chunk in the parent
parentChunk.list.splice(parentChunk.list.length - 1,0,fields.title);
}
}; };
Slicer.prototype.isBlank = function(s) { Slicer.prototype.isBlank = function(s) {
return (/^[\s\xA0]*$/g).test(s); return (/^[\s\xA0]*$/g).test(s);
}; };
Slicer.prototype.assignTitlesToChunks = function() { Slicer.prototype.makeTitle = function(prefix) {
var self = this; prefix = prefix || "";
// Create a title for each tiddler var count = (this.titleCounts[prefix] || 0) + 1;
var titles = {}; this.titleCounts[prefix] = count;
this.chunks.forEach(function(chunk) { return this.baseTiddlerTitle + "-" + prefix + "-" + count;
var base = chunk["toc-type"] === "document" ? "" : chunk["toc-type"] + "-" + chunk.title,
title = self.makeUniqueTitle(titles,base);
titles[title] = true;
chunk.title = title;
});
// Link up any indices in the tags and list fields
this.chunks.forEach(function(chunk) {
if(chunk.tags) {
chunk.tags.map(function(tag,index) {
if(typeof tag === "number") {
chunk.tags[index] = self.chunks[tag].title;
}
});
}
if(chunk.list) {
chunk.list.map(function(listItem,index) {
if(typeof listItem === "number") {
chunk.list[index] = self.chunks[listItem].title;
}
});
}
});
};
Slicer.prototype.makeUniqueTitle = function(tiddlers,rawText) {
// Remove characters other than lowercase alphanumeric and spaces
var prefix = this.baseTiddlerTitle,
self = this,
cleanText;
if(rawText) {
// Replace non alpha characters with spaces
cleanText = rawText.toLowerCase().replace(/[^\s\xA0]/mg,function($0,$1,$2) {
if(($0 >= "a" && $0 <= "z") || ($0 >= "0" && $0 <= "9")) {
return $0;
} else {
return " ";
}
});
// Split on word boundaries
var words = cleanText.split(/[\s\xA0]+/mg);
// Remove common words
words = words.filter(function(word) {
return word && ("the and a of on i".split(" ").indexOf(word) === -1);
});
// Accumulate the number of words that will fit
var c = 0,
s = "";
while(c < words.length && (s.length + words[c].length + 1) < 50) {
s += (s === "" ? "" : "-") + words[c++];
}
prefix = prefix + s;
}
// Check for duplicates
c = 0;
var title = prefix;
while(title in tiddlers) {
title = prefix + "-" + (++c);
}
return title;
}; };
exports.Slicer = Slicer; exports.Slicer = Slicer;

View File

@ -11,7 +11,7 @@ Slice this text tiddler into chunks
\end \end
<$button tooltip=<<hint>> aria-label=<<hint>> class=<<tv-config-toolbar-class>>> <$button tooltip=<<hint>> aria-label=<<hint>> class=<<tv-config-toolbar-class>>>
<$action-sendmessage $message="tm-modal" $param="$:/plugins/tiddlywiki/text/slicer/ui/slice-modal" currentTiddler=<<currentTiddler>>/> <$action-sendmessage $message="tm-modal" $param="$:/plugins/tiddlywiki/text-slicer/ui/slice-modal" currentTiddler=<<currentTiddler>>/>
<$list filter="[<tv-config-toolbar-icons>prefix[yes]]"> <$list filter="[<tv-config-toolbar-icons>prefix[yes]]">
{{$:/plugins/tiddlywiki/text-slicer/images/text-slicer-icon}} {{$:/plugins/tiddlywiki/text-slicer/images/text-slicer-icon}}
</$list> </$list>