1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-07-13 07:24:21 +00:00
TiddlyWiki5/plugins/tiddlywiki/text-slicer/slicer.js
Jermolene 3cfad8b044 Refactoring of text-slicer
Now we process the rendered HTML of tiddlers, which allows us to
process HTML generated by MS Word. In fact, the HTML that MS Word
generates is so awful, I’ve instead been using Mammoth to do the
conversion: https://github.com/mwilliamson/mammoth.js

Also some necessary improvements to the fake dom implementation.
2015-08-10 11:41:23 +01:00

234 lines
7.1 KiB
JavaScript

/*\
title: $:/plugins/tiddlywiki/text-slicer/slicer.js
type: application/javascript
module-type: startup
Setup the root widget event handlers
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
// Export name and synchronous status
exports.name = "slicer";
exports.platforms = ["browser"];
exports.after = ["startup"];
exports.synchronous = true;
var SLICER_OUTPUT_TITLE = "$:/TextSlicer";
// Install the root widget event handlers
exports.startup = function() {
$tw.rootWidget.addEventListener("tm-slice-tiddler",function(event) {
var slicer = new Slicer($tw.wiki,event.param);
// slicer.sliceTiddler();
// slicer.outputTiddlers();
// Slice up and output the tiddler
slicer.outputTiddlers(slicer.sliceTiddler(event.param),event.param,event.param);
slicer.destroy();
});
};
function Slicer(wiki,sourceTitle) {
this.wiki = wiki;
this.sourceTitle = sourceTitle;
this.currentId = 0;
this.iframe = null; // Reference to iframe used for HTML parsing
}
Slicer.prototype.destroy = function() {
// Remove the iframe from the DOM
if(this.iframe && this.iframe.parentNode) {
this.iframe.parentNode.removeChild(this.iframe);
}
};
Slicer.prototype.nextId = function() {
return ++this.currentId;
};
Slicer.prototype.getSourceHtmlDocument = function(tiddler) {
this.iframe = document.createElement("iframe");
document.body.appendChild(this.iframe);
this.iframe.contentWindow.document.open();
this.iframe.contentWindow.document.write(tiddler.fields.text);
this.iframe.contentWindow.document.close();
return this.iframe.contentWindow.document;
};
Slicer.prototype.getSourceWikiDocument = function(tiddler) {
var widgetNode = this.wiki.makeTranscludeWidget(this.sourceTitle,{document: $tw.fakeDocument, parseAsInline: false}),
container = $tw.fakeDocument.createElement("div");
widgetNode.render(container,null);
return container;
};
Slicer.prototype.getSourceDocument = function() {
var tiddler = $tw.wiki.getTiddler(this.sourceTitle);
if(tiddler.fields.type === "text/html") {
return this.getSourceHtmlDocument(tiddler);
} else {
return this.getSourceWikiDocument(tiddler);
}
};
// Slice a tiddler into individual tiddlers
Slicer.prototype.sliceTiddler = function(title) {
var self = this,
tiddlers = {},
domNode = this.getSourceDocument(),
parentStack = [],
addTiddler = function(fields) {
if(fields.title) {
tiddlers[fields.title] = $tw.utils.extend({},tiddlers[fields.title],fields);
return fields.title;
} else {
return null;
}
},
addToList = function(parent,child) {
var parentTiddler = tiddlers[parent] || {},
parentList = parentTiddler.list || [];
parentList.push(child);
addTiddler($tw.utils.extend({title: parent},parentTiddler,{list: parentList}));
},
convertTypeToLevel = function(type) {
if(type.charAt(0) === "h") {
return parseInt(type.charAt(1),10);
} else {
return null;
}
},
popParentStackUntil = function(type) {
// Pop the stack to remove any entries at the same or lower level
var newLevel = convertTypeToLevel(type),
topIndex = parentStack.length - 1;
do {
var topLevel = convertTypeToLevel(parentStack[parentStack.length - 1].type);
if(topLevel !== null && topLevel < newLevel ) {
break;
}
parentStack.length--;
} while(true);
return parentStack[parentStack.length - 1].title;
},
isBlank = function(s) {
return (/^[\s\xA0]*$/mg).test(s);
},
processNodeList = function(domNodeList) {
$tw.utils.each(domNodeList,function(domNode) {
var parentTitle,
text = domNode.textContent,
nodeType = domNode.nodeType;
if(nodeType === 1) {
var tagName = domNode.tagName.toLowerCase();
if(tagName === "p" && (domNode.getAttribute("style") || "").indexOf("mso-list:") !== -1) {
tagName = "li";
}
if(tagName === "h1" || tagName === "h2" || tagName === "h3" || tagName === "h4") {
if(!isBlank(text)) {
parentTitle = popParentStackUntil(tagName);
addToList(parentTitle,text);
parentStack.push({type: tagName, title: addTiddler({
title: text,
text: "<<display-heading-tiddler level:'" + tagName + "'>>",
list: [],
tags: [parentTitle]
})});
}
} else if(tagName === "ul" || tagName === "ol") {
var listTitle = title + "-list-" + self.nextId();
parentTitle = parentStack[parentStack.length - 1].title;
addToList(parentTitle,listTitle);
parentStack.push({type: tagName, title: addTiddler({
title: listTitle,
text: "<<display-list-tiddler type:'" + tagName + "'>>",
list: [],
tags: [parentTitle]
})});
processNodeList(domNode.childNodes);
parentStack.pop();
} else if(tagName === "li") {
if(!isBlank(text)) {
var listItemTitle = title + "-listitem-" + self.nextId();
parentTitle = parentStack[parentStack.length - 1].title;
addToList(parentTitle,listItemTitle);
addTiddler({
title: listItemTitle,
text: text,
list: [],
tags: [parentTitle]
});
}
} else if(tagName === "p") {
if(!isBlank(text)) {
parentTitle = parentStack[parentStack.length - 1].title;
addToList(parentTitle,addTiddler({
title: title + "-para-" + self.nextId(),
text: text,
tags: [parentTitle]
}));
}
} else if(domNode.hasChildNodes()) {
processNodeList(domNode.childNodes);
}
}
});
};
parentStack.push({type: "h0", title: addTiddler({
title: "Sliced up " + title,
text: "{{||$:/plugins/tiddlywiki/text-slicer/templates/display-document}}",
list: []
})});
console.log(domNode);
processNodeList(domNode.childNodes);
return tiddlers;
};
// Output directly to the output tiddlers
Slicer.prototype.outputTiddlers = function(tiddlers,title,navigateFromTitle) {
$tw.utils.each(tiddlers,function(tiddlerFields) {
var title = tiddlerFields.title;
if(title) {
$tw.wiki.addTiddler(new $tw.Tiddler($tw.wiki.getCreationFields(),tiddlerFields,$tw.wiki.getModificationFields()));
}
});
// Navigate to output
var story = new $tw.Story({wiki: $tw.wiki});
story.navigateTiddler("Sliced up " + title,navigateFromTitle);
};
// Output via an import tiddler
Slicer.prototype.outputTiddlers_viaImportTiddler = function(tiddlers,navigateFromTitle) {
// Get the current slicer output tiddler
var slicerOutputTiddler = $tw.wiki.getTiddler(SLICER_OUTPUT_TITLE),
slicerOutputData = $tw.wiki.getTiddlerData(SLICER_OUTPUT_TITLE,{}),
newFields = new Object({
title: SLICER_OUTPUT_TITLE,
type: "application/json",
"plugin-type": "import",
"status": "pending"
});
// Process each tiddler
slicerOutputData.tiddlers = slicerOutputData.tiddlers || {};
$tw.utils.each(tiddlers,function(tiddlerFields) {
var title = tiddlerFields.title;
if(title) {
slicerOutputData.tiddlers[title] = tiddlerFields;
}
});
// Save the slicer output tiddler
newFields.text = JSON.stringify(slicerOutputData,null,$tw.config.preferences.jsonSpaces);
$tw.wiki.addTiddler(new $tw.Tiddler(slicerOutputTiddler,newFields));
// Navigate to output
var story = new $tw.Story({wiki: $tw.wiki});
story.navigateTiddler(SLICER_OUTPUT_TITLE,navigateFromTitle);
};
})();