diff --git a/src/ui/js/diff2html-ui-base.ts b/src/ui/js/diff2html-ui-base.ts index 031d008..20b10e8 100644 --- a/src/ui/js/diff2html-ui-base.ts +++ b/src/ui/js/diff2html-ui-base.ts @@ -52,10 +52,13 @@ export class Diff2HtmlUI { synchronisedScroll(): void { this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => { - const [left, right] = [].slice.call(wrapper.querySelectorAll('.d2h-file-side-diff')) as HTMLElement[]; + const [left, right] = Array().slice.call(wrapper.querySelectorAll('.d2h-file-side-diff')); + if (left === undefined || right === undefined) return; + const onScroll = (event: Event): void => { if (event === null || event.target === null) return; + if (event.target === left) { right.scrollTop = left.scrollTop; right.scrollLeft = left.scrollLeft; @@ -70,29 +73,28 @@ export class Diff2HtmlUI { } fileListToggle(startVisible: boolean): void { - const hashTag = this.getHashTag(); - - const showBtn = this.targetElement.querySelector('.d2h-show') as HTMLElement; - const hideBtn = this.targetElement.querySelector('.d2h-hide') as HTMLElement; - const fileList = this.targetElement.querySelector('.d2h-file-list') as HTMLElement; + const showBtn: HTMLElement | null = this.targetElement.querySelector('d2h-show'); + const hideBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-hide'); + const fileList: HTMLElement | null = this.targetElement.querySelector('.d2h-file-list'); if (showBtn === null || hideBtn === null || fileList === null) return; - function show(): void { + const show: () => void = () => { showBtn.style.display = 'none'; hideBtn.style.display = 'inline'; fileList.style.display = 'block'; - } + }; - function hide(): void { + const hide: () => void = () => { showBtn.style.display = 'inline'; hideBtn.style.display = 'none'; fileList.style.display = 'none'; - } + }; showBtn.addEventListener('click', () => show()); hideBtn.addEventListener('click', () => hide()); + const hashTag = this.getHashTag(); if (hashTag === 'files-summary-show') show(); else if (hashTag === 'files-summary-hide') hide(); else if (startVisible) show(); @@ -117,11 +119,11 @@ export class Diff2HtmlUI { if (this.hljs === null) return; const text = line.textContent; - const lineParent = line.parentNode as HTMLElement; + const lineParent = line.parentNode; - if (lineParent === null || text === null) return; + if (text === null || lineParent === null || !this.isElement(lineParent)) return; - const lineState = lineParent.className.indexOf('d2h-del') !== -1 ? oldLinesState : newLinesState; + const lineState = lineParent.classList.contains('d2h-del') ? oldLinesState : newLinesState; const language = file.getAttribute('data-lang'); const result = @@ -130,9 +132,9 @@ export class Diff2HtmlUI { : this.hljs.highlightAuto(text); if (this.instanceOfIHighlightResult(result)) { - if (lineParent.className.indexOf('d2h-del') !== -1) { + if (lineParent.classList.contains('d2h-del')) { oldLinesState = result.top; - } else if (lineParent.className.indexOf('d2h-ins') !== -1) { + } else if (lineParent.classList.contains('d2h-ins')) { newLinesState = result.top; } else { oldLinesState = result.top; @@ -159,18 +161,15 @@ export class Diff2HtmlUI { const diffTable = body.getElementsByClassName('d2h-diff-table')[0]; diffTable.addEventListener('mousedown', event => { - if (event === null || event.target === null) return; - - const mouseEvent = event as MouseEvent; - const target = mouseEvent.target as HTMLElement; - const table = target.closest('.d2h-diff-table'); + if (event === null || !this.isElement(event.target)) return; + const table = event.target.closest('.d2h-diff-table'); if (table !== null) { - if (target.closest('.d2h-code-line,.d2h-code-side-line') !== null) { + if (event.target.closest('.d2h-code-line,.d2h-code-side-line') !== null) { table.classList.remove('selecting-left'); table.classList.add('selecting-right'); this.currentSelectionColumnId = 1; - } else if (target.closest('.d2h-code-linenumber,.d2h-code-side-linenumber') !== null) { + } else if (event.target.closest('.d2h-code-linenumber,.d2h-code-side-linenumber') !== null) { table.classList.remove('selecting-right'); table.classList.add('selecting-left'); this.currentSelectionColumnId = 0; @@ -179,8 +178,9 @@ export class Diff2HtmlUI { }); diffTable.addEventListener('copy', event => { - const clipboardEvent = event as ClipboardEvent; - const clipboardData = clipboardEvent.clipboardData; + if (!this.isClipboardEvent(event)) return; + + const clipboardData = event.clipboardData; const text = this.getSelectedText(); if (clipboardData === null || text === undefined) return; @@ -231,4 +231,12 @@ export class Diff2HtmlUI { return text; } + + private isElement(arg?: unknown): arg is Element { + return arg !== null && (arg as Element)?.classList !== undefined; + } + + private isClipboardEvent(arg?: unknown): arg is ClipboardEvent { + return arg !== null && (arg as ClipboardEvent)?.clipboardData !== undefined; + } } diff --git a/src/ui/js/highlight.js-helpers.ts b/src/ui/js/highlight.js-helpers.ts index 24e9a38..f1fdccc 100644 --- a/src/ui/js/highlight.js-helpers.ts +++ b/src/ui/js/highlight.js-helpers.ts @@ -63,6 +63,10 @@ export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], va let result = ''; const nodeStack = []; + function isElement(arg?: unknown): arg is Element { + return arg !== null && (arg as Element)?.attributes !== undefined; + } + function selectStream(): NodeEvent[] { if (!original.length || !highlighted.length) { return original.length ? original : highlighted; @@ -88,9 +92,12 @@ export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], va } function open(node: Node): void { - const htmlNode = node as HTMLElement; - result += `<${tag(node)} ${[].map - .call(htmlNode.attributes, (attr: Attr) => `${attr.nodeName}="${escape(attr.value)}"`) + if (!isElement(node)) { + throw new Error('Node is not an Element'); + } + + result += `<${tag(node)} ${Array() + .map.call(node.attributes, attr => `${attr.nodeName}="${escape(attr.value)}"`) .join(' ')}>`; } diff --git a/website/templates/pages/demo/demo.ts b/website/templates/pages/demo/demo.ts index 82010ca..2739fd2 100644 --- a/website/templates/pages/demo/demo.ts +++ b/website/templates/pages/demo/demo.ts @@ -208,6 +208,30 @@ type Elements = { }; }; +function isHTMLInputElement(arg?: unknown): arg is HTMLInputElement { + return arg !== null && (arg as HTMLInputElement)?.value !== undefined; +} + +function getHTMLInputElementById(id: string): HTMLInputElement { + const element = document.getElementById(id); + + if (!isHTMLInputElement(element)) { + throw new Error(`Could not find html input element with id '${id}'`); + } + + return element; +} + +function getHTMLElementById(id: string): HTMLElement { + const element = document.getElementById(id); + + if (element === null) { + throw new Error(`Could not find html element with id '${id}'`); + } + + return element; +} + document.addEventListener('DOMContentLoaded', async () => { // Improves browser compatibility require('whatwg-fetch'); @@ -226,20 +250,20 @@ document.addEventListener('DOMContentLoaded', async () => { const elements: Elements = { structure: { - diffTarget: document.getElementById('url-diff-container') as HTMLElement, + diffTarget: getHTMLElementById('url-diff-container'), }, url: { - input: document.getElementById('url') as HTMLInputElement, - button: document.getElementById('url-btn') as HTMLElement, + input: getHTMLInputElementById('url'), + button: getHTMLElementById('url-btn'), }, options: { - outputFormat: document.getElementById('diff-url-options-output-format') as HTMLInputElement, - matching: document.getElementById('diff-url-options-matching') as HTMLInputElement, - wordsThreshold: document.getElementById('diff-url-options-match-words-threshold') as HTMLInputElement, - matchingMaxComparisons: document.getElementById('diff-url-options-matching-max-comparisons') as HTMLInputElement, + outputFormat: getHTMLInputElementById('diff-url-options-output-format'), + matching: getHTMLInputElementById('diff-url-options-matching'), + wordsThreshold: getHTMLInputElementById('diff-url-options-match-words-threshold'), + matchingMaxComparisons: getHTMLInputElementById('diff-url-options-matching-max-comparisons'), }, checkboxes: { - drawFileList: document.getElementById('diff-url-options-show-files') as HTMLInputElement, + drawFileList: getHTMLInputElementById('diff-url-options-show-files'), }, };