Merge pull request #277 from rtfpessoa/improve-api-for-standalone-usage

clean: Improve diff2html-ui APIs for standalone usage
This commit is contained in:
Rodrigo Fernandes 2020-01-08 22:45:57 +00:00 committed by GitHub
commit 746d3e625e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 38 deletions

View file

@ -254,8 +254,8 @@ export default {
> Create a Diff2HtmlUI instance > Create a Diff2HtmlUI instance
```ts ```ts
constructor(diffInput: string | DiffFile[], target: HTMLElement) // diff2html-ui, diff2html-ui-slim constructor(target: HTMLElement, diffInput?: string | DiffFile[]) // diff2html-ui, diff2html-ui-slim
constructor(diffInput: string | DiffFile[], target: HTMLElement, config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) // diff2html-ui-base constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) // diff2html-ui-base
``` ```
> Generate and inject in the document the Pretty HTML representation of the diff > Generate and inject in the document the Pretty HTML representation of the diff
@ -270,6 +270,7 @@ draw(): void
synchronisedScroll(): void synchronisedScroll(): void
fileListToggle(startVisible: boolean): void fileListToggle(startVisible: boolean): void
highlightCode(): void highlightCode(): void
smartSelection(): void
``` ```
> Check out the [docs/demo.html](./docs/demo.html) for a demo example. > Check out the [docs/demo.html](./docs/demo.html) for a demo example.
@ -303,9 +304,9 @@ highlightCode(): void
const targetElement = document.getElementById('destination-elem-id'); const targetElement = document.getElementById('destination-elem-id');
const configuration = { drawFileList: true, matching: 'lines' }; const configuration = { drawFileList: true, matching: 'lines' };
const diff2htmlUi = new Diff2HtmlUI(diffString, targetElement, configuration); const diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
// or // or
const diff2htmlUi = new Diff2HtmlUI(diffJson, targetElement, configuration); const diff2htmlUi = new Diff2HtmlUI(targetElement, diffJson, configuration);
``` ```
#### Draw #### Draw
@ -338,7 +339,7 @@ index 0000001..0ddf2ba
+console.log("Hello from Diff2Html!")`; +console.log("Hello from Diff2Html!")`;
const targetElement = document.getElementById('myDiffElement'); const targetElement = document.getElementById('myDiffElement');
const configuration = { inputFormat: 'json', drawFileList: true, matching: 'lines', highlight: true }; const configuration = { inputFormat: 'json', drawFileList: true, matching: 'lines', highlight: true };
const diff2htmlUi = new Diff2HtmlUI(diffString, targetElement, configuration); const diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
diff2htmlUi.draw(); diff2htmlUi.draw();
diff2htmlUi.highlightCode(); diff2htmlUi.highlightCode();
}); });
@ -359,7 +360,7 @@ index 0000001..0ddf2ba
```js ```js
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const targetElement = document.getElementById('myDiffElement'); const targetElement = document.getElementById('myDiffElement');
var diff2htmlUi = new Diff2HtmlUI(lineDiffExample, targetElement, { drawFileList: true, matching: 'lines' }); var diff2htmlUi = new Diff2HtmlUI(targetElement, lineDiffExample, { drawFileList: true, matching: 'lines' });
diff2htmlUi.draw(); diff2htmlUi.draw();
diff2htmlUi.fileListToggle(false); diff2htmlUi.fileListToggle(false);
}); });

View file

