import BaseFormatter from './base.js'; class HtmlFormatter extends BaseFormatter { typeFormattterErrorFormatter(context, err) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions context.out(`
${err}
`); } formatValue(context, value) { context.out(`
${htmlEscape(JSON.stringify(value, null, 2))}
`); } formatTextDiffString(context, value) { const lines = this.parseTextDiff(value); context.out(''); } rootBegin(context, type, nodeType) { const nodeClass = `jsondiffpatch-${type}${nodeType ? ` jsondiffpatch-child-node-type-${nodeType}` : ''}`; context.out(`
`); } rootEnd(context) { context.out(`
${context.hasArrows ? '` : ''}`); } nodeBegin(context, key, leftKey, type, nodeType) { const nodeClass = `jsondiffpatch-${type}${nodeType ? ` jsondiffpatch-child-node-type-${nodeType}` : ''}`; context.out(`
  • ` + `
    ${leftKey}
    `); } nodeEnd(context) { context.out('
  • '); } format_unchanged(context, delta, left) { if (typeof left === 'undefined') { return; } context.out('
    '); this.formatValue(context, left); context.out('
    '); } format_movedestination(context, delta, left) { if (typeof left === 'undefined') { return; } context.out('
    '); this.formatValue(context, left); context.out('
    '); } format_node(context, delta, left) { // recurse const nodeType = delta._t === 'a' ? 'array' : 'object'; context.out(`'); } format_added(context, delta) { context.out('
    '); this.formatValue(context, delta[0]); context.out('
    '); } format_modified(context, delta) { context.out('
    '); this.formatValue(context, delta[0]); context.out('
    ' + '
    '); this.formatValue(context, delta[1]); context.out('
    '); } format_deleted(context, delta) { context.out('
    '); this.formatValue(context, delta[0]); context.out('
    '); } format_moved(context, delta) { context.out('
    '); this.formatValue(context, delta[0]); context.out(`
    ${delta[1]}
    `); // draw an SVG arrow from here to move destination context.out( /* jshint multistr: true */ '
    `); context.hasArrows = true; } format_textdiff(context, delta) { context.out('
    '); this.formatTextDiffString(context, delta[0]); context.out('
    '); } } function htmlEscape(text) { let html = text; const replacements = [ [/&/g, '&'], [//g, '>'], [/'/g, '''], [/"/g, '"'], ]; for (let i = 0; i < replacements.length; i++) { html = html.replace(replacements[i][0], replacements[i][1]); } return html; } const adjustArrows = function jsondiffpatchHtmlFormatterAdjustArrows(nodeArg) { const node = nodeArg || document; const getElementText = ({ textContent, innerText }) => textContent || innerText; const eachByQuery = (el, query, fn) => { const elems = el.querySelectorAll(query); for (let i = 0, l = elems.length; i < l; i++) { fn(elems[i]); } }; const eachChildren = ({ children }, fn) => { for (let i = 0, l = children.length; i < l; i++) { fn(children[i], i); } }; eachByQuery(node, '.jsondiffpatch-arrow', ({ parentNode, children, style }) => { const arrowParent = parentNode; const svg = children[0]; const path = svg.children[1]; svg.style.display = 'none'; const destination = getElementText(arrowParent.querySelector('.jsondiffpatch-moved-destination')); const container = arrowParent.parentNode; let destinationElem; eachChildren(container, (child) => { if (child.getAttribute('data-key') === destination) { destinationElem = child; } }); if (!destinationElem) { return; } try { const distance = destinationElem.offsetTop - arrowParent.offsetTop; svg.setAttribute('height', `${Math.abs(distance) + 6}`); style.top = `${-8 + (distance > 0 ? 0 : distance)}px`; const curve = distance > 0 ? `M30,0 Q-10,${Math.round(distance / 2)} 26,${distance - 4}` : `M30,${-distance} Q-10,${Math.round(-distance / 2)} 26,4`; path.setAttribute('d', curve); svg.style.display = ''; } catch (err) { // continue regardless of error } }); }; export const showUnchanged = (show, node, delay) => { const el = node || document.body; const prefix = 'jsondiffpatch-unchanged-'; const classes = { showing: `${prefix}showing`, hiding: `${prefix}hiding`, visible: `${prefix}visible`, hidden: `${prefix}hidden`, }; const list = el.classList; if (!list) { return; } if (!delay) { list.remove(classes.showing); list.remove(classes.hiding); list.remove(classes.visible); list.remove(classes.hidden); if (show === false) { list.add(classes.hidden); } return; } if (show === false) { list.remove(classes.showing); list.add(classes.visible); setTimeout(() => { list.add(classes.hiding); }, 10); } else { list.remove(classes.hiding); list.add(classes.showing); list.remove(classes.hidden); } const intervalId = setInterval(() => { adjustArrows(el); }, 100); setTimeout(() => { list.remove(classes.showing); list.remove(classes.hiding); if (show === false) { list.add(classes.hidden); list.remove(classes.visible); } else { list.add(classes.visible); list.remove(classes.hidden); } setTimeout(() => { list.remove(classes.visible); clearInterval(intervalId); }, delay + 400); }, delay); }; export const hideUnchanged = (node, delay) => showUnchanged(false, node, delay); export default HtmlFormatter; let defaultInstance; export function format(delta, left) { if (!defaultInstance) { defaultInstance = new HtmlFormatter(); } return defaultInstance.format(delta, left); }