mergely
Version:
A javascript UI for diff/merge
340 lines (328 loc) • 11.5 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mergely - Example 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" />
<meta name="color-scheme" content="dark light">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<!-- Bootstrap -->
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
<!-- jquery -->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.slim.js"></script>
<!-- Mergely -->
<link type="text/css" rel="stylesheet" href="/lib/mergely.css" />
<script type="text/javascript" src="/lib/mergely.js"></script>
<style type="text/css">
html, body {
margin: 0;
}
body.dark-mode {
background-color: #000;
}
body.dark-mode .btn {
color: white;
}
body.dark-mode .text-black-50 {
color: #606060 ;
}
.mergely-editor .CodeMirror {
/* adjust line height for bootstrap */
line-height: 21px;
}
#compare {
height: 300px;
visibility: hidden;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">Mergely</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<span class="p-1">
<button class="btn btn-secondary" id="doc-new">
<span class="bi-file-earmark-text" />
New
</button>
</span>
<span class="p-1">
<button class="btn btn-secondary" id="doc-diff">
<span class="bi-file-diff" />
Download diff
</button>
</span>
<span class="p-1">
<button class="btn btn-secondary" id="prev-change" title="Go previous change">
<span class="bi-arrow-up-circle-fill" />
</button>
</span>
<span class="p-1">
<button class="btn btn-secondary" id="next-change" title="Go next change">
<span class="bi-arrow-down-circle-fill" />
</button>
</span>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
Options
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><b class="dropdown-item">Editor options</b></li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="dark-mode">
<label class="form-check-label" for="dark-mode">
Dark mode
</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input checked class="form-check-input" type="checkbox" role="switch"
id="view-sidebar">
<label class="form-check-label" for="view-sidebar">
View sidebars
</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input checked class="form-check-input" type="checkbox" role="switch"
id="wrap-lines">
<label class="form-check-label" for="wrap-lines">
Wrap lines
</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input checked class="form-check-input" type="checkbox" role="switch"
id="line-numbers">
<label class="form-check-label" for="line-numbers">
Line numbers
</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="read-only">
<label class="form-check-label" for="read-only">
Read only
</label>
</div>
</div>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li><b class="dropdown-item">Diff options</b></li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="ignore-whitespace">
<label class="form-check-label" for="ignore-whitespace">
Ignore whitespace
</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="ignore-case">
<label class="form-check-label" for="ignore-case">
Ignore case
</label>
</div>
</div>
</li>
<li>
<div class="dropdown-item">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="ignore-ignoreaccents">
<label class="form-check-label" for="ignore-ignoreaccents">
Ignore accent characters
</label>
</div>
</div>
</li>
</ul>
</li>
</ul>
<form class="d-flex flex-nowrap">
<input id="search-text" class="form-control me-2" type="search" placeholder="Search"
aria-label="Search">
<div class="btn-group">
<button id="search" type="button" class="btn btn-danger">Search</button>
<button type="button" class="btn btn-danger dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a id="search-lhs" class="dropdown-item" href="#">
<span class="bi-check"></span>Search left editor</a>
</li>
<li><a id="search-rhs" class="dropdown-item" href="#">
<span class="bi-check" style="color:transparent"></span>Search right editor</a>
</li>
</ul>
</div>
</form>
</div>
</div>
</nav>
<div id="compare"></div>
<div class="container-fluid" style="font-size:12px">
<div class="row">
<div class="col" id="lhs-info">
<button class="btn p-0" id="download-lhs">
<span class="bi-box-arrow-in-down" />
</button>
<span class="text-black-50" id="lhs-characters"></span>
<span class="text-black-50" id="lhs-changes"></span>
</div>
<div class="col" style="flex: 0 0 24px">
</div>
<div class="col" id="rhs-info">
<button class="btn p-0" id="download-rhs">
<span class="bi-box-arrow-in-down" />
</button>
<span class="text-black-50" id="rhs-characters"></span>
<span class="text-black-50" id="rhs-changes"></span>
</div>
</div>
</div>
<script>
const mergely = new Mergely('#compare', {
license: 'lgpl',
wrap_lines: true
});
mergely.once('updated', () => {
mergely.lhs('the qúíck red FOX\njumped over the hairy dog');
mergely.rhs('\tThe quick brown fox\njumped over the lazy dog');
mergely.once('updated', () => {
document.getElementById('compare').style.visibility = 'visible';
});
jQuery('#ignore-whitespace').on('click', (ev) => {
mergely.options({ ignorews: ev.target.checked });
});
jQuery('#ignore-case').on('click', (ev) => {
mergely.options({ ignorecase: ev.target.checked });
});
jQuery('#ignore-ignoreaccents').on('click', (ev) => {
mergely.options({ ignoreaccents: ev.target.checked });
});
jQuery('#view-sidebar').on('click', (ev) => {
mergely.options({ sidebar: ev.target.checked });
});
jQuery('#wrap-lines').on('click', (ev) => {
mergely.options({ wrap_lines: ev.target.checked });
});
jQuery('#line-numbers').on('click', (ev) => {
mergely.options({ line_numbers: ev.target.checked });
});
jQuery('#read-only').on('click', (ev) => {
const lhsCm = mergely.cm('lhs');
lhsCm.setOption('readOnly', ev.target.checked);
const rhsCm = mergely.cm('rhs');
rhsCm.setOption('readOnly', ev.target.checked);
mergely.options({});
});
jQuery('#next-change').on('click', (ev) => {
mergely.scrollToDiff('next');
});
jQuery('#prev-change').on('click', (ev) => {
mergely.scrollToDiff('prev');
});
jQuery('#dark-mode').on('click', (ev) => {
document.body.classList.toggle('dark-mode');
document.querySelector('.mergely-editor').classList.toggle('dark-mode');
// basically a no-op to force colors to be recalculated
mergely.options({});
});
jQuery('#doc-new').on('click', () => {
mergely.lhs('');
mergely.rhs('');
});
jQuery('#doc-diff').on('click', () => downloadText(mergely.diff(), 'doc.diff'));
jQuery('#download-lhs').on('click', () => downloadText(mergely.get('lhs'), 'lhs.txt'));
jQuery('#download-rhs').on('click', () => downloadText(mergely.diff('rhs'), 'rhs.txt'));
let searchLhs = true;
jQuery('#search-lhs').on('click', (ev) => {
searchLhs = true;
const lhs = document.getElementById('search-lhs').children[0];
const rhs = document.getElementById('search-rhs').children[0];
lhs.style = 'color:initial';
rhs.style = 'color:transparent';
});
jQuery('#search-rhs').on('click', (ev) => {
searchLhs = false;
const lhs = document.getElementById('search-lhs').children[0];
const rhs = document.getElementById('search-rhs').children[0];
lhs.style = 'color:transparent';
rhs.style = 'color:initial';
});
jQuery('#search-text').on('keypress', (ev) => {
console.log(ev.which)
if (event.which === 13) {
ev.preventDefault();
jQuery('#search').click();
}
});
jQuery('#search').on('click', (ev) => {
ev.preventDefault();
const text = jQuery('#search-text').get(0).value;
const direction = searchLhs ? 'lhs' : 'rhs';
mergely.search(direction, text);
});
});
mergely.on('updated', () => {
const info = mergely.summary();
jQuery('#lhs-characters').text(`${info.lhsLength} characters`);
jQuery('#rhs-characters').text(`${info.rhsLength} characters`);
if (info.numChanges) {
jQuery('#lhs-changes').text('(changed)');
jQuery('#rhs-changes').text('(changed)');
} else {
jQuery('#lhs-changes').text('(no changes)');
jQuery('#rhs-changes').text('(no changes)');
}
});
function downloadText(text, filename) {
const b64 = btoa(text);
const link = document.createElement('a');
link.download = filename;
link.href = 'data:text/csv;charset=utf-8;base64,' + b64;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
</body>
</html>