diff --git a/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_diff.png b/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_diff.png index 5c392a5b09..8c9cbe7238 100644 Binary files a/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_diff.png and b/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_diff.png differ diff --git a/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_empty.png b/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_empty.png index 82d7b78b61..dce34ae9b1 100644 Binary files a/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_empty.png and b/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_empty.png differ diff --git a/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_paragraph.png b/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_paragraph.png index d6900669ba..479cc13ff6 100644 Binary files a/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_paragraph.png and b/docs/assets/themes/zeppelin/img/docs-img/revisions_comparator_paragraph.png differ diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html index 491a48d9d7..0799858b20 100644 --- a/zeppelin-web/src/app/notebook/notebook-actionBar.html +++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html @@ -141,10 +141,7 @@ limitations under the License. diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 549a28af81..4b2a3da68d 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -31,6 +31,7 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, $scope.tableToggled = false $scope.viewOnly = false $scope.showSetting = false + $scope.showRevisionsComparator = false $scope.looknfeelOption = ['default', 'simple', 'report'] $scope.cronOption = [ {name: 'None', value: undefined}, @@ -59,11 +60,6 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, } $scope.noteRevisions = [] - $scope.firstNoteRevisionForCompare = null - $scope.secondNoteRevisionForCompare = null - $scope.mergeNoteRevisionsForCompare = null - $scope.currentFirstRevisionForCompare = 'Choose...' - $scope.currentSecondRevisionForCompare = 'Choose...' $scope.currentRevision = 'Head' $scope.revisionView = isRevisionPath($location.path()) @@ -294,99 +290,6 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, } } - // compare revisions - $scope.compareRevisions = function () { - if ($scope.firstNoteRevisionForCompare && $scope.secondNoteRevisionForCompare) { - let paragraphs1 = $scope.firstNoteRevisionForCompare.note.paragraphs - let paragraphs2 = $scope.secondNoteRevisionForCompare.note.paragraphs - let merge = { - added: [], - deleted: [], - compared: [] - } - for (let p1 of paragraphs1) { - let p2 = null - for (let p of paragraphs2) { - if (p1.id === p.id) { - p2 = p - break - } - } - if (p2 === null) { - merge.deleted.push({paragraph: p1, firstString: (p1.text || '').split('\n')[0]}) - } else { - let colorClass = '' - let span = null - let text1 = p1.text || '' - let text2 = p2.text || '' - - let diff = window.JsDiff.diffLines(text1, text2) - let diffHtml = document.createDocumentFragment() - let identical = true - let identicalClass = 'color-black' - - diff.forEach(function(part) { - colorClass = part.added ? 'color-green' : part.removed ? 'color-red' : identicalClass - span = document.createElement('span') - span.className = colorClass - if (identical && colorClass !== identicalClass) { - identical = false - } - span.appendChild(document.createTextNode(part.value)) - diffHtml.appendChild(span) - }) - - let pre = document.createElement('pre') - pre.appendChild(diffHtml) - - merge.compared.push( - {paragraph: p1, diff: pre.innerHTML, identical: identical, firstString: (p1.text || '').split('\n')[0]}) - } - } - - for (let p2 of paragraphs2) { - let p1 = null - for (let p of paragraphs1) { - if (p2.id === p.id) { - p1 = p - break - } - } - if (p1 === null) { - merge.added.push({paragraph: p2, firstString: (p2.text || '').split('\n')[0]}) - } - } - $scope.mergeNoteRevisionsForCompare = merge - } - } - - $scope.getNoteRevisionForReview = function (revision, position) { - if (position) { - if (position === 'first') { - $scope.currentFirstRevisionForCompare = revision.message - } else { - $scope.currentSecondRevisionForCompare = revision.message - } - websocketMsgSrv.getNoteByRevisionForCompare($routeParams.noteId, revision.id, position) - } - } - - $scope.$on('noteRevisionForCompare', function (event, data) { - console.debug('received note revision for compare %o', data) - if (data.note && data.position) { - if (data.position === 'first') { - $scope.firstNoteRevisionForCompare = data - } else { - $scope.secondNoteRevisionForCompare = data - } - - if ($scope.firstNoteRevisionForCompare !== null && $scope.secondNoteRevisionForCompare !== null && - $scope.firstNoteRevisionForCompare.revisionId !== $scope.secondNoteRevisionForCompare.revisionId) { - $scope.compareRevisions() - } - } - }) - $scope.runAllParagraphs = function (noteId) { BootstrapDialog.confirm({ closable: true, @@ -678,6 +581,12 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, orderChanged: function (event) {} } + $scope.closeAdditionalBoards = function() { + $scope.closeSetting() + $scope.closePermissions() + $scope.closeRevisionsComparator() + } + $scope.openSetting = function () { $scope.showSetting = true getInterpreterBindings() @@ -727,8 +636,26 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, if ($scope.showSetting) { $scope.closeSetting() } else { + $scope.closeAdditionalBoards() $scope.openSetting() - $scope.closePermissions() + angular.element('html, body').animate({ scrollTop: 0 }, 'slow') + } + } + + $scope.openRevisionsComparator = function () { + $scope.showRevisionsComparator = true + } + + $scope.closeRevisionsComparator = function () { + $scope.showRevisionsComparator = false + } + + $scope.toggleRevisionsComparator = function () { + if ($scope.showRevisionsComparator) { + $scope.closeRevisionsComparator() + } else { + $scope.closeAdditionalBoards() + $scope.openRevisionsComparator() angular.element('html, body').animate({ scrollTop: 0 }, 'slow') } } @@ -965,8 +892,8 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, angular.element('#selectReaders').select2({}) angular.element('#selectWriters').select2({}) } else { + $scope.closeAdditionalBoards() $scope.openPermissions() - $scope.closeSetting() } } } diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html index cfce36d543..4f86748505 100644 --- a/zeppelin-web/src/app/notebook/notebook.html +++ b/zeppelin-web/src/app/notebook/notebook.html @@ -13,8 +13,15 @@ limitations under the License. -->
-
+ +
+
+

