| /* |
| * 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 moment from 'moment'; |
| |
| import {isParagraphRunning} from './paragraph/paragraph.status'; |
| |
| angular.module('zeppelinWebApp').controller('NotebookCtrl', NotebookCtrl); |
| |
| function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope, |
| $http, websocketMsgSrv, baseUrlSrv, $timeout, saveAsService, |
| ngToast, noteActionService, noteVarShareService, TRASH_FOLDER_ID, |
| heliumService) { |
| 'ngInject'; |
| |
| ngToast.dismiss(); |
| |
| $scope.note = null; |
| $scope.actionOnFormSelectionChange = true; |
| $scope.hideForms = false; |
| $scope.disableForms = false; |
| $scope.editorToggled = false; |
| $scope.tableToggled = false; |
| $scope.viewOnly = false; |
| $scope.showSetting = false; |
| $scope.showRevisionsComparator = false; |
| $scope.collaborativeMode = false; |
| $scope.collaborativeModeUsers = []; |
| $scope.looknfeelOption = ['default', 'simple', 'report']; |
| $scope.noteFormTitle = null; |
| $scope.cronOption = [ |
| {name: 'None', value: undefined}, |
| {name: '1m', value: '0 0/1 * * * ?'}, |
| {name: '5m', value: '0 0/5 * * * ?'}, |
| {name: '1h', value: '0 0 0/1 * * ?'}, |
| {name: '3h', value: '0 0 0/3 * * ?'}, |
| {name: '6h', value: '0 0 0/6 * * ?'}, |
| {name: '12h', value: '0 0 0/12 * * ?'}, |
| {name: '1d', value: '0 0 0 * * ?'}, |
| ]; |
| |
| $scope.formatRevisionDate = function(date) { |
| return moment.unix(date).format('MMMM Do YYYY, h:mm a'); |
| }; |
| |
| $scope.interpreterSettings = []; |
| $scope.interpreterBindings = []; |
| $scope.isNoteDirty = null; |
| $scope.saveTimer = null; |
| $scope.paragraphWarningDialog = {}; |
| |
| let connectedOnce = false; |
| let isRevisionPath = function(path) { |
| let pattern = new RegExp('^.*\/notebook\/[a-zA-Z0-9_]*\/revision\/[a-zA-Z0-9_]*'); |
| return pattern.test(path); |
| }; |
| |
| $scope.noteRevisions = []; |
| $scope.currentRevision = 'Head'; |
| $scope.revisionView = isRevisionPath($location.path()); |
| |
| $scope.search = { |
| searchText: '', |
| occurrencesExists: false, |
| needHighlightFirst: false, |
| occurrencesHidden: false, |
| replaceText: '', |
| needToSendNextOccurrenceAfterReplace: false, |
| occurrencesCount: 0, |
| currentOccurrence: 0, |
| searchBoxOpened: false, |
| searchBoxWidth: 350, |
| left: '0px', |
| }; |
| let currentSearchParagraph = 0; |
| |
| $scope.$watch('note', function(value) { |
| let title; |
| if (value) { |
| title = value.name.substr(value.name.lastIndexOf('/') + 1, value.name.length); |
| title += ' - Zeppelin'; |
| } else { |
| title = 'Zeppelin'; |
| } |
| $rootScope.pageTitle = title; |
| }, true); |
| |
| $scope.$on('setConnectedStatus', function(event, param) { |
| if (connectedOnce && param) { |
| initNotebook(); |
| } |
| connectedOnce = true; |
| }); |
| |
| $scope.addEvent = function(config) { |
| let removeEventByID = function(id) { |
| let events = jQuery._data(config.element, 'events')[config.eventType]; |
| if (!events) { |
| return; |
| } |
| for (let i=0; i < events.length; i++) { |
| if (events[i].data && events[i].data.eventID === id) { |
| events.splice(i, 1); |
| i--; |
| } |
| } |
| }; |
| |
| removeEventByID(config.eventID); |
| angular.element(config.element).bind(config.eventType, {eventID: config.eventID}, config.handler); |
| angular.element(config.onDestroyElement).scope().$on('$destroy', () => { |
| removeEventByID(config.eventID); |
| }); |
| }; |
| |
| $scope.getCronOptionNameFromValue = function(value) { |
| if (!value) { |
| return ''; |
| } |
| |
| for (let o in $scope.cronOption) { |
| if ($scope.cronOption[o].value === value) { |
| return $scope.cronOption[o].name; |
| } |
| } |
| return value; |
| }; |
| |
| $scope.blockAnonUsers = function() { |
| let zeppelinVersion = $rootScope.zeppelinVersion; |
| let url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/security/notebook_authorization.html'; |
| let content = 'Only authenticated user can set the permission.' + |
| '<a data-toggle="tooltip" data-placement="top" title="Learn more" target="_blank" href=' + url + '>' + |
| '<i class="icon-question" />' + |
| '</a>'; |
| BootstrapDialog.show({ |
| closable: false, |
| closeByBackdrop: false, |
| closeByKeyboard: false, |
| title: 'No permission', |
| message: content, |
| buttons: [{ |
| label: 'Close', |
| action: function(dialog) { |
| dialog.close(); |
| }, |
| }], |
| }); |
| }; |
| |
| /** Init the new controller */ |
| const initNotebook = function() { |
| noteVarShareService.clear(); |
| if ($routeParams.revisionId) { |
| websocketMsgSrv.getNoteByRevision($routeParams.noteId, $routeParams.revisionId); |
| } else { |
| websocketMsgSrv.getNote($routeParams.noteId); |
| } |
| websocketMsgSrv.listRevisionHistory($routeParams.noteId); |
| let currentRoute = $route.current; |
| if (currentRoute) { |
| setTimeout( |
| function() { |
| let routeParams = currentRoute.params; |
| let $id = angular.element('#' + routeParams.paragraph + '_container'); |
| |
| if ($id.length > 0) { |
| // adjust for navbar |
| let top = $id.offset().top - 103; |
| angular.element('html, body').scrollTo({top: top, left: 0}); |
| } |
| }, |
| 1000 |
| ); |
| } |
| }; |
| |
| initNotebook(); |
| |
| $scope.focusParagraphOnClick = function(clickEvent) { |
| if (!$scope.note) { |
| return; |
| } |
| for (let i = 0; i < $scope.note.paragraphs.length; i++) { |
| let paragraphId = $scope.note.paragraphs[i].id; |
| if (jQuery.contains(angular.element('#' + paragraphId + '_container')[0], clickEvent.target)) { |
| $scope.$broadcast('focusParagraph', paragraphId, 0, null, true); |
| break; |
| } |
| } |
| }; |
| |
| // register mouseevent handler for focus paragraph |
| document.addEventListener('click', $scope.focusParagraphOnClick); |
| |
| let keyboardShortcut = function(keyEvent) { |
| // handle keyevent |
| if (!$scope.viewOnly && !$scope.revisionView) { |
| $scope.$broadcast('keyEvent', keyEvent); |
| } |
| }; |
| |
| $scope.keydownEvent = function(keyEvent) { |
| if ((keyEvent.ctrlKey || keyEvent.metaKey) && String.fromCharCode(keyEvent.which).toLowerCase() === 's') { |
| keyEvent.preventDefault(); |
| } |
| |
| keyboardShortcut(keyEvent); |
| }; |
| |
| // register mouseevent handler for focus paragraph |
| document.addEventListener('keydown', $scope.keydownEvent); |
| |
| $scope.paragraphOnDoubleClick = function(paragraphId) { |
| $scope.$broadcast('doubleClickParagraph', paragraphId); |
| }; |
| |
| // Move the note to trash and go back to the main page |
| $scope.moveNoteToTrash = function(noteId) { |
| noteActionService.moveNoteToTrash(noteId, true); |
| }; |
| |
| // Remove the note permanently if it's in the trash |
| $scope.removeNote = function(noteId) { |
| noteActionService.removeNote(noteId, true); |
| }; |
| |
| $scope.isTrash = function(note) { |
| return note && note.path ? note.path.split('/')[1] === TRASH_FOLDER_ID : false; |
| }; |
| |
| // Export notebook |
| let limit = 0; |
| |
| websocketMsgSrv.listConfigurations(); |
| $scope.$on('configurationsInfo', function(scope, event) { |
| limit = event.configurations['zeppelin.websocket.max.text.message.size']; |
| }); |
| |
| $scope.exportNote = function() { |
| let jsonContent = JSON.stringify($scope.note); |
| if (jsonContent.length > limit) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: 'Note size exceeds importable limit (' + limit + ')', |
| message: 'Do you still want to export this note?', |
| callback: function(result) { |
| if (result) { |
| saveAsService.saveAs(jsonContent, $scope.note.name, 'json'); |
| } |
| }, |
| }); |
| } else { |
| saveAsService.saveAs(jsonContent, $scope.note.name, 'json'); |
| } |
| }; |
| |
| // Clone note |
| $scope.cloneNote = function(noteId) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Do you want to clone this note?', |
| callback: function(result) { |
| if (result) { |
| websocketMsgSrv.cloneNote(noteId); |
| $location.path('/'); |
| } |
| }, |
| }); |
| }; |
| |
| // checkpoint/commit notebook |
| $scope.checkpointNote = function(commitMessage) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Commit note to current repository?', |
| callback: function(result) { |
| if (result) { |
| websocketMsgSrv.checkpointNote($routeParams.noteId, commitMessage); |
| } |
| }, |
| }); |
| document.getElementById('note.checkpoint.message').value = ''; |
| }; |
| |
| // set notebook head to given revision |
| $scope.setNoteRevision = function() { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Set notebook head to current revision?', |
| callback: function(result) { |
| if (result) { |
| websocketMsgSrv.setNoteRevision($routeParams.noteId, $routeParams.revisionId); |
| } |
| }, |
| }); |
| }; |
| |
| $scope.preVisibleRevisionsComparator = function() { |
| $scope.mergeNoteRevisionsForCompare = null; |
| $scope.firstNoteRevisionForCompare = null; |
| $scope.secondNoteRevisionForCompare = null; |
| $scope.currentFirstRevisionForCompare = 'Choose...'; |
| $scope.currentSecondRevisionForCompare = 'Choose...'; |
| $scope.$apply(); |
| }; |
| |
| $scope.$on('listRevisionHistory', function(event, data) { |
| console.debug('received list of revisions %o', data); |
| $scope.noteRevisions = data.revisionList; |
| if ($scope.noteRevisions) { |
| if ($scope.noteRevisions.length === 0 || $scope.noteRevisions[0].id !== 'Head') { |
| $scope.noteRevisions.splice(0, 0, { |
| id: 'Head', |
| message: 'Head', |
| }); |
| } |
| if ($routeParams.revisionId) { |
| let index = _.findIndex($scope.noteRevisions, |
| {'id': $routeParams.revisionId}); |
| if (index > -1) { |
| $scope.currentRevision = $scope.noteRevisions[index].message; |
| } |
| } |
| } |
| }); |
| |
| $scope.$on('noteRevision', function(event, data) { |
| console.log('received note revision %o', data); |
| if (data.note) { |
| $scope.note = data.note; |
| initializeLookAndFeel(); |
| } else { |
| $location.path('/'); |
| } |
| }); |
| |
| $scope.$on('setNoteRevisionResult', function(event, data) { |
| console.log('received set note revision result %o', data); |
| if (data.status) { |
| $location.path('/notebook/' + $routeParams.noteId); |
| } |
| }); |
| |
| $scope.visitRevision = function(revision) { |
| if (revision.id) { |
| if (revision.id === 'Head') { |
| $location.path('/notebook/' + $routeParams.noteId); |
| } else { |
| $location.path('/notebook/' + $routeParams.noteId + '/revision/' + revision.id); |
| } |
| } else { |
| ngToast.danger({content: 'There is a problem with this Revision', |
| verticalPosition: 'top', |
| dismissOnTimeout: false, |
| }); |
| } |
| }; |
| |
| $scope.runAllParagraphs = function(noteId) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Run all paragraphs?', |
| callback: function(result) { |
| if (result) { |
| const paragraphs = $scope.note.paragraphs.map((p) => { |
| return { |
| id: p.id, |
| title: p.title, |
| paragraph: p.text, |
| config: p.config, |
| params: p.settings.params, |
| }; |
| }); |
| websocketMsgSrv.runAllParagraphs(noteId, paragraphs); |
| } |
| }, |
| }); |
| }; |
| |
| $scope.saveNote = function() { |
| if ($scope.note && $scope.note.paragraphs) { |
| _.forEach($scope.note.paragraphs, function(par) { |
| angular |
| .element('#' + par.id + '_paragraphColumn_main') |
| .scope() |
| .saveParagraph(par); |
| }); |
| $scope.isNoteDirty = null; |
| } |
| }; |
| |
| $scope.clearAllParagraphOutput = function(noteId) { |
| noteActionService.clearAllParagraphOutput(noteId); |
| }; |
| |
| $scope.toggleAllEditor = function() { |
| if ($scope.editorToggled) { |
| $scope.$broadcast('openEditor'); |
| } else { |
| $scope.$broadcast('closeEditor'); |
| } |
| $scope.editorToggled = !$scope.editorToggled; |
| }; |
| |
| $scope.showAllEditor = function() { |
| $scope.$broadcast('openEditor'); |
| }; |
| |
| $scope.hideAllEditor = function() { |
| $scope.$broadcast('closeEditor'); |
| }; |
| |
| $scope.toggleAllTable = function() { |
| if ($scope.tableToggled) { |
| $scope.$broadcast('openTable'); |
| } else { |
| $scope.$broadcast('closeTable'); |
| } |
| $scope.tableToggled = !$scope.tableToggled; |
| }; |
| |
| $scope.showAllTable = function() { |
| $scope.$broadcast('openTable'); |
| }; |
| |
| $scope.hideAllTable = function() { |
| $scope.$broadcast('closeTable'); |
| }; |
| |
| /** |
| * @returns {boolean} true if one more paragraphs are running. otherwise return false. |
| */ |
| $scope.isNoteRunning = function() { |
| if (!$scope.note) { |
| return false; |
| } |
| |
| for (let i = 0; i < $scope.note.paragraphs.length; i++) { |
| if (isParagraphRunning($scope.note.paragraphs[i])) { |
| return true; |
| } |
| } |
| |
| return false; |
| }; |
| |
| $scope.killSaveTimer = function() { |
| if ($scope.saveTimer) { |
| $timeout.cancel($scope.saveTimer); |
| $scope.saveTimer = null; |
| } |
| }; |
| |
| $scope.startSaveTimer = function() { |
| $scope.killSaveTimer(); |
| $scope.isNoteDirty = true; |
| // console.log('startSaveTimer called ' + $scope.note.id); |
| $scope.saveTimer = $timeout(function() { |
| $scope.saveNote(); |
| }, 10000); |
| }; |
| |
| $scope.setLookAndFeel = function(looknfeel) { |
| $scope.note.config.looknfeel = looknfeel; |
| if ($scope.revisionView === true) { |
| $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel); |
| } else { |
| $scope.setConfig(); |
| } |
| }; |
| |
| $scope.setNoteFormTitle = function(noteFormTitle) { |
| $scope.note.config.noteFormTitle = noteFormTitle; |
| $scope.setConfig(); |
| }; |
| |
| /** Set cron expression for this note **/ |
| $scope.setCronScheduler = function(cronExpr) { |
| if (cronExpr) { |
| if (!$scope.note.config.cronExecutingUser) { |
| $scope.note.config.cronExecutingUser = $rootScope.ticket.principal; |
| } |
| if (!$scope.note.config.cronExecutingRoles) { |
| $scope.note.config.cronExecutingRoles = $rootScope.ticket.roles; |
| } |
| } else { |
| $scope.note.config.cronExecutingUser = ''; |
| $scope.note.config.cronExecutingRoles = ''; |
| } |
| $scope.note.config.cron = cronExpr; |
| $scope.setConfig(); |
| }; |
| |
| /** Set release resource for this note **/ |
| $scope.setReleaseResource = function(value) { |
| $scope.note.config.releaseresource = value; |
| $scope.setConfig(); |
| }; |
| |
| /** Update note config **/ |
| $scope.setConfig = function(config) { |
| if (config) { |
| $scope.note.config = config; |
| } |
| websocketMsgSrv.updateNote($scope.note.id, $scope.note.name, $scope.note.config); |
| }; |
| |
| /** Update the note name */ |
| $scope.updateNoteName = function(newName) { |
| const trimmedNewName = newName.trim(); |
| if (trimmedNewName.length > 0 && $scope.note.name !== trimmedNewName) { |
| $scope.note.name = trimmedNewName; |
| websocketMsgSrv.renameNote($scope.note.id, $scope.note.name, true); |
| } |
| }; |
| |
| const initializeLookAndFeel = function() { |
| if (!$scope.note.config.looknfeel) { |
| $scope.note.config.looknfeel = 'default'; |
| } else { |
| $scope.viewOnly = $scope.note.config.looknfeel === 'report' ? true : false; |
| } |
| |
| if ($scope.note.paragraphs && $scope.note.paragraphs[0]) { |
| $scope.note.paragraphs[0].focus = true; |
| } |
| $rootScope.$broadcast('setLookAndFeel', $scope.note.config.looknfeel); |
| }; |
| |
| let cleanParagraphExcept = function(paragraphId, note) { |
| let noteCopy = {}; |
| noteCopy.id = note.id; |
| noteCopy.name = note.name; |
| noteCopy.config = note.config; |
| noteCopy.info = note.info; |
| noteCopy.paragraphs = []; |
| for (let i = 0; i < note.paragraphs.length; i++) { |
| if (note.paragraphs[i].id === paragraphId) { |
| noteCopy.paragraphs[0] = note.paragraphs[i]; |
| if (!noteCopy.paragraphs[0].config) { |
| noteCopy.paragraphs[0].config = {}; |
| } |
| noteCopy.paragraphs[0].config.editorHide = true; |
| noteCopy.paragraphs[0].config.tableHide = false; |
| break; |
| } |
| } |
| return noteCopy; |
| }; |
| |
| let addPara = function(paragraph, index) { |
| $scope.note.paragraphs.splice(index, 0, paragraph); |
| $scope.note.paragraphs.map((para) => { |
| if (para.id === paragraph.id) { |
| para.focus = true; |
| |
| // we need `$timeout` since angular DOM might not be initialized |
| $timeout(() => { |
| $scope.$broadcast('focusParagraph', para.id, 0, null, false); |
| }); |
| } |
| }); |
| }; |
| |
| let removePara = function(paragraphId) { |
| let removeIdx; |
| _.each($scope.note.paragraphs, function(para, idx) { |
| if (para.id === paragraphId) { |
| removeIdx = idx; |
| } |
| }); |
| return $scope.note.paragraphs.splice(removeIdx, 1); |
| }; |
| |
| $scope.$on('addParagraph', function(event, paragraph, index) { |
| if ($scope.paragraphUrl || $scope.revisionView === true) { |
| return; |
| } |
| addPara(paragraph, index); |
| }); |
| |
| $scope.$on('removeParagraph', function(event, paragraphId) { |
| if ($scope.paragraphUrl || $scope.revisionView === true) { |
| return; |
| } |
| removePara(paragraphId); |
| }); |
| |
| $scope.$on('moveParagraph', function(event, paragraphId, newIdx) { |
| if ($scope.revisionView === true) { |
| return; |
| } |
| let removedPara = removePara(paragraphId); |
| if (removedPara && removedPara.length === 1) { |
| addPara(removedPara[0], newIdx); |
| } |
| }); |
| |
| $scope.$on('updateNote', function(event, name, config, info) { |
| /** update Note name */ |
| if (name !== $scope.note.name) { |
| console.log('change note name to : %o', $scope.note.name); |
| $scope.note.name = name; |
| } |
| $scope.note.config = config; |
| $scope.note.info = info; |
| initializeLookAndFeel(); |
| }); |
| |
| let getInterpreterBindings = function() { |
| websocketMsgSrv.getInterpreterBindings($scope.note.id); |
| }; |
| |
| $scope.$on('interpreterBindings', function(event, data) { |
| $scope.interpreterBindings = data.interpreterBindings; |
| $scope.interpreterBindingsOrig = angular.copy($scope.interpreterBindings); // to check dirty |
| |
| let selected = false; |
| let key; |
| let setting; |
| |
| for (key in $scope.interpreterBindings) { |
| if($scope.interpreterBindings.hasOwnProperty(key)) { |
| setting = $scope.interpreterBindings[key]; |
| if (setting.selected) { |
| selected = true; |
| break; |
| } |
| } |
| } |
| |
| if (!selected) { |
| // make default selection |
| let selectedIntp = {}; |
| for (key in $scope.interpreterBindings) { |
| if ($scope.interpreterBindings.hasOwnProperty(key)) { |
| setting = $scope.interpreterBindings[key]; |
| if (!selectedIntp[setting.name]) { |
| setting.selected = true; |
| selectedIntp[setting.name] = true; |
| } |
| } |
| } |
| $scope.showSetting = true; |
| } |
| }); |
| |
| $scope.interpreterSelectionListeners = { |
| accept: function(sourceItemHandleScope, destSortableScope) { |
| return true; |
| }, |
| itemMoved: function(event) {}, |
| orderChanged: function(event) {}, |
| }; |
| |
| $scope.closeAdditionalBoards = function() { |
| $scope.closeSetting(); |
| $scope.closePermissions(); |
| $scope.closeRevisionsComparator(); |
| }; |
| |
| $scope.openSetting = function() { |
| $scope.showSetting = true; |
| getInterpreterBindings(); |
| }; |
| |
| $scope.closeSetting = function() { |
| if (isSettingDirty()) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Interpreter setting changes will be discarded.', |
| callback: function(result) { |
| if (result) { |
| $scope.$apply(function() { |
| $scope.showSetting = false; |
| }); |
| } |
| }, |
| }); |
| } else { |
| $scope.showSetting = false; |
| } |
| }; |
| |
| $scope.saveSetting = function() { |
| let selectedSettingIds = []; |
| for (let no in $scope.interpreterBindings) { |
| if ($scope.interpreterBindings.hasOwnProperty(no)) { |
| let setting = $scope.interpreterBindings[no]; |
| if (setting.selected) { |
| selectedSettingIds.push(setting.id); |
| } |
| } |
| } |
| websocketMsgSrv.saveInterpreterBindings($scope.note.id, selectedSettingIds); |
| console.log('Interpreter bindings %o saved', selectedSettingIds); |
| |
| _.forEach($scope.note.paragraphs, function(n, key) { |
| let regExp = /^\s*%/g; |
| if (n.text && !regExp.exec(n.text)) { |
| $scope.$broadcast('saveInterpreterBindings', n.id); |
| } |
| }); |
| |
| $scope.showSetting = false; |
| }; |
| |
| $scope.toggleSetting = function() { |
| if ($scope.showSetting) { |
| $scope.closeSetting(); |
| } else { |
| $scope.closeAdditionalBoards(); |
| $scope.openSetting(); |
| 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'); |
| } |
| }; |
| |
| let getPermissions = function(callback) { |
| $http.get(baseUrlSrv.getRestApiBase() + '/notebook/' + $scope.note.id + '/permissions') |
| .success(function(data, status, headers, config) { |
| $scope.permissions = data.body; |
| $scope.permissionsOrig = angular.copy($scope.permissions); // to check dirty |
| |
| let selectJson = { |
| tokenSeparators: [',', ' '], |
| ajax: { |
| url: function(params) { |
| if (!params.term) { |
| return false; |
| } |
| return baseUrlSrv.getRestApiBase() + '/security/userlist/' + params.term; |
| }, |
| delay: 250, |
| processResults: function(data, params) { |
| let results = []; |
| |
| if (data.body.users.length !== 0) { |
| let users = []; |
| for (let len = 0; len < data.body.users.length; len++) { |
| users.push({ |
| 'id': data.body.users[len], |
| 'text': data.body.users[len], |
| }); |
| } |
| results.push({ |
| 'text': 'Users :', |
| 'children': users, |
| }); |
| } |
| if (data.body.roles.length !== 0) { |
| let roles = []; |
| for (let len = 0; len < data.body.roles.length; len++) { |
| roles.push({ |
| 'id': data.body.roles[len], |
| 'text': data.body.roles[len], |
| }); |
| } |
| results.push({ |
| 'text': 'Roles :', |
| 'children': roles, |
| }); |
| } |
| return { |
| results: results, |
| pagination: { |
| more: false, |
| }, |
| }; |
| }, |
| cache: false, |
| }, |
| width: ' ', |
| tags: true, |
| minimumInputLength: 3, |
| }; |
| |
| $scope.setMyPermissions(); |
| angular.element('#selectOwners').select2(selectJson); |
| angular.element('#selectReaders').select2(selectJson); |
| angular.element('#selectRunners').select2(selectJson); |
| angular.element('#selectWriters').select2(selectJson); |
| if (callback) { |
| callback(); |
| } |
| }) |
| .error(function(data, status, headers, config) { |
| if (status !== 0) { |
| console.log('Error %o %o', status, data.message); |
| } |
| }); |
| }; |
| |
| $scope.openPermissions = function() { |
| $scope.showPermissions = true; |
| getPermissions(); |
| }; |
| |
| $scope.closePermissions = function() { |
| if (isPermissionsDirty()) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Changes will be discarded.', |
| callback: function(result) { |
| if (result) { |
| $scope.$apply(function() { |
| $scope.showPermissions = false; |
| }); |
| } |
| }, |
| }); |
| } else { |
| $scope.showPermissions = false; |
| } |
| }; |
| |
| function convertPermissionsToArray() { |
| $scope.permissions.owners = angular.element('#selectOwners').val(); |
| $scope.permissions.readers = angular.element('#selectReaders').val(); |
| $scope.permissions.runners = angular.element('#selectRunners').val(); |
| $scope.permissions.writers = angular.element('#selectWriters').val(); |
| angular.element('.permissionsForm select').find('option:not([is-select2="false"])').remove(); |
| } |
| |
| $scope.hasMatches = function() { |
| return $scope.search.occurrencesCount > 0; |
| }; |
| |
| const markAllOccurrences = function() { |
| $scope.search.occurrencesCount = 0; |
| $scope.search.occurrencesHidden = false; |
| currentSearchParagraph = 0; |
| $scope.$broadcast('markAllOccurrences', $scope.search.searchText); |
| $scope.search.currentOccurrence = $scope.search.occurrencesCount > 0 ? 1 : 0; |
| }; |
| |
| $scope.markAllOccurrencesAndHighlightFirst = function() { |
| $scope.search.needHighlightFirst = true; |
| markAllOccurrences(); |
| }; |
| |
| const increaseCurrentOccurence = function() { |
| ++$scope.search.currentOccurrence; |
| if ($scope.search.currentOccurrence > $scope.search.occurrencesCount) { |
| $scope.search.currentOccurrence = 1; |
| } |
| }; |
| |
| const decreaseCurrentOccurence = function() { |
| --$scope.search.currentOccurrence; |
| if ($scope.search.currentOccurrence === 0) { |
| $scope.search.currentOccurrence = $scope.search.occurrencesCount; |
| } |
| }; |
| |
| const sendNextOccurrenceMessage = function() { |
| if ($scope.search.occurrencesCount === 0) { |
| markAllOccurrences(); |
| if ($scope.search.occurrencesCount === 0) { |
| return; |
| } |
| } |
| if ($scope.search.occurrencesHidden) { |
| markAllOccurrences(); |
| } |
| $scope.$broadcast('nextOccurrence', $scope.note.paragraphs[currentSearchParagraph].id); |
| }; |
| |
| const sendPrevOccurrenceMessage = function() { |
| if ($scope.search.occurrencesCount === 0) { |
| markAllOccurrences(); |
| if ($scope.search.occurrencesCount === 0) { |
| return; |
| } |
| } |
| if ($scope.search.occurrencesHidden) { |
| markAllOccurrences(); |
| currentSearchParagraph = $scope.note.paragraphs.length - 1; |
| } |
| $scope.$broadcast('prevOccurrence', $scope.note.paragraphs[currentSearchParagraph].id); |
| }; |
| |
| const increaseCurrentSearchParagraph = function() { |
| ++currentSearchParagraph; |
| if (currentSearchParagraph >= $scope.note.paragraphs.length) { |
| currentSearchParagraph = 0; |
| } |
| }; |
| |
| const decreaseCurrentSearchParagraph = function() { |
| --currentSearchParagraph; |
| if (currentSearchParagraph === -1) { |
| currentSearchParagraph = $scope.note.paragraphs.length - 1; |
| } |
| }; |
| |
| $scope.$on('occurrencesExists', function(event, count) { |
| $scope.search.occurrencesCount += count; |
| if ($scope.search.needHighlightFirst) { |
| sendNextOccurrenceMessage(); |
| $scope.search.needHighlightFirst = false; |
| } |
| }); |
| |
| $scope.nextOccurrence = function() { |
| sendNextOccurrenceMessage(); |
| increaseCurrentOccurence(); |
| }; |
| |
| $scope.$on('noNextOccurrence', function(event) { |
| increaseCurrentSearchParagraph(); |
| sendNextOccurrenceMessage(); |
| }); |
| |
| $scope.prevOccurrence = function() { |
| sendPrevOccurrenceMessage(); |
| decreaseCurrentOccurence(); |
| }; |
| |
| $scope.$on('noPrevOccurrence', function(event) { |
| decreaseCurrentSearchParagraph(); |
| sendPrevOccurrenceMessage(); |
| }); |
| |
| $scope.$on('editorClicked', function() { |
| $scope.search.occurrencesHidden = true; |
| $scope.$broadcast('unmarkAll'); |
| }); |
| |
| $scope.replace = function() { |
| if ($scope.search.occurrencesCount === 0) { |
| $scope.markAllOccurrencesAndHighlightFirst(); |
| if ($scope.search.occurrencesCount === 0) { |
| return; |
| } |
| } |
| if ($scope.search.occurrencesHidden) { |
| $scope.markAllOccurrencesAndHighlightFirst(); |
| return; |
| } |
| $scope.$broadcast('replaceCurrent', $scope.search.searchText, $scope.search.replaceText); |
| if ($scope.search.needToSendNextOccurrenceAfterReplace) { |
| sendNextOccurrenceMessage(); |
| $scope.search.needToSendNextOccurrenceAfterReplace = false; |
| } |
| }; |
| |
| $scope.$on('occurrencesCountChanged', function(event, cnt) { |
| $scope.search.occurrencesCount += cnt; |
| if ($scope.search.occurrencesCount === 0) { |
| $scope.search.currentOccurrence = 0; |
| } else { |
| $scope.search.currentOccurrence += cnt + 1; |
| if ($scope.search.currentOccurrence > $scope.search.occurrencesCount) { |
| $scope.search.currentOccurrence = 1; |
| } |
| } |
| }); |
| |
| $scope.replaceAll = function() { |
| if ($scope.search.occurrencesCount === 0) { |
| return; |
| } |
| if ($scope.search.occurrencesHidden) { |
| $scope.markAllOccurrencesAndHighlightFirst(); |
| } |
| $scope.$broadcast('replaceAll', $scope.search.searchText, $scope.search.replaceText); |
| $scope.markAllOccurrencesAndHighlightFirst(); |
| }; |
| |
| $scope.$on('noNextOccurrenceAfterReplace', function() { |
| $scope.search.occurrencesCount = 0; |
| $scope.search.needHighlightFirst = false; |
| $scope.search.needToSendNextOccurrenceAfterReplace = false; |
| $scope.$broadcast('checkOccurrences'); |
| increaseCurrentSearchParagraph(); |
| if ($scope.search.occurrencesCount > 0) { |
| $scope.search.needToSendNextOccurrenceAfterReplace = true; |
| } |
| }); |
| |
| $scope.onPressOnFindInput = function(event) { |
| if (event.keyCode === 13) { |
| $scope.nextOccurrence(); |
| } |
| }; |
| |
| let makeSearchBoxVisible = function() { |
| if ($scope.search.searchBoxOpened) { |
| $scope.search.searchBoxOpened = false; |
| console.log('make 0'); |
| $scope.search.left = '0px'; |
| } else { |
| $scope.search.searchBoxOpened = true; |
| let searchGroupRect = angular.element('#searchGroup')[0].getBoundingClientRect(); |
| console.log('make visible'); |
| let dropdownRight = searchGroupRect.left + $scope.search.searchBoxWidth; |
| console.log(dropdownRight + ' ' + window.innerWidth); |
| if (dropdownRight + 5 > window.innerWidth) { |
| $scope.search.left = window.innerWidth - dropdownRight - 15 + 'px'; |
| } |
| } |
| }; |
| |
| $scope.searchClicked = function() { |
| makeSearchBoxVisible(); |
| }; |
| |
| $scope.$on('toggleSearchBox', function() { |
| let elem = angular.element('#searchGroup'); |
| if ($scope.search.searchBoxOpened) { |
| elem.removeClass('open'); |
| } else { |
| elem.addClass('open'); |
| } |
| $timeout(makeSearchBoxVisible()); |
| }); |
| |
| $scope.restartInterpreter = function(interpreter) { |
| const thisConfirm = BootstrapDialog.confirm({ |
| closable: false, |
| closeByBackdrop: false, |
| closeByKeyboard: false, |
| title: '', |
| message: 'Do you want to restart ' + _.escape(interpreter.name) + ' interpreter?', |
| callback: function(result) { |
| if (result) { |
| let payload = { |
| 'noteId': $scope.note.id, |
| }; |
| |
| thisConfirm.$modalFooter.find('button').addClass('disabled'); |
| thisConfirm.$modalFooter.find('button:contains("OK")') |
| .html('<i class="fa fa-circle-o-notch fa-spin"></i> Saving Setting'); |
| |
| $http.put(baseUrlSrv.getRestApiBase() + '/interpreter/setting/restart/' + interpreter.id, payload) |
| .success(function(data, status, headers, config) { |
| let index = _.findIndex($scope.interpreterSettings, {'id': interpreter.id}); |
| $scope.interpreterSettings[index] = data.body; |
| thisConfirm.close(); |
| }).error(function(data, status, headers, config) { |
| thisConfirm.close(); |
| console.log('Error %o %o', status, data.message); |
| BootstrapDialog.show({ |
| title: 'Error restart interpreter.', |
| message: _.escape(data.message), |
| }); |
| }); |
| return false; |
| } |
| }, |
| }); |
| }; |
| |
| $scope.savePermissions = function() { |
| if ($scope.isAnonymous || $rootScope.ticket.principal.trim().length === 0) { |
| $scope.blockAnonUsers(); |
| } |
| convertPermissionsToArray(); |
| if ($scope.isOwnerEmpty()) { |
| BootstrapDialog.show({ |
| closable: false, |
| title: 'Setting Owners Permissions', |
| message: 'Please fill the [Owners] field. If not, it will set as current user.\n\n' + |
| 'Current user : [ ' + _.escape($rootScope.ticket.principal) + ']', |
| buttons: [ |
| { |
| label: 'Set', |
| action: function(dialog) { |
| dialog.close(); |
| $scope.permissions.owners = [$rootScope.ticket.principal]; |
| $scope.setPermissions(); |
| }, |
| }, |
| { |
| label: 'Cancel', |
| action: function(dialog) { |
| dialog.close(); |
| $scope.openPermissions(); |
| }, |
| }, |
| ], |
| }); |
| } else { |
| $scope.setPermissions(); |
| } |
| }; |
| |
| $scope.setPermissions = function() { |
| $http.put(baseUrlSrv.getRestApiBase() + '/notebook/' + $scope.note.id + '/permissions', |
| $scope.permissions, {withCredentials: true}) |
| .success(function(data, status, headers, config) { |
| getPermissions(function() { |
| console.log('Note permissions %o saved', $scope.permissions); |
| BootstrapDialog.alert({ |
| closable: true, |
| title: 'Permissions Saved Successfully', |
| message: 'Owners : ' + _.escape($scope.permissions.owners) |
| + '\n\n' + |
| 'Readers : ' + _.escape($scope.permissions.readers) + |
| '\n\n' + |
| 'Runners : ' + _.escape($scope.permissions.runners) + |
| '\n\n' + |
| 'Writers : ' + _.escape($scope.permissions.writers), |
| }); |
| $scope.showPermissions = false; |
| }); |
| }) |
| .error(function(data, status, headers, config) { |
| console.log('Error %o %o', status, data.message); |
| BootstrapDialog.show({ |
| closable: false, |
| closeByBackdrop: false, |
| closeByKeyboard: false, |
| title: 'Insufficient privileges', |
| message: _.escape(data.message), |
| buttons: [ |
| { |
| label: 'Login', |
| action: function(dialog) { |
| dialog.close(); |
| angular.element('#loginModal').modal({ |
| show: 'true', |
| }); |
| }, |
| }, |
| { |
| label: 'Cancel', |
| action: function(dialog) { |
| dialog.close(); |
| $location.path('/'); |
| }, |
| }, |
| ], |
| }); |
| }); |
| }; |
| |
| $scope.togglePermissions = function() { |
| let principal = $rootScope.ticket.principal; |
| $scope.isAnonymous = principal === 'anonymous' ? true : false; |
| if (!!principal && $scope.isAnonymous) { |
| $scope.blockAnonUsers(); |
| } else { |
| if ($scope.showPermissions) { |
| $scope.closePermissions(); |
| angular.element('#selectOwners').select2({}); |
| angular.element('#selectReaders').select2({}); |
| angular.element('#selectRunners').select2({}); |
| angular.element('#selectWriters').select2({}); |
| } else { |
| $scope.closeAdditionalBoards(); |
| $scope.openPermissions(); |
| } |
| } |
| }; |
| |
| const arrayIntersection = function(arrayFirst, arraySecond) { |
| return arrayFirst.filter(function(x) { |
| if(arraySecond.indexOf(x) !== -1) { |
| return true; |
| } else { |
| return false; |
| } |
| }); |
| }; |
| |
| $scope.setMyPermissions = function() { |
| let myPermissions; |
| try { |
| myPermissions = JSON.parse($rootScope.ticket.roles); |
| } catch(err) { |
| myPermissions = []; |
| } |
| myPermissions.push($rootScope.ticket.principal); |
| |
| $scope.isOwner = !($scope.permissions.owners.length > 0 && |
| arrayIntersection(myPermissions, $scope.permissions.owners).length === 0); |
| |
| $scope.isWriter = !($scope.permissions.writers.length > 0 && |
| arrayIntersection(myPermissions, $scope.permissions.writers).length === 0); |
| |
| if (!$scope.isOwner && !$scope.isWriter) { |
| $scope.viewOnly = true; |
| $scope.note.config.looknfeel = 'report'; |
| initializeLookAndFeel(); |
| } |
| }; |
| |
| $scope.toggleNotePersonalizedMode = function() { |
| let personalizedMode = $scope.note.config.personalizedMode; |
| if ($scope.isOwner) { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: 'Setting the result display', |
| message: function(dialog) { |
| let modeText = $scope.note.config.personalizedMode === 'true' ? 'collaborate' : 'personalize'; |
| return 'Do you want to <span class="text-info">' + modeText + '</span> your analysis?'; |
| }, |
| callback: function(result) { |
| if (result) { |
| if ($scope.note.config.personalizedMode === undefined) { |
| $scope.note.config.personalizedMode = 'false'; |
| } |
| $scope.note.config.personalizedMode = personalizedMode === 'true' ? 'false' : 'true'; |
| websocketMsgSrv.updatePersonalizedMode($scope.note.id, $scope.note.config.personalizedMode); |
| } |
| }, |
| }); |
| } |
| }; |
| |
| const isSettingDirty = function() { |
| // if (angular.equals($scope.interpreterBindings, $scope.interpreterBindingsOrig)) { |
| // return false; |
| // } else { |
| return false; |
| // } |
| }; |
| |
| const isPermissionsDirty = function() { |
| if (angular.equals($scope.permissions, $scope.permissionsOrig)) { |
| return false; |
| } else { |
| return true; |
| } |
| }; |
| |
| angular.element(document).click(function() { |
| angular.element('.ace_autocomplete').hide(); |
| }); |
| |
| $scope.isOwnerEmpty = function() { |
| if ($scope.permissions.owners.length > 0) { |
| for (let i = 0; i < $scope.permissions.owners.length; i++) { |
| if ($scope.permissions.owners[i].trim().length > 0) { |
| return false; |
| } else if (i === $scope.permissions.owners.length - 1) { |
| return true; |
| } |
| } |
| } else { |
| return true; |
| } |
| }; |
| |
| /* |
| ** $scope.$on functions below |
| */ |
| |
| $scope.$on('runAllAbove', function(event, paragraph, isNeedConfirm) { |
| let allParagraphs = $scope.note.paragraphs; |
| let toRunParagraphs = []; |
| |
| for (let i = 0; allParagraphs[i] !== paragraph; i++) { |
| if (i === allParagraphs.length - 1) { |
| return; |
| } // if paragraph not in array of all paragraphs |
| toRunParagraphs.push(allParagraphs[i]); |
| } |
| |
| const paragraphs = toRunParagraphs.map((p) => { |
| return { |
| id: p.id, |
| title: p.title, |
| paragraph: p.text, |
| config: p.config, |
| params: p.settings.params, |
| }; |
| }); |
| |
| if (!isNeedConfirm) { |
| websocketMsgSrv.runAllParagraphs($scope.note.id, paragraphs); |
| } else { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Run all above?', |
| callback: function(result) { |
| if (result) { |
| websocketMsgSrv.runAllParagraphs($scope.note.id, paragraphs); |
| } |
| }, |
| }); |
| } |
| |
| $scope.saveCursorPosition(paragraph); |
| }); |
| |
| $scope.$on('collaborativeModeStatus', function(event, data) { |
| $scope.collaborativeMode = Boolean(data.status); |
| $scope.collaborativeModeUsers = data.users; |
| }); |
| |
| $scope.$on('patchReceived', function(event, data) { |
| $scope.collaborativeMode = true; |
| }); |
| |
| |
| $scope.$on('runAllBelowAndCurrent', function(event, paragraph, isNeedConfirm) { |
| let allParagraphs = $scope.note.paragraphs; |
| let toRunParagraphs = []; |
| |
| for (let i = allParagraphs.length - 1; allParagraphs[i] !== paragraph; i--) { |
| if (i < 0) { |
| return; |
| } // if paragraph not in array of all paragraphs |
| toRunParagraphs.push(allParagraphs[i]); |
| } |
| |
| toRunParagraphs.push(paragraph); |
| toRunParagraphs.reverse(); |
| |
| const paragraphs = toRunParagraphs.map((p) => { |
| return { |
| id: p.id, |
| title: p.title, |
| paragraph: p.text, |
| config: p.config, |
| params: p.settings.params, |
| }; |
| }); |
| |
| if (!isNeedConfirm) { |
| websocketMsgSrv.runAllParagraphs($scope.note.id, paragraphs); |
| } else { |
| BootstrapDialog.confirm({ |
| closable: true, |
| title: '', |
| message: 'Run current and all below?', |
| callback: function(result) { |
| if (result) { |
| websocketMsgSrv.runAllParagraphs($scope.note.id, paragraphs); |
| } |
| }, |
| }); |
| } |
| |
| $scope.saveCursorPosition(paragraph); |
| }); |
| |
| $scope.saveCursorPosition = function(paragraph) { |
| let angParagEditor = angular |
| .element('#' + paragraph.id + '_paragraphColumn_main') |
| .scope().editor; |
| let col = angParagEditor.selection.lead.column; |
| let row = angParagEditor.selection.lead.row; |
| $scope.$broadcast('focusParagraph', paragraph.id, row + 1, col); |
| }; |
| |
| $scope.$on('moveParagraphUp', function(event, paragraph) { |
| let newIndex = -1; |
| for (let i = 0; i < $scope.note.paragraphs.length; i++) { |
| if ($scope.note.paragraphs[i].id === paragraph.id) { |
| newIndex = i - 1; |
| break; |
| } |
| } |
| if (newIndex < 0 || newIndex >= $scope.note.paragraphs.length) { |
| return; |
| } |
| // save dirtyText of moving paragraphs. |
| let prevParagraph = $scope.note.paragraphs[newIndex]; |
| angular |
| .element('#' + paragraph.id + '_paragraphColumn_main') |
| .scope() |
| .saveParagraph(paragraph); |
| angular |
| .element('#' + prevParagraph.id + '_paragraphColumn_main') |
| .scope() |
| .saveParagraph(prevParagraph); |
| websocketMsgSrv.moveParagraph(paragraph.id, newIndex); |
| }); |
| |
| $scope.$on('moveParagraphDown', function(event, paragraph) { |
| let newIndex = -1; |
| for (let i = 0; i < $scope.note.paragraphs.length; i++) { |
| if ($scope.note.paragraphs[i].id === paragraph.id) { |
| newIndex = i + 1; |
| break; |
| } |
| } |
| |
| if (newIndex < 0 || newIndex >= $scope.note.paragraphs.length) { |
| return; |
| } |
| // save dirtyText of moving paragraphs. |
| let nextParagraph = $scope.note.paragraphs[newIndex]; |
| angular |
| .element('#' + paragraph.id + '_paragraphColumn_main') |
| .scope() |
| .saveParagraph(paragraph); |
| angular |
| .element('#' + nextParagraph.id + '_paragraphColumn_main') |
| .scope() |
| .saveParagraph(nextParagraph); |
| websocketMsgSrv.moveParagraph(paragraph.id, newIndex); |
| }); |
| |
| $scope.$on('moveFocusToPreviousParagraph', function(event, currentParagraphId) { |
| let focus = false; |
| for (let i = $scope.note.paragraphs.length - 1; i >= 0; i--) { |
| if (focus === false) { |
| if ($scope.note.paragraphs[i].id === currentParagraphId) { |
| focus = true; |
| continue; |
| } |
| } else { |
| $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, -1); |
| break; |
| } |
| } |
| }); |
| |
| $scope.$on('moveFocusToNextParagraph', function(event, currentParagraphId) { |
| let focus = false; |
| for (let i = 0; i < $scope.note.paragraphs.length; i++) { |
| if (focus === false) { |
| if ($scope.note.paragraphs[i].id === currentParagraphId) { |
| focus = true; |
| continue; |
| } |
| } else { |
| $scope.$broadcast('focusParagraph', $scope.note.paragraphs[i].id, 0); |
| break; |
| } |
| } |
| }); |
| |
| $scope.$on('insertParagraph', function(event, paragraphId, position) { |
| if ($scope.revisionView === true) { |
| return; |
| } |
| let newIndex = -1; |
| for (let i = 0; i < $scope.note.paragraphs.length; i++) { |
| if ($scope.note.paragraphs[i].id === paragraphId) { |
| // determine position of where to add new paragraph; default is below |
| if (position === 'above') { |
| newIndex = i; |
| } else { |
| newIndex = i + 1; |
| } |
| break; |
| } |
| } |
| |
| if (newIndex < 0 || newIndex > $scope.note.paragraphs.length) { |
| return; |
| } |
| websocketMsgSrv.insertParagraph(newIndex); |
| }); |
| |
| $scope.$on('setNoteContent', function(event, note) { |
| if (note === undefined) { |
| $location.path('/'); |
| } |
| |
| $scope.note = note; |
| |
| $scope.paragraphUrl = $routeParams.paragraphId; |
| $scope.asIframe = $routeParams.asIframe; |
| if ($scope.paragraphUrl) { |
| $scope.note = cleanParagraphExcept($scope.paragraphUrl, $scope.note); |
| $scope.$broadcast('$unBindKeyEvent', $scope.$unBindKeyEvent); |
| $rootScope.$broadcast('setIframe', $scope.asIframe); |
| initializeLookAndFeel(); |
| return; |
| } |
| |
| initializeLookAndFeel(); |
| |
| // open interpreter binding setting when there're none selected |
| getInterpreterBindings(); |
| getPermissions(); |
| let isPersonalized = $scope.note.config.personalizedMode; |
| isPersonalized = isPersonalized === undefined ? 'false' : isPersonalized; |
| $scope.note.config.personalizedMode = isPersonalized; |
| }); |
| |
| $scope.$on('$routeChangeStart', function(event, next, current) { |
| if (!$scope.note || !$scope.note.paragraphs) { |
| return; |
| } |
| if ($scope.note && $scope.note.paragraphs) { |
| $scope.note.paragraphs.map((par) => { |
| if ($scope.allowLeave === true) { |
| return; |
| } |
| let thisScope = angular.element( |
| '#' + par.id + '_paragraphColumn_main').scope(); |
| |
| if (thisScope.dirtyText === undefined || |
| thisScope.originalText === undefined || |
| thisScope.dirtyText === thisScope.originalText) { |
| return true; |
| } else { |
| event.preventDefault(); |
| $scope.showParagraphWarning(next); |
| } |
| }); |
| } |
| }); |
| |
| $scope.showParagraphWarning = function(next) { |
| if ($scope.paragraphWarningDialog.opened !== true) { |
| $scope.paragraphWarningDialog = BootstrapDialog.show({ |
| closable: false, |
| closeByBackdrop: false, |
| closeByKeyboard: false, |
| title: 'Do you want to leave this site?', |
| message: 'Changes that you have made will not be saved.', |
| buttons: [{ |
| label: 'Stay', |
| action: function(dialog) { |
| dialog.close(); |
| }, |
| }, { |
| label: 'Leave', |
| action: function(dialog) { |
| dialog.close(); |
| let locationToRedirect = next['$$route']['originalPath']; |
| Object.keys(next.pathParams).map((key) => { |
| locationToRedirect = locationToRedirect.replace(':' + key, |
| next.pathParams[key]); |
| }); |
| $scope.allowLeave = true; |
| $location.path(locationToRedirect); |
| }, |
| }], |
| }); |
| } |
| }; |
| |
| $scope.$on('saveNoteForms', function(event, data) { |
| $scope.note.noteForms = data.formsData.forms; |
| $scope.note.noteParams = data.formsData.params; |
| }); |
| |
| $scope.isShowNoteForms = function() { |
| if ($scope.note && !_.isEmpty($scope.note.noteForms) && !$scope.paragraphUrl) { |
| return true; |
| } |
| return false; |
| }; |
| |
| $scope.saveNoteForms = function() { |
| websocketMsgSrv.saveNoteForms($scope.note); |
| }; |
| |
| $scope.removeNoteForms = function(formName) { |
| websocketMsgSrv.removeNoteForms($scope.note, formName); |
| }; |
| |
| $scope.$on('$destroy', function() { |
| angular.element(window).off('beforeunload'); |
| $scope.killSaveTimer(); |
| $scope.saveNote(); |
| |
| document.removeEventListener('click', $scope.focusParagraphOnClick); |
| document.removeEventListener('keydown', $scope.keyboardShortcut); |
| }); |
| |
| $scope.$on('$unBindKeyEvent', function() { |
| document.removeEventListener('click', $scope.focusParagraphOnClick); |
| document.removeEventListener('keydown', $scope.keyboardShortcut); |
| }); |
| |
| let content = document.getElementById('content'); |
| if (content && content.id) { |
| $scope.addEvent({ |
| eventID: content.id, |
| eventType: 'resize', |
| element: window, |
| onDestroyElement: content, |
| handler: () => { |
| const actionbarHeight = document.getElementById('actionbar').lastElementChild.clientHeight; |
| angular.element(document.getElementById('content')).css('padding-top', actionbarHeight - 20); |
| }, |
| }); |
| } |
| } |