• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

README.asciidocH A D30-Sep-20218.9 KiB174132

diffview.cssH A D30-Sep-20212.5 KiB8377

README.asciidoc

1== jsdifflib - A Javascript visual diff tool & library
2
3* <<intro,Introduction>>
4* <<overview,Overview>>
5* <<python-interop,Python Interoperability>>
6* <<demo,Demo & Examples>>
7** <<diff-js,Diffing using Javascript>>
8** <<diff-python,Diffing using Python>>
9* <<status,Future Directions / Status>>
10* <<license,License>>
11* <<downloads,Downloads>>
12* <<history,Release History>>
13
14[[intro]]
15== Introduction
16
17http://cemerick.com[I] needed a good in-browser visual diff tool, and couldn't find anything suitable, so I built *jsdifflib* in Feb 2007 and open-sourced it soon thereafter.  It's apparently been used a fair bit since then.  Maybe you'll find it useful.
18
19[[overview]]
20== Overview
21
22jsdifflib is a Javascript library that provides:
23
24. a partial reimplementation of Python's difflib module (specifically, the SequenceMatcher class)
25. a visual diff view generator, that offers side-by-side as well as inline formatting of file data
26
27Yes, I ripped off the formatting of the diff view from the Trac project. It's a near-ideal presentation of diff data as far as I'm concerned. If you don't agree, you can hack the CSS to your heart's content.
28
29jsdifflib does not require jQuery or any other Javascript library.
30
31[[python-interop]]
32== Python Interoperability
33
34The main reason why I reimplemented Python's difflib module in Javascript to serve as the algorithmic basis for jsdifflib was that I didn't want to mess with the actual diff algorithm -- I wanted to concentrate on getting the in-browser view right. However, because jsdifflib's API matches Python's difflib's SequenceMatcher class in its entirety, it's trivial to do the actual diffing on the server-side, using Python, and pipe the results of that diff calculation to your in-browser diff view. So, you have the choice of doing everything in Javascript on the browser, or falling back to server-side diff processing if you are diffing really large files.
35
36Most of the time, we do the latter, simply because while jsdifflib is pretty fast all by itself, and is totally usable for diffing "normal" files (i.e. fewer than 100K lines or so), we regularly need to diff files that are 1 or 2 orders of magnitude larger than that. For that, server-side diffing is a necessity.
37
38[[demo]]
39== Demo & Examples
40
41You can give jsdifflib a try without downloading anything. Just click the link below, put some content to be diffed in the two textboxes, and diff away.
42
43http://cemerick.github.com/jsdifflib/demo.html[*Try jsdifflib*]
44
45That page also contains all of the examples you'll need to use jsdifflib yourself, but let's look at them here, anyway.
46
47[[diff-js]]
48=== Diffing using Javascript
49
50Here's the function from the demo HTML file linked to above that diffs the two pieces of text entered into the textboxes on the page:
51
52----
53function diffUsingJS() {
54    // get the baseText and newText values from the two textboxes, and split them into lines
55    var base = difflib.stringAsLines($("baseText").value);
56    var newtxt = difflib.stringAsLines($("newText").value);
57
58    // create a SequenceMatcher instance that diffs the two sets of lines
59    var sm = new difflib.SequenceMatcher(base, newtxt);
60
61    // get the opcodes from the SequenceMatcher instance
62    // opcodes is a list of 3-tuples describing what changes should be made to the base text
63    // in order to yield the new text
64    var opcodes = sm.get_opcodes();
65    var diffoutputdiv = $("diffoutput");
66    while (diffoutputdiv.firstChild) diffoutputdiv.removeChild(diffoutputdiv.firstChild);
67    var contextSize = $("contextSize").value;
68    contextSize = contextSize ? contextSize : null;
69
70    // build the diff view and add it to the current DOM
71    diffoutputdiv.appendChild(diffview.buildView({
72        baseTextLines: base,
73        newTextLines: newtxt,
74        opcodes: opcodes,
75        // set the display titles for each resource
76        baseTextName: "Base Text",
77        newTextName: "New Text",
78        contextSize: contextSize,
79        viewType: $("inline").checked ? 1 : 0
80    }));
81
82    // scroll down to the diff view window.
83    location = url + "#diff";
84}
85----
86
87There's not a whole lot to say about this function. The most notable aspect of it is that the `diffview.buildView()` function takes an object/map with specific attributes, rather than a list of arguments. Those attributes are mostly self-explanatory, but are nonetheless described in detail in code documentation in diffview.js.
88
89[[diff-python]]
90=== Diffing using Python
91
92This isn't enabled in the demo link above, but I've included it to exemplify how one might use the opcode output from a web-based Python backend to drive jsdifflib's diff view.
93
94----
95function diffUsingPython() {
96    dojo.io.bind({
97        url: "/diff/postYieldDiffData",
98        method: "POST",
99        content: {
100            baseText: $("baseText").value,
101            newText: $("newText").value,
102            ignoreWhitespace: "Y"
103        },
104        load: function (type, data, evt) {
105            try {
106                data = eval('(' + data + ')');
107                while (diffoutputdiv.firstChild) diffoutputdiv.removeChild(diffoutputdiv.firstChild);
108                $("output").appendChild(diffview.buildView({
109                    baseTextLines: data.baseTextLines,
110                    newTextLines: data.newTextLines,
111                    opcodes: data.opcodes,
112                    baseTextName: data.baseTextName,
113                    newTextName: data.newTextName,
114                    contextSize: contextSize
115                }));
116            } catch (ex) {
117                alert("An error occurred updating the diff view:\n" + ex.toString());
118            }
119        },
120        error: function (type, evt) {
121            alert('Error occurred getting diff data. Check the server logs.');
122        },
123        type: 'text/javascript'
124    });
125}
126----
127
128[WARNING]
129====
130This dojo code was written in 2007, and I haven't _looked_ at dojo for years now.  In any case, you should be able to grok what's going on.
131====
132
133As you can see, I'm partial to using dojo for ajaxy stuff. All that is happening here is the base and new text is being POSTed to a Python server-side process (we like pylons, but you could just as easily use a simple Python script as a cgi). That process then needs to diff the provided text using an instance of Python's difflib.SequenceMatcher class, and return the opcodes from that SequenceMatcher instance to the browser (in this case, using JSON serialization). In the interest of completeness, here's the controller action from our pylons application that does this (don't try to match up the parameters shown below with the POST parameters shown in the Javascript function above; the latter is only here as an example):
134
135----
136@jsonify
137def diff (self, baseText, newText, baseTextName="Base Text", newTextName="New Text"):
138    opcodes = SequenceMatcher(isjunk, baseText, newText).get_opcodes()
139    return dict(baseTextLines=baseText, newTextLines=newText, opcodes=opcodes,
140                baseTextName=baseTextName, newTextName=newTextName)
141----
142
143[[status]]
144== Future Directions
145
146The top priorities would be to implement the ignoring of empty lines, and the indication of diffs at the character level with sub-highlighting (similar to what Trac's diff view does).
147
148I'd also like to see the `difflib.SequenceMatcher` reimplementation gain some more speed -- it's virtually a line-by-line translation from the Python implementation, so there's plenty that could be done to make it more performant in Javascript. However, that would mean making the reimplementation diverge even more from the "reference" Python implementation. Given that I don't really want to worry about the algorithm, that's not appealing. I'd much rather use a server-side process when the in-browser diffing is a little too pokey.
149
150Other than that, I'm open to suggestions.
151
152[NOTE]
153====
154I'm no longer actively developing jsdifflib.  It's been sequestered (mostly out of simple neglect) to my company's servers for too long; now that it's on github, I'm hoping that many of the people that find it useful will submit pull requests to improve the library.  I will do what I can to curate that process.
155====
156
157[[license]]
158== License
159
160jsdifflib carries a BSD license. As such, it may be used in other products or services with appropriate attribution (including commercial offerings). The license is prepended to each of jsdifflib's files.
161
162[[downloads]]
163== Downloads
164
165jsdifflib consists of three files -- two Javascript files, and one CSS file. Why two Javascript files? Because I wanted to keep the reimplementation of the python difflib.SequenceMatcher class separate from the actual visual diff view generator. Feel free to combine and/or optimize them in your deployment environment.
166
167You can download the files separately by navigating the project on github, you can clone the repo, or you can download a zipped distribution via the "Downloads" button at the top of this project page.
168
169[[history]]
170== Release History
171
172* 1.1.0 (May 18, 2011): Move project to github; no changes in functionality
173* 1.0.0 (February 22, 2007): Initial release
174