Revisions comparator

+
+
+ +
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.css b/zeppelin-web/src/app/notebook/paragraph/paragraph.css index d0a362920c..8ecc7bba75 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.css +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.css @@ -270,117 +270,6 @@ table.table-shortcut { font-size: 10px !important; } -.revisions-comparator-modal-dialog { - width: 80%; - margin: 30px auto; - height: 80%; -} - -.revisions-comparator-modal-dialog .modal-content { - height: 100%; -} - -.revisions-comparator-modal-header { - min-height: 16.428571429px; - padding: 15px; - border-bottom: 1px solid #9cb4c5; - background-color: #3071a9; - border: 2px solid #3071a9; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -.revisions-comparator-modal-header .close { - color: #cfcfcf; - opacity: 1; -} - -.revisions-comparator-modal-title { - color: white; - margin-top: 2px; - margin-bottom: 2px; -} - -.revisions-comparator-code-panel { - display: inline-block; - width: 50%; - float: left; - height: 300px; - overflow-y: auto; - max-height: 300px -} - -.revisions-comparator-code-panel-title { - width: 50%; - float: left; - font-size: 14px; - padding: 5px; - background-color: #f5f5f5; -} - -.revisions-comparator-bar { - margin: 10px auto; - width: 400px; -} - -.revisions-comparator-status { - font-size: 12px; - padding-left: 10px; -} - -#diffPanel { - height: calc(100% - 60px); -} - -#diffPanel .panel-group { - height: inherit; - overflow: auto; -} - -.revision-name-for-compare { - cursor: default; - overflow: hidden; - display: inline-block; - max-width: 100px; - width: 100px; - padding: 1px 10px; -} - -.revisions-comparator-caret { - margin: 0 0 10px 6px; -} - -.revisions-comparator-link, .revisions-comparator-link:hover, -.revisions-comparator-link:visited, .revisions-comparator-link:focus { - text-decoration: none; - color: #000; -} - -.revisions-comparator-forst-string { - display: block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - font-size: 12px; - color: grey; -} - -.color-green { - color: green; -} - -.color-red { - color: red; -} - -.color-black { - color: black; -} - -.color-orange { - color: orange; -} - /* Paragraph Title */ diff --git a/zeppelin-web/src/app/notebook/revisions-comparator.html b/zeppelin-web/src/app/notebook/revisions-comparator.html deleted file mode 100644 index 1d02ac92cc..0000000000 --- a/zeppelin-web/src/app/notebook/revisions-comparator.html +++ /dev/null @@ -1,126 +0,0 @@ - - - diff --git a/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.component.js b/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.component.js new file mode 100644 index 0000000000..186658b789 --- /dev/null +++ b/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.component.js @@ -0,0 +1,159 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import revisionsComparatorTemplate from './revisions-comparator.html' +import './revisions-comparator.css' +import moment from 'moment' + +function RevisionsComparatorController($scope, websocketMsgSrv, $routeParams) { + 'ngInject' + + $scope.firstNoteRevisionForCompare = null + $scope.secondNoteRevisionForCompare = null + $scope.mergeNoteRevisionsForCompare = null + $scope.currentFirstRevisionForCompare = 'Choose...' + $scope.currentSecondRevisionForCompare = 'Choose...' + + $scope.getNoteRevisionForReview = function(revision, position) { + if (position) { + if (position === 'first') { + $scope.currentFirstRevisionForCompare = revision.message + } else { + $scope.currentSecondRevisionForCompare = revision.message + } + websocketMsgSrv.getNoteByRevisionForCompare($routeParams.noteId, revision.id, position) + } + } + + // compare revisions + $scope.compareRevisions = function() { + if ($scope.firstNoteRevisionForCompare && $scope.secondNoteRevisionForCompare) { + let paragraphs1 = $scope.firstNoteRevisionForCompare.note.paragraphs + let paragraphs2 = $scope.secondNoteRevisionForCompare.note.paragraphs + let added = 'added' + let deleted = 'deleted' + let compared = 'compared' + let merge = [] + for (let p1 of paragraphs1) { + let p2 = null + for (let p of paragraphs2) { + if (p1.id === p.id) { + p2 = p + break + } + } + if (p2 === null) { + merge.push({paragraph: p1, firstString: (p1.text || '').split('\n')[0], type: deleted}) + } else { + let colorClass = '' + let span = null + let text1 = p1.text || '' + let text2 = p2.text || '' + + let diff = window.JsDiff.diffLines(text1, text2) + let diffHtml = document.createDocumentFragment() + let identical = true + let identicalClass = 'color-black' + + diff.forEach(function (part) { + colorClass = part.added ? 'color-green' : part.removed ? 'color-red' : identicalClass + span = document.createElement('span') + span.className = colorClass + if (identical && colorClass !== identicalClass) { + identical = false + } + span.appendChild(document.createTextNode(part.value)) + diffHtml.appendChild(span) + }) + + let pre = document.createElement('pre') + pre.appendChild(diffHtml) + + merge.push( + { + paragraph: p1, + diff: pre.innerHTML, + identical: identical, + firstString: (p1.text || '').split('\n')[0], + type: compared + }) + } + } + + for (let p2 of paragraphs2) { + let p1 = null + for (let p of paragraphs1) { + if (p2.id === p.id) { + p1 = p + break + } + } + if (p1 === null) { + merge.push({paragraph: p2, firstString: (p2.text || '').split('\n')[0], type: added}) + } + } + + merge.sort(function(a, b) { + if (a.type === added) { + return -1 + } + if (a.type === compared) { + return 1 + } + if (a.type === deleted) { + if (b.type === compared) { + return -1 + } else { + return 1 + } + } + }) + + $scope.mergeNoteRevisionsForCompare = merge + } + } + + $scope.$on('noteRevisionForCompare', function (event, data) { + console.debug('received note revision for compare %o', data) + if (data.note && data.position) { + if (data.position === 'first') { + $scope.firstNoteRevisionForCompare = data + } else { + $scope.secondNoteRevisionForCompare = data + } + + if ($scope.firstNoteRevisionForCompare !== null && $scope.secondNoteRevisionForCompare !== null && + $scope.firstNoteRevisionForCompare.revisionId !== $scope.secondNoteRevisionForCompare.revisionId) { + $scope.compareRevisions() + } + } + }) + + $scope.formatRevisionDate = function(date) { + return moment.unix(date).format('MMMM Do YYYY, h:mm a') + } +} + +export const RevisionsComparatorComponent = { + template: revisionsComparatorTemplate, + controller: RevisionsComparatorController, + bindings: { + noteRevisions: '<' + } +} + +export const RevisionsComparatorModule = angular + .module('zeppelinWebApp') + .component('revisionsComparator', RevisionsComparatorComponent) + .name diff --git a/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.css b/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.css new file mode 100644 index 0000000000..4bf32afdc3 --- /dev/null +++ b/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.css @@ -0,0 +1,115 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.revisions-comparator { + background: white; + padding: 10px 15px 15px 15px; + margin-left: -10px; + margin-right: -10px; + font-family: 'Roboto', sans-serif; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); + border-bottom: 1px solid #E5E5E5; +} + +.revisions-comparator-panel-heading { + padding: 10px 0px; +} + +.revisions-comparator-code-panel { + display: inline-block; + width: 50%; + float: left; + height: 300px; + overflow-y: auto; + max-height: 300px +} + +.revisions-comparator-code-panel-title { + width: 50%; + float: left; + font-size: 14px; + padding: 5px; + background-color: #f5f5f5; +} + +.revisions-comparator-bar { + width: 400px; + padding-bottom: 10px; +} + +.revisions-comparator-status { + font-size: 12px; + padding-left: 10px; +} + +#diffPanel { + height: 100%; +} + +#diffPanel .panel-group { + height: inherit; + overflow: auto; +} + +.revision-name-for-compare { + cursor: default; + overflow: hidden; + vertical-align: bottom; + display: inline-block; + max-width: 100px; + padding: 1px 5px; +} + +.revisions-comparator-caret { + padding-bottom: 5px; +} + +.revisions-comparator-link, .revisions-comparator-link:hover, +.revisions-comparator-link:visited, .revisions-comparator-link:focus { + text-decoration: none; + outline: none; + color: #000; +} + +.revisions-comparator-first-string { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-size: 12px; + color: grey; +} + +.revisions-comparator-dropdown { + padding: 5px; + font-size: 12px; + border-radius: 3px; +} + +.color-green { + color: green; +} + +.color-red { + color: red; +} + +.color-black { + color: black; +} + +.color-orange { + color: orange; +} + diff --git a/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.html b/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.html new file mode 100644 index 0000000000..565d622852 --- /dev/null +++ b/zeppelin-web/src/app/notebook/revisions-comparator/revisions-comparator.html @@ -0,0 +1,87 @@ + + +
+
+ + +
+ compare with +
+ + +
+
+
+
+
+ +
+ Revision: {{currentFirstRevisionForCompare}} + Revision: {{currentSecondRevisionForCompare}} +

+        
{{p.paragraph.text}}
+
{{p.paragraph.text}}
+

+        
{{p.paragraph.text}}
+

+      
+
+
+
diff --git a/zeppelin-web/src/index.js b/zeppelin-web/src/index.js index 3cf052bba6..ed8f1d8f9c 100644 --- a/zeppelin-web/src/index.js +++ b/zeppelin-web/src/index.js @@ -42,6 +42,7 @@ import './app/interpreter/interpreter-item.directive.js' import './app/interpreter/widget/number-widget.directive.js' import './app/credential/credential.controller.js' import './app/configuration/configuration.controller.js' +import './app/notebook/revisions-comparator/revisions-comparator.component.js' import './app/notebook/paragraph/paragraph.controller.js' import './app/notebook/paragraph/clipboard.controller.js' import './app/notebook/paragraph/resizable.directive.js'