Compare commits
26 Commits
encoder-wi
...
v5.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64a20ee5b1 | ||
|
|
a469a65101 | ||
|
|
65c71be17e | ||
|
|
b9e3641c85 | ||
|
|
6ea7450536 | ||
|
|
692d01f165 | ||
|
|
84c27739ec | ||
|
|
c4c6e8abd8 | ||
|
|
e7357e4ff1 | ||
|
|
a673d33e89 | ||
|
|
4818e05d2d | ||
|
|
07f32b9a7d | ||
|
|
60d18f8d5c | ||
|
|
4bd19c6e2e | ||
|
|
02e383d94d | ||
|
|
b09671febe | ||
|
|
1702bd6e0a | ||
|
|
5e4dfc9e78 | ||
|
|
827487a598 | ||
|
|
bc46617d5e | ||
|
|
dec1e9509d | ||
|
|
0733f3a6c6 | ||
|
|
254adf15ab | ||
|
|
a07073a13c | ||
|
|
3facfec6b7 | ||
|
|
2b561c1856 |
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,3 +1,66 @@
|
|||||||
|
# [5.3.0](https://github.com/wickedest/Mergely/compare/v5.2.0...v5.3.0) (2024-06-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Supports unicode diacritical marks when rendering line diff (fixes [#169](https://github.com/wickedest/Mergely/issues/169)) ([#197](https://github.com/wickedest/Mergely/issues/197)) ([a469a65](https://github.com/wickedest/Mergely/commit/a469a6510122356a7cae3fb1259e999e6cc34c94))
|
||||||
|
|
||||||
|
# [5.2.0](https://github.com/wickedest/Mergely/compare/v5.1.4...v5.2.0) (2024-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Allows height to be not explicit height, e.g. 'inherit' or '100%' ([#196](https://github.com/wickedest/Mergely/issues/196)) ([b9e3641](https://github.com/wickedest/Mergely/commit/b9e3641c852a8926db5efdf33e65a607d5f2df5e))
|
||||||
|
|
||||||
|
## [5.1.4](https://github.com/wickedest/Mergely/compare/v5.1.3...v5.1.4) (2024-05-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* removed accidental change that enabled code display modes (e.g. javascript, python, html) ([692d01f](https://github.com/wickedest/Mergely/commit/692d01f1653ae8f1163a2c6228f457549086b75d))
|
||||||
|
|
||||||
|
## [5.1.3](https://github.com/wickedest/Mergely/compare/v5.1.2...v5.1.3) (2024-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **chore:** updated deps ([#195](https://github.com/wickedest/Mergely/issues/195)) ([c4c6e8a](https://github.com/wickedest/Mergely/commit/c4c6e8abd8f02762d5803774789673f76a95e932))
|
||||||
|
|
||||||
|
## [5.1.2](https://github.com/wickedest/Mergely/compare/v5.1.1...v5.1.2) (2024-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* trace only when debug enabled ([#194](https://github.com/wickedest/Mergely/issues/194)) ([60d18f8](https://github.com/wickedest/Mergely/commit/60d18f8d5c0df349b4806b2e8a6c0f79d9f8074e))
|
||||||
|
|
||||||
|
## [5.1.1](https://github.com/wickedest/Mergely/compare/v5.1.0...v5.1.1) (2024-03-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **#183:** fixes undefined 'k' due to scoped 'let' ([02e383d](https://github.com/wickedest/Mergely/commit/02e383d94d685e41cb68d945b9726bbcbfeb0ccf))
|
||||||
|
|
||||||
|
# [5.1.0](https://github.com/wickedest/Mergely/compare/v5.0.4...v5.1.0) (2023-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* support CM modes: go, javascript, htmlmixed, markdown, python ([827487a](https://github.com/wickedest/Mergely/commit/827487a5983cb89ef41415435d44239e35983b9a))
|
||||||
|
|
||||||
|
## [5.0.4](https://github.com/wickedest/Mergely/compare/v5.0.3...v5.0.4) (2023-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* updated CDN example ([dec1e95](https://github.com/wickedest/Mergely/commit/dec1e9509d94811914e77cbc33dc1aaedf154f7c))
|
||||||
|
|
||||||
|
## [5.0.3](https://github.com/wickedest/Mergely/compare/v5.0.2...v5.0.3) (2023-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Updated docs with CDN example ([254adf1](https://github.com/wickedest/Mergely/commit/254adf15ab09fe9c5c3dff542d0a7f62ce2c9782))
|
||||||
|
|
||||||
## [5.0.2](https://github.com/wickedest/Mergely/compare/v5.0.1...v5.0.2) (2023-04-24)
|
## [5.0.2](https://github.com/wickedest/Mergely/compare/v5.0.1...v5.0.2) (2023-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
https://mergely.com
|
https://mergely.com
|
||||||
|
|
||||||
Mergely is a JavaScript component for differencing and merging files interactively in a browser (diff/merge). It provides a rich API that enables you to easily integrate Mergely into your existing web application. It is suitable for comparing text files online, for example, .txt, .html, .xml, .c, .cpp, .java, etc.
|
Mergely is a JavaScript component for differencing and merging files interactively in a browser (diff/merge). It provides a rich API that enables you to easily integrate Mergely into your existing web application. It is suitable for comparing text files online, such as .txt, .html, .xml, .c, .cpp, .java, .js, etc.
|
||||||
|
|
||||||
Mergely has a JavaScript implementation of the Longest Common Subsequence (LCS) diff algorithm, and a customizable markup engine. It computes the diff within the browser.
|
Mergely has a JavaScript implementation of the Longest Common Subsequence (LCS) diff algorithm, and a customizable markup engine. It computes the diff within the browser.
|
||||||
|
|
||||||
@@ -36,11 +36,11 @@ rm -rf .git
|
|||||||
|
|
||||||
### Usage via CDN
|
### Usage via CDN
|
||||||
|
|
||||||
Unpack mergely.tgz into a folder, for example, `./lib`, and add the following to the `<head>` of your target HTML source file.
|
Add the following to the `<head>` of your target HTML source file. Note that `codemirror` is bundled.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mergely/5.0.0/mergely.min.js"></script>
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mergely/5.0.0/mergely.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mergely/5.0.0/mergely.css"></script>
|
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mergely/5.0.0/mergely.css" />
|
||||||
```
|
```
|
||||||
|
|
||||||
### Synchronous initialization
|
### Synchronous initialization
|
||||||
@@ -82,6 +82,16 @@ Mergely will emit an `updated` event when the editor is first initialized, and e
|
|||||||
</body>
|
</body>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Visualization modes
|
||||||
|
|
||||||
|
Mergely supports the following CodeMirror visualizations for [mode](codemirror.net/5/doc/manual.html#option_mode):
|
||||||
|
|
||||||
|
* go
|
||||||
|
* javascript
|
||||||
|
* htmlmixed
|
||||||
|
* markdown
|
||||||
|
* python
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
|Option|Type|Default value|Description|
|
|Option|Type|Default value|Description|
|
||||||
|
|||||||
38
examples/cdn.html
Normal file
38
examples/cdn.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Mergely - Example full page editor</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="chrome=1, IE=edge">
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta name="description" content="" />
|
||||||
|
<meta name="keywords" content="mergely,diff,merge,compare" />
|
||||||
|
<meta name="author" content="Jamie Peabody" />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||||
|
|
||||||
|
<!-- Mergely -->
|
||||||
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mergely/5.0.0/mergely.min.js"></script>
|
||||||
|
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mergely/5.0.0/mergely.css" />
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#compare {
|
||||||
|
height: inherit;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="compare"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const mergely = new Mergely('#compare');
|
||||||
|
mergely.once('updated', () => {
|
||||||
|
mergely.lhs('the quick red fox\njumped over the hairy dog');
|
||||||
|
mergely.rhs('the quick brown fox\njumped over the lazy dog');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -21,6 +21,9 @@ This example demonstrates the minimum amount of code required to use Mergely.
|
|||||||
<h1>Examples</h1>
|
<h1>Examples</h1>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
<dt><a href="cdn.html">cdn.html</a></dt>
|
||||||
|
<dd>Demonstrates how to use Mergely with CDN.</dd>
|
||||||
|
|
||||||
<dt><a href="editor.html">editor.html</a></dt>
|
<dt><a href="editor.html">editor.html</a></dt>
|
||||||
<dd>An example editor showcasing some of Mergely's API features.</dd>
|
<dd>An example editor showcasing some of Mergely's API features.</dd>
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
"changelog" : {
|
"changelog" : {
|
||||||
"commitTypes": [
|
"commitTypes": [
|
||||||
|
"docs",
|
||||||
"feat",
|
"feat",
|
||||||
"fix",
|
"fix",
|
||||||
"perf",
|
"perf",
|
||||||
|
|||||||
9067
package-lock.json
generated
9067
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mergely",
|
"name": "mergely",
|
||||||
"version": "5.0.2",
|
"version": "5.3.0",
|
||||||
"description": "A javascript UI for diff/merge",
|
"description": "A javascript UI for diff/merge",
|
||||||
"license": "(GPL-3.0 OR LGPL-3.0 OR MPL-1.1 OR SEE LICENSE IN LICENSE)",
|
"license": "(GPL-3.0 OR LGPL-3.0 OR MPL-1.1 OR SEE LICENSE IN LICENSE)",
|
||||||
"author": {
|
"author": {
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
"karma-sourcemap-loader": "^0.4.0",
|
"karma-sourcemap-loader": "^0.4.0",
|
||||||
"karma-webpack": "^5.0.0",
|
"karma-webpack": "^5.0.0",
|
||||||
"mocha": "^9.1.4",
|
"mocha": "^9.1.4",
|
||||||
"semantic-release": "^21.0.1",
|
"semantic-release": "^21.1.2",
|
||||||
"simple-mock": "^0.8.0",
|
"simple-mock": "^0.8.0",
|
||||||
"standard-version": "^9.3.2",
|
"standard-version": "^9.3.2",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
|
|||||||
180
src/diff-view.js
180
src/diff-view.js
@@ -2,6 +2,7 @@ const CodeMirror = require('codemirror');
|
|||||||
require('codemirror/addon/search/searchcursor.js');
|
require('codemirror/addon/search/searchcursor.js');
|
||||||
require('codemirror/addon/selection/mark-selection.js');
|
require('codemirror/addon/selection/mark-selection.js');
|
||||||
require('codemirror/lib/codemirror.css');
|
require('codemirror/lib/codemirror.css');
|
||||||
|
|
||||||
const dom = require('./dom.js');
|
const dom = require('./dom.js');
|
||||||
const VDoc = require('./vdoc');
|
const VDoc = require('./vdoc');
|
||||||
|
|
||||||
@@ -52,9 +53,7 @@ CodeMirrorDiffView.prototype.unbind = function() {
|
|||||||
if (this._unbound) {
|
if (this._unbound) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.settings._debug) {
|
this.trace('api#unbind');
|
||||||
trace('api#unbind');
|
|
||||||
}
|
|
||||||
if (this._changedTimeout != null) {
|
if (this._changedTimeout != null) {
|
||||||
clearTimeout(this._changedTimeout);
|
clearTimeout(this._changedTimeout);
|
||||||
}
|
}
|
||||||
@@ -66,7 +65,6 @@ CodeMirrorDiffView.prototype.unbind = function() {
|
|||||||
this.el.removeChild(this.el.lastChild);
|
this.el.removeChild(this.el.lastChild);
|
||||||
}
|
}
|
||||||
if (this._origEl) {
|
if (this._origEl) {
|
||||||
this.el.style = this._origEl.style;
|
|
||||||
this.el.className = this._origEl.className;
|
this.el.className = this._origEl.className;
|
||||||
}
|
}
|
||||||
this._unbound = true;
|
this._unbound = true;
|
||||||
@@ -83,9 +81,7 @@ CodeMirrorDiffView.prototype.readOnly = function(side) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.lhs = function(text) {
|
CodeMirrorDiffView.prototype.lhs = function(text) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#lhs', text && text.length);
|
||||||
trace('api#lhs', text && text.length);
|
|
||||||
}
|
|
||||||
// invalidate existing changes and current position
|
// invalidate existing changes and current position
|
||||||
this.changes = [];
|
this.changes = [];
|
||||||
this._current_diff = -1;
|
this._current_diff = -1;
|
||||||
@@ -94,33 +90,25 @@ CodeMirrorDiffView.prototype.lhs = function(text) {
|
|||||||
|
|
||||||
CodeMirrorDiffView.prototype.rhs = function(text) {
|
CodeMirrorDiffView.prototype.rhs = function(text) {
|
||||||
// invalidate existing changes and current position
|
// invalidate existing changes and current position
|
||||||
if (this.settings._debug) {
|
this.trace('api#rhs', text && text.length);
|
||||||
trace('api#rhs', text && text.length);
|
|
||||||
}
|
|
||||||
this.changes = [];
|
this.changes = [];
|
||||||
this._current_diff = -1;
|
this._current_diff = -1;
|
||||||
this.editor.rhs.setValue(text);
|
this.editor.rhs.setValue(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.update = function() {
|
CodeMirrorDiffView.prototype.update = function() {
|
||||||
if (this.settings._debug) {
|
this.trace('api#update');
|
||||||
trace('api#update');
|
|
||||||
}
|
|
||||||
this.el.dispatchEvent(new Event('changed'));
|
this.el.dispatchEvent(new Event('changed'));
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.unmarkup = function() {
|
CodeMirrorDiffView.prototype.unmarkup = function() {
|
||||||
if (this.settings._debug) {
|
this.trace('api#unmarkup');
|
||||||
trace('api#unmarkup');
|
|
||||||
}
|
|
||||||
this._clear();
|
this._clear();
|
||||||
this.el.dispatchEvent(new Event('updated'));
|
this.el.dispatchEvent(new Event('updated'));
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
|
CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#scrollToDiff', direction);
|
||||||
trace('api#scrollToDiff', direction);
|
|
||||||
}
|
|
||||||
if (!this.changes.length) return;
|
if (!this.changes.length) return;
|
||||||
if (direction === 'next') {
|
if (direction === 'next') {
|
||||||
if (this._current_diff === this.changes.length - 1
|
if (this._current_diff === this.changes.length - 1
|
||||||
@@ -137,18 +125,14 @@ CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
|
|||||||
this._current_diff = Math.max(--this._current_diff, 0);
|
this._current_diff = Math.max(--this._current_diff, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.settings._debug) {
|
this.trace('change', 'current-diff', this._current_diff);
|
||||||
trace('change', 'current-diff', this._current_diff);
|
|
||||||
}
|
|
||||||
// _current_diff changed, refresh the view
|
// _current_diff changed, refresh the view
|
||||||
this._scroll_to_change(this.changes[this._current_diff]);
|
this._scroll_to_change(this.changes[this._current_diff]);
|
||||||
this.setChanges(this.changes);
|
this.setChanges(this.changes);
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
|
CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#mergeCurrentChange', side);
|
||||||
trace('api#mergeCurrentChange', side);
|
|
||||||
}
|
|
||||||
if (!this.changes.length) return;
|
if (!this.changes.length) return;
|
||||||
if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
|
if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
|
||||||
this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
|
this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
|
||||||
@@ -159,9 +143,7 @@ CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.scrollTo = function(side, num) {
|
CodeMirrorDiffView.prototype.scrollTo = function(side, num) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#scrollTo', side, num);
|
||||||
trace('api#scrollTo', side, num);
|
|
||||||
}
|
|
||||||
const ed = this.editor[side];
|
const ed = this.editor[side];
|
||||||
ed.setCursor(num);
|
ed.setCursor(num);
|
||||||
ed.centerOnCursor();
|
ed.centerOnCursor();
|
||||||
@@ -175,9 +157,7 @@ CodeMirrorDiffView.prototype.setOptions = function(opts) {
|
|||||||
...this.settings,
|
...this.settings,
|
||||||
...opts
|
...opts
|
||||||
};
|
};
|
||||||
if (this.settings._debug) {
|
this.trace('api#setOptions', opts);
|
||||||
trace('api#setOptions', opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if options set after init
|
// if options set after init
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
@@ -210,9 +190,7 @@ CodeMirrorDiffView.prototype.setOptions = function(opts) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.get = function(side) {
|
CodeMirrorDiffView.prototype.get = function(side) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#get', side);
|
||||||
trace('api#get', side);
|
|
||||||
}
|
|
||||||
const ed = this.editor[side];
|
const ed = this.editor[side];
|
||||||
const value = ed.getValue();
|
const value = ed.getValue();
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
@@ -222,16 +200,12 @@ CodeMirrorDiffView.prototype.get = function(side) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.cm = function(side) {
|
CodeMirrorDiffView.prototype.cm = function(side) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#cm', 'side');
|
||||||
trace('api#cm', 'side');
|
|
||||||
}
|
|
||||||
return this.editor[side];
|
return this.editor[side];
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.search = function(side, query, direction) {
|
CodeMirrorDiffView.prototype.search = function(side, query, direction) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#search', side, query, direction);
|
||||||
trace('api#search', side, query, direction);
|
|
||||||
}
|
|
||||||
const editor = this.editor[side];
|
const editor = this.editor[side];
|
||||||
if (!editor.getSearchCursor) {
|
if (!editor.getSearchCursor) {
|
||||||
throw new Error('install CodeMirror search addon');
|
throw new Error('install CodeMirror search addon');
|
||||||
@@ -258,9 +232,7 @@ CodeMirrorDiffView.prototype.search = function(side, query, direction) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.resize = function() {
|
CodeMirrorDiffView.prototype.resize = function() {
|
||||||
if (this.settings._debug) {
|
this.trace('api#resize');
|
||||||
trace('api#resize');
|
|
||||||
}
|
|
||||||
const parent = this.el;
|
const parent = this.el;
|
||||||
const contentHeight = parent.offsetHeight - 2;
|
const contentHeight = parent.offsetHeight - 2;
|
||||||
|
|
||||||
@@ -283,19 +255,18 @@ CodeMirrorDiffView.prototype.resize = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.bind = function(container) {
|
CodeMirrorDiffView.prototype.bind = function(container) {
|
||||||
if (this.settings._debug) {
|
this.trace('api#bind', container);
|
||||||
trace('api#bind', container);
|
|
||||||
}
|
|
||||||
this._origEl = {
|
|
||||||
style: container.style,
|
|
||||||
className: container.className
|
|
||||||
};
|
|
||||||
const el = dom.getMergelyContainer({ clazz: container.className });
|
const el = dom.getMergelyContainer({ clazz: container.className });
|
||||||
const computedStyle = window.getComputedStyle(container);
|
const computedStyle = window.getComputedStyle(container);
|
||||||
if (!computedStyle.height || computedStyle.height === '0px') {
|
if (!el.style.height
|
||||||
|
&& (!computedStyle.height || computedStyle.height === '0px')
|
||||||
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The element "${container.id}" requires an explicit height`);
|
`The element "${container.id}" requires an explicit height`);
|
||||||
}
|
}
|
||||||
|
this._origEl = {
|
||||||
|
className: container.className
|
||||||
|
};
|
||||||
this.id = `${container.id}`;
|
this.id = `${container.id}`;
|
||||||
this.lhsId = `${container.id}-lhs`;
|
this.lhsId = `${container.id}-lhs`;
|
||||||
this.rhsId = `${container.id}-rhs`;
|
this.rhsId = `${container.id}-rhs`;
|
||||||
@@ -407,9 +378,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.editor.rhs.on('beforeChange', (cm, ev) => {
|
this.editor.rhs.on('beforeChange', (cm, ev) => {
|
||||||
if (this.settings._debug) {
|
this.trace('event#rhs-beforeChange', ev);
|
||||||
trace('event#rhs-beforeChange', ev);
|
|
||||||
}
|
|
||||||
if (ev.text.length > 1
|
if (ev.text.length > 1
|
||||||
|| ((ev.from.line - ev.to.line) && ev.origin === '+delete')) {
|
|| ((ev.from.line - ev.to.line) && ev.origin === '+delete')) {
|
||||||
this._clear();
|
this._clear();
|
||||||
@@ -417,24 +386,16 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.editor.lhs.on('change', (instance, ev) => {
|
this.editor.lhs.on('change', (instance, ev) => {
|
||||||
if (this.settings._debug) {
|
this.trace('event#lhs-change');
|
||||||
trace('event#lhs-change');
|
|
||||||
}
|
|
||||||
this._changing();
|
this._changing();
|
||||||
if (this.settings._debug) {
|
this.trace('event#lhs-change [emitted]');
|
||||||
trace('event#lhs-change [emitted]');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.editor.lhs.on('scroll', () => {
|
this.editor.lhs.on('scroll', () => {
|
||||||
if (this._skipscroll.lhs) {
|
if (this._skipscroll.lhs) {
|
||||||
if (this.settings._debug) {
|
this.trace('event#lhs-scroll (skipped)');
|
||||||
trace('event#lhs-scroll (skipped)');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (this.settings._debug) {
|
this.trace('event#lhs-scroll');
|
||||||
trace('event#lhs-scroll');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// firefox scroll-linked effect render issue
|
// firefox scroll-linked effect render issue
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -442,21 +403,15 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
|||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
this.editor.rhs.on('change', (instance, ev) => {
|
this.editor.rhs.on('change', (instance, ev) => {
|
||||||
if (this.settings._debug) {
|
this.trace('event#rhs-change', ev);
|
||||||
trace('event#rhs-change', ev);
|
|
||||||
}
|
|
||||||
this._changing();
|
this._changing();
|
||||||
});
|
});
|
||||||
this.editor.rhs.on('scroll', () => {
|
this.editor.rhs.on('scroll', () => {
|
||||||
if (this._skipscroll.rhs) {
|
if (this._skipscroll.rhs) {
|
||||||
if (this.settings._debug) {
|
this.trace('event#rhs-scroll (skipped)');
|
||||||
trace('event#rhs-scroll (skipped)');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (this.settings._debug) {
|
this.trace('event#rhs-scroll');
|
||||||
trace('event#rhs-scroll');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// firefox scroll-linked effect render issue
|
// firefox scroll-linked effect render issue
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -469,7 +424,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
|||||||
const resize = () => {
|
const resize = () => {
|
||||||
if (this.settings._debug) {
|
if (this.settings._debug) {
|
||||||
traceTimeStart('event#resize');
|
traceTimeStart('event#resize');
|
||||||
trace('event#resize [start]');
|
this.trace('event#resize [start]');
|
||||||
}
|
}
|
||||||
this.resize();
|
this.resize();
|
||||||
if (this.settings._debug) {
|
if (this.settings._debug) {
|
||||||
@@ -526,16 +481,12 @@ CodeMirrorDiffView.prototype.bind = function(container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.editor.lhs.on('gutterClick', (cm, n, gutterClass, ev) => {
|
this.editor.lhs.on('gutterClick', (cm, n, gutterClass, ev) => {
|
||||||
if (this.settings._debug) {
|
this.trace('event#gutterClick', 'lhs', n, ev);
|
||||||
trace('event#gutterClick', 'lhs', n, ev);
|
|
||||||
}
|
|
||||||
gutterClicked.call(this, 'lhs', n, ev);
|
gutterClicked.call(this, 'lhs', n, ev);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.rhs.on('gutterClick', (cm, n, gutterClass, ev) => {
|
this.editor.rhs.on('gutterClick', (cm, n, gutterClass, ev) => {
|
||||||
if (this.settings._debug) {
|
this.trace('event#gutterClick', 'rhs', n, ev);
|
||||||
trace('event#gutterClick', 'rhs', n, ev);
|
|
||||||
}
|
|
||||||
gutterClicked.call(this, 'rhs', n, ev);
|
gutterClicked.call(this, 'rhs', n, ev);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -683,18 +634,14 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
|
|||||||
const vp = this.editor[oside].getViewport();
|
const vp = this.editor[oside].getViewport();
|
||||||
let scroll = true;
|
let scroll = true;
|
||||||
if (last_change) {
|
if (last_change) {
|
||||||
if (this.settings._debug) {
|
this.trace('scroll#_scrolling', 'last change before midline', last_change);
|
||||||
trace('scroll#_scrolling', 'last change before midline', last_change);
|
|
||||||
}
|
|
||||||
if (midline.line >= vp.from && midline <= vp.to) {
|
if (midline.line >= vp.from && midline <= vp.to) {
|
||||||
scroll = false;
|
scroll = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (scroll || force_scroll) {
|
if (scroll || force_scroll) {
|
||||||
// scroll the other side
|
// scroll the other side
|
||||||
if (this.settings._debug) {
|
this.trace('scroll#_scrolling', 'other side', oside, 'pos:', top_to - top_adjust);
|
||||||
trace('scroll#_scrolling', 'other side', oside, 'pos:', top_to - top_adjust);
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable linked scroll events for the opposite editor because this
|
// disable linked scroll events for the opposite editor because this
|
||||||
// triggers the next one explicitly, and we don't want to link the
|
// triggers the next one explicitly, and we don't want to link the
|
||||||
@@ -702,21 +649,21 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
|
|||||||
// coming in 2s, so this will "link" scrolling the other editor to
|
// coming in 2s, so this will "link" scrolling the other editor to
|
||||||
// this editor until this editor stops scrolling and times out.
|
// this editor until this editor stops scrolling and times out.
|
||||||
this._skipscroll[oside] = true;
|
this._skipscroll[oside] = true;
|
||||||
trace('scroll#set oside skip set:', oside, this._skipscroll);
|
this.trace('scroll#set oside skip set:', oside, this._skipscroll);
|
||||||
if (this._linkedScrollTimeout[oside]) {
|
if (this._linkedScrollTimeout[oside]) {
|
||||||
clearTimeout(this._linkedScrollTimeout[oside]);
|
clearTimeout(this._linkedScrollTimeout[oside]);
|
||||||
trace('scroll#clearing timeout:', this._skipscroll);
|
this.trace('scroll#clearing timeout:', this._skipscroll);
|
||||||
}
|
}
|
||||||
this._linkedScrollTimeout[oside] = setTimeout(() => {
|
this._linkedScrollTimeout[oside] = setTimeout(() => {
|
||||||
this._skipscroll[oside] = false;
|
this._skipscroll[oside] = false;
|
||||||
trace('scroll#set oside skip unset:', oside, this._skipscroll);
|
this.trace('scroll#set oside skip unset:', oside, this._skipscroll);
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
const top = top_to - top_adjust;
|
const top = top_to - top_adjust;
|
||||||
// scroll the opposite editor
|
// scroll the opposite editor
|
||||||
this.editor[oside].scrollTo(left_to, top);
|
this.editor[oside].scrollTo(left_to, top);
|
||||||
} else if (this.settings._debug) {
|
} else {
|
||||||
trace('scroll#_scrolling', 'not scrolling other side');
|
this.trace('scroll#_scrolling', 'not scrolling other side');
|
||||||
}
|
}
|
||||||
this._renderChanges();
|
this._renderChanges();
|
||||||
|
|
||||||
@@ -727,23 +674,19 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
|
|||||||
|
|
||||||
CodeMirrorDiffView.prototype._changing = function() {
|
CodeMirrorDiffView.prototype._changing = function() {
|
||||||
if (!this.settings.autoupdate) {
|
if (!this.settings.autoupdate) {
|
||||||
if (this.settings._debug) {
|
this.trace('change#_changing autoupdate is disabled');
|
||||||
trace('change#_changing autoupdate is disabled');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.settings._debug) {
|
if (this.settings._debug) {
|
||||||
traceTimeStart('change#_changing');
|
traceTimeStart('change#_changing');
|
||||||
trace('change#_changing [start]');
|
this.trace('change#_changing [start]');
|
||||||
}
|
}
|
||||||
const handleChange = () => {
|
const handleChange = () => {
|
||||||
this._changedTimeout = null;
|
this._changedTimeout = null;
|
||||||
this.el.dispatchEvent(new Event('changed'));
|
this.el.dispatchEvent(new Event('changed'));
|
||||||
};
|
};
|
||||||
if (this.settings.change_timeout > 0) {
|
if (this.settings.change_timeout > 0) {
|
||||||
if (this.settings._debug) {
|
this.trace('change#setting timeout', this.settings.change_timeout)
|
||||||
trace('change#setting timeout', this.settings.change_timeout)
|
|
||||||
}
|
|
||||||
if (this._changedTimeout != null) {
|
if (this._changedTimeout != null) {
|
||||||
clearTimeout(this._changedTimeout);
|
clearTimeout(this._changedTimeout);
|
||||||
}
|
}
|
||||||
@@ -757,9 +700,7 @@ CodeMirrorDiffView.prototype._changing = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CodeMirrorDiffView.prototype.setChanges = function(changes) {
|
CodeMirrorDiffView.prototype.setChanges = function(changes) {
|
||||||
if (this.settings._debug) {
|
this.trace('change#setChanges');
|
||||||
trace('change#setChanges');
|
|
||||||
}
|
|
||||||
this._clear();
|
this._clear();
|
||||||
// after clear, set the new changes
|
// after clear, set the new changes
|
||||||
this.changes = changes;
|
this.changes = changes;
|
||||||
@@ -769,7 +710,7 @@ CodeMirrorDiffView.prototype.setChanges = function(changes) {
|
|||||||
CodeMirrorDiffView.prototype._renderChanges = function() {
|
CodeMirrorDiffView.prototype._renderChanges = function() {
|
||||||
if (this.settings._debug) {
|
if (this.settings._debug) {
|
||||||
traceTimeStart('draw#_renderChanges');
|
traceTimeStart('draw#_renderChanges');
|
||||||
trace('draw#_renderChanges [start]', this.changes.length, 'changes');
|
this.trace('draw#_renderChanges [start]', this.changes.length, 'changes');
|
||||||
}
|
}
|
||||||
this._clearCanvases();
|
this._clearCanvases();
|
||||||
this._calculateOffsets(this.changes);
|
this._calculateOffsets(this.changes);
|
||||||
@@ -812,6 +753,9 @@ CodeMirrorDiffView.prototype._set_top_offset = function (side) {
|
|||||||
// this is the distance from the top of the screen to the top of the
|
// this is the distance from the top of the screen to the top of the
|
||||||
// content of the first codemirror editor
|
// content of the first codemirror editor
|
||||||
const topnode = this._queryElement('.CodeMirror-measure');
|
const topnode = this._queryElement('.CodeMirror-measure');
|
||||||
|
if (!topnode.offsetParent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const top_offset = topnode.offsetParent.offsetTop + 4;
|
const top_offset = topnode.offsetParent.offsetTop + 4;
|
||||||
|
|
||||||
// restore editor's scroll position
|
// restore editor's scroll position
|
||||||
@@ -1097,12 +1041,9 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
|
|||||||
const ctx_lhs = mcanvas_lhs.getContext('2d');
|
const ctx_lhs = mcanvas_lhs.getContext('2d');
|
||||||
const ctx_rhs = mcanvas_rhs.getContext('2d');
|
const ctx_rhs = mcanvas_rhs.getContext('2d');
|
||||||
|
|
||||||
if (this.settings._debug
|
this.trace('draw#_renderDiff', 'visible page height', ex.visible_page_height);
|
||||||
&& this.settings._debug) {
|
this.trace('draw#_renderDiff', 'scroller-top lhs', ex.lhs_scroller.scrollTop);
|
||||||
trace('draw#_renderDiff', 'visible page height', ex.visible_page_height);
|
this.trace('draw#_renderDiff', 'scroller-top rhs', ex.rhs_scroller.scrollTop);
|
||||||
trace('draw#_renderDiff', 'scroller-top lhs', ex.lhs_scroller.scrollTop);
|
|
||||||
trace('draw#_renderDiff', 'scroller-top rhs', ex.rhs_scroller.scrollTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
ex.lhs_margin.removeEventListener('click', this._handleLhsMarginClick);
|
ex.lhs_margin.removeEventListener('click', this._handleLhsMarginClick);
|
||||||
ex.rhs_margin.removeEventListener('click', this._handleRhsMarginClick);
|
ex.rhs_margin.removeEventListener('click', this._handleRhsMarginClick);
|
||||||
@@ -1127,17 +1068,16 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
|
|||||||
const rhs_y_end = change['rhs-y-end'] - rhsScrollTop;
|
const rhs_y_end = change['rhs-y-end'] - rhsScrollTop;
|
||||||
|
|
||||||
if (Number.isNaN(lhs_y_start)) {
|
if (Number.isNaN(lhs_y_start)) {
|
||||||
trace('draw#_renderDiff', 'unexpected NaN',
|
this.trace(
|
||||||
change['lhs-y-start'], change['lhs-y-end']);
|
'draw#_renderDiff unexpected NaN',
|
||||||
|
change['lhs-y-start'], change['lhs-y-end']
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw margin indicators
|
// draw margin indicators
|
||||||
if (this.settings._debug
|
this.trace('draw#_renderDiff', 'draw1', 'marker',
|
||||||
&& this.settings._debug) {
|
lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
|
||||||
trace('draw#_renderDiff', 'draw1', 'marker',
|
|
||||||
lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mkr_lhs_y_start = change['lhs-y-start'] * lratio;
|
const mkr_lhs_y_start = change['lhs-y-start'] * lratio;
|
||||||
const mkr_lhs_y_end = Math.max(change['lhs-y-end'] * lratio, 5);
|
const mkr_lhs_y_end = Math.max(change['lhs-y-end'] * lratio, 5);
|
||||||
@@ -1266,4 +1206,10 @@ CodeMirrorDiffView.prototype._queryElement = function(selector) {
|
|||||||
return this[cacheName];
|
return this[cacheName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeMirrorDiffView.prototype.trace = function(...args) {
|
||||||
|
if (this.settings._debug) {
|
||||||
|
console.log(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = CodeMirrorDiffView;
|
module.exports = CodeMirrorDiffView;
|
||||||
|
|||||||
10
src/diff.js
10
src/diff.js
@@ -146,7 +146,7 @@ diff.prototype._sms = function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Extend the reverse path.
|
// Extend the reverse path.
|
||||||
for (k = kup - d; k <= kup + d; k += 2) {
|
for (let k = kup - d; k <= kup + d; k += 2) {
|
||||||
// find the only or better starting point
|
// find the only or better starting point
|
||||||
if (k === kup + d) {
|
if (k === kup + d) {
|
||||||
x = vector_u[offset_up + k - 1]; // up
|
x = vector_u[offset_up + k - 1]; // up
|
||||||
@@ -244,7 +244,9 @@ function CodeifyText(lhs, rhs, options) {
|
|||||||
|
|
||||||
if (typeof lhs === 'string') {
|
if (typeof lhs === 'string') {
|
||||||
if (this.options.split === 'chars') {
|
if (this.options.split === 'chars') {
|
||||||
this.lhs = lhs.split('');
|
// split characters and include their diacritical marks
|
||||||
|
this.lhs = lhs.match(/\p{Letter}\p{Mark}*|\p{White_Space}/gu) || [];
|
||||||
|
// this.lhs = [...lhs];
|
||||||
} else if (this.options.split === 'words') {
|
} else if (this.options.split === 'words') {
|
||||||
this.lhs = lhs.split(/\s/);
|
this.lhs = lhs.split(/\s/);
|
||||||
} else if (this.options.split === 'lines') {
|
} else if (this.options.split === 'lines') {
|
||||||
@@ -255,7 +257,9 @@ function CodeifyText(lhs, rhs, options) {
|
|||||||
}
|
}
|
||||||
if (typeof rhs === 'string') {
|
if (typeof rhs === 'string') {
|
||||||
if (this.options.split === 'chars') {
|
if (this.options.split === 'chars') {
|
||||||
this.rhs = rhs.split('');
|
// split characters and include their diacritical marks
|
||||||
|
this.rhs = rhs.match(/\p{Letter}\p{Mark}*|\p{White_Space}/gu) || [];
|
||||||
|
// this.rhs = [...rhs];
|
||||||
} else if (this.options.split === 'words') {
|
} else if (this.options.split === 'words') {
|
||||||
this.rhs = rhs.split(/\s/);
|
this.rhs = rhs.split(/\s/);
|
||||||
} else if (this.options.split === 'lines') {
|
} else if (this.options.split === 'lines') {
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ const defaultOptions = {
|
|||||||
vpcolor: 'rgba(0, 0, 200, 0.5)',
|
vpcolor: 'rgba(0, 0, 200, 0.5)',
|
||||||
license: 'lgpl',
|
license: 'lgpl',
|
||||||
cmsettings: {
|
cmsettings: {
|
||||||
styleSelectedText: true
|
styleSelectedText: true,
|
||||||
|
mode: null
|
||||||
},
|
},
|
||||||
lhs_cmsettings: {},
|
lhs_cmsettings: {},
|
||||||
rhs_cmsettings: {},
|
rhs_cmsettings: {},
|
||||||
@@ -45,7 +46,9 @@ class Mergely {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const computedStyle = window.getComputedStyle(element);
|
const computedStyle = window.getComputedStyle(element);
|
||||||
if (!computedStyle.height || computedStyle.height === '0px') {
|
if (!element.style.height
|
||||||
|
&& (!computedStyle.height || computedStyle.height === '0px')
|
||||||
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The element "${selector}" requires an explicit height`);
|
`The element "${selector}" requires an explicit height`);
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/vdoc.js
20
src/vdoc.js
@@ -2,6 +2,8 @@ const diff = require('./diff');
|
|||||||
|
|
||||||
const trace = console.log;
|
const trace = console.log;
|
||||||
|
|
||||||
|
const expLetters = new RegExp(/\p{Letter}\p{Mark}*|\p{White_Space}/gu);
|
||||||
|
|
||||||
class VDoc {
|
class VDoc {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
@@ -275,15 +277,18 @@ class VLine {
|
|||||||
editor.setGutterMarker(this.id, name, item);
|
editor.setGutterMarker(this.id, name, item);
|
||||||
}
|
}
|
||||||
if (this.markup.length) {
|
if (this.markup.length) {
|
||||||
|
// while Mergely diffs unicode chars (letters+mark), CM is by character,
|
||||||
|
// so diffs need to be mapped.
|
||||||
|
const mapped = mapLettersToChars(editor.getValue());
|
||||||
for (const markup of this.markup) {
|
for (const markup of this.markup) {
|
||||||
const [ charFrom, charTo, className ] = markup;
|
const [ charFrom, charTo, className ] = markup;
|
||||||
const fromPos = { line: this.id };
|
const fromPos = { line: this.id };
|
||||||
const toPos = { line: this.id };
|
const toPos = { line: this.id };
|
||||||
if (charFrom >= 0) {
|
if (charFrom >= 0) {
|
||||||
fromPos.ch = charFrom;
|
fromPos.ch = mapped[charFrom];
|
||||||
}
|
}
|
||||||
if (charTo >= 0) {
|
if (charTo >= 0) {
|
||||||
toPos.ch = charTo;
|
toPos.ch = mapped[charTo];
|
||||||
}
|
}
|
||||||
this._clearMarkup.push(
|
this._clearMarkup.push(
|
||||||
editor.markText(fromPos, toPos, { className }));
|
editor.markText(fromPos, toPos, { className }));
|
||||||
@@ -334,4 +339,15 @@ function getExtents(side, change) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapLettersToChars(text) {
|
||||||
|
let match;
|
||||||
|
let mapped = {};
|
||||||
|
let index = 0;
|
||||||
|
expLetters.lastIndex = 0;
|
||||||
|
while ((match = expLetters.exec(text)) !== null) {
|
||||||
|
mapped[index++] = match.index;
|
||||||
|
}
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = VDoc;
|
module.exports = VDoc;
|
||||||
|
|||||||
@@ -233,7 +233,109 @@ describe('markup', () => {
|
|||||||
expect(rhs_spans[1].innerText).to.equal('h');
|
expect(rhs_spans[1].innerText).to.equal('h');
|
||||||
expect(rhs_spans[2].innerText).to.equal('ir');
|
expect(rhs_spans[2].innerText).to.equal('ir');
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
name: 'single word single diacritic non-spacing marks',
|
||||||
|
lhs: 'كلمة',
|
||||||
|
rhs: 'كَلمة',
|
||||||
|
check: (editor) => {
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(lhs_spans).to.have.length(1);
|
||||||
|
expect(lhs_spans[0].innerText).to.equal('ك');
|
||||||
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(rhs_spans).to.have.length(1);
|
||||||
|
expect(rhs_spans[0].innerText).to.equal('كَ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'single word multiple diacritic non-spacing marks',
|
||||||
|
lhs: ['\u006E', '\u0061', '\u0314', '\u0065'].join(''), // na̔e
|
||||||
|
rhs: ['\u006E', '\u0061', '\u0314', '\u034A', '\u0065'].join(''), // na̔͊e
|
||||||
|
check: (editor) => {
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(lhs_spans).to.have.length(1);
|
||||||
|
expect(lhs_spans[0].innerText).to.equal(['\u0061', '\u0314'].join(''));
|
||||||
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(rhs_spans).to.have.length(1);
|
||||||
|
expect(rhs_spans[0].innerText).to.equal('a̔͊');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'multiple words diacritic non-spacing marks',
|
||||||
|
lhs: 'كلمة اخرى',
|
||||||
|
rhs: 'كْلمة اخرى',
|
||||||
|
check: (editor) => {
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(lhs_spans).to.have.length(1);
|
||||||
|
expect(lhs_spans[0].innerText).to.equal('ك');
|
||||||
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(rhs_spans).to.have.length(1);
|
||||||
|
expect(rhs_spans[0].innerText).to.equal('كْ');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nonnormalizable diacritic non-spacing marks',
|
||||||
|
lhs: 'naeg',
|
||||||
|
// there are 2 marks on 'e', tilde (0303) and x (0353)
|
||||||
|
rhs: ['\u006E', '\u0061', '\u0353', '\u0065', '\u0353', '\u0303', '\u0067'].join(''),
|
||||||
|
check: (editor) => {
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(lhs_spans).to.have.length(1);
|
||||||
|
expect(lhs_spans[0].innerText).to.equal('ae');
|
||||||
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(rhs_spans).to.have.length(1);
|
||||||
|
expect(rhs_spans[0].innerText).to.equal(
|
||||||
|
['\u0061', '\u0353', '\u0065', '\u0353', '\u0303'].join('')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nonnormalizable diacritic non-spacing marks',
|
||||||
|
lhs: [
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
'x',
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
].join(''),
|
||||||
|
// there are 2 marks on 'e', tilde (0303) and x (0353)
|
||||||
|
rhs: [
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
'y',
|
||||||
|
'\u0065', '\u0353', '\u0303',
|
||||||
|
].join(''),
|
||||||
|
check: (editor) => {
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
||||||
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
||||||
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(lhs_spans).to.have.length(1);
|
||||||
|
expect(lhs_spans[0].innerText).to.equal('x');
|
||||||
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
||||||
|
expect(rhs_spans).to.have.length(1);
|
||||||
|
expect(rhs_spans[0].innerText).to.equal('y');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// to debug, add `only: true` to the test `opts` above, and run `npm run debug`
|
// to debug, add `only: true` to the test `opts` above, and run `npm run debug`
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ const defaultOptions = {
|
|||||||
vpcolor: 'rgba(0, 0, 200, 0.5)',
|
vpcolor: 'rgba(0, 0, 200, 0.5)',
|
||||||
license: 'lgpl',
|
license: 'lgpl',
|
||||||
cmsettings: {
|
cmsettings: {
|
||||||
styleSelectedText: true
|
styleSelectedText: true,
|
||||||
|
mode: null
|
||||||
},
|
},
|
||||||
_debug: false
|
_debug: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const chalk = require('chalk');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -50,8 +51,8 @@ module.exports = {
|
|||||||
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
|
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
|
||||||
console.log('-'.repeat(78));
|
console.log('-'.repeat(78));
|
||||||
console.log('Applications:');
|
console.log('Applications:');
|
||||||
console.log('http://localhost:8080/app.html');
|
console.log(chalk.bold(chalk.underline(chalk.cyan('http://localhost:8080/app.html'))));
|
||||||
console.log('http://localhost:8080/app-styles.html');
|
console.log(chalk.bold(chalk.underline(chalk.cyan('http://localhost:8080/app-styles.html'))));
|
||||||
console.log('-'.repeat(78));
|
console.log('-'.repeat(78));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ module.exports = (mode) => {
|
|||||||
...webpackDevConfig.output,
|
...webpackDevConfig.output,
|
||||||
path: path.join(__dirname, 'lib'),
|
path: path.join(__dirname, 'lib'),
|
||||||
filename: './[name].js',
|
filename: './[name].js',
|
||||||
|
library: {
|
||||||
|
name: 'mergely',
|
||||||
|
type: 'umd',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: true,
|
minimize: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user