From 7381a2bfa5067855a8bc027cb6b2611c2bb94c73 Mon Sep 17 00:00:00 2001 From: Keanu Lee Date: Mon, 7 Apr 2014 16:13:21 -0700 Subject: [PATCH 1/2] Added support for atomic tags; ignore tag attributes; interpret whitespace. --- .gitignore | 1 - README.md | 2 +- js/htmldiff.js | 482 +++++++++++++++++++++++++++++ package.json | 4 +- src/htmldiff.coffee | 125 +++++++- test/diff.spec.coffee | 14 +- test/html_to_tokens.spec.coffee | 37 +++ test/render_operations.spec.coffee | 27 ++ 8 files changed, 672 insertions(+), 20 deletions(-) create mode 100644 js/htmldiff.js diff --git a/.gitignore b/.gitignore index 06f62bf..3c3629e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -*.js node_modules diff --git a/README.md b/README.md index 7da4ee1..fce14f0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # htmldiff.js ### HTML Diffing in JavaScript (ok, CoffeeScript actually.) -[![Build Status](https://secure.travis-ci.org/tnwinc/htmldiff.js.png)](http://travis-ci.org/tnwinc/htmldiff.js) +[![Build Status](https://travis-ci.org/keanulee/htmldiff.js.svg?branch=master)](https://travis-ci.org/keanulee/htmldiff.js) `htmldiff.js` is a CoffeeScript port of https://github.com/myobie/htmldiff (This one has a few more tests.) diff --git a/js/htmldiff.js b/js/htmldiff.js new file mode 100644 index 0000000..e4c56f0 --- /dev/null +++ b/js/htmldiff.js @@ -0,0 +1,482 @@ +// Generated by CoffeeScript 1.7.1 +(function() { + var Match, calculate_operations, consecutive_where, create_index, diff, find_match, find_matching_blocks, get_key_for_token, html_to_tokens, is_end_of_atomic_tag, is_end_of_tag, is_start_of_atomic_tag, is_start_of_tag, is_tag, is_whitespace, isnt_tag, op_map, recursively_find_matching_blocks, render_operations, wrap; + + is_end_of_tag = function(char) { + return char === '>'; + }; + + is_start_of_tag = function(char) { + return char === '<'; + }; + + is_whitespace = function(char) { + return /^\s+$/.test(char); + }; + + is_tag = function(token) { + return /^\s*<[^>]+>\s*$/.test(token); + }; + + isnt_tag = function(token) { + return !is_tag(token); + }; + + + /* + * Checks if the current word is the beginning of an atomic tag. An atomic tag is one whose + * child nodes should not be compared - the entire tag should be treated as one token. + * + * @param {string} word The characters of the current token read so far. + * + * @return {string|null} The name of the atomic tag if the word will be an atomic tag, + * null otherwise + */ + + is_start_of_atomic_tag = function(word) { + var result; + result = /^<(iframe|object|math|svg)/.exec(word); + if (result) { + result = result[1]; + } + return result; + }; + + + /* + * Checks if the current word is the end of an atomic tag (i.e. it has all the characters, + * except for the end bracket of the closing tag, such as "

') + .eql ['

', '', '

'] + + it 'should identify an object tag as a single token', -> + (expect @cut '

') + .eql ['

', '', '

'] + + it 'should identify a math tag as a single token', -> + (expect @cut '

' + + 'π' + + '' + + 'r2

') + .eql [ + '

', + '' + + 'π' + + '' + + 'r2', + '

'] + + it 'should identify a svg tag as a single token', -> + (expect @cut '

' + + '' + + '

') + .eql [ + '

', + '' + + '' + + '', + '

'] diff --git a/test/render_operations.spec.coffee b/test/render_operations.spec.coffee index a233315..179889a 100644 --- a/test/render_operations.spec.coffee +++ b/test/render_operations.spec.coffee @@ -63,3 +63,30 @@ describe 'render_operations', -> it 'should keep the change inside the

', -> (expect @res).to.equal '

thisI is awesome

' + + describe 'empty tokens', -> + it 'should not be wrapped', -> + before = ['text'] + after = ['text', ' '] + + @res = @cut before, after + + (expect @res).to.equal 'text' + + describe 'tags with attributes', -> + it 'should treat attribute changes as equal and output the after tag', -> + before = ['

', 'this', ' ', 'is', ' ', 'awesome', '

'] + after = ['

', 'this', ' ', 'is', ' ', 'awesome', '

'] + + @res = @cut before, after + + (expect @res).to.equal '

this is awesome

' + + it 'should show changes within tags with different attributes', -> + before = ['

', 'this', ' ', 'is', ' ', 'awesome', '

'] + after = ['

', 'that', ' ', 'is', ' ', 'awesome', '

'] + + @res = @cut before, after + + (expect @res).to.equal \ + '

thisthat is awesome

' From fcf51222893da261f2d22eca88e8d8bfa1c90962 Mon Sep 17 00:00:00 2001 From: Keanu Lee Date: Tue, 8 Apr 2014 12:27:45 -0700 Subject: [PATCH 2/2] PR comments --- src/htmldiff.coffee | 5 +++-- test/html_to_tokens.spec.coffee | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/htmldiff.coffee b/src/htmldiff.coffee index ec0475c..d746473 100644 --- a/src/htmldiff.coffee +++ b/src/htmldiff.coffee @@ -6,7 +6,8 @@ isnt_tag = (token)-> not is_tag token ### * Checks if the current word is the beginning of an atomic tag. An atomic tag is one whose - * child nodes should not be compared - the entire tag should be treated as one token. + * child nodes should not be compared - the entire tag should be treated as one token. This + * is useful for tags where it does not make sense to insert and tags. * * @param {string} word The characters of the current token read so far. * @@ -118,7 +119,7 @@ html_to_tokens = (html)-> ### * Creates a key that should be used to match tokens. This is useful, for example, if we want * to consider two open tag tokens as equal, even if they don't have the same attributes. We - * use a key instead of overwriting the token because we may want to render original string + * use a key instead of overwriting the token because we may want to render the original string * without losing the attributes. * * @param {string} token The token to create the key for. diff --git a/test/html_to_tokens.spec.coffee b/test/html_to_tokens.spec.coffee index a770b18..8455d61 100644 --- a/test/html_to_tokens.spec.coffee +++ b/test/html_to_tokens.spec.coffee @@ -55,7 +55,7 @@ describe 'html_to_tokens', -> 'r2', '

'] - it 'should identify a svg tag as a single token', -> + it 'should identify an svg tag as a single token', -> (expect @cut '

' + '' + '

')