X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/d6739c1158e486b02885cd010f70ab4494a0404c..refs/pull/84/head:/resources/assets/js/controllers.js diff --git a/resources/assets/js/controllers.js b/resources/assets/js/controllers.js index 5d8990754..dbd2e1ae6 100644 --- a/resources/assets/js/controllers.js +++ b/resources/assets/js/controllers.js @@ -1,9 +1,10 @@ "use strict"; -module.exports = function (ngApp) { +module.exports = function (ngApp, events) { ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService', function ($scope, $attrs, $http, $timeout, imageManagerService) { + $scope.images = []; $scope.imageType = $attrs.imageType; $scope.selectedImage = false; @@ -12,26 +13,47 @@ module.exports = function (ngApp) { $scope.hasMore = false; $scope.imageUpdateSuccess = false; $scope.imageDeleteSuccess = false; + $scope.uploadedTo = $attrs.uploadedTo; + var page = 0; var previousClickTime = 0; var dataLoaded = false; var callback = false; + /** + * Simple returns the appropriate upload url depending on the image type set. + * @returns {string} + */ $scope.getUploadUrl = function () { return '/images/' + $scope.imageType + '/upload'; }; + /** + * Runs on image upload, Adds an image to local list of images + * and shows a success message to the user. + * @param file + * @param data + */ $scope.uploadSuccess = function (file, data) { $scope.$apply(() => { $scope.images.unshift(data); }); + events.emit('success', 'Image uploaded'); }; + /** + * Runs the callback and hides the image manager. + * @param returnData + */ function callbackAndHide(returnData) { if (callback) callback(returnData); $scope.showing = false; } + /** + * Image select action. Checks if a double-click was fired. + * @param image + */ $scope.imageSelect = function (image) { var dblClickTime = 300; var currentTime = Date.now(); @@ -48,10 +70,19 @@ module.exports = function (ngApp) { previousClickTime = currentTime; }; + /** + * Action that runs when the 'Select image' button is clicked. + * Runs the callback and hides the image manager. + */ $scope.selectButtonClick = function () { callbackAndHide($scope.selectedImage); }; + /** + * Show the image manager. + * Takes a callback to execute later on. + * @param doneCallback + */ function show(doneCallback) { callback = doneCallback; $scope.showing = true; @@ -62,6 +93,8 @@ module.exports = function (ngApp) { } } + // Connects up the image manger so it can be used externally + // such as from TinyMCE. imageManagerService.show = show; imageManagerService.showExternal = function (doneCallback) { $scope.$apply(() => { @@ -70,10 +103,16 @@ module.exports = function (ngApp) { }; window.ImageManager = imageManagerService; + /** + * Hide the image manager + */ $scope.hide = function () { $scope.showing = false; }; + /** + * Fetch the list image data from the server. + */ function fetchData() { var url = '/images/' + $scope.imageType + '/all/' + page; $http.get(url).then((response) => { @@ -83,27 +122,37 @@ module.exports = function (ngApp) { }); } + $scope.fetchData = fetchData; + + /** + * Save the details of an image. + * @param event + */ $scope.saveImageDetails = function (event) { event.preventDefault(); var url = '/images/update/' + $scope.selectedImage.id; $http.put(url, this.selectedImage).then((response) => { - $scope.imageUpdateSuccess = true; - $timeout(() => { - $scope.imageUpdateSuccess = false; - }, 3000); + events.emit('success', 'Image details updated'); }, (response) => { - var errors = response.data; - var message = ''; - Object.keys(errors).forEach((key) => { - message += errors[key].join('\n'); - }); - $scope.imageUpdateFailure = message; - $timeout(() => { - $scope.imageUpdateFailure = false; - }, 5000); + if (response.status === 422) { + var errors = response.data; + var message = ''; + Object.keys(errors).forEach((key) => { + message += errors[key].join('\n'); + }); + events.emit('error', message); + } else if (response.status === 403) { + events.emit('error', response.data.error); + } }); }; + /** + * Delete an image from system and notify of success. + * Checks if it should force delete when an image + * has dependant pages. + * @param event + */ $scope.deleteImage = function (event) { event.preventDefault(); var force = $scope.dependantPages !== false; @@ -112,18 +161,26 @@ module.exports = function (ngApp) { $http.delete(url).then((response) => { $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1); $scope.selectedImage = false; - $scope.imageDeleteSuccess = true; - $timeout(() => { - $scope.imageDeleteSuccess = false; - }, 3000); + events.emit('success', 'Image successfully deleted'); }, (response) => { // Pages failure if (response.status === 400) { $scope.dependantPages = response.data; + } else if (response.status === 403) { + events.emit('error', response.data.error); } }); }; + /** + * Simple date creator used to properly format dates. + * @param stringDate + * @returns {Date} + */ + $scope.getDate = function (stringDate) { + return new Date(stringDate); + }; + }]); @@ -159,4 +216,115 @@ module.exports = function (ngApp) { }]); + ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce', + function ($scope, $http, $attrs, $interval, $timeout, $sce) { + + $scope.editorOptions = require('./pages/page-form'); + $scope.editContent = ''; + $scope.draftText = ''; + var pageId = Number($attrs.pageId); + var isEdit = pageId !== 0; + var autosaveFrequency = 30; // AutoSave interval in seconds. + var isMarkdown = $attrs.editorType === 'markdown'; + $scope.isUpdateDraft = Number($attrs.pageUpdateDraft) === 1; + $scope.isNewPageDraft = Number($attrs.pageNewDraft) === 1; + + // Set inital header draft text + if ($scope.isUpdateDraft || $scope.isNewPageDraft) { + $scope.draftText = 'Editing Draft' + } else { + $scope.draftText = 'Editing Page' + }; + + var autoSave = false; + + var currentContent = { + title: false, + html: false + }; + + if (isEdit) { + setTimeout(() => { + startAutoSave(); + }, 1000); + } + + // Actions specifically for the markdown editor + if (isMarkdown) { + $scope.displayContent = ''; + // Editor change event + $scope.editorChange = function (content) { + $scope.displayContent = $sce.trustAsHtml(content); + } + } + + if (!isMarkdown) { + $scope.editorChange = function() {}; + } + + /** + * Start the AutoSave loop, Checks for content change + * before performing the costly AJAX request. + */ + function startAutoSave() { + currentContent.title = $('#name').val(); + currentContent.html = $scope.editContent; + + autoSave = $interval(() => { + var newTitle = $('#name').val(); + var newHtml = $scope.editContent; + + if (newTitle !== currentContent.title || newHtml !== currentContent.html) { + currentContent.html = newHtml; + currentContent.title = newTitle; + saveDraft(); + } + + }, 1000 * autosaveFrequency); + } + + /** + * Save a draft update into the system via an AJAX request. + * @param title + * @param html + */ + function saveDraft() { + var data = { + name: $('#name').val(), + html: isMarkdown ? $sce.getTrustedHtml($scope.displayContent) : $scope.editContent + }; + + if (isMarkdown) data.markdown = $scope.editContent; + + $http.put('/ajax/page/' + pageId + '/save-draft', data).then((responseData) => { + $scope.draftText = responseData.data.message; + if (!$scope.isNewPageDraft) $scope.isUpdateDraft = true; + }); + } + + $scope.forceDraftSave = function() { + saveDraft(); + }; + + /** + * Discard the current draft and grab the current page + * content from the system via an AJAX request. + */ + $scope.discardDraft = function () { + $http.get('/ajax/page/' + pageId).then((responseData) => { + if (autoSave) $interval.cancel(autoSave); + $scope.draftText = 'Editing Page'; + $scope.isUpdateDraft = false; + $scope.$broadcast('html-update', responseData.data.html); + $scope.$broadcast('markdown-update', responseData.data.markdown || responseData.data.html); + $('#name').val(responseData.data.name); + $timeout(() => { + startAutoSave(); + }, 1000); + events.emit('success', 'Draft discarded, The editor has been updated with the current page content'); + }); + }; + + }]); + }; \ No newline at end of file