Compare commits

..

8 Commits

Author SHA1 Message Date
Jamie Peabody
88f21e2bc7 chore(ci): package-lock.json 2023-04-22 16:01:38 +01:00
Jamie Peabody
4160ab4f6e chore(ci): test 2023-04-22 15:59:44 +01:00
Jamie Peabody
16f1d2016d chore(ci): alpha, beta, next branches 2023-04-22 15:57:48 +01:00
Jamie Peabody
347a3f56ff chore(ci): updated webpack 2023-04-22 14:41:21 +01:00
Jamie Peabody
88d4a62a04 chore: updated examples 2023-04-22 14:40:58 +01:00
Jamie Peabody
b3e7b48d11 feat: dark mode 2023-04-22 14:13:30 +01:00
Jamie Peabody
fcea4efe2f chore: tweaked no-start/end styles 2023-04-22 13:44:33 +01:00
Jamie Peabody
080b8fd7b5 feat: v5
BREAKING CHANGE: Mergely is no longer a jQuery plugin.

BREAKING CHANGE: Removed `options.autoresize`

BREAKING CHANGE: Removed `options.editor_width`

BREAKING CHANGE: Removed `options.editor_height`

BREAKING CHANGE: Removed `options.fadein`

BREAKING CHANGE: Removed `options.fgcolor`

BREAKING CHANGE: Removed `options.resize`

BREAKING CHANGE: Removed `options.width`

BREAKING CHANGE: Removed `options.height`

BREAKING CHANGE: Removed `options.loaded` callback

BREAKING CHANGE: Removed `options.resized` callback

BREAKING CHANGE: Removed styles `.mergely-resizer`, `.mergely-full-screen-0`, and `.mergely-full-screen-8`

BREAKING CHANGE: Changed default for `options.change_timeout` changed from `150` to `50`.

BREAKING CHANGE: No longer automatically scrolls to first change.

feat: CodeMirror is now an explicit dependency.

feat: No longer necessary to separately require codemirror/addon/search/searchcursor

feat: No longer necessary to separately require codemirror/addon/selection/mark-selection

feat: `mergely.js` is now unminimized, and added new minimized version `mergely.min.js`

feat: Gutter click now scrolls to any line

feat: Mergely now emits `resize` event on resize

feat: The UI is now non-blocking as diff now runs in background

feat: Added support to provide `options.lhs` and `options.rhs` as strings

feat: #16 added titles to editor.mergely.com

fix: #165 block of changes at end of file are now distinguishable

fix: #140 fixed performance issue with large files

fix: Fixed issue where canvas markup was not rendered when `viewport` enabled

fix: Fixed timing issue where swap sides may not work as expected.

fix: Fixed issue where unmarkup did not emit an updated event.

fix: Fixed documentation issue where `merge` incorrectly stated: from the specified `side` to the opposite side.

fix: Fixed performance issue scrolling

fix: Fixed issue where initial render scrolled to first change, showing it at the bottom (as opposed to middle as expected)

fix: Fixed issue where line-diffs failed to diff non-alphanumeric characters
2023-04-22 12:41:12 +01:00
22 changed files with 4349 additions and 5593 deletions

View File

@@ -3,10 +3,7 @@ name: Run tests (branch)
on:
push:
branches-ignore:
- master
- alpha
- beta
- next
- 'master'
tags-ignore:
- 'v*'

View File

@@ -1,11 +0,0 @@
branches:
- master
- name: alpha
prerelease: true
plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
- "@semantic-release/changelog"
- "@semantic-release/npm"
- "@semantic-release/git"
- "@semantic-release/github"

View File

