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 @@
-
-
-
-
-
-
-
-
-
-
-
-
compare with
-
-
-
-
-
-
-
-
-
-
-
Revision: {{currentFirstRevisionForCompare}}
-
Revision: {{currentSecondRevisionForCompare}}
-
-
{{p.paragraph.text}}
-
-
-
-
-
-
Revision: {{currentFirstRevisionForCompare}}
-
Revision: {{currentSecondRevisionForCompare}}
-
{{p.paragraph.text}}
-
-
-
-
-
-
-
Revision: {{currentFirstRevisionForCompare}}
-
Diff with revision: {{currentSecondRevisionForCompare}}
-
{{p.paragraph.text}}
-
-
-
-
-
-
-
-
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'