wip: tweaks to website

This commit is contained in:
Rodrigo Fernandes 2019-10-13 19:21:19 +01:00
parent 4f607633dd
commit f72ee2ea46
No known key found for this signature in database
GPG key ID: 67157D2E3D4258B4
18 changed files with 315 additions and 330 deletions

View file

@ -8,13 +8,13 @@ jobs:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
- run: npm install
- run: yarn
- save_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- run: npm run coverage
- run: npm run check-coverage
- run: yarn run build
- run: yarn run coverage
build-latest: &latest-build
docker:
@ -29,6 +29,7 @@ jobs:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
- run: yarn run build
- run: yarn run lint
- run: yarn run coverage
- run: yarn run codacy
@ -38,40 +39,25 @@ jobs:
- docs
- build
build-node_4:
<<: *common-build
docker:
- image: node:4
build-node_5:
<<: *common-build
docker:
- image: node:5
build-node_6:
<<: *common-build
docker:
- image: node:6
build-node_7:
<<: *common-build
docker:
- image: node:7
build-node_8:
<<: *common-build
docker:
- image: node:8
build-node_9:
build-node_10:
<<: *common-build
docker:
- image: node:9
- image: node:10
build-node_10:
build-node_11:
<<: *common-build
docker:
- image: node:11
build-node_12:
<<: *latest-build
docker:
- image: node:10
- image: node:12
deploy:
machine:
@ -89,16 +75,13 @@ workflows:
version: 2
build:
jobs:
- build-node_4
- build-node_5
- build-node_6
- build-node_7
- build-node_8
- build-node_9
- build-node_10
- build-node_11
- build-node_12
- deploy:
requires:
- build-node_10
- build-node_12
filters:
branches:
only: master

View file

