-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
diff-text.js
148 lines (135 loc) · 4.07 KB
/
diff-text.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*\
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",diffs.reduce(function(acc,diff) {
if(diff[0] !== dmp.DIFF_EQUAL) {
acc++;
}
return acc;
},0).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;
})();