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:
parent
ab2ac78620
commit
7534a97518
@ -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": {
|
||||
}
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user