Skip to content

Commit 4d24476

Browse files
committed
Add revision modal with UIs and support to mark patch diff texts
1 parent 8e351e7 commit 4d24476

4 files changed

Lines changed: 178 additions & 2 deletions

File tree

public/js/index.js

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,8 @@ var ui = {
564564
},
565565
modal: {
566566
snippetImportProjects: $("#snippetImportModalProjects"),
567-
snippetImportSnippets: $("#snippetImportModalSnippets")
567+
snippetImportSnippets: $("#snippetImportModalSnippets"),
568+
revision: $("#revisionModal")
568569
}
569570
};
570571

@@ -1382,6 +1383,149 @@ ui.toolbar.beta.pdf.attr("download", "").attr("href", noteurl + "/pdf");
13821383
ui.toolbar.beta.slide.attr("href", noteurl + "/slide");
13831384

13841385
//modal actions
1386+
var revisions = [];
1387+
var revisionViewer = null;
1388+
var revisionList = ui.modal.revision.find('.ui-revision-list');
1389+
var revision = null;
1390+
var revisionTime = null;
1391+
ui.modal.revision.on('show.bs.modal', function (e) {
1392+
$.get(noteurl + '/revision')
1393+
.success(function(data) {
1394+
parseRevisions(JSON.parse(data).revision);
1395+
initRevisionViewer();
1396+
})
1397+
.error(function(err) {
1398+
1399+
})
1400+
.complete(function() {
1401+
//na
1402+
});
1403+
});
1404+
function checkRevisionViewer() {
1405+
if (revisionViewer) {
1406+
var container = $(revisionViewer.display.wrapper).parent();
1407+
$(revisionViewer.display.scroller).css('height', container.height() + 'px');
1408+
revisionViewer.refresh();
1409+
}
1410+
}
1411+
ui.modal.revision.on('shown.bs.modal', checkRevisionViewer);
1412+
$(window).resize(checkRevisionViewer);
1413+
function parseRevisions(_revisions) {
1414+
if (_revisions.length != revisions) {
1415+
revisions = _revisions;
1416+
var lastRevision = null;
1417+
if (revisionList.children().length > 0) {
1418+
lastRevision = revisionList.find('.active').attr('data-revision-time');
1419+
}
1420+
revisionList.html('');
1421+
for (var i = 0; i < revisions.length; i++) {
1422+
var revision = revisions[i];
1423+
var item = $('<a href="#" class="list-group-item"></a>');
1424+
item.attr('data-revision-time', revision.time);
1425+
if (lastRevision == revision.time) item.addClass('active');
1426+
var itemHeading = $('<h5 class="list-group-item-heading"></h5>');
1427+
itemHeading.html('<i class="fa fa-clock-o"></i> ' + moment(revision.time).format('llll'));
1428+
var itemText = $('<p class="list-group-item-text"></p>');
1429+
itemText.html('<i class="fa fa-file-text"></i> Length: ' + revision.length);
1430+
item.append(itemHeading).append(itemText);
1431+
item.click(function (e) {
1432+
var time = $(this).attr('data-revision-time');
1433+
selectRevision(time);
1434+
});
1435+
revisionList.append(item);
1436+
}
1437+
if (!lastRevision) {
1438+
selectRevision(revisions[0].time);
1439+
}
1440+
}
1441+
}
1442+
function selectRevision(time) {
1443+
if (time == revisionTime) return;
1444+
$.get(noteurl + '/revision/' + time)
1445+
.success(function(data) {
1446+
revision = JSON.parse(data);
1447+
revisionTime = time;
1448+
var lastScrollInfo = revisionViewer.getScrollInfo();
1449+
revisionList.children().removeClass('active');
1450+
revisionList.find('[data-revision-time="' + time + '"]').addClass('active');
1451+
var content = revision.content;
1452+
revisionViewer.setValue(content);
1453+
revisionViewer.scrollTo(null, lastScrollInfo.top);
1454+
// mark the text which have been insert or delete
1455+
if (revision.patch.length > 0) {
1456+
var bias = 0;
1457+
for (j = 0; j < revision.patch.length; j++) {
1458+
var patch = revision.patch[j];
1459+
var currIndex = patch.start1 + bias;
1460+
for (var i = 0; i < patch.diffs.length; i++) {
1461+
var diff = patch.diffs[i];
1462+
// ignore if diff only contains line breaks
1463+
if ((diff[1].match(new RegExp("\n", "g")) || []).length == diff[1].length) continue;
1464+
switch(diff[0]) {
1465+
case 0: // retain
1466+
currIndex += diff[1].length;
1467+
break;
1468+
case 1: // insert
1469+
var prePos = revisionViewer.posFromIndex(currIndex);
1470+
var postPos = revisionViewer.posFromIndex(currIndex + diff[1].length);
1471+
revisionViewer.markText(prePos, postPos, {
1472+
css: 'background-color: rgba(230,255,230,0.7); text-decoration: underline;'
1473+
});
1474+
currIndex += diff[1].length;
1475+
break;
1476+
case -1: // delete
1477+
var prePos = revisionViewer.posFromIndex(currIndex);
1478+
revisionViewer.replaceRange(diff[1], prePos);
1479+
var postPos = revisionViewer.posFromIndex(currIndex + diff[1].length);
1480+
revisionViewer.markText(prePos, postPos, {
1481+
css: 'background-color: rgba(255,230,230,0.7); text-decoration: line-through;'
1482+
});
1483+
bias += diff[1].length;
1484+
currIndex += diff[1].length;
1485+
break;
1486+
}
1487+
}
1488+
}
1489+
}
1490+
})
1491+
.error(function(err) {
1492+
1493+
})
1494+
.complete(function() {
1495+
//na
1496+
});
1497+
}
1498+
function initRevisionViewer() {
1499+
if (revisionViewer) return;
1500+
var revisionViewerTextArea = document.getElementById("revisionViewer");
1501+
revisionViewer = CodeMirror.fromTextArea(revisionViewerTextArea, {
1502+
mode: 'gfm',
1503+
viewportMargin: viewportMargin,
1504+
lineNumbers: true,
1505+
lineWrapping: true,
1506+
showCursorWhenSelecting: true,
1507+
inputStyle: "textarea",
1508+
gutters: ["CodeMirror-linenumbers"],
1509+
flattenSpans: true,
1510+
addModeClass: true,
1511+
readOnly: true,
1512+
autoRefresh: true,
1513+
scrollbarStyle: 'overlay'
1514+
});
1515+
}
1516+
$('#revisionModalDownload').click(function () {
1517+
if (!revision) return;
1518+
var filename = renderFilename(ui.area.markdown) + '_' + revisionTime + '.md';
1519+
var blob = new Blob([revision.content], {
1520+
type: "text/markdown;charset=utf-8"
1521+
});
1522+
saveAs(blob, filename);
1523+
});
1524+
$('#revisionModalRevert').click(function () {
1525+
if (!revision) return;
1526+
editor.setValue(revision.content);
1527+
ui.modal.revision.modal('hide');
1528+
});
13851529
//snippet projects
13861530
ui.modal.snippetImportProjects.change(function() {
13871531
var accesstoken = $("#snippetImportModalAccessToken").val(),

public/views/body.ejs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,5 @@
235235
</div>
236236
</div>
237237
<%- include signin-modal %>
238-
<%- include help-modal %>
238+
<%- include help-modal %>
239+
<%- include revision-modal %>

public/views/header.ejs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
</li>
2929
<li class="divider"></li>
3030
<li class="dropdown-header">Beta</li>
31+
<li role="presentation"><a role="menuitem" class="ui-beta-revision" tabindex="-1" data-toggle="modal" data-target="#revisionModal"><i class="fa fa-history fa-fw"></i> Revision</a>
32+
</li>
3133
<li role="presentation"><a role="menuitem" class="ui-beta-pdf" tabindex="-1" href="#" target="_self"><i class="fa fa-file-pdf-o fa-fw"></i> Export PDF</a>
3234
</li>
3335
<li role="presentation"><a role="menuitem" class="ui-beta-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> Slide Mode</a>
@@ -121,6 +123,8 @@
121123
</a>
122124
<ul class="dropdown-menu" role="menu" aria-labelledby="menu">
123125
<li class="dropdown-header">Beta</li>
126+
<li role="presentation"><a role="menuitem" class="ui-beta-revision" tabindex="-1" data-toggle="modal" data-target="#revisionModal"><i class="fa fa-history fa-fw"></i> Revision</a>
127+
</li>
124128
<li role="presentation"><a role="menuitem" class="ui-beta-pdf" tabindex="-1" href="#" target="_self"><i class="fa fa-file-pdf-o fa-fw"></i> Export PDF</a>
125129
</li>
126130
<li role="presentation"><a role="menuitem" class="ui-beta-slide" tabindex="-1" href="#" target="_blank"><i class="fa fa-tv fa-fw"></i> Slide Mode</a>

public/views/revision-modal.ejs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!-- revision modal -->
2+
<div class="modal fade" id="revisionModal" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
3+
<div class="modal-dialog modal-lg">
4+
<div class="modal-content">
5+
<div class="modal-header">
6+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
7+
</button>
8+
<h4 class="modal-title" id="mySmallModalLabel"><i class="fa fa-history"></i> Revision</h4>
9+
</div>
10+
<div class="modal-body">
11+
<div class="row">
12+
<div class="col-lg-4" style="max-height: calc(100vh - 215px); overflow: auto;">
13+
<div class="list-group ui-revision-list"></div>
14+
</div>
15+
<div class="col-lg-8" style="height: calc(100vh - 215px); overflow: hidden;">
16+
<textarea id="revisionViewer" style="display:none;"></textarea>
17+
</div>
18+
</div>
19+
</div>
20+
<div class="modal-footer">
21+
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
22+
<button type="button" class="btn btn-primary" id="revisionModalDownload">Download</button>
23+
<button type="button" class="btn btn-danger" id="revisionModalRevert">Revert</button>
24+
</div>
25+
</div>
26+
</div>
27+
</div>

0 commit comments

Comments
 (0)