@ -12,14 +12,7 @@
"es6": true,
"node": true,
"browser": true,
"commonjs": true,
"jquery": true,
"phantomjs": true,
"jasmine": true,
"mocha": true,
"amd": true,
"worker": true,
"qunit": true
"commonjs": true
},
"plugins": ["standard", "node", "import", "promise", "@typescript-eslint", "jest"],
"globals": {

209
README.md
View file

@ -1,9 +1,8 @@
# diff2html
[![Codacy Quality Badge](https://api.codacy.com/project/badge/Grade/06412dc3f5a14f568778d0db8a1f7dc8)](https://www.codacy.com/app/rtfpessoa/diff2html?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=rtfpessoa/diff2html&amp;utm_campaign=Badge_Grade)
[![Codacy Quality Badge](https://api.codacy.com/project/badge/Grade/06412dc3f5a14f568778d0db8a1f7dc8)](https://www.codacy.com/app/rtfpessoa/diff2html?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html&utm_campaign=Badge_Grade)
[![Codacy Coverage Badge](https://api.codacy.com/project/badge/Coverage/06412dc3f5a14f568778d0db8a1f7dc8)](https://www.codacy.com/app/rtfpessoa/diff2html?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html&utm_campaign=Badge_Coverage)
[![Circle CI](https://circleci.com/gh/rtfpessoa/diff2html.svg?style=svg)](https://circleci.com/gh/rtfpessoa/diff2html)
[![Dependency Status](https://dependencyci.com/github/rtfpessoa/diff2html/badge)](https://dependencyci.com/github/rtfpessoa/diff2html)
[![npm](https://img.shields.io/npm/v/diff2html.svg)](https://www.npmjs.com/package/diff2html)
[![Dependency Status](https://david-dm.org/rtfpessoa/diff2html.svg)](https://david-dm.org/rtfpessoa/diff2html)
@ -22,21 +21,21 @@ diff2html generates pretty HTML diffs from git or unified diff output.
## Features
* Supports git and unified diffs
- Supports git and unified diffs
* Line by line and Side by side diff
- Line by line and Side by side diff
* New and old line numbers
- New and old line numbers
* Inserted and removed lines
- Inserted and removed lines
* GitHub like style
- GitHub like style
* Code syntax highlight
- Code syntax highlight
* Line similarity matching
- Line similarity matching
* Easy code selection
- Easy code selection
## Online Example
@ -44,15 +43,15 @@ diff2html generates pretty HTML diffs from git or unified diff output.
## Distributions
* [WebJar](http://www.webjars.org/)
* [Node Module](https://www.npmjs.org/package/diff2html)
* [Bower Package](http://bower.io/search/?q=diff2html)
* [Node CLI](https://www.npmjs.org/package/diff2html-cli)
* Manually download and import [dist/diff2html.min.js](./dist/diff2html.min.js) into your page
- [WebJar](http://www.webjars.org/)
- [Node Module](https://www.npmjs.org/package/diff2html)
- [Node CLI](https://www.npmjs.org/package/diff2html-cli)
- Manually download and import:
- Browser
- [build/browser/diff2html.min.js](./build/browser/diff2html.min.js) - includes the diff parser and html generator
- [build/browser/diff2html-ui.min.js](./build/browser/diff2html-ui.min.js) - includes the wrapper of diff2html that adds highlight, synchronized scroll, and other nice features
- Node.js
- [build/commonjs-node/diff2html.js](./build/commonjs-node/diff2html.js) - includes the diff parser and html generator
## How to use
@ -62,7 +61,7 @@ Import the stylesheet
```html
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="dist/diff2html.css">
<link rel="stylesheet" type="text/css" href="dist/diff2html.css" />
```
You can also refer to it from a CDN like [CDNJS](https://cdnjs.com/libraries/diff2html).
@ -73,37 +72,41 @@ Import the stylesheet and the library code
```html
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="dist/diff2html.css">
<link rel="stylesheet" type="text/css" href="build/css/diff2html.min.css" />
<!-- Javascripts -->
<script type="text/javascript" src="dist/diff2html.js"></script>
<script type="text/javascript" src="build/browser/diff2html.min.js"></script>
```
It will now be available as a global variable named `Diff2Html`.
It will now be available as a global variable named `global.Diff2Html`.
```js
var diffHtml = Diff2Html.getPrettyHtml(
'<Unified Diff String>',
{inputFormat: 'diff', showFiles: true, matching: 'lines', outputFormat: 'side-by-side'}
);
document.getElementById("destination-elem-id").innerHTML = diffHtml;
document.addEventListener("DOMContentLoaded", () => {
var diffHtml = global.Diff2Html.html("<Unified Diff String>", {
drawFileList: true,
matching: "lines",
outputFormat: "side-by-side"
});
document.getElementById("destination-elem-id").innerHTML = diffHtml;
});
```
### Node Module
```js
let diff2html = require("diff2html").Diff2Html
const Diff2html = require("diff2html");
const diffJson = Diff2html.parse("<Unified Diff String>");
const diffHtml = Diff2html.html(diffJson, { drawFileList: true });
document.getElementById("destination-elem-id").innerHTML = diffHtml;
```
### Angular
* Typescript
- Typescript
```typescript
// import diff2html
import {Diff2Html} from 'diff2html'
import {Component, OnInit} from '@angular/core';
import * as Diff2Html from "diff2html";
import { Component, OnInit } from "@angular/core";
export class AppDiffComponent implements OnInit {
outputHtml: string;
@ -111,18 +114,18 @@ export class AppDiffComponent implements OnInit {
this.init();
}
ngOnInit() {
}
ngOnInit() {}
init() {
let strInput = "--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n";
let outputHtml = Diff2Html.getPrettyHtml(strInput, {inputFormat: 'diff', showFiles: true, matching: 'lines'});
let strInput =
"--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n";
let outputHtml = Diff2Html.html(strInput, { drawFileList: true, matching: "lines" });
this.outputHtml = outputHtml;
}
}
```
* HTML
- HTML
```html
<!DOCTYPE html>
@ -136,7 +139,7 @@ export class AppDiffComponent implements OnInit {
</html>
```
* `.angular-cli.json` - Add styles
- `.angular-cli.json` - Add styles
```json
"styles": [
@ -148,27 +151,28 @@ export class AppDiffComponent implements OnInit {
```vue
<template>
<div v-html="prettyHtml" />
<div v-html="prettyHtml" />
</template>
<script>
import { Diff2Html } from "diff2html";
import "diff2html/dist/diff2html.min.css";
import * as Diff2Html from "diff2html";
import "diff2html/build/css/diff2html.min.css";
export default {
data() {
return {
diffs: "--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n"
diffs:
"--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n"
};
},
computed: {
prettyHtml() {
return Diff2Html.getPrettyHtml(this.diffs, {
inputFormat: "diff",
showFiles: true,
matching: "lines",
outputFormat: "side-by-side"
});
inputFormat: "diff",
drawFileList: true,
matching: "lines",
outputFormat: "side-by-side"
});
}
}
};
@ -185,41 +189,40 @@ export default {
getPrettyHtml(input: any, configuration?: Options): string
> Check out the [src/diff2html.d.ts](./src/diff2html.d.ts) for a complete API definition in TypeScript.
> Check out the [docs/demo.html](./docs/demo.html) for a demo example.
## Configuration
The HTML output accepts a Javascript object with configuration. Possible options:
- `inputFormat`: the format of the input data: `'diff'` or `'json'`, default is `'diff'`
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
- `showFiles`: show a file list before the diff: `true` or `false`, default is `false`
- `diffStyle`: show differences level in each line: `word` or `char`, default is `word`
- `matching`: matching level: `'lines'` for matching lines, `'words'` for matching lines and words or `'none'`, default is `none`
- `matchWordsThreshold`: similarity threshold for word matching, default is 0.25
- `matchingMaxComparisons`: perform at most this much comparisons for line matching a block of changes, default is `2500`
- `maxLineSizeInBlockForComparison`: maximum number os characters of the bigger line in a block to apply comparison, default is `200`
- `maxLineLengthHighlight`: only perform diff changes highlight if lines are smaller than this, default is `10000`
- `compiledTemplates`: object with previously compiled templates to replace parts of the html
- `rawTemplates`: object with raw not compiled templates to replace parts of the html
- `renderNothingWhenEmpty`: render nothing if the diff shows no change in its comparison: `true` or `false`, default is `false`
- `inputFormat`: the format of the input data: `'diff'` or `'json'`, default is `'diff'`
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
- `drawFileList`: show a file list before the diff: `true` or `false`, default is `false`
- `diffStyle`: show differences level in each line: `word` or `char`, default is `word`
- `matching`: matching level: `'lines'` for matching lines, `'words'` for matching lines and words or `'none'`, default is `none`
- `matchWordsThreshold`: similarity threshold for word matching, default is 0.25
- `matchingMaxComparisons`: perform at most this much comparisons for line matching a block of changes, default is `2500`
- `maxLineSizeInBlockForComparison`: maximum number os characters of the bigger line in a block to apply comparison, default is `200`
- `maxLineLengthHighlight`: only perform diff changes highlight if lines are smaller than this, default is `10000`
- `compiledTemplates`: object with previously compiled templates to replace parts of the html
- `rawTemplates`: object with raw not compiled templates to replace parts of the html
- `renderNothingWhenEmpty`: render nothing if the diff shows no change in its comparison: `true` or `false`, default is `false`
> For more information regarding the possible templates look into [src/templates](https://github.com/rtfpessoa/diff2html/tree/master/src/templates)
** Diff2HtmlUI Helper Options **
- `synchronisedScroll`: scroll both panes in side-by-side mode: `true` or `false`, default is `false`
- `synchronisedScroll`: scroll both panes in side-by-side mode: `true` or `false`, default is `false`
> For more information regarding the possible templates look into [src/templates](https://github.com/rtfpessoa/diff2html/tree/master/src/templates)
## Diff2HtmlUI Helper
> Simple wrapper to ease simple tasks in the browser such as: code highlight and js effects
* Invoke Diff2html
* Inject output in DOM element
* Enable collapsible file summary list
* Enable syntax highlight of the code in the diffs
- Invoke Diff2html
- Inject output in DOM element
- Enable collapsible file summary list
- Enable syntax highlight of the code in the diffs
### How to use
@ -227,52 +230,56 @@ The HTML output accepts a Javascript object with configuration. Possible options
```html
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="dist/diff2html.css">
<link rel="stylesheet" type="text/css" href="build/css/diff2html.min.css" />
<!-- Javascripts -->
<script type="text/javascript" src="dist/diff2html.js"></script>
<script type="text/javascript" src="dist/diff2html-ui.js"></script>
<script type="text/javascript" src="build/browser/diff2html-ui.min.js"></script>
```
#### Init
```js
var diff2htmlUi = new Diff2HtmlUI({diff: diffString});
const targetElement = document.getElementById("destination-elem-id");
const configuration = { drawFileList: true, matching: "lines" };
const diff2htmlUi = new global.Diff2HtmlUI(diffString, targetElement, configuration);
// or
var diff2htmlUi = new Diff2HtmlUI({json: diffJson});
const diff2htmlUi = new global.Diff2HtmlUI(diffJson, targetElement, configuration);
```
#### Draw
```js
diff2htmlUi.draw('html-target-elem', {inputFormat: 'json', showFiles: true, matching: 'lines'});
diff2htmlUi.draw();
```
#### Syntax Highlight
> Add the dependencies.
Choose one color scheme, and add the main highlight code. Note that the stylesheet for the color scheme must come **before** the main diff2html stylesheet.
If your favourite language is not included in the default package also add its javascript highlight file.
```html
<!-- Stylesheet -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/github.min.css">
<link rel="stylesheet" type="text/css" href="dist/diff2html.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/github.min.css" />
<link rel="stylesheet" type="text/css" href="build/css/diff2html.min.css" />
<!-- Javascripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/languages/scala.min.js"></script>
<script type="text/javascript" src="dist/diff2html-ui.js"></script>
<script type="text/javascript" src="build/browser/diff2html-ui.min.js"></script>
```
> Invoke the Diff2HtmlUI helper
> Pass the option `highlight` with value true or invoke `diff2htmlUi.highlightCode()` after `diff2htmlUi.draw()`.
```js
$(document).ready(function() {
var diff2htmlUi = new Diff2HtmlUI({diff: lineDiffExample});
diff2htmlUi.draw('#line-by-line', {inputFormat: 'json', showFiles: true, matching: 'lines'});
diff2htmlUi.highlightCode('#line-by-line');
document.addEventListener("DOMContentLoaded", () => {
const diffString = `diff --git a/sample.js b/sample.js
index 0000001..0ddf2ba
--- a/sample.js
+++ b/sample.js
@@ -1 +1 @@
-console.log("Hello World!")
+console.log("Hello from Diff2Html!")`;
const targetElement = document.getElementById("myDiffElement");
const configuration = { inputFormat: "json", drawFileList: true, matching: "lines", highlight: true };
const diff2htmlUi = new global.Diff2HtmlUI(diffString, targetElement, configuration);
diff2htmlUi.draw();
diff2htmlUi.highlightCode();
});
```
@ -282,17 +289,18 @@ $(document).ready(function() {
```html
<!-- Javascripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.js"></script>
<script type="text/javascript" src="dist/diff2html-ui.js"></script>
<script type="text/javascript" src="build/browser/diff2html-ui.min.js"></script>
```
> Invoke the Diff2HtmlUI helper
> Pass the option `fileListToggle` with value true or invoke `diff2htmlUi.fileListToggle()` after `diff2htmlUi.draw()`.
```js
$(document).ready(function() {
var diff2htmlUi = new Diff2HtmlUI({diff: lineDiffExample});
diff2htmlUi.draw('#line-by-line', {inputFormat: 'json', showFiles: true, matching: 'lines'});
diff2htmlUi.fileListCloseable('#line-by-line', false);
document.addEventListener("DOMContentLoaded", () => {
const targetElement = document.getElementById("myDiffElement");
var diff2htmlUi = new global.Diff2HtmlUI(lineDiffExample, targetElement, { drawFileList: true, matching: "lines" });
diff2htmlUi.draw();
diff2htmlUi.fileListToggle(false);
});
```
@ -301,11 +309,13 @@ $(document).ready(function() {
### 1. Out of memory or Slow execution
#### Causes:
* Big files
* Big lines
- Big files
- Big lines
#### Fix:
* Disable the line matching algorithm, by setting the option `{"matching": "none"}` when invoking diff2html
- Disable the line matching algorithm, by setting the option `{"matching": "none"}` when invoking diff2html
## Contributions
@ -355,6 +365,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View file

@ -32,12 +32,12 @@
"url": "https://www.github.com/rtfpessoa/diff2html/issues"
},
"engines": {
"node": ">=4"
"node": "8.* || >=10"
},
"scripts": {
"lint": "eslint '*/**/*.{js,jsx,ts,tsx}'",
"style": "yarn run lint",
"test": "jest",
"test": "yarn run build-scripts && yarn run build-templates && jest",
"coverage": "jest --collectCoverage",
"coverage-html": "yarn run coverage && open ./coverage/index.html",
"codacy": "cat ./coverage/lcov.info | codacy-coverage",

View file

@ -22,13 +22,13 @@ rm -rf ${OUTPUT_DIR}
mkdir -p ${OUTPUT_DIR}
echo "Generating js aggregation file in ${OUTPUT_JS_FILE}"
browserify -e ${INPUT_JS_FILE} -o ${OUTPUT_JS_FILE}
browserify -e ${INPUT_JS_FILE} -o ${OUTPUT_JS_FILE} -s global
echo "Minifying ${OUTPUT_JS_FILE} to ${OUTPUT_MIN_JS_FILE}"
terser ${OUTPUT_JS_FILE} -c -o ${OUTPUT_MIN_JS_FILE}
echo "Generating js ui aggregation file in ${OUTPUT_JS_UI_FILE}"
browserify -e ${INPUT_JS_UI_FILE} -o ${OUTPUT_JS_UI_FILE}
browserify -e ${INPUT_JS_UI_FILE} -o ${OUTPUT_JS_UI_FILE} -s global
echo "Minifying ${OUTPUT_JS_UI_FILE} to ${OUTPUT_MIN_JS_UI_FILE}"
terser ${OUTPUT_JS_UI_FILE} -c -o ${OUTPUT_MIN_JS_UI_FILE}

View file

@ -12,9 +12,7 @@ type OptionsType = {
const templatesRoot = "website/templates";
const pagesRoot = `${templatesRoot}/pages`;
const options: OptionsType = {
all: {
demoUrl: "demo.html?diff=https://github.com/rtfpessoa/diff2html/pull/106"
},
all: {},
demo: {
extraClass: "template-index-min"
}

View file

@ -172,7 +172,7 @@ function namespace(name: string): string {
// Write a template foreach file that matches template extension
const templates = extractFiles(options.argv.remain)
.map(file => {
const timmedFileContents = fs.readFileSync(file, "utf-8").trim();
const timmedFileContents = fs.readFileSync(file, "utf8").trim();
if (!timmedFileContents) return;

View file

@ -415,7 +415,7 @@ describe("DiffParser", () => {
"+test2r\n"
];
diffs.forEach(function(diff) {
diffs.forEach(diff => {
const result = parse(diff);
const file1 = result[0];
expect(result.length).toEqual(1);

View file

@ -256,32 +256,32 @@ describe("Diff2Html", () => {
});
it("should generate pretty line by line html from diff", () => {
const result = html(diffExample1);
const result = html(diffExample1, { drawFileList: false });
expect(result).toEqual(htmlLineExample1);
});
it("should generate pretty line by line html from json", () => {
const result = html(jsonExample1);
const result = html(jsonExample1, { drawFileList: false });
expect(result).toEqual(htmlLineExample1);
});
it("should generate pretty diff with files summary", () => {
const result = html(diffExample1, { showFiles: true });
const result = html(diffExample1, { drawFileList: true });
expect(result).toEqual(htmlLineExample1WithFilesSummary);
});
it("should generate pretty side by side html from diff", () => {
const result = html(diffExample1, { outputFormat: "side-by-side" });
const result = html(diffExample1, { outputFormat: "side-by-side", drawFileList: false });
expect(result).toEqual(htmlSideExample1);
});
it("should generate pretty side by side html from json", () => {
const result = html(jsonExample1, { outputFormat: "side-by-side" });
const result = html(jsonExample1, { outputFormat: "side-by-side", drawFileList: false });
expect(result).toEqual(htmlSideExample1);
});
it("should generate pretty side by side html from diff 2", () => {
const result = html(diffExample1, { outputFormat: "side-by-side", showFiles: true });
const result = html(diffExample1, { outputFormat: "side-by-side", drawFileList: true });
expect(result).toEqual(htmlSideExample1WithFilesSummary);
});
@ -518,7 +518,7 @@ describe("Diff2Html", () => {
"</div>\n" +
"</div>";
const result = html(diffExample2);
const result = html(diffExample2, { drawFileList: false });
expect(htmlExample2).toEqual(result);
});
});

View file

@ -254,7 +254,7 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
return false;
}
diffLines.forEach(function(line, lineIndex) {
diffLines.forEach((line, lineIndex) => {
// Unmerged paths, and possibly other non-diffable files
// https://github.com/scottgonzalez/pretty-diff/issues/11
// Also, remove some useless lines
@ -340,8 +340,9 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
}
if (
(currentFile && line.startsWith(hunkHeaderPrefix)) ||
(currentFile && currentFile.isGitDiff && currentFile.oldName && currentFile.newName && !currentBlock)
currentFile &&
(line.startsWith(hunkHeaderPrefix) ||
(currentFile.isGitDiff && currentFile.oldName && currentFile.newName && !currentBlock))
) {
startBlock(line);
return;
@ -403,7 +404,7 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
currentFile.oldName = getFilename(values[1], undefined, config.srcPrefix);
currentFile.newName = getFilename(values[2], undefined, config.dstPrefix);
startBlock("Binary file");
} else if ((values = binaryDiff.exec(line))) {
} else if (binaryDiff.test(line)) {
currentFile.isBinary = true;
startBlock(line);
} else if ((values = similarityIndex.exec(line))) {

View file

@ -13,14 +13,14 @@ export interface Diff2HtmlConfig
SideBySideRendererConfig,
HoganJsUtilsConfig {
outputFormat?: OutputFormatType;
showFiles?: boolean;
drawFileList?: boolean;
}
export const defaultDiff2HtmlConfig = {
...defaultLineByLineRendererConfig,
...defaultSideBySideRendererConfig,
outputFormat: "line-by-line" as OutputFormatType,
showFiles: false
drawFileList: true
};
export function parse(diffInput: string, configuration: Diff2HtmlConfig = {}): DiffFile[] {
@ -34,7 +34,7 @@ export function html(diffInput: string | DiffFile[], configuration: Diff2HtmlCon
const hoganUtils = new HoganJsUtils(config);
const fileList = config.showFiles ? fileListPrinter.render(diffJson, hoganUtils) : "";
const fileList = config.drawFileList ? fileListPrinter.render(diffJson, hoganUtils) : "";
const diffOutput =
config.outputFormat === "side-by-side"

View file

@ -10,10 +10,10 @@ export interface LineByLineRendererConfig extends renderUtils.RenderConfig {
}
export const defaultLineByLineRendererConfig = {
...renderUtils.defaultRenderConfig,
renderNothingWhenEmpty: false,
matchingMaxComparisons: 2500,
maxLineSizeInBlockForComparison: 200,
...renderUtils.defaultRenderConfig
maxLineSizeInBlockForComparison: 200
};
const genericTemplatesPath = "generic";

View file

@ -3,16 +3,24 @@ import * as HighlightJSInternals from "./highlight.js-internals";
import { html, Diff2HtmlConfig, defaultDiff2HtmlConfig } from "../../diff2html";
import { DiffFile } from "../../render-utils";
interface Diff2HtmlUIConfig extends Diff2HtmlConfig {
export interface Diff2HtmlUIConfig extends Diff2HtmlConfig {
synchronisedScroll?: boolean;
highlight?: boolean;
fileListToggle?: boolean;
fileListStartVisible?: boolean;
smartSelection?: boolean;
}
const defaultDiff2HtmlUIConfig = {
export const defaultDiff2HtmlUIConfig = {
...defaultDiff2HtmlConfig,
synchronisedScroll: true
synchronisedScroll: true,
highlight: true,
fileListToggle: true,
fileListStartVisible: false,
smartSelection: true
};
export default class Diff2HtmlUI {
export class Diff2HtmlUI {
readonly config: typeof defaultDiff2HtmlUIConfig;
readonly diffHtml: string;
targetElement: HTMLElement;
@ -26,32 +34,32 @@ export default class Diff2HtmlUI {
draw(): void {
this.targetElement.innerHTML = this.diffHtml;
this.initSelection();
if (this.config.smartSelection) this.initSelection();
if (this.config.synchronisedScroll) this.synchronisedScroll();
if (this.config.highlight) this.highlightCode();
if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible);
}
synchronisedScroll(): void {
this.targetElement.querySelectorAll(".d2h-file-wrapper").forEach(wrapper => {
const [left, right] = [].slice.call(wrapper.querySelectorAll(".d2h-file-side-diff")) as HTMLElement[];
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;
} else {
left.scrollTop = right.scrollTop;
left.scrollLeft = right.scrollLeft;
}
};
left.addEventListener("scroll", onScroll);
right.addEventListener("scroll", onScroll);
});
}
fileListCloseable(startVisible: boolean): void {
fileListToggle(startVisible: boolean): void {
const hashTag = this.getHashTag();
const showBtn = this.targetElement.querySelector(".d2h-show") as HTMLElement;
@ -61,13 +69,13 @@ export default class Diff2HtmlUI {
if (showBtn === null || hideBtn === null || fileList === null) return;
function show(): void {
showBtn.style.display = "";
hideBtn.style.display = "";
fileList.style.display = "";
showBtn.style.display = "none";
hideBtn.style.display = "inline";
fileList.style.display = "block";
}
function hide(): void {
showBtn.style.display = "none";
showBtn.style.display = "inline";
hideBtn.style.display = "none";
fileList.style.display = "none";
}
@ -204,7 +212,7 @@ export default class Diff2HtmlUI {
nodes.forEach((tr, i) => {
const td = tr.cells[tr.cells.length === 1 ? 0 : idx];
if (td === null || td.textContent === null) return;
if (td === undefined || td.textContent === null) return;
text += (i ? "\n" : "") + td.textContent.replace(/(?:\r\n|\r|\n)/g, "");
});
@ -213,8 +221,3 @@ export default class Diff2HtmlUI {
return text;
}
}
// TODO: Avoid disabling types
// eslint-disable-next-line
// @ts-ignore
global.Diff2HtmlUI = Diff2HtmlUI;

View file

@ -48,7 +48,7 @@ export function escapeForHtml(str: string): string {
* Converts all '\' in @path to unix style '/'
*/
export function unifyPath(path: string): string {
return path ? path.replace("\\", "/") : path;
return path ? path.replace(/\\/g, "/") : path;
}
/**

View file

@ -1,4 +1,4 @@
/* global Diff2HtmlUI */
/* global global */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/*
@ -16,33 +16,33 @@
const searchParam = "diff";
function getUrlFromSearch(search) {
try {
return search
.split("?")[1]
.split(searchParam + "=")[1]
.split("&")[0];
} catch (_ignore) {}
return null;
}
function getParamsFromSearch(search) {
const map = new Map();
const map = {};
try {
search
.split("?")[1]
.split("&")
.forEach(e => {
const values = e.split("=");
map.set(values[0], values[1]);
map[values[0]] = values[1];
});
} catch (_ignore) {}
return map;
}
function prepareUrl(url) {
function validateUrl(url) {
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
url
);
}
function prepareRequest(url) {
if (!validateUrl(url)) {
console.error("Invalid url provided!");
return;
}
let fetchUrl;
const headers = new Headers();
@ -93,150 +93,152 @@ function prepareUrl(url) {
}
return {
originalUrl: url,
url: fetchUrl,
headers: headers
};
}
function validateUrl(url) {
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
url
);
function getConfiguration(urlParams) {
// Removing `diff` form `urlParams` to avoid being inserted
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { diff, ...urlParamsRest } = urlParams;
const config = {
...global.defaultDiff2HtmlUIConfig,
...urlParamsRest
};
return Object.entries(config).reduce((object, [k, v]) => {
const newObject = !Number.isNaN(Number(v))
? { [k]: Number(v) }
: v === "true" && v === "false"
? { [k]: Boolean(v) }
: { [k]: v };
return { ...object, ...newObject };
}, {});
}
function updateUrl(url) {
const params = getParamsFromSearch(window.location.search);
if (params[searchParam] === url) return;
params[searchParam] = url;
const paramString = Object.keys(params)
.map(function(k) {
return k + "=" + params[k];
})
.join("&");
window.location = "demo.html?" + paramString;
}
function draw(req, forced, elements) {
if (!validateUrl(req.url)) {
console.error("Invalid url provided!");
return;
}
if (validateUrl(req.originalUrl)) updateUrl(req.originalUrl);
const outputFormat = elements.outputFormat.val();
const showFiles = elements.showFiles.is(":checked");
const matching = elements.matching.val();
const wordsThreshold = elements.wordsThreshold.val();
const matchingMaxComparisons = elements.matchingMaxComparisons.val();
fetch(req.url, {
function getDiff(request) {
return fetch(request.url, {
method: "GET",
headers: req.headers,
headers: request.headers,
mode: "cors",
cache: "default"
})
.then(function(res) {
.then(res => {
return res.text();
})
.then(function(data) {
const params = getParamsFromSearch(window.location.search);
delete params[searchParam];
if (forced) {
params.outputFormat = outputFormat;
params.showFiles = showFiles;
params.matching = matching;
params.wordsThreshold = wordsThreshold;
params.matchingMaxComparisons = matchingMaxComparisons;
} else {
params.outputFormat = params.outputFormat || outputFormat;
params.showFiles = String(params.showFiles) !== "false" || (params.showFiles === null && showFiles);
params.matching = params.matching || matching;
params.wordsThreshold = params.wordsThreshold || wordsThreshold;
params.matchingMaxComparisons = params.matchingMaxComparisons || matchingMaxComparisons;
elements.outputFormat.value = params.outputFormat;
elements.showFiles.setAttribute("checked", params.showFiles);
elements.matching.value = params.matching;
elements.wordsThreshold.value = params.wordsThreshold;
elements.matchingMaxComparisons.value = params.matchingMaxComparisons;
}
params.synchronisedScroll = params.synchronisedScroll || true;
const diff2htmlUi = new Diff2HtmlUI(data, elements.root);
if (outputFormat === "side-by-side") {
elements.container.css({ width: "100%" });
} else {
elements.container.css({ width: "" });
}
diff2htmlUi.draw();
diff2htmlUi.fileListCloseable(params.fileListCloseable || false);
if (params.highlight === undefined || params.highlight) {
diff2htmlUi.highlightCode();
}
return undefined;
})
.catch(() => {});
.catch(error => console.error("Failed to retrieve diff", error));
}
function smartDraw(urlOpt, urlElem, forced) {
const url = urlOpt || urlElem.val();
const req = prepareUrl(url);
draw(req, forced);
function draw(diffString, config, elements) {
const diff2htmlUi = new global.Diff2HtmlUI(diffString, elements.structure.diffTarget, config);
if (config.outputFormat === "side-by-side") {
elements.structure.container.style.width = "100%";
} else {
elements.structure.container.style.width = "";
}
diff2htmlUi.draw();
}
function bind(urlElem) {
$("#url-btn").click(e => {
e.preventDefault();
const url = urlElem.val();
smartDraw(url, urlElem);
});
async function prepareInitialState(elements) {
const urlParams = getParamsFromSearch(window.location.search);
const currentUrl = (urlParams && urlParams[searchParam]) || "https://github.com/rtfpessoa/diff2html/pull/106";
urlElem.on("paste", e => {
const url = e.originalEvent.clipboardData.getData("Text");
smartDraw(url, urlElem);
});
if (currentUrl !== elements.url.input.value) elements.url.input.value = currentUrl;
const request = prepareRequest(currentUrl);
const initialConfiguration = getConfiguration(urlParams);
const initialDiff = await getDiff(request);
return [initialConfiguration, initialDiff];
}
document.addEventListener("DOMContentLoaded", function() {
function updateBrowserUrl(config, newDiffUrl) {
if (history.pushState) {
const paramString = Object.entries(config)
.map(([k, v]) => k + "=" + v)
.join("&");
const newPageUrl =
window.location.protocol +
"//" +
window.location.host +
window.location.pathname +
"?" +
paramString +
"&" +
searchParam +
"=" +
newDiffUrl;
window.history.pushState({ path: newPageUrl }, "", newPageUrl);
}
}
document.addEventListener("DOMContentLoaded", async () => {
// Improves browser compatibility
require("whatwg-fetch");
const elements = {
root: document.getElementById("url-diff-container"),
container: document.getElementsByClassName("container"),
url: document.getElementById("url"),
outputFormat: document.getElementById("diff-url-options-output-format"),
showFiles: document.getElementById("diff-url-options-show-files"),
matching: document.getElementById("diff-url-options-matching"),
wordsThreshold: document.getElementById("diff-url-options-match-words-threshold"),
matchingMaxComparisons: document.getElementById("diff-url-options-matching-max-comparisons")
const drawAndUpdateUrl = async (diffUrl, diffString, config, elements) => {
updateBrowserUrl(config, diffUrl);
const newRequest = prepareRequest(diffUrl);
diffString = await getDiff(newRequest);
draw(diffString, config, elements);
};
if (window.location.search) {
const url = getUrlFromSearch(window.location.search);
elements.url.val(url);
smartDraw(url, elements.url);
}
const elements = {
structure: {
container: document.getElementsByClassName("container")[0],
diffTarget: document.getElementById("url-diff-container")
},
url: {
input: document.getElementById("url"),
button: document.getElementById("url-btn")
},
options: {
outputFormat: document.getElementById("diff-url-options-output-format"),
matching: document.getElementById("diff-url-options-matching"),
wordsThreshold: document.getElementById("diff-url-options-match-words-threshold"),
matchingMaxComparisons: document.getElementById("diff-url-options-matching-max-comparisons")
},
checkboxes: {
drawFileList: document.getElementById("diff-url-options-show-files")
}
};
bind();
let [config, diffString] = await prepareInitialState(elements);
elements.outputFormat
.add(elements.showFiles)
.add(elements.matching)
.add(elements.wordsThreshold)
.add(elements.matchingMaxComparisons)
.change(() => smartDraw(null, elements.url, true));
// Update HTML inputs from any changes in URL
elements.options.outputFormat.value = config.outputFormat;
elements.checkboxes.drawFileList.checked = config.drawFileList;
elements.options.matching.value = config.matching;
elements.options.wordsThreshold.value = config.wordsThreshold;
elements.options.matchingMaxComparisons.value = config.matchingMaxComparisons;
Object.entries(elements.options).forEach(([option, element]) =>
element.addEventListener("change", () => {
config[option] = element.value;
drawAndUpdateUrl(elements.url.input.value, diffString, config, elements);
})
);
Object.entries(elements.checkboxes).forEach(([option, checkbox]) =>
checkbox.addEventListener("change", () => {
config[option] = checkbox.checked;
drawAndUpdateUrl(elements.url.input.value, diffString, config, elements);
})
);
elements.url.button.addEventListener("click", async e => {
e.preventDefault();
const newDiffUrl = elements.url.input.value;
const newRequest = prepareRequest(newDiffUrl);
diffString = await getDiff(newRequest);
drawAndUpdateUrl(newDiffUrl, diffString, config, elements);
});
return drawAndUpdateUrl(elements.url.input.value, diffString, config, elements);
});
/* eslint-enable @typescript-eslint/explicit-function-return-type */

View file

@ -22,7 +22,7 @@
</div>
<div class=" col-md-2 col-xs-12 col-15">
<label title="Show the file list summary before the diff">File Summary
<input class="options-label-value" id="diff-url-options-show-files" type="checkbox" name="showFiles" checked/>
<input class="options-label-value" id="diff-url-options-show-files" type="checkbox" name="drawFileList" checked/>
</label>
</div>
<div class=" col-md-2 col-xs-12 col-15">
@ -78,7 +78,7 @@
<b>Can I send a custom url for a friend, colleague or co-worker?</b>
<p>Just add a url parameter called diff to current url using as value your Commit, Pull Request, Merge Request, Diff
or Patch url.</p>
<p>ex: <a href="{{ demoUrl }}">https://diff2html.xyz/{{ demoUrl }}</a>
<p>ex: <a href="demo.html">https://diff2html.xyz/demo.html</a>
</p>
</li>
<li>

View file

@ -5,17 +5,17 @@
</span>
<h1 class="hero-header">Diff parser and pretty html generator</h1>
<h4 class="text-muted">Better diffs, unmatched reviews.</h4>
<h2><a class="btn btn-lg" href="{{ demoUrl }}">Demo</a></h2>
<h2><a class="btn btn-lg" href="demo.html">Demo</a></h2>
<div class="screenshots screenshots-fan clearfix">
<img class="screenshot hidden-xs" src="img/snapshot-2.png">
<a class="screenshot" href="{{ demoUrl }}">
<a class="screenshot" href="demo.html">
<img src="img/snapshot-3.png">
</a>
<a class="screenshot hidden-xs" href="{{ demoUrl }}">
<a class="screenshot hidden-xs" href="demo.html">
<img src="img/snapshot-1.png">
</a>

View file

@ -45,7 +45,6 @@
ga('create', 'UA-78351861-2', 'auto');
ga('send', 'pageview');
</script>
</head>
<body class="template-index {{extraClass}}">
@ -76,7 +75,7 @@
</li>
<li>
<a href="{{ demoUrl }}">Demo</a>
<a href="demo.html">Demo</a>
</li>
<li>
@ -121,11 +120,6 @@
</div>
<!-- General JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
<script type="application/ld+json">
{
"@context": "http://schema.org/",