Skip to content

Commit d007c7b

Browse files
committed
Add caseSensitive configuration option, solves #128
1 parent 24a11da commit d007c7b

File tree

6 files changed

+88
-11
lines changed

6 files changed

+88
-11
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,21 @@ dd = new diffDOM.DiffDOM({
202202
#### Disable value diff detection
203203

204204
For forms that have been filled out by a user in ways that have changed which value is associated with an input field or which options are checked/selected without
205-
the DOM having been updated, the values are diffed. For use cases in which no changes have been made to any of the form values, one may choose to skip diffing the values. To do this, hand `false` as a third configuration option to diffDOM:
205+
the DOM having been updated, the values are diffed. For use cases in which no changes have been made to any of the form values, one may choose to skip diffing the values. To do this, set `valueDiffing` to `false` as a configuration option to diffDOM:
206206

207207
```js
208208
dd = new diffDOM.DiffDOM({
209209
valueDiffing: false,
210210
})
211211
```
212+
213+
#### Interprete strings as case caseSensitive
214+
215+
Strings of HTML can normally be interpreted case-insensitively as HTML tags don't differentiate between uppercase and lowercase. However, in the case of XML (SVGs, XHTML) there is a difference and this should be enabled. To do this, set `caseSensitive` to `true` as a configuration option to diffDOM:
216+
217+
218+
```js
219+
dd = new diffDOM.DiffDOM({
220+
caseSensitive: true,
221+
})
222+
```

browser/diffDOM.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

browser/diffDOM.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/diffDOM/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ interface DiffDOMOptions {
105105
maxDepth: number | false // False or a numeral. If set to a numeral, limits the level of depth that the the diff mechanism looks for differences. If false, goes through the entire tree.
106106
maxChildCount: number // False or a numeral. If set to a numeral, only does a simplified form of diffing of contents so that the number of diffs cannot be higher than the number of child nodes.
107107
valueDiffing: boolean // Whether to take into consideration the values of forms that differ from auto assigned values (when a user fills out a form).
108+
caseSensitive: boolean // Whether to preserve the case of an input string. Important when including CML (XHTML, SVG, etc.)
108109
// syntax: textDiff: function (node, currentValue, expectedValue, newValue)
109110
textDiff: (
110111
node: textNodeType | Text | Comment,

src/diffDOM/virtual/fromString.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const lookup = {
3636
wbr: true,
3737
}
3838

39-
const parseTag = (tag: string) => {
39+
const parseTag = (tag: string, caseSensitive: boolean) => {
4040
const res = {
4141
nodeName: "",
4242
attributes: {},
@@ -46,7 +46,7 @@ const parseTag = (tag: string) => {
4646

4747
let tagMatch = tag.match(/<\/?([^\s]+?)[/\s>]/)
4848
if (tagMatch) {
49-
res.nodeName = tagMatch[1]
49+
res.nodeName = caseSensitive ? tagMatch[1] : tagMatch[1].toUpperCase()
5050
if (lookup[tagMatch[1]] || tag.charAt(tag.length - 2) === "/") {
5151
voidElement = true
5252
}
@@ -97,7 +97,7 @@ const parseTag = (tag: string) => {
9797

9898
export const stringToObj = (
9999
html: string,
100-
options: DiffDOMOptionsPartial = { valueDiffing: true }
100+
options: DiffDOMOptionsPartial = { valueDiffing: true, caseSensitive: false }
101101
) => {
102102
const result: nodeType[] = []
103103
let current: { type: string; node: nodeType; voidElement: boolean }
@@ -128,7 +128,7 @@ export const stringToObj = (
128128
const nextChar = html.charAt(start)
129129

130130
if (isComment) {
131-
const comment = parseTag(tag).node
131+
const comment = parseTag(tag, options.caseSensitive).node
132132

133133
// if we're at root, push new base node
134134
if (level < 0) {
@@ -146,7 +146,7 @@ export const stringToObj = (
146146
}
147147

148148
if (isOpen) {
149-
current = parseTag(tag)
149+
current = parseTag(tag, options.caseSensitive)
150150
level++
151151
if (
152152
!current.voidElement &&
@@ -188,9 +188,11 @@ export const stringToObj = (
188188
if (!isOpen || current.voidElement) {
189189
if (
190190
level > -1 &&
191-
(current.voidElement ||
192-
current.node.nodeName.toUpperCase() ===
193-
tag.slice(2, -1).toUpperCase())
191+
(
192+
current.voidElement ||
193+
(options.caseSensitive && current.node.nodeName === tag.slice(2, -1)) ||
194+
(!options.caseSensitive && current.node.nodeName.toUpperCase() === tag.slice(2, -1).toUpperCase())
195+
)
194196
) {
195197
level--
196198
// move current up a level to match the end tag

tests/xmlString.test.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
import { DiffDOM, nodeToObj, stringToObj } from "../dist/index"
6+
7+
const strings = [
8+
`<svg height=50 width=50>
9+
<defs>
10+
<clipPath id="clipPath">
11+
<rect x="15" y="15" width="40" height="40" />
12+
</clipPath>
13+
</defs>
14+
15+
<circle cx=25 cy=25 r=20
16+
style="fill: #0000ff; clip-path: url(#clipPath);"
17+
/>
18+
</svg>`,
19+
`<svg></svg>`,
20+
21+
`<svg></svg>`,
22+
`<svg height=50 width=50>
23+
<defs>
24+
<clipPath id="clipPath">
25+
<rect x="15" y="15" width="40" height="40" />
26+
</clipPath>
27+
</defs>
28+
29+
<circle cx=25 cy=25 r=20
30+
style="fill: #0000ff; clip-path: url(#clipPath);"
31+
/>
32+
</svg>`,
33+
]
34+
35+
describe("string", () => {
36+
it("can diff and patch case sensitive xml strings", () => {
37+
const dd = new DiffDOM({
38+
debug: true,
39+
diffcap: 500,
40+
caseSensitive: true,
41+
})
42+
43+
for (let i = 0; i < strings.length; i += 2) {
44+
const el1Outer = document.createElement("div")
45+
const el2Outer = document.createElement("div")
46+
el1Outer.innerHTML = strings[i]
47+
el2Outer.innerHTML = strings[i + 1]
48+
const diffs = dd.diff(strings[i], strings[i + 1])
49+
expect(diffs).not.toHaveLength(0)
50+
const el1 = el1Outer.firstElementChild
51+
const el2 = el2Outer.firstElementChild
52+
const el1a = el1.cloneNode(true)
53+
dd.apply(el1a, diffs)
54+
expect(
55+
el1a.innerHTML
56+
).toEqual(el2.innerHTML)
57+
dd.undo(el1a, diffs)
58+
expect(
59+
el1a.innerHTML
60+
).toEqual(el1.innerHTML)
61+
}
62+
})
63+
})

0 commit comments

Comments
 (0)