mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-11-13 22:07:15 +00:00
Incorporate “diff” engine to show visual differences (#3112)
This commit is contained in:
143
core/modules/widgets/diff-text.js
Normal file
143
core/modules/widgets/diff-text.js
Normal file
@@ -0,0 +1,143 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/diff-text.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Widget to display a diff between two texts
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget,
|
||||
dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
|
||||
|
||||
var DiffTextWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
DiffTextWidget.prototype = new Widget();
|
||||
|
||||
DiffTextWidget.prototype.invisibleCharacters = {
|
||||
"\n": "↩︎\n",
|
||||
"\r": "⇠",
|
||||
"\t": "⇥\t"
|
||||
};
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
DiffTextWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create the diff
|
||||
var dmpObject = new dmp.diff_match_patch(),
|
||||
diffs = dmpObject.diff_main(this.getAttribute("source"),this.getAttribute("dest"));
|
||||
// Apply required cleanup
|
||||
switch(this.getAttribute("cleanup","semantic")) {
|
||||
case "none":
|
||||
// No cleanup
|
||||
break;
|
||||
case "efficiency":
|
||||
dmpObject.diff_cleanupEfficiency(diffs);
|
||||
break;
|
||||
default: // case "semantic"
|
||||
dmpObject.diff_cleanupSemantic(diffs);
|
||||
break;
|
||||
}
|
||||
// Create the elements
|
||||
var domContainer = this.document.createElement("div"),
|
||||
domDiff = this.createDiffDom(diffs);
|
||||
parent.insertBefore(domContainer,nextSibling);
|
||||
// Set variables
|
||||
this.setVariable("diff-count",Math.trunc((diffs.length - 1) / 2).toString());
|
||||
// Render child widgets
|
||||
this.renderChildren(domContainer,null);
|
||||
// Render the diff
|
||||
domContainer.appendChild(domDiff);
|
||||
// Save our container
|
||||
this.domNodes.push(domContainer);
|
||||
};
|
||||
|
||||
/*
|
||||
Create DOM elements representing a list of diffs
|
||||
*/
|
||||
DiffTextWidget.prototype.createDiffDom = function(diffs) {
|
||||
var self = this;
|
||||
// Create the element and assign the attributes
|
||||
var domPre = this.document.createElement("pre"),
|
||||
domCode = this.document.createElement("code");
|
||||
$tw.utils.each(diffs,function(diff) {
|
||||
var tag = diff[0] === dmp.DIFF_INSERT ? "ins" : (diff[0] === dmp.DIFF_DELETE ? "del" : "span"),
|
||||
className = diff[0] === dmp.DIFF_INSERT ? "tc-diff-insert" : (diff[0] === dmp.DIFF_DELETE ? "tc-diff-delete" : "tc-diff-equal"),
|
||||
dom = self.document.createElement(tag),
|
||||
text = diff[1],
|
||||
currPos = 0,
|
||||
re = /([\x00-\x1F])/mg,
|
||||
match = re.exec(text),
|
||||
span,
|
||||
printable;
|
||||
dom.className = className;
|
||||
while(match) {
|
||||
if(currPos < match.index) {
|
||||
dom.appendChild(self.document.createTextNode(text.slice(currPos,match.index)));
|
||||
}
|
||||
span = self.document.createElement("span");
|
||||
span.className = "tc-diff-invisible";
|
||||
printable = self.invisibleCharacters[match[0]] || ("[0x" + match[0].charCodeAt(0).toString(16) + "]");
|
||||
span.appendChild(self.document.createTextNode(printable));
|
||||
dom.appendChild(span);
|
||||
currPos = match.index + match[0].length;
|
||||
match = re.exec(text);
|
||||
}
|
||||
if(currPos < text.length) {
|
||||
dom.appendChild(self.document.createTextNode(text.slice(currPos)));
|
||||
}
|
||||
domCode.appendChild(dom);
|
||||
});
|
||||
domPre.appendChild(domCode);
|
||||
return domPre;
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
DiffTextWidget.prototype.execute = function() {
|
||||
// Make child widgets
|
||||
var parseTreeNodes;
|
||||
if(this.parseTreeNode && this.parseTreeNode.children && this.parseTreeNode.children.length > 0) {
|
||||
parseTreeNodes = this.parseTreeNode.children;
|
||||
} else {
|
||||
parseTreeNodes = [{
|
||||
type: "transclude",
|
||||
attributes: {
|
||||
tiddler: {type: "string", value: "$:/language/Diffs/CountMessage"}
|
||||
}
|
||||
}];
|
||||
}
|
||||
this.makeChildWidgets(parseTreeNodes);
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
DiffTextWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.source || changedAttributes.dest || changedAttributes.cleanup) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
}
|
||||
};
|
||||
|
||||
exports["diff-text"] = DiffTextWidget;
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user