• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

divio / django-cms / #30103

12 Nov 2025 03:42PM UTC coverage: 90.532%. Remained the same
#30103

push

travis-ci

web-flow
Merge 80b4ee016 into c38b75715

1306 of 2044 branches covered (63.89%)

294 of 321 new or added lines in 13 files covered. (91.59%)

458 existing lines in 7 files now uncovered.

9151 of 10108 relevant lines covered (90.53%)

11.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.67
/cms/static/cms/js/modules/dom-diff.js
1
/* eslint-env browser */
2
/* global DOMParser */
3
/*
4
 * Copyright https://github.com/django-cms/django-cms
5
 * Simple DOM diffing replacement using native browser APIs
6
 * Replaces diff-dom
7
 */
8

9
/**
10
 * Converts a DOM node to a plain object representation
11
 * @param {Node} node - DOM node to convert
12
 * @returns {Object} Object representation of the node
13
 */
14
export function nodeToObj(node) {
15
    if (!node || !node.nodeType) {
17✔
16
        return null;
4✔
17
    }
18

19
    if (node.nodeType === Node.TEXT_NODE) {
13✔
20
        return {
5✔
21
            nodeName: '#text',
22
            data: node.data
23
        };
24
    }
25

26
    if (node.nodeType === Node.ELEMENT_NODE) {
8!
27
        const obj = {
8✔
28
            nodeName: node.nodeName,
29
            attributes: {},
30
            childNodes: []
31
        };
32

33
        // Copy attributes
34
        for (const attr of node.attributes) {
8✔
35
            obj.attributes[attr.name] = attr.value;
3✔
36
        }
37

38
        // Copy child nodes
39
        for (const child of node.childNodes) {
8✔
40
            obj.childNodes.push(nodeToObj(child));
9✔
41
        }
42

43
        return obj;
8✔
44
    }
45

NEW
46
    return null;
×
47
}
48

49
/**
50
 * Simple DOM differ that uses native browser APIs
51
 * This is a lightweight replacement for DiffDOM
52
 */
53
export class DiffDOM {
54
    constructor() {
55
        // DOMParser is supported by all browsers in our Browserslist; we can rely on it.
56
        this.parser = new DOMParser();
146✔
57
    }
58

59
    /**
60
     * Calculate diff between old node and new HTML/object
61
     * Note: This is a simplified version that doesn't return a diff object
62
     * Instead, we'll apply changes directly in the apply() method
63
     *
64
     * @param {Node} oldNode - Current DOM node
65
     * @param {string|Object} newContent - New HTML string or node object
66
     * @returns {Object} Diff information
67
     */
68
    diff(oldNode, newContent) {
69
        let newNode;
70

71
        if (typeof newContent === 'string') {
15✔
72
            // Parse HTML string via DOMParser; unwrap the outer container (div) and use its children
73
            const doc = this.parser.parseFromString(newContent, 'text/html');
13✔
74

75
            newNode = doc.body.firstChild || doc.head.firstChild;
13!
76
        } else if (typeof newContent === 'object' && newContent.nodeName) {
2!
77
            // Convert object to DOM node
78
            newNode = this._objToNode(newContent);
2✔
79
        } else {
NEW
80
            newNode = newContent;
×
81
        }
82

83
        return {
15✔
84
            oldNode,
85
            newNode
86
        };
87
    }
88

89
    /**
90
     * Apply diff to update the DOM
91
     * @param {Node} target - Target node to update
92
     * @param {Object} diff - Diff object from diff()
93
     */
94
    apply(target, diff) {
95
        const { newNode } = diff;
15✔
96

97
        if (!newNode) {
15!
NEW
98
            return;
×
99
        }
100

101
        // Clear existing content
102
        while (target.firstChild) {
15✔
103
            target.removeChild(target.firstChild);
8✔
104
        }
105

106
        // Add new content
107
        if (newNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
15!
108
            // Clone all children from fragment
NEW
109
            while (newNode.firstChild) {
×
NEW
110
                target.appendChild(newNode.firstChild);
×
111
            }
112
        } else if (newNode.childNodes) {
15!
113
            // Copy all child nodes
114
            for (const child of Array.from(newNode.childNodes)) {
15✔
115
                target.appendChild(child.cloneNode(true));
30✔
116
            }
117
        }
118
    }
119

120
    /**
121
     * Convert object representation back to DOM node
122
     * @param {Object} obj - Object representation
123
     * @returns {Node} DOM node
124
     * @private
125
     */
126
    _objToNode(obj) {
127
        if (!obj) {
8!
NEW
128
            return null;
×
129
        }
130

131
        if (obj.nodeName === '#text') {
8✔
132
            return document.createTextNode(obj.data || '');
2!
133
        }
134

135
        const node = document.createElement(obj.nodeName);
6✔
136

137
        // Set attributes
138
        if (obj.attributes) {
6!
139
            for (const [name, value] of Object.entries(obj.attributes)) {
6✔
140
                node.setAttribute(name, value);
5✔
141
            }
142
        }
143

144
        // Add child nodes
145
        if (obj.childNodes) {
6!
146
            for (const child of obj.childNodes) {
6✔
147
                const childNode = this._objToNode(child);
6✔
148

149
                if (childNode) {
6!
150
                    node.appendChild(childNode);
6✔
151
                }
152
            }
153
        }
154

155
        return node;
6✔
156
    }
157
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc