From 3b300cea856ed0e09c77f0491cf13662fa64edc7 Mon Sep 17 00:00:00 2001 From: Jamie Peabody Date: Sun, 27 Feb 2022 21:18:03 +0000 Subject: [PATCH] chore: fixes mostly done --- README.md | 73 +- examples/README.md | 34 +- examples/ajax.html | 129 --- examples/app.html | 10 +- examples/app.js | 28 +- examples/{styles.html => change-styles.html} | 111 +- examples/editor.html | 344 ++++++ examples/{simple.html => fixed-height.html} | 51 +- examples/fixed-size.html | 43 + examples/full-page.html | 61 +- examples/index.html | 50 +- examples/lhs.txt | 2 - examples/rhs.txt | 2 - examples/scroll-to-first-change.html | 32 +- examples/{size.html => visible.html} | 57 +- favicon.svg | 1005 ++++++++++++++++++ package.json | 6 +- src/diff-view.js | 176 +-- src/lcs.js | 7 +- src/mergely.css | 75 +- src/vdoc.js | 103 +- test/mergely.spec.js | 13 + webpack.prod.js | 23 +- 23 files changed, 1882 insertions(+), 553 deletions(-) delete mode 100644 examples/ajax.html rename examples/{styles.html => change-styles.html} (82%) create mode 100644 examples/editor.html rename examples/{simple.html => fixed-height.html} (60%) create mode 100644 examples/fixed-size.html delete mode 100644 examples/lhs.txt delete mode 100644 examples/rhs.txt rename examples/{size.html => visible.html} (55%) mode change 100755 => 100644 create mode 100644 favicon.svg diff --git a/README.md b/README.md index 1b40243..c95ad89 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,20 @@ https://mergely.com -Mergely is a JavaScript component for differencing and merging files interactively in a browser (diff/merge), providing 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), providing 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. +Mergely has a JavaScript implementation of the Longest Common Subsequence (LCS) diff algorithm, and a customizable markup engine. It computs the diff within the browser. ## Installation ### Installation via webpack + The recommended way to install mergely and its dependencies is to use a Node package manager (`npm` or `yarn`) and [webpack](https://webpack.js.org/). It is highly recommended that you start by cloning [mergely-webpack](https://github.com/wickedest/mergely-webpack). The webpack has everything that you need to get started. ### Angular 6.1.1 + You can also use mergely within angular. You can start by cloning [mergely-angular](https://github.com/wickedest/mergely-angular). ### Installation via .tgz @@ -29,7 +31,7 @@ Unpack mergely.tgz into a folder, for example, `./lib`, and add the following to ``` -Create a div for the editor in ``. +Create a div for the editor in ``. This example uses a style that provides 8px padding (`mergely-full-screen-8`): @@ -71,22 +73,21 @@ $(document).ready(function () { If the editor contents are retrieved asynchronously (recommended), then retrieve the editor contents and set them: -```js -$(document).ready(function () { +```html + ``` ## Options - |Option|Type|Default value|Description| |------|----|-------------|-----------| |autoresize|boolean|`true`|Enables/disables the auto-resizing of the editor.| @@ -135,7 +136,7 @@ Clears the editor contents for the specified `side`. #### Example ```js -$('#mergely').mergely('clear', 'lhs'); +doc.clear('lhs'); ``` ### cm @@ -149,7 +150,7 @@ Gets the CodeMirror editor for the specified `side`. #### Example ```js -$('#mergely').mergely('cm', 'lhs'); +doc.cm('lhs'); ``` ### diff @@ -162,7 +163,7 @@ None. #### Example ```js -$('#mergely').mergely('diff'); +doc.diff(); ``` ### get @@ -176,7 +177,7 @@ Gets the text editor contents for the specified `side`. #### Example ```js -$('#mergely').mergely('get', 'lhs'); +doc.get('lhs'); ``` ### lhs @@ -190,7 +191,7 @@ Sets the value of the left-hand editor. #### Example ```js -$('#mergely').mergely('lhs', 'This is text'); +doc.lhs('This is text'); ``` ### merge @@ -204,7 +205,7 @@ Merges whole file from the specified `side` to the opposite side. #### Example ```js -$('#mergely').mergely('merge', 'lhs'); +doc.merge('lhs'); ``` ### mergeCurrentChange @@ -218,7 +219,7 @@ Merges the current change from the specified `side` to the opposite side. #### Example ```js -$('#mergely').mergely('mergeCurrentChange', 'lhs'); +doc.mergeCurrentChange('lhs'); ``` @@ -233,7 +234,7 @@ Sets the editor options. Will automatically update with the new settings unless #### Example ```js -$('#mergely').mergely('options', { line_numbers: true }); +doc.options({ line_numbers: true }); ``` ### options @@ -245,7 +246,7 @@ None. #### Example ```js -$('#mergely').mergely('options'); +doc.options(); ``` ### resize @@ -257,7 +258,7 @@ None. #### Example ```js -$('#mergely').mergely('resize'); +doc.resize(); ``` ### rhs @@ -271,7 +272,7 @@ Sets the value of the right-hand editor. #### Example ```js -$('#mergely').mergely('rhs', 'This is text'); +doc.rhs('This is text'); ``` ### scrollTo @@ -286,7 +287,7 @@ Scrolls the `side` to line number specified by `num`. #### Example ```js -$('#mergely').mergely('scrollTo', 'lhs', 100); +doc.scrollTo('lhs', 100); ``` ### scrollToDiff @@ -300,7 +301,7 @@ Scrolls to the next change specified by `direction`. #### Example ```js -$('#mergely').mergely('scrollToDiff', 'next'); +doc.scrollToDiff('next'); ``` ### search @@ -315,7 +316,7 @@ Search the editor for `text`, scrolling to the next available match. Repeating t #### Example ```js -$('#mergely').mergely('search', 'lhs', 'needle'); +doc.search('lhs', 'needle'); ``` ### summary @@ -336,8 +337,8 @@ None. #### Example ```js -console.log($('#mergely').mergely('summary')); -// { a: 0, c: 1, d: 0, lhsLength: 44, numChanges: 1, rhsLength: 45 } +console.log(doc.summary()); +// { a: 0, c: 1, d: 0, lhsLength: 44, rhsLength: 45, numChanges: 1 } ``` ### swap @@ -349,7 +350,7 @@ None. #### Example ```js -$('#mergely').mergely('swap'); +doc.swap(); ``` ### unmarkup @@ -361,19 +362,7 @@ None. #### Example ```js -$('#mergely').mergely('unmarkup'); -``` - -### update - -Manually updates the editor by recalculating the diff and applying new settings. - -#### Parameters -None. - -#### Example -```js -$('#mergely').mergely('update'); +doc.unmarkup(); ``` ## Events diff --git a/examples/README.md b/examples/README.md index adddd2c..31130b5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,33 +1,23 @@ # Mergely examples -To run these examples, you need to run: + +## Mergely development server + +To run the hot dev-server for development, you need to run: ```bash -$ npm run build $ npm start ``` -This runs Mergely in a hot dev-server for development. However, only the main example is hot-reloaded when mergely is changed. The others require a rebuild. +After, the application can be accessed http://localhost:8080 -## Main example -http://localhost:8080 +## Mergely examples -Shows basic functionality. +These examples require a build. -## Ajax example +```bash +$ npm run build:dist +$ npm run examples +``` -http://localhost:8080/examples/ajax.html - -Who still says "ajax" these days? Anyway, this example demonstrates how to load your left-hand and right-hand sources from external URL, and also how to skin your own editor, and provide drop-file functionality. - -## Simple example - -This example demonstrates how to load your left-hand and right-hand sources from static text. - -http://localhost:8080/examples/simple.html - -## Size example - -This example demonstrates how to size your editors, and also how to have multiple editors on the same page. - -http://localhost:8080/examples/size.html +After, the examples can be accessed http://localhost:3000 diff --git a/examples/ajax.html b/examples/ajax.html deleted file mode 100644 index 6c1356f..0000000 --- a/examples/ajax.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - Mergely - Simple Example - - - - - - - - - - - - - - - - - - - -
- - - - - - -
- -
- - Download - - - Download - -
- -
-
-
-
- -
- - diff --git a/examples/app.html b/examples/app.html index 9011c6b..026c790 100644 --- a/examples/app.html +++ b/examples/app.html @@ -4,20 +4,26 @@ This example demonstrates the minimum amount of code required to use Mergely. - Mergely - Application example + + Mergely - Application example + + -
+
diff --git a/examples/app.js b/examples/app.js index fe75b57..91be63a 100644 --- a/examples/app.js +++ b/examples/app.js @@ -19,24 +19,18 @@ document.onreadystatechange = function () { return; } - const doc = new Mergely('#mergely', { + const doc = new Mergely('#compare', { license: 'lgpl', - ignorews: true, - wrap_lines: false, - change_timeout: 50, - viewport: true, - cmsettings: { - readOnly: false - }, - lhs: function(setValue) { - setValue(macbeth.join('\n')); - }, - rhs: function(setValue) { - setValue(macbeth.join('\n') - .replace(/\brain\b/g, 'sleet') - .replace(/\bfog\b/g, 'smog')); - //.replace(/heart/g, 'head')); - } + // ignorews: true, + // wrap_lines: false, + // change_timeout: 50, + // viewport: true, + // cmsettings: { + // readOnly: true + // }, + line_numbers: false, + lhs: (setValue) => setValue(lhs), + rhs: (setValue) => setValue(rhs) }); // On init, scroll to first diff diff --git a/examples/styles.html b/examples/change-styles.html similarity index 82% rename from examples/styles.html rename to examples/change-styles.html index 22d557c..59d9446 100755 --- a/examples/styles.html +++ b/examples/change-styles.html @@ -1,15 +1,14 @@ - - Mergely - Simple Example + + Mergely - Example change styles + @@ -27,12 +26,12 @@ This example demonstrates the minimum amount of code required to use Mergely. } .column-layout { display: flex; - height: 90px; } .column-layout > * { flex: 1; + height: 90px; } - .full-width-layout { + .full-width-layout > * { height: 90px; } .dark { @@ -82,6 +81,12 @@ This example demonstrates the minimum amount of code required to use Mergely. .dark .CodeMirror-cursor { border-left: 2px solid #B19F73; } + .dark .CodeMirror-selected { + background-color: #424242; + } + .dark .CodeMirror-focused .CodeMirror-selected { + background-color: #07468b; + } @@ -95,7 +100,7 @@ This example demonstrates the minimum amount of code required to use Mergely.
- Removed and added, not at the start or end + Removed and added from the middle
@@ -152,110 +157,110 @@ This example demonstrates the minimum amount of code required to use Mergely. return; } -const data = [{ - lhs: `\ + const data = [{ + lhs: `\ the quick red fox jumped over the hairy dog `, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog ` -}, { - lhs: `\ + }, { + lhs: `\ the quick red fox jumped over the hairy dog `, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog ` -}, { - lhs: `\ + }, { + lhs: `\ the quick red fox jumped over the hairy dog `, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog ` -}, { - lhs: `\ + }, { + lhs: `\ the quick brown fox jumped over the lazy dog `, - rhs: `\ + rhs: `\ the quick red fox jumped over the hairy dog ` -}, { - lhs: ``, - rhs: `\ + }, { + lhs: ``, + rhs: `\ the quick brown fox ` -}, { - lhs: `\ + }, { + lhs: `\ the quick red fox `, - rhs: `` -}, { - lhs: ``, - rhs: `\ + rhs: `` + }, { + lhs: ``, + rhs: `\ the quick brown fox jumped over the lazy dog ` -}, { - lhs: `\ + }, { + lhs: `\ the quick red fox jumped over the hairy dog `, - rhs: `` -}, { - lhs: `\ + rhs: `` + }, { + lhs: `\ the quick brown fox jumped over the lazy dog`, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog and the fence` -}, { - lhs: `\ + }, { + lhs: `\ the quick brown fox jumped over the lazy dog and the fence`, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog` -}, { - lhs: `\ + }, { + lhs: `\ the quick red fox jumped over the hairy dog`, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog` -}, { - lhs: `\ + }, { + lhs: `\ the quick red fox jumped over the hairy dog and the fence `, - rhs: `\ + rhs: `\ the quick brown fox jumped over the lazy dog @@ -268,20 +273,16 @@ and the postman const { lhs, rhs } = data[i]; const doc = new Mergely(`#mergely${i}`, { license: 'lgpl-separate-notice', - wrap_lines: true, - ignorews: true, - lhs: function(setValue) { - setValue(lhs); - }, - rhs: function(setValue) { - setValue(rhs); - } + lhs: (setValue) => setValue(lhs), + rhs: (setValue) => setValue(rhs) }); - // On init, scroll to first diff - doc.once('updated', () => { - doc.scrollToDiff('next'); - }); + if (i === 0 || i === 1) { + // On init, scroll to first diff + doc.once('updated', () => { + doc.scrollToDiff('next'); + }); + } } }; diff --git a/examples/editor.html b/examples/editor.html new file mode 100644 index 0000000..9467d35 --- /dev/null +++ b/examples/editor.html @@ -0,0 +1,344 @@ + + + + + Mergely - Example editor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+ + + + diff --git a/examples/simple.html b/examples/fixed-height.html similarity index 60% rename from examples/simple.html rename to examples/fixed-height.html index 959cb76..cafae5d 100644 --- a/examples/simple.html +++ b/examples/fixed-height.html @@ -1,15 +1,14 @@ - - Mergely - Simple Example + + Mergely - Example fixed height editor + @@ -17,33 +16,29 @@ This example demonstrates the minimum amount of code required to use Mergely. - + - + -
-
-
-
-
+
+ + diff --git a/examples/fixed-size.html b/examples/fixed-size.html new file mode 100644 index 0000000..4b0434e --- /dev/null +++ b/examples/fixed-size.html @@ -0,0 +1,43 @@ + + + + + Mergely - Example fixed size editor + + + + + + + + + + + + + + + + + + + +
+ + + + diff --git a/examples/full-page.html b/examples/full-page.html index 7f01d0e..b732e24 100644 --- a/examples/full-page.html +++ b/examples/full-page.html @@ -1,50 +1,47 @@ - - + - Mergely - Full page example + Mergely - Example full page editor + - - - + + + - - - -
+ - diff --git a/examples/index.html b/examples/index.html index cd01fc5..4624510 100644 --- a/examples/index.html +++ b/examples/index.html @@ -10,27 +10,37 @@ This example demonstrates the minimum amount of code required to use Mergely. + + - +

Examples

+ +
+
editor.html
+
An example editor showcasing some of Mergely's API features.
+ +
fixed-height.html
+
A fixed height editor.
+ +
fixed-size.html
+
A fixed size editor.
+ +
full-page.html
+
A full page editor.
+ +
change-styles.html
+
Showcase the change styles.
+ +
scroll-to-first-change.html
+
Scrolls to first change on initial render.
+ +
visible.html
+
An editor that becomes visible once ready (less flicker).
+
diff --git a/examples/lhs.txt b/examples/lhs.txt deleted file mode 100644 index 14b478c..0000000 --- a/examples/lhs.txt +++ /dev/null @@ -1,2 +0,0 @@ -the quick red fox jumped -over the hairy dog diff --git a/examples/rhs.txt b/examples/rhs.txt deleted file mode 100644 index 13fde44..0000000 --- a/examples/rhs.txt +++ /dev/null @@ -1,2 +0,0 @@ -the quick brown fox jumped -over the lazy dog diff --git a/examples/scroll-to-first-change.html b/examples/scroll-to-first-change.html index e891157..8694acd 100644 --- a/examples/scroll-to-first-change.html +++ b/examples/scroll-to-first-change.html @@ -1,15 +1,13 @@ - - Mergely - Simple Example + Mergely - Example scroll to first change + @@ -25,25 +23,27 @@ This example demonstrates the minimum amount of code required to use Mergely. height: 100%; margin: 0; } + #compare { + height: 100%; + } -
+
diff --git a/examples/size.html b/examples/visible.html old mode 100755 new mode 100644 similarity index 55% rename from examples/size.html rename to examples/visible.html index d934251..3e5ce84 --- a/examples/size.html +++ b/examples/visible.html @@ -1,15 +1,13 @@ - - Mergely - Simple Example + Mergely - Example visibility + @@ -17,53 +15,36 @@ This example demonstrates the minimum amount of code required to use Mergely. - + -
- Inserted and removed at start but not end -
-
-
-
-
+
- diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..3817488 --- /dev/null +++ b/favicon.svg @@ -0,0 +1,1005 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/package.json b/package.json index 8563a69..8dba898 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ }, "main": "lib/mergely.js", "files": [ - "lib", + "lib/mergely.js", + "lib/mergely.min.js", + "lib/mergely.css", "examples", "README.md", "LICENSE" @@ -36,7 +38,7 @@ "babel-loader": "^8.2.3", "babel-plugin-syntax-dynamic-import": "^6.18.0", "chai": "^4.3.4", - "codemirror": "^5.65.0", + "codemirror": "^5.65.2", "copy-webpack-plugin": "^6.2.1", "css-loader": "^6.5.1", "git-conventional-commits": "^1.1.0", diff --git a/src/diff-view.js b/src/diff-view.js index b9a9ee2..ca09bce 100644 --- a/src/diff-view.js +++ b/src/diff-view.js @@ -30,6 +30,7 @@ Default for `options.change_timeout` changed to 50. No longer necessary to separately require codemirror/addon/search/searchcursor No longer necessary to separately require codemirror/addon/selection/mark-selection No longer automatically scrolls to first change. +`mergely.js` is now unminimized, and added new minimized version `mergely.min.js` FEATURE: Gutter click now scrolls to any line. @@ -49,6 +50,8 @@ Fixed issue where line-diffs failed to diff non-alphanumeric characters TODO: Linting Unbind loads of stuff +Test: search +Check: read-only modes */ const NOTICES = [ @@ -284,6 +287,7 @@ CodeMirrorDiffView.prototype._setOptions = function(opts) { // [0:margin] [1:lhs] [2:mid] [3:rhs] [4:margin], swaps 4 with 3 divs[4].parentNode.insertBefore(divs[4], divs[3]); } + this._get_colors(); this._changing(); } }; @@ -392,15 +396,16 @@ CodeMirrorDiffView.prototype.search = function(side, query, direction) { const searchDirection = (direction === 'prev') ? 'findPrevious' : 'findNext'; const start = { line: 0, ch: 0 }; - if ((editor.getSelection().length == 0) || (this.prev_query[side] != query)) { + if ((editor.getSelection().length === 0) || (this.prev_query[side] !== query)) { this.cursor[this.id] = editor.getSearchCursor(query, start, false); this.prev_query[side] = query; } - const cursor = this.cursor[this.id]; + let cursor = this.cursor[this.id]; if (cursor[searchDirection]()) { editor.setSelection(cursor.from(), cursor.to()); } else { + // FIXME: issue with CM search cursor not wrapping cursor = editor.getSearchCursor(query, start, false); } }; @@ -409,7 +414,7 @@ CodeMirrorDiffView.prototype.resize = function() { if (this.settings._debug) { trace('api#resize'); } - const parent = this.el.parentNode; + const parent = this.el; const { settings } = this; let height; const contentHeight = parent.offsetHeight - 2; @@ -417,7 +422,7 @@ CodeMirrorDiffView.prototype.resize = function() { const lhsMargin = this._queryElement(`#${this.id}-lhs-margin`); lhsMargin.style.height = `${contentHeight}px`; lhsMargin.height = `${contentHeight}`; - const midCanvas = this._queryElement(`#${this.id}-lhs-${this.id}-rhs-canvas`); + const midCanvas = this._queryElement(`#${this.id}-lhs-rhs-canvas`); midCanvas.style.height = `${contentHeight}px`; midCanvas.height = `${contentHeight}`; const rhsMargin = this._queryElement(`#${this.id}-rhs-margin`); @@ -444,26 +449,29 @@ CodeMirrorDiffView.prototype.diff = function() { return comparison.normal_form(); }; -CodeMirrorDiffView.prototype.bind = function(el) { +CodeMirrorDiffView.prototype.bind = function(container) { if (this.settings._debug) { - trace('api#bind', el); + trace('api#bind', container); } const { CodeMirror } = this; this._origEl = { - style: el.style, - className: el.className + style: container.style, + className: container.className }; - el.style.display = 'flex'; - el.style.height = '100%'; - el.style.position = 'relative'; - this.id = el.id; - const found = document.getElementById(this.id); + const el = getMergelyContainer({ clazz: container.className }); + const found = document.getElementById(container.id); if (!found) { - console.error(`Failed to find mergely: #${this.id}`); + console.error(`Failed to find mergely: #${container.id}`); return; } - this.lhsId = `${this.id}-lhs`; - this.rhsId = `${this.id}-rhs`; + const computedStyle = window.getComputedStyle(container); + if (!computedStyle.height || computedStyle.height === '0px') { + throw new Error( + `The element "${container.id}" requires an explicit height`); + } + this.id = `${container.id}`; + this.lhsId = `${container.id}-lhs`; + this.rhsId = `${container.id}-rhs`; this.chfns = { lhs: [], rhs: [] }; this.prev_query = []; this.cursor = []; @@ -476,26 +484,12 @@ CodeMirrorDiffView.prototype.bind = function(el) { this.merge_rhs_button = htmlToElement(rhsTemplate); // create the textarea and canvas elements - el.className += ' mergely-editor'; - const canvasLhs = htmlToElement(getMarginTemplate({ - id: this.id, - side: 'lhs' - })); - const canvasRhs = htmlToElement(getMarginTemplate({ - id: this.id, - side: 'rhs' - })); - const editorLhs = htmlToElement(getEditorTemplate({ - id: this.id, - side: 'lhs' - })); - const editorRhs = htmlToElement(getEditorTemplate({ - id: this.id, - side: 'rhs' - })); - const canvasMid = htmlToElement(getCenterCanvasTemplate({ - id: this.id - })); + // el.className += ' mergely-editor'; + const canvasLhs = getMarginTemplate({ id: this.lhsId }); + const canvasRhs = getMarginTemplate({ id: this.rhsId }); + const editorLhs = getEditorTemplate({ id: this.lhsId }); + const editorRhs = getEditorTemplate({ id: this.rhsId }); + const canvasMid = getCenterCanvasTemplate({ id: this.id }); el.append(canvasLhs); el.append(editorLhs); @@ -514,24 +508,25 @@ CodeMirrorDiffView.prototype.bind = function(el) { div.style.visibility = 'hidden'; } } + container.append(el); if (NOTICES.indexOf(this.settings.license) < 0) { - el.addEventListener('updated', () => { + container.addEventListener('updated', () => { const noticeTypes = { 'lgpl': 'GNU LGPL v3.0', 'gpl': 'GNU GPL v3.0', 'mpl': 'MPL 1.1' }; - const notice = noticeTypes[this.settings.license]; + let notice = noticeTypes[this.settings.license]; if (!notice) { notice = noticeTypes.lgpl; } const editor = this._queryElement(`#${this.id}`); - const splash = htmlToElement(getSplash({ + const splash = getSplash({ notice, left: (editor.offsetWidth - 300) / 2, top: (editor.offsetHeight - 58) / 3 - })); + }); editor.addEventListener('click', () => { splash.style.visibility = 'hidden'; splash.style.opacity = '0'; @@ -574,14 +569,23 @@ CodeMirrorDiffView.prototype.bind = function(el) { }); } - // If either editor gets a change, clear the view immediately - this.editor.lhs.on('beforeChange', () => { - if (!window.Worker) { + // If either editor gets a change, clear the view immediately if the ev.text + // array has multiple lines, or if there are multiple lines being deleted, + // or not using Worker + this.editor.lhs.on('beforeChange', (cm, ev) => { + if (ev.text.length > 1 + || ((ev.from.line - ev.to.line) && ev.origin === '+delete') + || !window.Worker) { this._clear(); } }); - this.editor.rhs.on('beforeChange', () => { - if (!window.Worker) { + this.editor.rhs.on('beforeChange', (cm, ev) => { + if (this.settings._debug) { + trace('event#rhs-beforeChange', ev); + } + if (ev.text.length > 1 + || ((ev.from.line - ev.to.line) && ev.origin === '+delete') + || !window.Worker) { this._clear(); } }); @@ -606,11 +610,11 @@ CodeMirrorDiffView.prototype.bind = function(el) { trace('event#lhs-scroll'); } } - this._scrolling({ side: 'lhs', id: this.lhsId }); + this._scrolling({ side: 'lhs' }); }); this.editor.rhs.on('change', (instance, ev) => { if (this.settings._debug) { - trace('event#rhs-change'); + trace('event#rhs-change', ev); } this._changing(); }); @@ -625,7 +629,7 @@ CodeMirrorDiffView.prototype.bind = function(el) { trace('event#rhs-scroll'); } } - this._scrolling({ side: 'rhs', id: this.rhsId }); + this._scrolling({ side: 'rhs' }); }); // resize event handeler @@ -768,7 +772,7 @@ CodeMirrorDiffView.prototype._get_colors = function() { this._colors = {}; const currentColor = htmlToElement('') - this.el.append(currentColor); + this.el.children[0].append(currentColor); const currentStyle = window.getComputedStyle(currentColor); this._colors.current = { border: currentStyle.borderTopColor @@ -777,7 +781,7 @@ CodeMirrorDiffView.prototype._get_colors = function() { const aColor = htmlToElement('') - this.el.append(aColor); + this.el.children[0].append(aColor); const aStyle = window.getComputedStyle(aColor); this._colors.a = { border: aStyle.borderTopColor, @@ -787,7 +791,7 @@ CodeMirrorDiffView.prototype._get_colors = function() { const dColor = htmlToElement('') - this.el.append(dColor); + this.el.children[0].append(dColor); const dStyle = window.getComputedStyle(dColor); this._colors.d = { border: dStyle.borderTopColor, @@ -797,7 +801,7 @@ CodeMirrorDiffView.prototype._get_colors = function() { const cColor = htmlToElement('') - this.el.append(cColor); + this.el.children[0].append(cColor); const cStyle = window.getComputedStyle(cColor); this._colors.c = { border: cStyle.borderTopColor, @@ -830,7 +834,7 @@ CodeMirrorDiffView.prototype._scroll_to_change = function(change) { led.focus(); }; -CodeMirrorDiffView.prototype._scrolling = function({ side, id }) { +CodeMirrorDiffView.prototype._scrolling = function({ side }) { if (this.settings._debug) { traceTimeStart(`scroll#_scrolling ${side}`); } @@ -998,7 +1002,10 @@ CodeMirrorDiffView.prototype._diff = function() { this.el.dispatchEvent(new Event('updated')); } if (this.settings._debug) { - trace('change#_diff starting worker'); + trace('change#_diff worker postMessage', { + lhs: lhs.length, + rhs: rhs.length + }); } this._diffWorker.postMessage({ lhs, rhs }); } else { @@ -1073,7 +1080,7 @@ CodeMirrorDiffView.prototype._calculateOffsets = function (changes) { // calculate extents of diff canvas this.draw_lhs_min = 0.5; this.draw_mid_width - = this._queryElement(`#${this.lhsId}-${this.rhsId}-canvas`).offsetWidth; + = this._queryElement(`#${this.id}-lhs-rhs-canvas`).offsetWidth; this.draw_rhs_max = this.draw_mid_width - 0.5; //24.5; this.draw_lhs_width = 5; this.draw_rhs_width = 5; @@ -1187,7 +1194,9 @@ CodeMirrorDiffView.prototype._markupLineChanges = function (changes) { // lhs changes if (lhsInView) { - // the change is inside the viewport + const osideEditable = this.settings.line_numbers + && !red.getOption('readOnly'); + vdoc.addRender('lhs', change, i, { isCurrent, lineDiff, @@ -1195,13 +1204,16 @@ CodeMirrorDiffView.prototype._markupLineChanges = function (changes) { getMergeHandler: (change, side, oside) => { return () => this._merge_change(change, side, oside); }, - mergeButton: red.getOption('readOnly') - ? null : this.merge_rhs_button.cloneNode(true) + mergeButton: osideEditable + ? this.merge_rhs_button.cloneNode(true) : null }); } // rhs changes if (rhsInView) { + const osideEditable = this.settings.line_numbers + && !led.getOption('readOnly'); + vdoc.addRender('rhs', change, i, { isCurrent, lineDiff, @@ -1209,8 +1221,8 @@ CodeMirrorDiffView.prototype._markupLineChanges = function (changes) { getMergeHandler: (change, side, oside) => { return () => this._merge_change(change, side, oside); }, - mergeButton: led.getOption('readOnly') - ? null : this.merge_lhs_button.cloneNode(true) + mergeButton: osideEditable + ? this.merge_lhs_button.cloneNode(true) : null }); } @@ -1220,6 +1232,7 @@ CodeMirrorDiffView.prototype._markupLineChanges = function (changes) { vdoc.addInlineDiff(change, { ignoreaccents: this.settings.ignoreaccents, ignorews: this.settings.ignorews, + ignorecase: this.settings.ignorecase, getText: (side, lineNum) => { if (side === 'lhs') { const text = led.getLine(lineNum); @@ -1298,9 +1311,9 @@ CodeMirrorDiffView.prototype._draw_info = function() { const lhsScroll = this.editor.lhs.getScrollerElement(); const rhsScroll = this.editor.rhs.getScrollerElement(); const visible_page_height = lhsScroll.offsetHeight; // fudged - const dcanvas = document.getElementById(`${this.lhsId}-${this.rhsId}-canvas`); + const dcanvas = document.getElementById(`${this.id}-lhs-rhs-canvas`); if (dcanvas == undefined) { - throw new Error('Failed to find: ' + this.lhsId + '-' + this.rhsId + '-canvas'); + throw new Error(`Failed to find: ${this.id}-lhs-rhs-canvas`); } const lhs_margin = document.getElementById(`${this.id}-lhs-margin`); const rhs_margin = document.getElementById(`${this.id}-rhs-margin`); @@ -1496,13 +1509,6 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) { } }; -// CodeMirrorDiffView.prototype.trace = function(name) { -// if(this.settings._debug.indexOf(name) >= 0) { -// arguments[0] = `${name}:`; -// console.log([].slice.apply(arguments)); -// } -// } - CodeMirrorDiffView.prototype._queryElement = function(selector) { const cacheName = `_element:${selector}`; const element = this[cacheName] || document.querySelector(selector); @@ -1523,27 +1529,33 @@ function htmlToElement(html) { return template.content.firstChild; } -function getMarginTemplate({ id, side }) { - return `\ -
- -
;`; +function getMergelyContainer({ clazz = '' }) { + const classes = [ 'mergely-editor', clazz ] + return htmlToElement(`\ +
`); } -function getEditorTemplate({ id, side }) { - return `\ -`; +function getMarginTemplate({ id }) { + return htmlToElement(`\ +
+ +
`); +} + +function getEditorTemplate({ id }) { + return htmlToElement(`\ +`); } function getCenterCanvasTemplate({ id }) { - return `\ + return htmlToElement(`\
- -
`; + +
`); } function getSplash({ notice, left, top }) { - return `\ + return htmlToElement(`\

@@ -1551,7 +1563,7 @@ function getSplash({ notice, left, top }) { ${notice} license. For the full license, see http://www.mergely.com/license.

-
`; +
`); } module.exports = CodeMirrorDiffView; diff --git a/src/lcs.js b/src/lcs.js index 5205fe9..4d0b45c 100644 --- a/src/lcs.js +++ b/src/lcs.js @@ -54,11 +54,12 @@ LCS.prototype.clear = function () { }; LCS.prototype.diff = function (added, removed) { - const d = new diff(this.x, this.y, { + const result = new diff(this.x, this.y, { ignorews: !!this.options.ignorews, - ignoreaccents: !!this.options.ignoreaccents + ignoreaccents: !!this.options.ignoreaccents, + ignorecase: !!this.options.ignorecase }); - const changes = DiffParser(d.normal_form()); + const changes = DiffParser(result.normal_form()); for (let i = 0; i < changes.length; ++i) { const change = changes[i]; if (this.options.ignorews) { diff --git a/src/mergely.css b/src/mergely.css index 00b4d58..2c4cbf5 100644 --- a/src/mergely.css +++ b/src/mergely.css @@ -1,6 +1,6 @@ .mergely-editor .CodeMirror { line-height: 18px; - height: 100%; + height: auto; width: 100%; } @@ -76,13 +76,17 @@ z-index: 100; background-color: #fff; border: 1px solid black; - width: 300px; + width: 325px; padding: 10px; font-family: arial; font-size: 11px; transform: translate(0, 20%); } +.mergely-splash p { + margin: 11px 0 11px; +} + .mergely-icon { background-image: url('data:image/png;base64,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'); background-repeat: no-repeat; @@ -93,7 +97,7 @@ .mergely-splash span.mergely-icon { float: left; - padding-right: 10px; + margin-right: 10px; } .mergely-editor .merge-button { @@ -108,8 +112,7 @@ color: #717171; } .mergely-editor .CodeMirror { - border-left: 1px solid #ccc; - border-right: 1px solid #ccc; + border: 1px solid #ccc; } .mergely-editor .mergely.CodeMirror-gutter-background.current { background-color: #777; @@ -131,7 +134,9 @@ border-color: #a3d1ff; } .mergely-editor .mergely.a.rhs { - background-color: #ddeeff; + background-color: #eff7ff; + color: #0000ff; + opacity: 0.75; } .mergely-editor .mergely.d { border-color: #ff7f7f; @@ -145,8 +150,66 @@ .mergely-editor .mergely.ch.d.lhs { text-decoration: line-through; color: #ff0000; + background-color: transparent; } .mergely-editor .mergely.current { border-color: #000; } /* ---------------------------------------------------------------------- */ +.mergely-editor.dark-mode { + background-color: #0b0e10; +} +.mergely-editor.dark-mode .CodeMirror { + background-color: #12171b; +} +.mergely-editor.dark-mode .CodeMirror-code { + color: #bababa; +} +.mergely-editor.dark-mode .mergely.a.rhs { + color: #00beff; + background-color: transparent; +} +.mergely-editor .mergely.a { + border-color: #00beff; +} +.mergely-editor.dark-mode .mergely.current { + border-color: #616161; +} +.mergely-editor.dark-mode .mergely.c { + border-color: #313131; +} +.mergely-editor.dark-mode .mergely.d { + border-color: #9f3fff; +} +.mergely-editor .mergely.d.lhs { + background-color: transparent; +} +.mergely-editor.dark-mode .mergely.ch.d.lhs { + color: #bc4fff; +} +.mergely-editor.dark-mode .CodeMirror-gutters { + background-color: transparent; + border-right: 1px dotted #2e343a; +} +.mergely-editor.dark-mode .mergely.current .CodeMirror-linenumber { + background-color: #6a89f7; +} +.mergely-editor.dark-mode .CodeMirror-linenumber { + color: #999; +} +.mergely-editor.dark-mode .mergely-column { + border: 1px solid transparent; +} +.mergely-editor.dark-mode .mergely.current { + border-color: #6a89f7; +} +.mergely-editor.dark-mode .CodeMirror-cursor { + border-left: 2px solid #B19F73; +} +.mergely-editor.dark-mode .CodeMirror-selected { + background-color: #424242; +} +.mergely-editor.dark-mode .CodeMirror-focused .CodeMirror-selected { + background-color: #07468b; +} +/* ---------------------------------------------------------------------- */ diff --git a/src/vdoc.js b/src/vdoc.js index 71d4edf..165b70d 100644 --- a/src/vdoc.js +++ b/src/vdoc.js @@ -90,7 +90,7 @@ class VDoc { this._setRenderedChange(side, changeId); } - addInlineDiff(change, { getText, ignorews, ignoreaccents }) { + addInlineDiff(change, { getText, ignorews, ignoreaccents, ignorecase }) { const { lf, lt, olf, olt } = getExtents('lhs', change); const vdoc = this; @@ -107,7 +107,8 @@ class VDoc { // is empty. const lcs = new LCS(lhsText, rhsText, { ignoreaccents, - ignorews + ignorews, + ignorecase }); // TODO: there might be an LCS performance gain here to move @@ -149,7 +150,7 @@ class VDoc { if (id < viewport.from || id > viewport.to) { continue; } - + const vline = this._getLine(side, id); if (vline.rendered) { continue; @@ -199,34 +200,36 @@ class VLine { return; } this.editor = editor; - if (this.background.size) { - const clazz = Array.from(this.background).join(' '); - editor.addLineClass(this.id, 'background', clazz); - } - if (this.gutter.size) { - const clazz = Array.from(this.gutter).join(' '); - editor.addLineClass(this.id, 'gutter', clazz); - } - if (this.marker) { - const [ name, item, handler ] = this.marker; - item.addEventListener('click', handler); - editor.setGutterMarker(this.id, name, item); - } - if (this.markup.length) { - 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 = charFrom; - } - if (charTo >= 0) { - toPos.ch = charTo; - } - this._clearMarkup.push( - editor.markText(fromPos, toPos, { className })); + editor.operation(() => { + if (this.background.size) { + const clazz = Array.from(this.background).join(' '); + editor.addLineClass(this.id, 'background', clazz); } - } + if (this.gutter.size) { + const clazz = Array.from(this.gutter).join(' '); + editor.addLineClass(this.id, 'gutter', clazz); + } + if (this.marker) { + const [ name, item, handler ] = this.marker; + item.addEventListener('click', handler); + editor.setGutterMarker(this.id, name, item); + } + if (this.markup.length) { + 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 = charFrom; + } + if (charTo >= 0) { + toPos.ch = charTo; + } + this._clearMarkup.push( + editor.markText(fromPos, toPos, { className })); + } + } + }); this.rendered = true; } @@ -236,26 +239,28 @@ class VLine { return; } - if (this.background) { - editor.removeLineClass(this.id, 'background'); - } - if (this.gutter) { - editor.removeLineClass(this.id, 'gutter'); - } - if (this.marker) { - const [ , item, handler ] = this.marker; - // set with `null` to clear marker - editor.setGutterMarker(this.id, name, null); - item.removeEventListener('click', handler); - item.remove(); - } - if (this._clearMarkup.length) { - for (const markup of this._clearMarkup) { - markup.clear(); + editor.operation(() => { + if (this.background) { + editor.removeLineClass(this.id, 'background'); } - this._clearMarkup = []; - this.markup = []; - } + if (this.gutter) { + editor.removeLineClass(this.id, 'gutter'); + } + if (this.marker) { + const [ name, item, handler ] = this.marker; + // set with `null` to clear marker + editor.setGutterMarker(this.id, name, null); + item.removeEventListener('click', handler); + item.remove(); + } + if (this._clearMarkup.length) { + for (const markup of this._clearMarkup) { + markup.clear(); + } + this._clearMarkup = []; + this.markup = []; + } + }); } } diff --git a/test/mergely.spec.js b/test/mergely.spec.js index e64f3c9..b6429db 100644 --- a/test/mergely.spec.js +++ b/test/mergely.spec.js @@ -159,6 +159,19 @@ describe('mergely', function () { ...initOptions }); }); + + it.only('initializes with and set lhs/rhs on update', function (done) { + const editor = init({ _debug: true }); + expect(editor).to.exist; + const test = () => { + done(); + }; + editor.once('updated', () => { + editor.on('updated', test); + editor.lhs('left-hand side text'); + editor.rhs('right-hand side text'); + }); + }); }); describe('clear', () => { diff --git a/webpack.prod.js b/webpack.prod.js index 9f5bd05..373d41d 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -2,11 +2,14 @@ const webpack = require('webpack'); const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -module.exports = { +// const dev = require('./webpack.dev'); + +const dev = { mode: 'development', entry: { mergely: './src/mergely.js', }, + devtool: 'inline-source-map', output: { path: path.join(__dirname, 'lib'), filename: './[name].js', @@ -19,11 +22,6 @@ module.exports = { test: /\.(js)$/, exclude: /node_modules/, use: ['babel-loader'] - }, { - test: /worker\.js$/, - use: { - loader: 'worker-loader' - } }] }, resolve: { @@ -31,6 +29,15 @@ module.exports = { }, externals: { CodeMirror: 'CodeMirror' + } +}; + +const prod = { + ...dev, + mode: 'production', + output: { + ...dev.output, + filename: './[name].min.js', }, plugins: [ new CopyWebpackPlugin({ @@ -42,3 +49,7 @@ module.exports = { }) ] }; + +module.exports = (mode) => { + return [ dev, prod ]; +}