@@ -1,195 +1,7 @@
## [5.4.6](https://github.com/wickedest/Mergely/compare/v5.4.5...v5.4.6) (2026-02-24)
### Bug Fixes
* **#228:** Clicking on edit marker in gutter now accounts for top position of editor. ([cc7df68](https://github.com/wickedest/Mergely/commit/cc7df68aea62e2e3a18c996fa56048f1e583faa5)), closes [#228](https://github.com/wickedest/Mergely/issues/228)
## [5.4.5](https://github.com/wickedest/Mergely/compare/v5.4.4...v5.4.5) (2026-02-23)
### Bug Fixes
* **#222:** Fixed issue with poor rendering performance and marker misalignment ([27778f0](https://github.com/wickedest/Mergely/commit/27778f0840895bf5e029d8c962e99890b8a0a8f6)), closes [#222](https://github.com/wickedest/Mergely/issues/222) [#227](https://github.com/wickedest/Mergely/issues/227)
## [5.4.4](https://github.com/wickedest/Mergely/compare/v5.4.3...v5.4.4) (2026-02-23)
### Bug Fixes
* Updated examples README.md ([f1e58f5](https://github.com/wickedest/Mergely/commit/f1e58f58fea85628b1dd345b98b23923f05d8c2e))
## [5.4.3](https://github.com/wickedest/Mergely/compare/v5.4.2...v5.4.3) (2026-02-23)
### Bug Fixes
* Removed whitespace from example ([69ecf76](https://github.com/wickedest/Mergely/commit/69ecf76fe6066ded1efca06f9ee0ad985e8ed047))
## [5.4.2](https://github.com/wickedest/Mergely/compare/v5.4.1...v5.4.2) (2026-02-23)
### Bug Fixes
* Removed whitespace from example ([00a7917](https://github.com/wickedest/Mergely/commit/00a79170a8d4b701d6da98c1d6ea921b592fb3d1))
## [5.4.1](https://github.com/wickedest/Mergely/compare/v5.4.0...v5.4.1) (2026-02-23)
### Bug Fixes
* **#225:** Fixes rendering issue when there are multiple editors ([1a2ee6a](https://github.com/wickedest/Mergely/commit/1a2ee6af3d7d87160e72e8faca5e38cf01b36fcb))
# [5.4.0](https://github.com/wickedest/Mergely/compare/v5.3.6...v5.4.0) (2025-11-15)
### Features
* **#220:** Exposes Mergely's instance of CodeMirror as Mergely.CodeMirror ([#221](https://github.com/wickedest/Mergely/issues/221)) ([d6d8d06](https://github.com/wickedest/Mergely/commit/d6d8d06accaa4fa3a4352b95620e3e456226d444)), closes [#220](https://github.com/wickedest/Mergely/issues/220)
## [5.3.6](https://github.com/wickedest/Mergely/compare/v5.3.5...v5.3.6) (2024-10-11)
### Bug Fixes
* **#207:** Updated documentation for search method. ([#214](https://github.com/wickedest/Mergely/issues/214)) ([350e5af](https://github.com/wickedest/Mergely/commit/350e5af2e8b2e852364dc5a5729e03e3bc75358b)), closes [#207](https://github.com/wickedest/Mergely/issues/207)
## [5.3.5](https://github.com/wickedest/Mergely/compare/v5.3.4...v5.3.5) (2024-10-11)
### Bug Fixes
* bump dependencies ([#213](https://github.com/wickedest/Mergely/issues/213)) ([83df02d](https://github.com/wickedest/Mergely/commit/83df02d967ce3100a33216f8ca77c2168bd53234))
## [5.3.4](https://github.com/wickedest/Mergely/compare/v5.3.3...v5.3.4) (2024-10-11)
### Reverts
* Revert "build(deps-dev): bump webpack from 5.91.0 to 5.94.0 (#203)" ([c36d2a7](https://github.com/wickedest/Mergely/commit/c36d2a7ba9ca895844126711aa00b9634814e1dc)), closes [#203](https://github.com/wickedest/Mergely/issues/203)
## [5.3.3](https://github.com/wickedest/Mergely/compare/v5.3.2...v5.3.3) (2024-10-11)
### Bug Fixes
* **#209:** Fixes issue with options not being preserved when individually set. ([#211](https://github.com/wickedest/Mergely/issues/211)) ([4cd8897](https://github.com/wickedest/Mergely/commit/4cd8897c6b157d506560bb09f508f9debe2a4211))
## [5.3.2](https://github.com/wickedest/Mergely/compare/v5.3.1...v5.3.2) (2024-07-31)
### Bug Fixes
* Removed debug code ([c7b0fd0](https://github.com/wickedest/Mergely/commit/c7b0fd03b01cb3267d292c2985ca8b62adda11e0))
## [5.3.1](https://github.com/wickedest/Mergely/compare/v5.3.0...v5.3.1) (2024-06-19)
### Bug Fixes
* Fixed a regression rendering inline markup for numbers, punctuation, and symbols ([#199](https://github.com/wickedest/Mergely/issues/199)) ([c40672c](https://github.com/wickedest/Mergely/commit/c40672c458723bdd0d9a0062ef1457e435866765))
# [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)
### Bug Fixes
* **scroll:** fixed issue where first rhs scroll was unlinked ([7b2040c](https://github.com/wickedest/Mergely/commit/7b2040c6ad17ea70a09720f54d9a255cdc57cd67))
## [5.0.1](https://github.com/wickedest/Mergely/compare/v5.0.0...v5.0.1) (2023-04-23)
### Bug Fixes
* update release ([a8b497b](https://github.com/wickedest/Mergely/commit/a8b497bc9b68beb5d8265106d9f010aa9489dd17))
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [5.0.0](https://github.com/wickedest/Mergely/compare/v4.3.9...v5.0.0) (2023-04-23)
### BREAKING CHANGES
- Mergely is no longer a jQuery plugin.
- Removed options.autoresize
- Removed options.editor_width
- Removed options.editor_height
- Removed options.fadein
- Removed options.fgcolor
- Removed options.resize
- Removed options.width
- Removed options.height
- Removed options.loaded callback
- Removed options.resized callback
- Removed styles .mergely-resizer, .mergely-full-screen-0, and .mergely-full-screen-8
- Changed default for options.change_timeout changed from 150 to 50.
- No longer automatically scrolls to first change.
### 4.3.9 (2022-01-19)

View File

@@ -4,7 +4,7 @@
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, such as .txt, .html, .xml, .c, .cpp, .java, .js, 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, for example, .txt, .html, .xml, .c, .cpp, .java, 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.
@@ -36,11 +36,11 @@ rm -rf .git
### Usage via CDN
Add the following to the `<head>` of your target HTML source file. Note that `codemirror` is bundled.
Unpack mergely.tgz into a folder, for example, `./lib`, and add the following to the `<head>` of your target HTML source file.
```html
<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" />
<script 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>
```
### Synchronous initialization
@@ -82,18 +82,6 @@ Mergely will emit an `updated` event when the editor is first initialized, and e
</body>
```
### Visualization modes
Mergely does not support any CodeMirror highlighting for different languages (xml, html, javascript etc.) out of the box.
You can compile your own Mergely-Version with support for the desired modes by adding them to diff-view.js. For example:
```
require('codemirror/mode/go/go.js');
require('codemirror/mode/javascript/javascript.js');
require('codemirror/mode/htmlmixed/htmlmixed.js');
require('codemirror/mode/markdown/markdown.js');
require('codemirror/mode/python/python.js');
```
## Options
|Option|Type|Default value|Description|
@@ -355,7 +343,7 @@ Scrolls to the next change specified by `direction`.
doc.scrollToDiff('next');
```
### <a name="search"></a>search(side: string, needle: string, direction: string = 'next')
### <a name="search"></a>search(side: string, needle: string)
Search the editor for `needle`, scrolling to the next available match. Repeating the call will find the next available token.
@@ -365,7 +353,6 @@ Search the editor for `needle`, scrolling to the next available match. Repeating
|----|----|-----------|
|side|string|The editor side, either `lhs` or `rhs`.|
|needle|string|The text for which to search.|
|direction|string|The direction to search, either `prev` or `next`.|
#### Example

View File

@@ -1,5 +1,6 @@
# Mergely examples
## Mergely development server
To run the hot dev-server for development, you need to run:

View File

@@ -2,6 +2,9 @@ require('codemirror/addon/selection/mark-selection.js');
require('codemirror/lib/codemirror.css');
require('../src/mergely.css');
document.onreadystatechange = function () {
if (document.readyState !== 'complete') {
return;

View File

@@ -14,6 +14,7 @@ the quick brown fox
jumped over the lazy dog
`;
document.onreadystatechange = function () {
if (document.readyState !== 'complete') {
return;

View File

@@ -1,38 +0,0 @@
<!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>

View File

@@ -21,9 +21,6 @@ This example demonstrates the minimum amount of code required to use Mergely.
<h1>Examples</h1>
<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>
<dd>An example editor showcasing some of Mergely's API features.</dd>

View File

@@ -22,7 +22,6 @@
"changelog" : {
"commitTypes": [
"docs",
"feat",
"fix",
"perf",

9094
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "mergely",
"version": "5.4.6",
"version": "5.0.0-rc0",
"description": "A javascript UI for diff/merge",
"license": "(GPL-3.0 OR LGPL-3.0 OR MPL-1.1 OR SEE LICENSE IN LICENSE)",
"author": {
@@ -14,7 +14,7 @@
},
"repository": {
"type": "git",
"url": "git@github.com:wickedest/Mergely.git"
"url": "git+https://github.com/wickedest/Mergely.git"
},
"main": "lib/mergely.js",
"files": [
@@ -36,8 +36,6 @@
"@commitlint/cli": "^17.6.1",
"@commitlint/config-conventional": "^15.0.0",
"@commitlint/prompt-cli": "^15.0.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"babel-loader": "^8.2.3",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"chai": "^4.3.4",
@@ -57,7 +55,7 @@
"karma-sourcemap-loader": "^0.4.0",
"karma-webpack": "^5.0.0",
"mocha": "^9.1.4",
"semantic-release": "^21.1.2",
"semantic-release": "^21.0.1",
"simple-mock": "^0.8.0",
"standard-version": "^9.3.2",
"style-loader": "^3.3.1",

View File

@@ -2,7 +2,6 @@ const CodeMirror = require('codemirror');
require('codemirror/addon/search/searchcursor.js');
require('codemirror/addon/selection/mark-selection.js');
require('codemirror/lib/codemirror.css');
const dom = require('./dom.js');
const VDoc = require('./vdoc');
@@ -18,11 +17,9 @@ function CodeMirrorDiffView(el, options) {
const coords = this.cursorCoords(null, 'local');
this.scrollTo(null,
(coords.top + coords.bottom) / 2 - (this.getScrollerElement().clientHeight / 2));
});
this.init(el, options);
};
CodeMirrorDiffView.CodeMirror = CodeMirror;
});
this.init(el, options);
};
const trace = console.log;
const traceTimeStart = console.time;
@@ -47,15 +44,16 @@ CodeMirrorDiffView.prototype.init = function(el, options = {}) {
lineNumbers: this.settings.line_numbers,
gutters: (this.settings.line_numbers && [ 'merge', 'CodeMirror-linenumbers' ]) || [],
};
this._vdoc = new VDoc({ _debug: this.settings._debug });
this._linkedScrollTimeout = {};
this._vdoc = new VDoc();
};
CodeMirrorDiffView.prototype.unbind = function() {
if (this._unbound) {
return;
}
this.trace('api#unbind');
if (this.settings._debug) {
trace('api#unbind');
}
if (this._changedTimeout != null) {
clearTimeout(this._changedTimeout);
}
@@ -67,6 +65,7 @@ CodeMirrorDiffView.prototype.unbind = function() {
this.el.removeChild(this.el.lastChild);
}
if (this._origEl) {
this.el.style = this._origEl.style;
this.el.className = this._origEl.className;
}
this._unbound = true;
@@ -83,7 +82,9 @@ CodeMirrorDiffView.prototype.readOnly = function(side) {
}
CodeMirrorDiffView.prototype.lhs = function(text) {
this.trace('api#lhs', text && text.length);
if (this.settings._debug) {
trace('api#lhs', text && text.length);
}
// invalidate existing changes and current position
this.changes = [];
this._current_diff = -1;
@@ -92,25 +93,33 @@ CodeMirrorDiffView.prototype.lhs = function(text) {
CodeMirrorDiffView.prototype.rhs = function(text) {
// invalidate existing changes and current position
this.trace('api#rhs', text && text.length);
if (this.settings._debug) {
trace('api#rhs', text && text.length);
}
this.changes = [];
this._current_diff = -1;
this.editor.rhs.setValue(text);
};
CodeMirrorDiffView.prototype.update = function() {
this.trace('api#update');
if (this.settings._debug) {
trace('api#update');
}
this.el.dispatchEvent(new Event('changed'));
};
CodeMirrorDiffView.prototype.unmarkup = function() {
this.trace('api#unmarkup');
if (this.settings._debug) {
trace('api#unmarkup');
}
this._clear();
this.el.dispatchEvent(new Event('updated'));
};
CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
this.trace('api#scrollToDiff', direction);
if (this.settings._debug) {
trace('api#scrollToDiff', direction);
}
if (!this.changes.length) return;
if (direction === 'next') {
if (this._current_diff === this.changes.length - 1
@@ -127,14 +136,18 @@ CodeMirrorDiffView.prototype.scrollToDiff = function(direction) {
this._current_diff = Math.max(--this._current_diff, 0);
}
}
this.trace('change', 'current-diff', this._current_diff);
if (this.settings._debug) {
trace('change', 'current-diff', this._current_diff);
}
// _current_diff changed, refresh the view
this._scroll_to_change(this.changes[this._current_diff]);
this.setChanges(this.changes);
};
CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
this.trace('api#mergeCurrentChange', side);
if (this.settings._debug) {
trace('api#mergeCurrentChange', side);
}
if (!this.changes.length) return;
if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
@@ -145,7 +158,9 @@ CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) {
};
CodeMirrorDiffView.prototype.scrollTo = function(side, num) {
this.trace('api#scrollTo', side, num);
if (this.settings._debug) {
trace('api#scrollTo', side, num);
}
const ed = this.editor[side];
ed.setCursor(num);
ed.centerOnCursor();
@@ -159,7 +174,9 @@ CodeMirrorDiffView.prototype.setOptions = function(opts) {
...this.settings,
...opts
};
this.trace('api#setOptions', opts);
if (this.settings._debug) {
trace('api#setOptions', opts);
}
// if options set after init
if (this.editor) {
@@ -192,7 +209,9 @@ CodeMirrorDiffView.prototype.setOptions = function(opts) {
};
CodeMirrorDiffView.prototype.get = function(side) {
this.trace('api#get', side);
if (this.settings._debug) {
trace('api#get', side);
}
const ed = this.editor[side];
const value = ed.getValue();
if (value === undefined) {
@@ -202,12 +221,16 @@ CodeMirrorDiffView.prototype.get = function(side) {
};
CodeMirrorDiffView.prototype.cm = function(side) {
this.trace('api#cm', 'side');
if (this.settings._debug) {
trace('api#cm', 'side');
}
return this.editor[side];
};
CodeMirrorDiffView.prototype.search = function(side, query, direction) {
this.trace('api#search', side, query, direction);
if (this.settings._debug) {
trace('api#search', side, query, direction);
}
const editor = this.editor[side];
if (!editor.getSearchCursor) {
throw new Error('install CodeMirror search addon');
@@ -234,7 +257,9 @@ CodeMirrorDiffView.prototype.search = function(side, query, direction) {
};
CodeMirrorDiffView.prototype.resize = function() {
this.trace('api#resize');
if (this.settings._debug) {
trace('api#resize');
}
const parent = this.el;
const contentHeight = parent.offsetHeight - 2;
@@ -257,18 +282,24 @@ CodeMirrorDiffView.prototype.resize = function() {
};
CodeMirrorDiffView.prototype.bind = function(container) {
this.trace('api#bind', container);
if (this.settings._debug) {
trace('api#bind', container);
}
this._origEl = {
style: container.style,
className: container.className
};
const el = dom.getMergelyContainer({ clazz: container.className });
// const found = document.getElementById(container.id);
// if (!found) {
// console.error(`Failed to find mergely: #${container.id}`);
// return;
// }
const computedStyle = window.getComputedStyle(container);
if (!el.style.height
&& (!computedStyle.height || computedStyle.height === '0px')
) {
if (!computedStyle.height || computedStyle.height === '0px') {
throw new Error(
`The element "${container.id}" requires an explicit height`);
}
this._origEl = {
className: container.className
};
this.id = `${container.id}`;
this.lhsId = `${container.id}-lhs`;
this.rhsId = `${container.id}-rhs`;
@@ -318,7 +349,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
if (!notice) {
notice = noticeTypes.lgpl;
}
const editor = this.el;
const editor = this._queryElement(`#${this.id}`);
const splash = dom.getSplash({
notice,
left: (editor.offsetWidth - 300) / 2,
@@ -380,7 +411,9 @@ CodeMirrorDiffView.prototype.bind = function(container) {
}
});
this.editor.rhs.on('beforeChange', (cm, ev) => {
this.trace('event#rhs-beforeChange', ev);
if (this.settings._debug) {
trace('event#rhs-beforeChange', ev);
}
if (ev.text.length > 1
|| ((ev.from.line - ev.to.line) && ev.origin === '+delete')) {
this._clear();
@@ -388,37 +421,45 @@ CodeMirrorDiffView.prototype.bind = function(container) {
});
this.editor.lhs.on('change', (instance, ev) => {
this.trace('event#lhs-change');
if (this.settings._debug) {
trace('event#lhs-change');
}
this._changing();
this.trace('event#lhs-change [emitted]');
if (this.settings._debug) {
trace('event#lhs-change [emitted]');
}
});
this.editor.lhs.on('scroll', () => {
if (this._skipscroll.lhs) {
this.trace('event#lhs-scroll (skipped)');
if (this.settings._debug) {
trace('event#lhs-scroll (skipped)');
}
return;
} else {
this.trace('event#lhs-scroll');
if (this.settings._debug) {
trace('event#lhs-scroll');
}
}
// firefox scroll-linked effect render issue
setTimeout(() => {
this._scrolling({ side: 'lhs' });
}, 1);
this._scrolling({ side: 'lhs' });
});
this.editor.rhs.on('change', (instance, ev) => {
this.trace('event#rhs-change', ev);
if (this.settings._debug) {
trace('event#rhs-change', ev);
}
this._changing();
});
this.editor.rhs.on('scroll', () => {
if (this._skipscroll.rhs) {
this.trace('event#rhs-scroll (skipped)');
if (this.settings._debug) {
trace('event#rhs-scroll (skipped)');
}
return;
} else {
this.trace('event#rhs-scroll');
if (this.settings._debug) {
trace('event#rhs-scroll');
}
}
// firefox scroll-linked effect render issue
setTimeout(() => {
this._scrolling({ side: 'rhs' });
}, 1);
this._scrolling({ side: 'rhs' });
});
// resize event handeler
@@ -426,7 +467,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
const resize = () => {
if (this.settings._debug) {
traceTimeStart('event#resize');
this.trace('event#resize [start]');
trace('event#resize [start]');
}
this.resize();
if (this.settings._debug) {
@@ -451,6 +492,7 @@ CodeMirrorDiffView.prototype.bind = function(container) {
ev.preventDefault();
return;
}
const ed = this.editor[side];
// See if the user clicked the line number of a difference:
let found = false;
for (let i = 0; i < this.changes.length; ++i) {
@@ -482,12 +524,16 @@ CodeMirrorDiffView.prototype.bind = function(container) {
}
this.editor.lhs.on('gutterClick', (cm, n, gutterClass, ev) => {
this.trace('event#gutterClick', 'lhs', n, ev);
if (this.settings._debug) {
trace('event#gutterClick', 'lhs', n, ev);
}
gutterClicked.call(this, 'lhs', n, ev);
});
this.editor.rhs.on('gutterClick', (cm, n, gutterClass, ev) => {
this.trace('event#gutterClick', 'rhs', n, ev);
if (this.settings._debug) {
trace('event#gutterClick', 'rhs', n, ev);
}
gutterClicked.call(this, 'rhs', n, ev);
});
@@ -514,7 +560,7 @@ CodeMirrorDiffView.prototype._clearMarkup = function () {
traceTimeStart('draw#_clearMarkup');
}
this._vdoc.clear();
this._vdoc = new VDoc({ _debug: this.settings._debug });
this._vdoc = new VDoc();
if (this.settings._debug) {
traceTimeEnd('draw#_clearMarkup');
}
@@ -588,10 +634,7 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
const scroller = this.editor[side].getScrollerElement();
const { top } = scroller.getBoundingClientRect();
let height;
if (scroller.offsetParent === null) {
return;
}
if (this.midway == undefined) {
if (true || this.midway == undefined) {
height = scroller.clientHeight
- (scroller.offsetHeight - scroller.offsetParent.offsetHeight);
this.midway = (height / 2.0 + top).toFixed(2);
@@ -635,14 +678,18 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
const vp = this.editor[oside].getViewport();
let scroll = true;
if (last_change) {
this.trace('scroll#_scrolling', 'last change before midline', last_change);
if (this.settings._debug) {
trace('scroll#_scrolling', 'last change before midline', last_change);
}
if (midline.line >= vp.from && midline <= vp.to) {
scroll = false;
}
}
if (scroll || force_scroll) {
// scroll the other side
this.trace('scroll#_scrolling', 'other side', oside, 'pos:', top_to - top_adjust);
if (this.settings._debug) {
trace('scroll#_scrolling', 'other side', oside, 'pos:', top_to - top_adjust);
}
// disable linked scroll events for the opposite editor because this
// triggers the next one explicitly, and we don't want to link the
@@ -650,21 +697,18 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
// coming in 2s, so this will "link" scrolling the other editor to
// this editor until this editor stops scrolling and times out.
this._skipscroll[oside] = true;
this.trace('scroll#set oside skip set:', oside, this._skipscroll);
if (this._linkedScrollTimeout[oside]) {
clearTimeout(this._linkedScrollTimeout[oside]);
this.trace('scroll#clearing timeout:', this._skipscroll);
if (this._linkedScrollTimeout) {
clearTimeout(this._linkedScrollTimeout);
}
this._linkedScrollTimeout[oside] = setTimeout(() => {
this._linkedScrollTimeout = setTimeout(() => {
this._skipscroll[oside] = false;
this.trace('scroll#set oside skip unset:', oside, this._skipscroll);
}, 100);
const top = top_to - top_adjust;
// scroll the opposite editor
this.editor[oside].scrollTo(left_to, top);
} else {
this.trace('scroll#_scrolling', 'not scrolling other side');
} else if (this.settings._debug) {
trace('scroll#_scrolling', 'not scrolling other side');
}
this._renderChanges();
@@ -675,19 +719,23 @@ CodeMirrorDiffView.prototype._scrolling = function({ side }) {
CodeMirrorDiffView.prototype._changing = function() {
if (!this.settings.autoupdate) {
this.trace('change#_changing autoupdate is disabled');
if (this.settings._debug) {
trace('change#_changing autoupdate is disabled');
}
return;
}
if (this.settings._debug) {
traceTimeStart('change#_changing');
this.trace('change#_changing [start]');
trace('change#_changing [start]');
}
const handleChange = () => {
this._changedTimeout = null;
this.el.dispatchEvent(new Event('changed'));
};
if (this.settings.change_timeout > 0) {
this.trace('change#setting timeout', this.settings.change_timeout)
if (this.settings._debug) {
trace('change#setting timeout', this.settings.change_timeout)
}
if (this._changedTimeout != null) {
clearTimeout(this._changedTimeout);
}
@@ -701,7 +749,9 @@ CodeMirrorDiffView.prototype._changing = function() {
};
CodeMirrorDiffView.prototype.setChanges = function(changes) {
this.trace('change#setChanges');
if (this.settings._debug) {
trace('change#setChanges');
}
this._clear();
// after clear, set the new changes
this.changes = changes;
@@ -711,7 +761,7 @@ CodeMirrorDiffView.prototype.setChanges = function(changes) {
CodeMirrorDiffView.prototype._renderChanges = function() {
if (this.settings._debug) {
traceTimeStart('draw#_renderChanges');
this.trace('draw#_renderChanges [start]', this.changes.length, 'changes');
trace('draw#_renderChanges [start]', this.changes.length, 'changes');
}
this._clearCanvases();
this._calculateOffsets(this.changes);
@@ -740,10 +790,6 @@ CodeMirrorDiffView.prototype._isChangeInView = function(side, vp, change) {
return true;
}
// there are 3 conditions to test
// 1: the change "from" is within the viewport from/to
// 2: the change "to" is within the viewport from/to
// 3: the change is so big that the viewport is within
return (change[`${side}-line-from`] >= vp.from && change[`${side}-line-from`] <= vp.to) ||
(change[`${side}-line-to`] >= vp.from && change[`${side}-line-to`] <= vp.to) ||
(vp.from >= change[`${side}-line-from`] && vp.to <= change[`${side}-line-to`]);
@@ -758,9 +804,6 @@ CodeMirrorDiffView.prototype._set_top_offset = function (side) {
// this is the distance from the top of the screen to the top of the
// content of the first codemirror editor
const topnode = this._queryElement('.CodeMirror-measure');
if (!topnode.offsetParent) {
return false;
}
const top_offset = topnode.offsetParent.offsetTop + 4;
// restore editor's scroll position
@@ -948,10 +991,14 @@ CodeMirrorDiffView.prototype._markupLineChanges = function (changes) {
}
}
led.operation(() => {
vdoc.update('lhs', led, lhsvp);
for (let i = 0; i < changes.length; ++i) {
vdoc.update('lhs', i, led, lhsvp);
}
});
red.operation(() => {
vdoc.update('rhs', red, rhsvp);
for (let i = 0; i < changes.length; ++i) {
vdoc.update('rhs', i, red, rhsvp);
}
});
if (this.settings._debug) {
traceTimeEnd('draw#_markupLineChanges');
@@ -1046,9 +1093,12 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
const ctx_lhs = mcanvas_lhs.getContext('2d');
const ctx_rhs = mcanvas_rhs.getContext('2d');
this.trace('draw#_renderDiff', 'visible page height', ex.visible_page_height);
this.trace('draw#_renderDiff', 'scroller-top lhs', ex.lhs_scroller.scrollTop);
this.trace('draw#_renderDiff', 'scroller-top rhs', ex.rhs_scroller.scrollTop);
if (this.settings._debug
&& this.settings._debug) {
trace('draw#_renderDiff', 'visible page height', ex.visible_page_height);
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.rhs_margin.removeEventListener('click', this._handleRhsMarginClick);
@@ -1073,16 +1123,17 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
const rhs_y_end = change['rhs-y-end'] - rhsScrollTop;
if (Number.isNaN(lhs_y_start)) {
this.trace(
'draw#_renderDiff unexpected NaN',
change['lhs-y-start'], change['lhs-y-end']
);
trace('draw#_renderDiff', 'unexpected NaN',
change['lhs-y-start'], change['lhs-y-end']);
continue;
}
// draw margin indicators
this.trace('draw#_renderDiff', 'draw1', 'marker',
lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
if (this.settings._debug
&& this.settings._debug) {
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_end = Math.max(change['lhs-y-end'] * lratio, 5);
@@ -1094,8 +1145,8 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
ctx_lhs.strokeRect(1.5, mkr_lhs_y_start, 4.5, Math.max(mkr_lhs_y_end - mkr_lhs_y_start, 5));
ctx_lhs.stroke();
const mkr_rhs_y_start = change['rhs-y-start'] * rratio;
const mkr_rhs_y_end = Math.max(change['rhs-y-end'] * rratio, 5);
const mkr_rhs_y_start = change['rhs-y-start'] * lratio;
const mkr_rhs_y_end = Math.max(change['rhs-y-end'] * lratio, 5);
ctx_rhs.beginPath();
ctx_rhs.fillStyle = '#a3a3a3';
ctx_rhs.strokeStyle = '#000';
@@ -1190,9 +1241,7 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
ex.lhs_scroller.scrollTo({ top: sto });
};
this._handleRhsMarginClick = function (ev) {
// `top` accounts for the editor starting at position other than 0 on page
const { top } = ev.currentTarget.offsetParent.getBoundingClientRect();
const y = (ev.pageY - top) - ex.rhs_xyoffset.top - (rto / 2);
const y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2);
const sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.scrollHeight);
ex.rhs_scroller.scrollTo({ top: sto });
};
@@ -1206,17 +1255,11 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) {
CodeMirrorDiffView.prototype._queryElement = function(selector) {
const cacheName = `_element:${selector}`;
const element = this[cacheName] || this.el.querySelector(selector);
const element = this[cacheName] || document.querySelector(selector);
if (!this[cacheName]) {
this[cacheName] = element;
}
return this[cacheName];
}
CodeMirrorDiffView.prototype.trace = function(...args) {
if (this.settings._debug) {
console.log(...args);
}
}
module.exports = CodeMirrorDiffView;

View File

@@ -146,7 +146,7 @@ diff.prototype._sms = function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower
}
}
// Extend the reverse path.
for (let k = kup - d; k <= kup + d; k += 2) {
for (k = kup - d; k <= kup + d; k += 2) {
// find the only or better starting point
if (k === kup + d) {
x = vector_u[offset_up + k - 1]; // up
@@ -241,13 +241,10 @@ function CodeifyText(lhs, rhs, options) {
this.ctxs = {};
this.options = options;
this.options.split = this.options.split || 'lines';
const exp = /\p{Letter}\p{Mark}*|\p{Number}\p{Mark}*|\p{Punctuation}\p{Mark}*|\p{Symbol}\p{Mark}*|\p{White_Space}/gu;
if (typeof lhs === 'string') {
if (this.options.split === 'chars') {
// split characters and include their diacritical marks
this.lhs = lhs.match(exp) || [];
// this.lhs = [...lhs];
this.lhs = lhs.split('');
} else if (this.options.split === 'words') {
this.lhs = lhs.split(/\s/);
} else if (this.options.split === 'lines') {
@@ -258,9 +255,7 @@ function CodeifyText(lhs, rhs, options) {
}
if (typeof rhs === 'string') {
if (this.options.split === 'chars') {
// split characters and include their diacritical marks
this.rhs = rhs.match(exp) || [];
// this.rhs = [...rhs];
this.rhs = rhs.split('');
} else if (this.options.split === 'words') {
this.rhs = rhs.split(/\s/);
} else if (this.options.split === 'lines') {

View File

@@ -67,7 +67,7 @@ function getColors(el) {
function getMergelyContainer({ clazz = '' }) {
const classes = [ 'mergely-editor', clazz ]
return htmlToElement(`\
<div class="${classes.join(' ')}" style="display:flex;height:100%;position:relative;overflow:hidden;"></div>`);
<div class="${classes.join(' ')}" style="display:flex;height:100%;position:relative;"></div>`);
}
function getMarginTemplate({ id }) {

View File

@@ -17,7 +17,7 @@
}
.mergely-editor .CodeMirror-selected {
background: #0f73ff47;
background: #ffcb0f;
}
.mergely-splash {
@@ -52,6 +52,8 @@
.mergely-editor .merge-button {
height: 18px;
cursor: pointer;
width: 26px;
padding-left: 3px;
}
/* common stles */

View File

@@ -23,8 +23,7 @@ const defaultOptions = {
vpcolor: 'rgba(0, 0, 200, 0.5)',
license: 'lgpl',
cmsettings: {
styleSelectedText: true,
mode: null
styleSelectedText: true
},
lhs_cmsettings: {},
rhs_cmsettings: {},
@@ -46,9 +45,7 @@ class Mergely {
}
const computedStyle = window.getComputedStyle(element);
if (!element.style.height
&& (!computedStyle.height || computedStyle.height === '0px')
) {
if (!computedStyle.height || computedStyle.height === '0px') {
throw new Error(
`The element "${selector}" requires an explicit height`);
}
@@ -117,16 +114,18 @@ class Mergely {
_setOptions(options) {
if (this._options && this._options._debug) {
trace('api#options');
trace('api#options', opts);
}
const colors = dom.getColors(this.el);
this._options = {
...defaultOptions,//lgpl
...(this._options || this._initOptions),
...this._initOptions,
...options//lgpl-separate-notice
};
this._viewOptions = {
...this._options,
...defaultOptions,
...this._initOptions,
...options,
_colors: colors
};
}
@@ -187,7 +186,7 @@ class Mergely {
diff() {
if (this._options._debug) {
trace('api#diff');
trace('api#diff', side);
}
const lhs_text = this.get('lhs');
const rhs_text = this.get('rhs');
@@ -253,8 +252,6 @@ class Mergely {
}
}
Mergely.CodeMirror = CodeMirrorDiffView.CodeMirror;
window.Mergely = Mergely;
module.exports = Mergely;

View File

@@ -1,14 +1,7 @@
const diff = require('./diff');
const trace = console.log;
const expLetters = new RegExp(
/\p{Letter}\p{Mark}*|\p{Number}\p{Mark}*|\p{Punctuation}\p{Mark}*|\p{Symbol}\p{Mark}*|\p{White_Space}/gu
);
class VDoc {
constructor(options) {
this.options = options;
constructor() {
this._lines = {
lhs: {},
rhs: {}
@@ -20,9 +13,6 @@ class VDoc {
}
addRender(side, change, changeId, options) {
if (this.options._debug) {
trace('vdoc#addRender', side, changeId, change);
}
const {
isCurrent,
lineDiff,
@@ -33,9 +23,6 @@ class VDoc {
const alreadyRendered = !!this._rendered[side][changeId];
if (alreadyRendered) {
if (this.options._debug) {
trace('vdoc#addRender (already rendered)', side, changeId, change);
}
return;
}
@@ -129,9 +116,6 @@ class VDoc {
}
addInlineDiff(change, changeId, { getText, ignorews, ignoreaccents, ignorecase }) {
if (this.options._debug) {
trace('vdoc#addInlineDiff', changeId, change);
}
const { lf, lt, olf, olt } = getExtents('lhs', change);
const vdoc = this;
@@ -185,9 +169,6 @@ class VDoc {
}
_setRenderedChange(side, changeId) {
if (this.options._debug) {
trace('vdoc#_setRenderedChange', side, changeId);
}
return this._rendered[side][changeId] = true;
}
@@ -201,17 +182,11 @@ class VDoc {
return line;
}
update(side, editor, viewport) {
if (this.options._debug) {
trace('vdoc#update', side, editor, viewport);
}
const keys = Object.keys(this._lines[side]);
// while Mergely diffs unicode diacritic chars (letters+mark),
// CM is by character, so diffs need to be mapped.
const mappedChars = mapLettersToChars(editor.getValue());
for (const key of keys) {
const { id } = this._lines[side][key];
update(side, changeId, editor, viewport) {
this._setRenderedChange(side, changeId);
const lines = Object.keys(this._lines[side]);
for (let i = 0; i < lines.length; ++i) {
const id = lines[i];
if (id < viewport.from || id > viewport.to) {
continue;
}
@@ -220,14 +195,11 @@ class VDoc {
if (vline.rendered) {
continue;
}
vline.update(editor, mappedChars);
vline.update(editor);
}
}
clear() {
if (this.options._debug) {
trace('vdoc#clear');
}
for (const lineId in this._lines.lhs) {
this._lines.lhs[lineId].clear();
}
@@ -261,7 +233,7 @@ class VLine {
this.markup.push([ charFrom, charTo, className ]);
}
update(editor, mappedChars) {
update(editor) {
if (this.rendered) {
// FIXME: probably do not need this now
console.log('already rendered', this.id);
@@ -283,17 +255,15 @@ class VLine {
editor.setGutterMarker(this.id, name, item);
}
if (this.markup.length) {
// while Mergely diffs unicode diacritic chars (letters+mark),
// CM is by character, so diffs need to be mapped.
for (const markup of this.markup) {
const [ charFrom, charTo, className ] = markup;
const fromPos = { line: this.id };
const toPos = { line: this.id };
if (charFrom >= 0) {
fromPos.ch = mappedChars[charFrom];
fromPos.ch = charFrom;
}
if (charTo >= 0) {
toPos.ch = mappedChars[charTo];
toPos.ch = charTo;
}
this._clearMarkup.push(
editor.markText(fromPos, toPos, { className }));
@@ -344,15 +314,4 @@ 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;

View File

@@ -42,7 +42,6 @@ describe('markup', () => {
const LHS_CHANGE_START = '.mergely.lhs.c.CodeMirror-linebackground.start';
const LHS_CHANGE_END = '.mergely.lhs.c.CodeMirror-linebackground.end';
const LHS_CHANGE_START_AND_END = '.mergely.lhs.c.CodeMirror-linebackground.start.end';
const RHS_CHANGE_START = '.mergely.rhs.c.CodeMirror-linebackground.start';
const RHS_CHANGE_END = '.mergely.rhs.c.CodeMirror-linebackground.end';
@@ -234,130 +233,7 @@ describe('markup', () => {
expect(rhs_spans[1].innerText).to.equal('h');
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');
}
},
{
name: 'Changes with non-letter chars',
lhs: '~# 00 == ! (dog) \n',
rhs: '~? 11 ++ ] (fox) .\n',
check: (editor) => {
expect(editor.querySelectorAll(LHS_CHANGE_START_AND_END + '.cid-0')).to.have.length(1);
expect(editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0')).to.have.length(6);
expect(editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0')).to.have.length(7);
const lhs_changes = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
const rhs_changes = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
const lhs_values = [];
for (const value of lhs_changes.values()) {
lhs_values.push(value.innerText);
}
const rhs_values = [];
for (const value of rhs_changes.values()) {
rhs_values.push(value.innerText);
}
expect(lhs_values).to.deep.equal(['#', '00', '==', '!', 'd', 'g']);
expect(rhs_values).to.deep.equal(['?', '11', '++', ']', 'f', 'x', '.']);
}
},
}
];
// to debug, add `only: true` to the test `opts` above, and run `npm run debug`

View File

@@ -22,8 +22,7 @@ const defaultOptions = {
vpcolor: 'rgba(0, 0, 200, 0.5)',
license: 'lgpl',
cmsettings: {
styleSelectedText: true,
mode: null
styleSelectedText: true
},
_debug: false
};
@@ -62,17 +61,19 @@ describe('mergely', function () {
try {
const { children } = editor.el.children[0];
const items = Array.from(children).map(a => a.className);
expect(items[0] == 'mergely-margin');
expect(items[1] == 'mergely-column');
// NOTE: if running karma debug, these tests can fail because
// the debugger grabs the focus and the CodeMirror instance
// loses `CodeMirror-focused`
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
expect(items[3] == 'mergely-margin');
expect(items[4] == 'mergely-column');
expect(items[5] == 'CodeMirror cm-s-default');
expect(items[6] == 'mergely-column');
expect(items[7] == 'mergely-splash');
expect(items).to.deep.equal([
'mergely-margin',
'mergely-column',
'CodeMirror cm-s-default CodeMirror-focused',
'mergely-canvas',
'mergely-column',
'CodeMirror cm-s-default',
'mergely-margin',
'mergely-splash'
]);
expect(editor.get('lhs')).to.equal('');
expect(editor.get('rhs')).to.equal('');
done();
@@ -97,17 +98,15 @@ describe('mergely', function () {
// NOTE: if running karma debug, these tests can fail because
// the debugger grabs the focus and the CodeMirror instance
// loses `CodeMirror-focused`
expect(items[0] == 'mergely-margin');
expect(items[1] == 'mergely-column');
// NOTE: if running karma debug, these tests can fail because
// the debugger grabs the focus and the CodeMirror instance
// loses `CodeMirror-focused`
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
expect(items[3] == 'mergely-margin');
expect(items[4] == 'mergely-column');
expect(items[5] == 'CodeMirror cm-s-default');
expect(items[6] == 'mergely-column');
expect(items[7] == 'mergely-splash');
expect(items).to.deep.equal([
'mergely-margin',
'mergely-column',
'CodeMirror cm-s-default CodeMirror-focused',
'mergely-canvas',
'mergely-column',
'CodeMirror cm-s-default',
'mergely-margin'
]);
expect(editor.get('lhs')).to.equal('left-hand side text');
expect(editor.get('rhs')).to.equal('right-hand side text');
done();
@@ -129,17 +128,18 @@ describe('mergely', function () {
try {
const { children } = editor.el.children[0];
const items = Array.from(children).map(a => a.className);
expect(items[0] == 'mergely-margin');
expect(items[1] == 'mergely-column');
// NOTE: if running karma debug, these tests can fail because
// the debugger grabs the focus and the CodeMirror instance
// loses `CodeMirror-focused`
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
expect(items[3] == 'mergely-margin');
expect(items[4] == 'mergely-column');
expect(items[5] == 'CodeMirror cm-s-default');
expect(items[6] == 'mergely-column');
expect(items[7] == 'mergely-splash');
expect(items).to.deep.equal([
'mergely-margin',
'mergely-column',
'CodeMirror cm-s-default CodeMirror-focused',
'mergely-canvas',
'mergely-column',
'CodeMirror cm-s-default',
'mergely-margin'
]);
expect(editor.get('lhs')).to.equal('left-hand side text');
expect(editor.get('rhs')).to.equal('right-hand side text');
done();
@@ -160,17 +160,18 @@ describe('mergely', function () {
try {
const { children } = editor.el.children[0];
const items = Array.from(children).map(a => a.className);
expect(items[0] == 'mergely-margin');
expect(items[1] == 'mergely-column');
// NOTE: if running karma debug, these tests can fail because
// the debugger grabs the focus and the CodeMirror instance
// loses `CodeMirror-focused`
expect(items[2].indexOf('CodeMirror cm-s-default' == 0));
expect(items[3] == 'mergely-margin');
expect(items[4] == 'mergely-column');
expect(items[5] == 'CodeMirror cm-s-default');
expect(items[6] == 'mergely-column');
expect(items[7] == 'mergely-splash');
expect(items).to.deep.equal([
'mergely-margin',
'mergely-column',
'CodeMirror cm-s-default CodeMirror-focused',
'mergely-canvas',
'mergely-column',
'CodeMirror cm-s-default',
'mergely-margin'
]);
expect(children[0].style.visibility).to.equal('hidden');
expect(children[6].style.visibility).to.equal('hidden');
done();
@@ -501,24 +502,6 @@ describe('mergely', function () {
});
});
it('should preserve changed option', function (done) {
const editor = init({
change_timeout: 0,
license: 'lgpl-separate-notice',
lhs: 'left-hand side text',
rhs: 'right-hand side text'
});
editor.once('updated', () => {
editor.options({ sidebar: false });
expect(editor.options().wrap_lines).to.equal(false, 'wrap_lines');
expect(editor.options().sidebar).to.equal(false, 'sidebar');
editor.options({ wrap_lines: false });
expect(editor.options().wrap_lines).to.equal(false, 'wrap_lines-2');
expect(editor.options().sidebar).to.equal(false, 'sidebar-2');
done();
});
});
it('should ignore white-space', function (done) {
const editor = init({
height: 100,

View File

@@ -1,5 +1,4 @@
const path = require('path')
const chalk = require('chalk');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
@@ -51,8 +50,8 @@ module.exports = {
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
console.log('-'.repeat(78));
console.log('Applications:');
console.log(chalk.bold(chalk.underline(chalk.cyan('http://localhost:8080/app.html'))));
console.log(chalk.bold(chalk.underline(chalk.cyan('http://localhost:8080/app-styles.html'))));
console.log('http://localhost:8080/app.html');
console.log('http://localhost:8080/app-styles.html');
console.log('-'.repeat(78));
});
}

View File

@@ -15,10 +15,6 @@ module.exports = (mode) => {
...webpackDevConfig.output,
path: path.join(__dirname, 'lib'),
filename: './[name].js',
library: {
name: 'mergely',
type: 'umd',
}
},
optimization: {
minimize: true,