Add revision modal with UIs and support to mark patch diff texts
This commit is contained in:
parent
8e351e7e33
commit
4d24476316
4 changed files with 178 additions and 2 deletions
|
@ -564,7 +564,8 @@ var ui = {
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
snippetImportProjects: $("#snippetImportModalProjects"),
|
snippetImportProjects: $("#snippetImportModalProjects"),
|
||||||
snippetImportSnippets: $("#snippetImportModalSnippets")
|
snippetImportSnippets: $("#snippetImportModalSnippets"),
|
||||||
|
revision: $("#revisionModal")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1382,6 +1383,149 @@ ui.toolbar.beta.pdf.attr("download", "").attr("href", noteurl + "/pdf");
|
||||||
ui.toolbar.beta.slide.attr("href", noteurl + "/slide");
|
ui.toolbar.beta.slide.attr("href", noteurl + "/slide");
|
||||||
|
|
||||||
//modal actions
|
//modal actions
|
||||||
|
var revisions = [];
|
||||||
|
var revisionViewer = null;
|
||||||
|
var revisionList = ui.modal.revision.find('.ui-revision-list');
|
||||||
|
var revision = null;
|
||||||
|
var revisionTime = null;
|
||||||
|
ui.modal.revision.on('show.bs.modal', function (e) {
|
||||||
|
$.get(noteurl + '/revision')
|
||||||
|
.success(function(data) {
|
||||||
|
parseRevisions(JSON.parse(data).revision);
|
||||||
|
initRevisionViewer();
|
||||||
|
})
|
||||||
|
.error(function(err) {
|
||||||
|
|
||||||
|
})
|
||||||
|
.complete(function() {
|
||||||
|
//na
|
||||||
|
});
|
||||||
|
});
|
||||||
|
function checkRevisionViewer() {
|
||||||
|
if (revisionViewer) {
|
||||||
|
var container = $(revisionViewer.display.wrapper).parent();
|
||||||
|
$(revisionViewer.display.scroller).css('height', container.height() + 'px');
|
||||||
|
revisionViewer.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.modal.revision.on('shown.bs.modal', checkRevisionViewer);
|
||||||
|
$(window).resize(checkRevisionViewer);
|
||||||
|
function parseRevisions(_revisions) {
|
||||||
|
if (_revisions.length != revisions) {
|
||||||
|
revisions = _revisions;
|
||||||
|
var lastRevision = null;
|
||||||
|
if (revisionList.children().length > 0) {
|
||||||
|
lastRevision = revisionList.find('.active').attr('data-revision-time');
|
||||||
|
}
|
||||||
|
revisionList.html('');
|
||||||
|
for (var i = 0; i < revisions.length; i++) {
|
||||||
|
var revision = revisions[i];
|
||||||
|
var item = $('<a href="#" class="list-group-item"></a>');
|
||||||
|
item.attr('data-revision-time', revision.time);
|
||||||
|
if (lastRevision == revision.time) item.addClass('active');
|
||||||
|
var itemHeading = $('<h5 class="list-group-item-heading"></h5>');
|
||||||
|
itemHeading.html('<i class="fa fa-clock-o"></i> ' + moment(revision.time).format('llll'));
|
||||||
|
var itemText = $('<p class="list-group-item-text"></p>');
|
||||||
|
itemText.html('<i class="fa fa-file-text"></i> Length: ' + revision.length);
|
||||||
|
item.append(itemHeading).append(itemText);
|
||||||
|
item.click(function (e) {
|
||||||
|
var time = $(this).attr('data-revision-time');
|
||||||
|
selectRevision(time);
|
||||||
|
});
|
||||||
|
revisionList.append(item);
|
||||||
|
}
|
||||||
|
if (!lastRevision) {
|
||||||
|
selectRevision(revisions[0].time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function selectRevision(time) {
|
||||||
|
if (time == revisionTime) return;
|
||||||
|
$.get(noteurl + '/revision/' + time)
|
||||||
|
.success(function(data) {
|
||||||
|
revision = JSON.parse(data);
|
||||||
|
revisionTime = time;
|
||||||
|
var lastScrollInfo = revisionViewer.getScrollInfo();
|
||||||
|
revisionList.children().removeClass('active');
|
||||||
|
revisionList.find('[data-revision-time="' + time + '"]').addClass('active');
|
||||||
|
var content = revision.content;
|
||||||
|
revisionViewer.setValue(content);
|
||||||
|
revisionViewer.scrollTo(null, lastScrollInfo.top);
|
||||||
|
// mark the text which have been insert or delete
|
||||||
|
if (revision.patch.length > 0) {
|
||||||
|
var bias = 0;
|
||||||
|
for (j = 0; j < revision.patch.length; j++) {
|
||||||
|
var patch = revision.patch[j];
|
||||||
|
var currIndex = patch.start1 + bias;
|
||||||
|
for (var i = 0; i < patch.diffs.length; i++) {
|
||||||
|
var diff = patch.diffs[i];
|
||||||
|
// ignore if diff only contains line breaks
|
||||||
|
if ((diff[1].match(new RegExp("\n", "g")) || []).length == diff[1].length) continue;
|
||||||
|
switch(diff[0]) {
|
||||||
|
case 0: // retain
|
||||||
|
currIndex += diff[1].length;
|
||||||
|
break;
|
||||||
|
case 1: // insert
|
||||||
|
var prePos = revisionViewer.posFromIndex(currIndex);
|
||||||
|
var postPos = revisionViewer.posFromIndex(currIndex + diff[1].length);
|
||||||
|
revisionViewer.markText(prePos, postPos, {
|
||||||
|
css: 'background-color: rgba(230,255,230,0.7); text-decoration: underline;'
|
||||||
|
});
|
||||||
|
currIndex += diff[1].length;
|
||||||
|
break;
|
||||||
|
case -1: // delete
|
||||||
|
var prePos = revisionViewer.posFromIndex(currIndex);
|
||||||
|
revisionViewer.replaceRange(diff[1], prePos);
|
||||||
|
var postPos = revisionViewer.posFromIndex(currIndex + diff[1].length);
|
||||||
|
revisionViewer.markText(prePos, postPos, {
|
||||||
|
css: 'background-color: rgba(255,230,230,0.7); text-decoration: line-through;'
|
||||||
|
});
|
||||||
|
bias += diff[1].length;
|
||||||
|
currIndex += diff[1].length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function(err) {
|
||||||
|
|
||||||
|
})
|
||||||
|
.complete(function() {
|
||||||
|
//na
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function initRevisionViewer() {
|
||||||
|
if (revisionViewer) return;
|
||||||
|
var revisionViewerTextArea = document.getElementById("revisionViewer");
|
||||||
|
revisionViewer = CodeMirror.fromTextArea(revisionViewerTextArea, {
|
||||||
|
mode: 'gfm',
|
||||||
|
viewportMargin: viewportMargin,
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: true,
|
||||||
|
showCursorWhenSelecting: true,
|
||||||
|
inputStyle: "textarea",
|
||||||
|
gutters: ["CodeMirror-linenumbers"],
|
||||||
|
flattenSpans: true,
|
||||||
|
addModeClass: true,
|
||||||
|
readOnly: true,
|
||||||
|
autoRefresh: true,
|
||||||
|
scrollbarStyle: 'overlay'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$('#revisionModalDownload').click(function () {
|
||||||
|
if (!revision) return;
|
||||||
|
var filename = renderFilename(ui.area.markdown) + '_' + revisionTime + '.md';
|
||||||
|
var blob = new Blob([revision.content], {
|
||||||
|
type: "text/markdown;charset=utf-8"
|
||||||
|
});
|
||||||
|
saveAs(blob, filename);
|
||||||
|
});
|
||||||
|
$('#revisionModalRevert').click(function () {
|
||||||
|
if (!revision) return;
|
||||||
|
editor.setValue(revision.content);
|
||||||
|
ui.modal.revision.modal('hide');
|
||||||
|
});
|
||||||
//snippet projects
|
//snippet projects
|
||||||
ui.modal.snippetImportProjects.change(function() {
|
ui.modal.snippetImportProjects.change(function() {
|
||||||
var accesstoken = $("#snippetImportModalAccessToken").val(),
|
var accesstoken = $("#snippetImportModalAccessToken").val(),
|
||||||
|
|
|
@ -236,3 +236,4 @@
|
||||||
</div>
|
</div>
|
||||||
<%- include signin-modal %>
|
<%- include signin-modal %>
|
||||||
<%- include help-modal %>
|
<%- include help-modal %>
|
||||||
|
<%- include revision-modal %>
|
|
@ -28,6 +28,8 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="dropdown-header">Beta</li>
|
<li class="dropdown-header">Beta</li>
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
<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>
|
<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>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<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 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="menu">
|
<ul class="dropdown-menu" role="menu" aria-labelledby="menu">
|
||||||
<li class="dropdown-header">Beta</li>
|
<li class="dropdown-header">Beta</li>
|
||||||
|
<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>
|
||||||
|
</li>
|
||||||
<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>
|
<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>
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<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>
|
||||||
|
|
27
public/views/revision-modal.ejs
Normal file
27
public/views/revision-modal.ejs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<!-- revision modal -->
|
||||||
|
<div class="modal fade" id="revisionModal" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title" id="mySmallModalLabel"><i class="fa fa-history"></i> Revision</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-4" style="max-height: calc(100vh - 215px); overflow: auto;">
|
||||||
|
<div class="list-group ui-revision-list"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-8" style="height: calc(100vh - 215px); overflow: hidden;">
|
||||||
|
<textarea id="revisionViewer" style="display:none;"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="revisionModalDownload">Download</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="revisionModalRevert">Revert</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
Reference in a new issue