diff --git a/client/app/pages/queries/compare-query-dialog.css b/client/app/pages/queries/compare-query-dialog.css
new file mode 100644
index 0000000000..ce2d01370e
--- /dev/null
+++ b/client/app/pages/queries/compare-query-dialog.css
@@ -0,0 +1,54 @@
+/* Compare Query Version container */
+/* Offers slight visual improvement (alignment) to modern UAs */
+.compare-query-version {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.diff-removed {
+ background-color: rgba(208, 2, 27, 0.3);
+}
+
+.diff-added {
+ background-color: rgba(65, 117, 5, 0.3);
+}
+
+.query-diff-container span {
+ display: inline-block;
+ border-radius: 3px;
+ line-height: 20px;
+ vertical-align: middle;
+ margin: 0 5px 0 0;
+}
+
+.query-diff-container > div:not(.compare-query-version-controls) {
+ float: left;
+ width: calc(50% - 5px);
+ margin: 0 10px 0 0;
+}
+
+.compare-query-version {
+ background-color: #f5f5f5;
+ padding: 5px;
+ border: 1px solid #ccc;
+ margin-right: 15px;
+ border-radius: 3px;
+}
+
+.diff-content {
+ border: 1px solid #ccc;
+ background-color: #f5f5f5;
+ border-radius: 3px;
+ padding: 15px;
+}
+
+.query-diff-container > div:last-child {
+ margin: 0;
+}
+
+.compare-query-version-controls {
+ display: flex;
+ align-items: center;
+ margin-bottom: 25px;
+}
diff --git a/client/app/pages/queries/compare-query-dialog.html b/client/app/pages/queries/compare-query-dialog.html
new file mode 100644
index 0000000000..5214046055
--- /dev/null
+++ b/client/app/pages/queries/compare-query-dialog.html
@@ -0,0 +1,33 @@
+
+
+
+
+ Compare current version to
+
+
+
+
+
+
Current Version {{$ctrl.currentQuery.version}}
+
+
+
+ {{part.value}}
+
+
+
+
+
+
Previous Version {{$ctrl.previousQueryVersion + 1}}
+
+
{{$ctrl.previousQuery}}
+
+
+
diff --git a/client/app/pages/queries/compare-query-dialog.js b/client/app/pages/queries/compare-query-dialog.js
new file mode 100644
index 0000000000..e95e9cb8e1
--- /dev/null
+++ b/client/app/pages/queries/compare-query-dialog.js
@@ -0,0 +1,66 @@
+import * as jsDiff from 'diff';
+import template from './compare-query-dialog.html';
+import './compare-query-dialog.css';
+
+const CompareQueryDialog = {
+ controller(clientConfig, $http) {
+ 'ngInject';
+
+ this.currentQuery = this.resolve.query;
+ this.previousQuery = '';
+ this.currentDiff = [];
+ this.previousDiff = [];
+ this.versions = [];
+ this.previousQueryVersion = this.currentQuery.version - 2; // due to 0-indexed versions[]
+
+ this.compareQueries = (isInitialLoad) => {
+ if (!isInitialLoad) {
+ this.previousQueryVersion = document.getElementById('version-choice').value - 1; // due to 0-indexed versions[]
+ }
+
+ this.previousQuery = this.versions[this.previousQueryVersion].change.query.current;
+ this.currentDiff = jsDiff.diffChars(this.previousQuery, this.currentQuery.query);
+ document.querySelector('.compare-query-revert-wrapper').classList.remove('hidden');
+ };
+
+ this.revertQuery = () => {
+ this.resolve.query.query = this.previousQuery;
+ this.resolve.saveQuery();
+
+ // Close modal.
+ this.dismiss();
+ };
+
+ $http.get(`/api/queries/${this.currentQuery.id}/version`).then((response) => {
+ this.versions = response.data;
+
+ const compare = (a, b) => {
+ if (a.object_version < b.object_version) {
+ return -1;
+ } else if (a.object_version > b.object_version) {
+ return 1;
+ }
+ return 0;
+ };
+
+ this.versions.sort(compare);
+ this.compareQueries(true);
+ });
+ },
+ scope: {
+ query: '=',
+ saveQuery: '<',
+ },
+ bindings: {
+ resolve: '<',
+ close: '&',
+ dismiss: '&',
+ },
+ template,
+};
+
+export default function init(ngModule) {
+ ngModule.component('compareQueryDialog', CompareQueryDialog);
+}
+
+init.init = true;
diff --git a/client/app/pages/queries/query.html b/client/app/pages/queries/query.html
index a950b2b534..1744f9be79 100644
--- a/client/app/pages/queries/query.html
+++ b/client/app/pages/queries/query.html
@@ -69,6 +69,9 @@