1
0
mirror of synced 2025-12-07 22:28:50 +08:00

Compare commits

...

4 Commits
4.0.5 ... 4.0.8

Author SHA1 Message Date
Jamie Peabody
d9f2edc1ad chore: webpack upgrade (#98)
* upgraded to webpack 4.x

* updated webpack
2018-11-17 09:12:45 +00:00
Jamie Peabody
3d44470103 4.0.7 2018-08-06 13:01:19 +01:00
Jamie Peabody
2c8f6cd340 chore: updated docs 2018-08-06 13:01:13 +01:00
Jamie Peabody
6716096215 patch(issue #89): fixes missing merge buttons (#91)
* patch(fix-89): fixes missing merge buttons

* 4.0.6
2018-07-17 13:46:54 -07:00
14 changed files with 277 additions and 97 deletions

19
.babelrc.js Normal file
View File

@@ -0,0 +1,19 @@
module.exports = function(api) {
return {
presets: [
[
"@babel/preset-env",
{
targets: {
chrome: 59,
edge: 13,
firefox: 50,
ie: 11
},
// for uglifyjs...
forceAllTransforms: api.env("production"),
},
],
],
};
};

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/node_modules
/lib
mergely-*.tgz
package-lock.json
*.log

View File

@@ -1,5 +1,23 @@
# Changes
## 4.0.8
* chore: updated webpack
## 4.0.7
* chore: updated documentation
## 4.0.6
* #89: fixes missing merge buttons
## 4.0.5
* #85: fixes XSS vulnerability with DOM id
## 4.0.2
* #83: fixes poor rendering performance
## 4.0.0
### Breaking changes

View File

@@ -9,6 +9,9 @@ Mergely is a javascript component to diff/merge files interactively in a browser
### Installation via webpack
The recommended way to install mergely is to use npm and [webpack](https://webpack.js.org/) to install mergely and its dependencies. It is highly recommended that you start by cloning [mergely-webpack](https://github.com/wickedest/mergely-webpack). It 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
Unpack mergely.tgz into a folder, e.g. `./lib`, and then add the following to the `<head>` of your target html source file.
@@ -46,9 +49,6 @@ The following example can be used to set the `lhs` and `rhs` editors synchronous
```js
$(document).ready(function () {
// initialize mergely
$('#mergely').mergely();
// set editor content
$('#mergely').mergely({
lhs: function(setValue) {

21
examples/app.html Normal file
View File

@@ -0,0 +1,21 @@
<!--
This example demonstrates the minimum amount of code required to use Mergely.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" /><title>Mergely - Simple Example</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="Merge and Diff your documents with diff online and share" />
<meta name="keywords" content="diff,merge,compare,jsdiff,comparison,difference,file,text,unix,patch,algorithm,saas,longest common subsequence" />
<meta name="author" content="Jamie Peabody" />
</head>
<body>
<div class="mergely-full-screen-8">
<div class="mergely-resizer">
<div id="mergely"></div>
</div>
</div>
</body>
</html>

18
examples/app.js Normal file
View File

@@ -0,0 +1,18 @@
require('codemirror/lib/codemirror.css');
require('../src/mergely.css');
$(document).ready(function () {
$('#mergely').mergely({
license: 'lgpl',
cmsettings: {
readOnly: false
},
_debug: '',
lhs: function(setValue) {
setValue('the quick red fox\njumped over the hairy dog');
},
rhs: function(setValue) {
setValue('the quick brown fox\njumped over the lazy dog');
}
});
});

View File

@@ -1,6 +1,5 @@
const path = require('path');
const webpackCfg = require('./webpack.config');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = function(config) {
config.set({
@@ -40,9 +39,11 @@ module.exports = function(config) {
webpack: {
entry: './src/mergely.js',
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('css-loader') }
]
rules: [{
test: /\.(js)$/,
exclude: /node_modules/,
use: ['babel-loader']
}]
},
resolve: {
extensions: ['.js'],
@@ -52,7 +53,11 @@ module.exports = function(config) {
}
},
plugins: [
new ExtractTextPlugin('mergely.css')
new CopyWebpackPlugin([{
from: 'src/mergely.css',
to: 'mergely.css',
toType: 'file'
}])
]
},
webpackServer: {

34
package.json Executable file → Normal file
View File

@@ -1,17 +1,12 @@
{
"name": "mergely",
"version": "4.0.5",
"version": "4.0.8",
"description": "A javascript UI for diff/merge",
"directories": {
"doc": "doc",
"example": "examples",
"test": "test"
},
"scripts": {
"build": "rm -rf lib && webpack",
"test": "karma start",
"test:chrome": "karma start --browsers Chrome --singleRun=false"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wickedest/Mergely.git"
@@ -20,7 +15,8 @@
"files": [
"lib",
"examples",
"README.md"
"README.md",
"LICENSE"
],
"keywords": [
"merge",
@@ -38,14 +34,20 @@
},
"homepage": "https://github.com/wickedest/Mergely#readme",
"devDependencies": {
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"@webpack-cli/init": "^0.1.2",
"babel-loader": "^8.0.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"chai": "^4.1.2",
"codemirror": "^5.32.0",
"css-loader": "^0.28.7",
"extract-text-webpack-plugin": "^3.0.2",
"copy-webpack-plugin": "^4.6.0",
"css-loader": "^0.28.11",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^3.4.2",
"jquery": "^3.2.1",
"karma": "^2.0.0",
"karma": "^3.1.1",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage-istanbul-reporter": "^1.3.0",
@@ -53,6 +55,16 @@
"karma-mocha-reporter": "^2.2.5",
"karma-webpack": "^2.0.9",
"mocha": "^4.0.1",
"webpack": "^3.8.1"
"style-loader": "^0.23.0",
"uglifyjs-webpack-plugin": "^2.0.1",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.9"
},
"scripts": {
"build": "rm -rf lib && webpack --config ./webpack.prod.js",
"start": "webpack-dev-server -w --debug --progress --colors --config ./webpack.dev.js --content-base ./dist --inline --hot --host 0.0.0.0",
"test": "karma start",
"test:chrome": "karma start --browsers Chrome --singleRun=false"
}
}

16
src/Timer.js Normal file
View File

@@ -0,0 +1,16 @@
class Timer {
static start() {
Timer.t0 = Date.now();
}
static stop() {
var t1 = Date.now();
var td = t1 - Timer.t0;
Timer.t0 = t1;
return td;
}
}
Timer.t0 = 0;
exports = module.exports = Timer;

View File

@@ -1,25 +1,13 @@
"use strict";
require('./mergely.css');
(function(jQuery, CodeMirror) {
var Mgly = {};
Mgly.Timer = function(){
var self = this;
self.start = function() { self.t0 = new Date().getTime(); };
self.stop = function() {
var t1 = new Date().getTime();
var d = t1 - self.t0;
self.t0 = t1;
return d;
};
self.start();
};
Mgly.ChangeExpression = new RegExp(/(^(?![><\-])*\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
const Timer = require('./Timer');
Mgly.DiffParser = function(diff) {
var changes = [];
var change_id = 0;
@@ -461,11 +449,11 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
lineNumbers: this.settings.line_numbers
};
var lhs_gutters = [];
if (this.lhs_cmsettings.line_numbers) {
if (this.lhs_cmsettings.lineNumbers) {
lhs_gutters = ['merge', 'CodeMirror-linenumbers']
}
var rhs_gutters = [];
if (this.rhs_cmsettings.line_numbers) {
if (this.rhs_cmsettings.lineNumbers) {
rhs_gutters = ['merge', 'CodeMirror-linenumbers']
}
jQuery.extend(true, this.lhs_cmsettings, this.settings.cmsettings, { gutters: lhs_gutters }, this.settings);
@@ -683,7 +671,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
else {
// homebrew
var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid #222;margin-right:5px;margin-top: -2px;';
var style = 'opacity:0.6;height:16px;background-color:#bfbfbf;cursor:pointer;text-align:center;color:#eee;border:1px solid #848484;margin-right:5px;margin-top:-2px;';
merge_lhs_button = '<div style="' + style + '" title="Merge left">&lt;</div>';
merge_rhs_button = '<div style="' + style + '" title="Merge right">&gt;</div>';
}
@@ -751,7 +739,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}).append('<p><img width="36" height="36" alt="mergely" src="' + icon + '" style="float:left;padding-right:10px;" />This software is a Combined Work using Mergely and is covered by the ' + lic + ' license. For the full license, see <a target="_blank" href="http://www.mergely.com">http://www.mergely.com/license.</a></p>');
jQuery('body').one('click', function () {
jQuery('#mergely-splash').fadeOut(100, 'linear', function () {
this.remove();
jQuery('#mergely-splash').remove();
});
});
}
@@ -950,13 +938,13 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
else this.trace('scroll', 'not scrolling other side');
if (this.settings.autoupdate) {
var timer = new Mgly.Timer();
Timer.start();
this._calculate_offsets(editor_name1, editor_name2, this.changes);
this.trace('change', 'offsets time', timer.stop());
this.trace('change', 'offsets time', Timer.stop());
this._markup_changes(editor_name1, editor_name2, this.changes);
this.trace('change', 'markup time', timer.stop());
this.trace('change', 'markup time', Timer.stop());
this._draw_diff(editor_name1, editor_name2, this.changes);
this.trace('change', 'draw time', timer.stop());
this.trace('change', 'draw time', Timer.stop());
}
this.trace('scroll', 'scrolled');
}
@@ -966,9 +954,9 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
var self = this;
if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
this.changed_timeout = setTimeout(function(){
var timer = new Mgly.Timer();
Timer.start();
self._changed(editor_name1, editor_name2);
self.trace('change', 'total time', timer.stop());
self.trace('change', 'total time', Timer.stop());
}, this.settings.change_timeout);
},
_changed: function(editor_name1, editor_name2) {
@@ -979,7 +967,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
var self = this, name, editor, fns, timer, i, change, l;
var clear_changes = function() {
timer = new Mgly.Timer();
Timer.start();
for (i = 0, l = editor.lineCount(); i < l; ++i) {
editor.removeLineClass(i, 'background');
}
@@ -993,7 +981,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
change.clear();
}
editor.clearGutter('merge');
self.trace('change', 'clear time', timer.stop());
self.trace('change', 'clear time', Timer.stop());
};
for (name in this.editor) {
@@ -1029,23 +1017,23 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
_diff: function(editor_name1, editor_name2) {
var lhs = this.editor[editor_name1].getValue();
var rhs = this.editor[editor_name2].getValue();
var timer = new Mgly.Timer();
Timer.start();
var d = new Mgly.diff(lhs, rhs, this.settings);
this.trace('change', 'diff time', timer.stop());
this.trace('change', 'diff time', Timer.stop());
this.changes = Mgly.DiffParser(d.normal_form());
this.trace('change', 'parse time', timer.stop());
this.trace('change', 'parse time', Timer.stop());
if (this._current_diff === undefined && this.changes.length) {
// go to first difference on start-up
this._current_diff = 0;
this._scroll_to_change(this.changes[0]);
}
this.trace('change', 'scroll_to_change time', timer.stop());
this.trace('change', 'scroll_to_change time', Timer.stop());
this._calculate_offsets(editor_name1, editor_name2, this.changes);
this.trace('change', 'offsets time', timer.stop());
this.trace('change', 'offsets time', Timer.stop());
this._markup_changes(editor_name1, editor_name2, this.changes);
this.trace('change', 'markup time', timer.stop());
this.trace('change', 'markup time', Timer.stop());
this._draw_diff(editor_name1, editor_name2, this.changes);
this.trace('change', 'draw time', timer.stop());
this.trace('change', 'draw time', Timer.stop());
},
_parse_diff: function (editor_name1, editor_name2, diff) {
this.trace('diff', 'diff results:\n', diff);
@@ -1235,7 +1223,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
var lhsvp = this._get_viewport_side(editor_name1);
var rhsvp = this._get_viewport_side(editor_name2);
var timer = new Mgly.Timer();
Timer.start();
led.operation(function() {
for (var i = 0; i < changes.length; ++i) {
var change = changes[i];
@@ -1288,7 +1276,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
}.bind(this));
this.trace('change', 'markup lhs-editor time', timer.stop());
this.trace('change', 'markup lhs-editor time', Timer.stop());
red.operation(function() {
for (var i = 0; i < changes.length; ++i) {
var change = changes[i];
@@ -1340,7 +1328,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
}
}.bind(this));
this.trace('change', 'markup rhs-editor time', timer.stop());
this.trace('change', 'markup rhs-editor time', Timer.stop());
// mark text deleted, LCS changes
var marktext = [], i, j, k, p;
@@ -1400,7 +1388,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
}
}
this.trace('change', 'LCS marktext time', timer.stop());
this.trace('change', 'LCS marktext time', Timer.stop());
// mark changes outside closure
led.operation(function() {
@@ -1420,7 +1408,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
});
this.trace('change', 'LCS markup time', timer.stop());
this.trace('change', 'LCS markup time', Timer.stop());
// merge buttons
var ed = {lhs:led, rhs:red};
@@ -1484,7 +1472,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
}
this.trace('change', 'markup buttons time', timer.stop());
this.trace('change', 'markup buttons time', Timer.stop());
},
_merge_change : function(change, side, oside) {
if (!change) return;

View File

@@ -402,6 +402,7 @@ describe('mergely', function () {
for (let i = 0; i < opt.next; ++i) {
$('#mergely').mergely('scrollToDiff', 'next');
}
expect($('.merge-button').length > 0).to.be.true;
$('#mergely').mergely('mergeCurrentChange', opt.dir);
if (opt.dir === 'lhs') {
expect($('#mergely').mergely('get', 'lhs')).to.equal(opt.expect || opt.rhs);

View File

@@ -1,38 +0,0 @@
const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
mergely: './src/mergely.js',
// 'mergely.min': './src/mergely.js'
},
output: {
path: path.join(__dirname, 'lib'),
filename: './[name].js',
library: 'mergely',
libraryTarget: 'umd',
umdNamedDefine: true
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('css-loader') }
]
},
resolve: {
extensions: ['.js']
},
externals: {
jquery: 'jQuery',
CodeMirror: 'CodeMirror'
},
plugins: [
// new webpack.optimize.UglifyJsPlugin({
// sourceMap: true,
// include: /\.js$/,
// // include: /\.min\.js$/,
// exclude: /node_modules/
// }),
new ExtractTextPlugin('mergely.css')
]
};

80
webpack.dev.js Normal file
View File

@@ -0,0 +1,80 @@
const webpack = require('webpack')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
module: {
rules: [{
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'examples')
],
test: /\.js$/
}, {
test: /\.(js)$/,
exclude: /node_modules/,
use: ['babel-loader']
}, {
test: /\.css$/,
use: [{
loader: 'style-loader',
options: {
sourceMap: true
}
}, {
loader: 'css-loader'
}]
}]
},
resolve: {
extensions: ['.js'],
alias: {
'CodeMirror': path.join(__dirname, 'node_modules', 'codemirror'),
'jQuery': path.join(__dirname, 'node_modules', 'jquery')
}
},
plugins: [
new HtmlWebpackPlugin({
template: 'examples/app.html',
filename: 'mergely.html'
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
new webpack.ProvidePlugin({
CodeMirror: 'codemirror'
})
],
entry: {
app: [
'./examples/app',
'./src/mergely',
]
},
output: {
filename: 'mergely.js',
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/
}
},
chunks: 'async',
minChunks: 1,
minSize: 30000,
name: true
}
}
}

38
webpack.prod.js Normal file
View File

@@ -0,0 +1,38 @@
const webpack = require('webpack');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
mergely: './src/mergely.js',
},
output: {
path: path.join(__dirname, 'lib'),
filename: './[name].js',
library: 'mergely',
libraryTarget: 'umd',
umdNamedDefine: true
},
module: {
rules: [{
test: /\.(js)$/,
exclude: /node_modules/,
use: ['babel-loader']
}]
},
resolve: {
extensions: ['.js']
},
externals: {
jquery: 'jQuery',
CodeMirror: 'CodeMirror'
},
plugins: [
new CopyWebpackPlugin([{
from: 'src/mergely.css',
to: 'mergely.css',
toType: 'file'
}])
]
};