@ -832,5 +832,109 @@ describe('Diff2Html', () => {
</div>" </div>"
`); `);
}); });
it('should generate html correctly without escaping twice', () => {
const diff =
'--- src/index.html\n' +
'+++ src/index.html\n' +
'@@ -1,2 +1,2 @@\n' +
'-<!-- commented code -->\n' +
'-</div>\n' +
'+<html>\n' +
'+<body>';
const result = html(diff);
expect(result).toMatchInlineSnapshot(`
"<div class=\\"d2h-file-list-wrapper\\">
<div class=\\"d2h-file-list-header\\">
<span class=\\"d2h-file-list-title\\">Files changed (1)</span>
<a class=\\"d2h-file-switch d2h-hide\\">hide</a>
<a class=\\"d2h-file-switch d2h-show\\">show</a>
</div>
<ol class=\\"d2h-file-list\\">
<li class=\\"d2h-file-list-line\\">
<span class=\\"d2h-file-name-wrapper\\">
<svg aria-hidden=\\"true\\" class=\\"d2h-icon d2h-changed\\" height=\\"16\\" title=\\"modified\\" version=\\"1.1\\"
viewBox=\\"0 0 14 16\\" width=\\"14\\">
<path d=\\"M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z\\"></path>
</svg> <a href=\\"#d2h-597266\\" class=\\"d2h-file-name\\">src/index.html</a>
<span class=\\"d2h-file-stats\\">
<span class=\\"d2h-lines-added\\">+2</span>
<span class=\\"d2h-lines-deleted\\">-2</span>
</span>
</span>
</li>
</ol>
</div><div class=\\"d2h-wrapper\\">
<div id=\\"d2h-597266\\" class=\\"d2h-file-wrapper\\" data-lang=\\"html\\">
<div class=\\"d2h-file-header\\">
<span class=\\"d2h-file-name-wrapper\\">
<svg aria-hidden=\\"true\\" class=\\"d2h-icon\\" height=\\"16\\" version=\\"1.1\\" viewBox=\\"0 0 12 16\\" width=\\"12\\">
<path d=\\"M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z\\"></path>
</svg> <span class=\\"d2h-file-name\\">src/index.html</span>
<span class=\\"d2h-tag d2h-changed d2h-changed-tag\\">CHANGED</span></span>
</div>
<div class=\\"d2h-file-diff\\">
<div class=\\"d2h-code-wrapper\\">
<table class=\\"d2h-diff-table\\">
<tbody class=\\"d2h-diff-tbody\\">
<tr>
<td class=\\"d2h-code-linenumber d2h-info\\"></td>
<td class=\\"d2h-info\\">
<div class=\\"d2h-code-line d2h-info\\">@@ -1,2 +1,2 @@</div>
</td>
</tr><tr>
<td class=\\"d2h-code-linenumber d2h-del d2h-change\\">
<div class=\\"line-num1\\">1</div>
<div class=\\"line-num2\\"></div>
</td>
<td class=\\"d2h-del d2h-change\\">
<div class=\\"d2h-code-line d2h-del d2h-change\\">
<span class=\\"d2h-code-line-prefix\\">-</span>
<span class=\\"d2h-code-line-ctn\\"><del>&lt;!-- commented code --&gt;</del></span>
</div>
</td>
</tr><tr>
<td class=\\"d2h-code-linenumber d2h-del d2h-change\\">
<div class=\\"line-num1\\">2</div>
<div class=\\"line-num2\\"></div>
</td>
<td class=\\"d2h-del d2h-change\\">
<div class=\\"d2h-code-line d2h-del d2h-change\\">
<span class=\\"d2h-code-line-prefix\\">-</span>
<span class=\\"d2h-code-line-ctn\\"><del>&lt;&#x2F;div</del>&gt;</span>
</div>
</td>
</tr><tr>
<td class=\\"d2h-code-linenumber d2h-ins d2h-change\\">
<div class=\\"line-num1\\"></div>
<div class=\\"line-num2\\">1</div>
</td>
<td class=\\"d2h-ins d2h-change\\">
<div class=\\"d2h-code-line d2h-ins d2h-change\\">
<span class=\\"d2h-code-line-prefix\\">+</span>
<span class=\\"d2h-code-line-ctn\\"><ins>&lt;html&gt;</ins></span>
</div>
</td>
</tr><tr>
<td class=\\"d2h-code-linenumber d2h-ins d2h-change\\">
<div class=\\"line-num1\\"></div>
<div class=\\"line-num2\\">2</div>
</td>
<td class=\\"d2h-ins d2h-change\\">
<div class=\\"d2h-code-line d2h-ins d2h-change\\">
<span class=\\"d2h-code-line-prefix\\">+</span>
<span class=\\"d2h-code-line-ctn\\"><ins>&lt;body</ins>&gt;</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>"
`);
});
}); });
}); });

View file

