1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-23 10:07:19 +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
}
},
{
"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",
"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": {
}
},

View File

@ -51,11 +51,12 @@ function Slicer(options) {
this.chunks = []; // Array of tiddlers without titles, addressed by their index. We use the title field to hold the plain text content
this.currentChunk = null; // Index of the chunk currently being written to
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
this.chunks.push({
"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>",
list: [],
tags: [],
@ -64,6 +65,10 @@ function Slicer(options) {
"slicer-output-mode": this.outputMode
});
this.parentStack.push({chunk: 0, actions: this.getMatchingSlicerRuleActions("(document)")});
this.insertPrecedingChunk({
"toc-type": "anchor",
"title": this.baseTiddlerTitle + "-anchor-"
});
// Set up the parser
var sax = require("$:/plugins/tiddlywiki/sax/sax.js");
this.sax = sax.parser(false,{
@ -261,6 +266,13 @@ Slicer.prototype.onCloseNamespace = function(info) {
Slicer.prototype.onOpenTag = function(node) {
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
if(actions.startNewChunk) {
// If this is a heading, pop off any higher or equal level headings first
@ -281,21 +293,39 @@ Slicer.prototype.onOpenTag = function(node) {
// Render the tag inline in the current chunk unless we should ignore it
if(!actions.dontRenderTag) {
if(actions.isImage) {
this.onImage(node);
this.onOpenImage(node);
} else if(actions.isAnchor) {
this.onOpenAnchor(node);
} else {
var markupInfo = actions.markup && actions.markup[this.outputMode];
if(markupInfo) {
this.addTextToCurrentChunk(markupInfo.prefix);
} else {
this.addTextToCurrentChunk("<" + node.name + (node.isSelfClosing ? "/" : "") + ">");
this.addTextToCurrentChunk("<" + node.name + (node.isSelfClosing ? "/" : "") + ">");
}
}
}
// 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;
if(url.slice(0,5) === "data:") {
// var parts = url.slice(5).split(",");
@ -314,11 +344,14 @@ Slicer.prototype.onCloseTag = function(name) {
actions = e.actions,
selfClosing = e.isSelfClosing;
// Set the caption if required
if(actions.setCaption) {
this.chunks[this.currentChunk].caption = this.chunks[this.currentChunk].title;
}
// TODO
// if(actions.setCaption) {
// this.chunks[this.currentChunk].caption = this.chunks[this.currentChunk].title;
// }
// 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];
if(markupInfo) {
this.addTextToCurrentChunk(markupInfo.suffix);
@ -355,11 +388,10 @@ Slicer.prototype.onText = function(text) {
});
}
this.addTextToCurrentChunk(text);
this.addTextToCurrentChunk(text,"title");
this.addTextToCurrentChunk(text,"caption");
};
Slicer.prototype.onEnd = function() {
this.assignTitlesToChunks();
this.callback(null,this.chunks);
};
@ -367,6 +399,7 @@ Slicer.prototype.addTextToCurrentChunk = function(str,field) {
field = field || "text";
if(this.currentChunk === null && str.trim() !== "") {
this.startNewChunk({
title: this.makeTitle("paragraph"),
"toc-type": "paragraph"
});
}
@ -376,86 +409,54 @@ Slicer.prototype.addTextToCurrentChunk = function(str,field) {
};
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({},{
title: "",
title: title,
text: "",
tags: [parentIndex],
caption: "",
tags: [parentChunk.title],
list: [],
role: this.role
},fields));
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) {
return (/^[\s\xA0]*$/g).test(s);
};
Slicer.prototype.assignTitlesToChunks = function() {
var self = this;
// Create a title for each tiddler
var titles = {};
this.chunks.forEach(function(chunk) {
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;
Slicer.prototype.makeTitle = function(prefix) {
prefix = prefix || "";
var count = (this.titleCounts[prefix] || 0) + 1;
this.titleCounts[prefix] = count;
return this.baseTiddlerTitle + "-" + prefix + "-" + count;
};
exports.Slicer = Slicer;

View File

@ -11,7 +11,7 @@ Slice this text tiddler into chunks
\end
<$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]]">
{{$:/plugins/tiddlywiki/text-slicer/images/text-slicer-icon}}
</$list>