@ -93,11 +93,11 @@ export function escapeForHtml(str: string): string {
/** /**
* Deconstructs diff @line by separating the content from the prefix type * Deconstructs diff @line by separating the content from the prefix type
*/ */
export function deconstructLine(line: string, isCombined: boolean): DiffLineParts { export function deconstructLine(line: string, isCombined: boolean, escape = true): DiffLineParts {
const indexToSplit = prefixLength(isCombined); const indexToSplit = prefixLength(isCombined);
return { return {
prefix: line.substring(0, indexToSplit), prefix: line.substring(0, indexToSplit),
content: escapeForHtml(line.substring(indexToSplit)), content: escape ? escapeForHtml(line.substring(indexToSplit)) : line.substring(indexToSplit),
}; };
} }
@ -216,8 +216,8 @@ export function diffHighlight(
): HighlightedLines { ): HighlightedLines {
const { matching, maxLineLengthHighlight, matchWordsThreshold, diffStyle } = { ...defaultRenderConfig, ...config }; const { matching, maxLineLengthHighlight, matchWordsThreshold, diffStyle } = { ...defaultRenderConfig, ...config };
const line1 = deconstructLine(diffLine1, isCombined); const line1 = deconstructLine(diffLine1, isCombined, false);
const line2 = deconstructLine(diffLine2, isCombined); const line2 = deconstructLine(diffLine2, isCombined, false);
if (line1.content.length > maxLineLengthHighlight || line2.content.length > maxLineLengthHighlight) { if (line1.content.length > maxLineLengthHighlight || line2.content.length > maxLineLengthHighlight) {
return { return {
@ -256,10 +256,11 @@ export function diffHighlight(
const highlightedLine = diff.reduce((highlightedLine, part) => { const highlightedLine = diff.reduce((highlightedLine, part) => {
const elemType = part.added ? 'ins' : part.removed ? 'del' : null; const elemType = part.added ? 'ins' : part.removed ? 'del' : null;
const addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : ''; const addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
const escapedValue = escapeForHtml(part.value);
return elemType !== null return elemType !== null
? `${highlightedLine}<${elemType}${addClass}>${part.value}</${elemType}>` ? `${highlightedLine}<${elemType}${addClass}>${escapedValue}</${elemType}>`
: `${highlightedLine}${part.value}`; : `${highlightedLine}${escapedValue}`;
}, ''); }, '');
return { return {

View file

@ -29,16 +29,21 @@ export class Diff2HtmlUI {
currentSelectionColumnId = -1; currentSelectionColumnId = -1;
constructor(diffInput: string | DiffFile[], target: HTMLElement, config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) { constructor(
target: HTMLElement,
diffInput?: string | DiffFile[],
config: Diff2HtmlUIConfig = {},
hljs?: HighlightJS,
) {
this.config = { ...defaultDiff2HtmlUIConfig, ...config }; this.config = { ...defaultDiff2HtmlUIConfig, ...config };
this.diffHtml = html(diffInput, this.config); this.diffHtml = diffInput !== undefined ? html(diffInput, this.config) : target.innerHTML;
this.targetElement = target; this.targetElement = target;
if (hljs !== undefined) this.hljs = hljs; if (hljs !== undefined) this.hljs = hljs;
} }
draw(): void { draw(): void {
this.targetElement.innerHTML = this.diffHtml; this.targetElement.innerHTML = this.diffHtml;
if (this.config.smartSelection) this.initSelection(); if (this.config.smartSelection) this.smartSelection();
if (this.config.synchronisedScroll) this.synchronisedScroll(); if (this.config.synchronisedScroll) this.synchronisedScroll();
if (this.config.highlight) this.highlightCode(); if (this.config.highlight) this.highlightCode();
if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible); if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible);
@ -142,29 +147,13 @@ export class Diff2HtmlUI {
} }
line.classList.add('hljs'); line.classList.add('hljs');
line.classList.add('result.language'); line.classList.add(result.language);
line.innerHTML = result.value; line.innerHTML = result.value;
}); });
}); });
} }
private instanceOfIHighlightResult(object: IHighlightResult | IAutoHighlightResult): object is IHighlightResult { smartSelection(): void {
return 'top' in object;
}
private getHashTag(): string | null {
const docUrl = document.URL;
const hashTagIndex = docUrl.indexOf('#');
let hashTag = null;
if (hashTagIndex !== -1) {
hashTag = docUrl.substr(hashTagIndex + 1);
}
return hashTag;
}
private initSelection(): void {
const body = document.getElementsByTagName('body')[0]; const body = document.getElementsByTagName('body')[0];
const diffTable = body.getElementsByClassName('d2h-diff-table')[0]; const diffTable = body.getElementsByClassName('d2h-diff-table')[0];
@ -200,6 +189,22 @@ export class Diff2HtmlUI {
}); });
} }
private instanceOfIHighlightResult(object: IHighlightResult | IAutoHighlightResult): object is IHighlightResult {
return 'top' in object;
}
private getHashTag(): string | null {
const docUrl = document.URL;
const hashTagIndex = docUrl.indexOf('#');
let hashTag = null;
if (hashTagIndex !== -1) {
hashTag = docUrl.substr(hashTagIndex + 1);
}
return hashTag;
}
private getSelectedText(): string | undefined { private getSelectedText(): string | undefined {
const sel = window.getSelection(); const sel = window.getSelection();

View file

@ -4,8 +4,8 @@ import { DiffFile } from '../../types';
import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base'; import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base';
export class Diff2HtmlUI extends Diff2HtmlUIBase { export class Diff2HtmlUI extends Diff2HtmlUIBase {
constructor(diffInput: string | DiffFile[], target: HTMLElement, config: Diff2HtmlUIConfig = {}) { constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}) {
super(diffInput, target, config, hljs); super(target, diffInput, config, hljs);
} }
} }

View file

@ -4,8 +4,8 @@ import { DiffFile } from '../../types';
import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base'; import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base';
export class Diff2HtmlUI extends Diff2HtmlUIBase { export class Diff2HtmlUI extends Diff2HtmlUIBase {
constructor(diffInput: string | DiffFile[], target: HTMLElement, config: Diff2HtmlUIConfig = {}) { constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}) {
super(diffInput, target, config, hljs); super(target, diffInput, config, hljs);
} }
} }

View file

@ -151,7 +151,7 @@ async function getDiff(request: Request): Promise<string> {
} }
function draw(diffString: string, config: Diff2HtmlUIConfig, elements: Elements): void { function draw(diffString: string, config: Diff2HtmlUIConfig, elements: Elements): void {
const diff2htmlUi = new Diff2HtmlUI(diffString, elements.structure.diffTarget, config); const diff2htmlUi = new Diff2HtmlUI(elements.structure.diffTarget, diffString, config);
diff2htmlUi.draw(); diff2htmlUi.draw();
} }