]> BookStack Code Mirror - bookstack/commitdiff
Created solution for JS translations
authorDan Brown <redacted>
Sat, 31 Dec 2016 14:27:40 +0000 (14:27 +0000)
committerDan Brown <redacted>
Sat, 31 Dec 2016 14:27:40 +0000 (14:27 +0000)
Also tidied up existing components and JS

26 files changed:
app/Http/Controllers/AttachmentController.php
app/Http/Controllers/Controller.php
app/Http/Controllers/HomeController.php
resources/assets/js/controllers.js
resources/assets/js/directives.js
resources/assets/js/global.js
resources/assets/js/translations.js [new file with mode: 0644]
resources/assets/sass/_tables.scss
resources/lang/de/settings.php
resources/lang/en/components.php
resources/lang/en/entities.php
resources/lang/en/errors.php
resources/views/base.blade.php
resources/views/chapters/move.blade.php
resources/views/components/entity-selector-popup.blade.php [moved from resources/views/partials/entity-selector-popup.blade.php with 87% similarity]
resources/views/components/entity-selector.blade.php [moved from resources/views/partials/entity-selector.blade.php with 92% similarity]
resources/views/components/image-manager.blade.php [moved from resources/views/partials/image-manager.blade.php with 80% similarity]
resources/views/components/image-picker.blade.php
resources/views/components/toggle-switch.blade.php
resources/views/pages/edit.blade.php
resources/views/pages/move.blade.php
resources/views/partials/custom-styles.blade.php
resources/views/settings/index.blade.php
resources/views/settings/roles/index.blade.php
resources/views/users/edit.blade.php
routes/web.php

index a81cb8a68167182be8afeaae606ed9b3b31e133f..b5e7db41ee3aa89d76f37faee3eee3406c7dd056 100644 (file)
@@ -117,7 +117,7 @@ class AttachmentController extends Controller
         }
 
         $attachment = $this->attachmentService->updateFile($attachment, $request->all());
-        return $this->jsonSuccess($attachment, trans('entities.attachments_updated_success'));
+        return response()->json($attachment);
     }
 
     /**
index c5255c0ba53715436d09cf82c16b963ca201e8ad..2b6c88fe0b73748ab5cbdf61a64138dcdc4d5c49 100644 (file)
@@ -117,17 +117,6 @@ abstract class Controller extends BaseController
         return true;
     }
 
-    /**
-     * Send a json respons with a message attached as a header.
-     * @param $data
-     * @param string $successMessage
-     * @return $this
-     */
-    protected function jsonSuccess($data, $successMessage = "")
-    {
-        return response()->json($data)->header('message-success', $successMessage);
-    }
-
     /**
      * Send back a json error message.
      * @param string $messageText
index 2fc64b2368c9a59d3fa302b5f5d0ea44a6dc3fb9..e325b93229868c60bd7135068953f90b8603cd7d 100644 (file)
@@ -43,4 +43,39 @@ class HomeController extends Controller
         ]);
     }
 
+    /**
+     * Get a js representation of the current translations
+     * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
+     */
+    public function getTranslations() {
+        $locale = trans()->getLocale();
+        $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale;
+        if (cache()->has($cacheKey) && config('app.env') !== 'development') {
+            $resp = cache($cacheKey);
+        } else {
+            $translations = [
+                // Get only translations which might be used in JS
+                'common' => trans('common'),
+                'components' => trans('components'),
+                'entities' => trans('entities'),
+                'errors' => trans('errors')
+            ];
+            if ($locale !== 'en') {
+                $enTrans = [
+                    'common' => trans('common', [], null, 'en'),
+                    'components' => trans('components', [], null, 'en'),
+                    'entities' => trans('entities', [], null, 'en'),
+                    'errors' => trans('errors', [], null, 'en')
+                ];
+                $translations = array_replace_recursive($enTrans, $translations);
+            }
+            $resp = 'window.translations = ' . json_encode($translations);
+            cache()->put($cacheKey, $resp, 120);
+        }
+
+        return response($resp, 200, [
+            'Content-Type' => 'application/javascript'
+        ]);
+    }
+
 }
index af740f508e9585fa83daf971d328c96bc5392bba..0d57b09add8307f7284ceb9c417f7bd768751a1f 100644 (file)
@@ -64,7 +64,7 @@ export default function (ngApp, events) {
                 $scope.$apply(() => {
                     $scope.images.unshift(data);
                 });
-                events.emit('success', 'Image uploaded');
+                events.emit('success', trans('components.image_upload_success'));
             };
 
             /**
@@ -151,10 +151,9 @@ export default function (ngApp, events) {
                 if ($scope.searching) components['term'] = $scope.searchTerm;
 
 
-                let urlQueryString = Object.keys(components).map((key) => {
+                url += Object.keys(components).map((key) => {
                     return key + '=' + encodeURIComponent(components[key]);
                 }).join('&');
-                url += urlQueryString;
 
                 $http.get(url).then((response) => {
                     $scope.images = $scope.images.concat(response.data.images);
@@ -209,7 +208,7 @@ export default function (ngApp, events) {
                 event.preventDefault();
                 let url = window.baseUrl('/images/update/' + $scope.selectedImage.id);
                 $http.put(url, this.selectedImage).then(response => {
-                    events.emit('success', 'Image details updated');
+                    events.emit('success', trans('components.image_update_success'));
                 }, (response) => {
                     if (response.status === 422) {
                         let errors = response.data;
@@ -238,7 +237,7 @@ export default function (ngApp, events) {
                 $http.delete(url).then((response) => {
                     $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
                     $scope.selectedImage = false;
-                    events.emit('success', 'Image successfully deleted');
+                    events.emit('success', trans('components.image_delete_success'));
                 }, (response) => {
                     // Pages failure
                     if (response.status === 400) {
@@ -309,9 +308,9 @@ export default function (ngApp, events) {
 
         // Set initial header draft text
         if ($scope.isUpdateDraft || $scope.isNewPageDraft) {
-            $scope.draftText = 'Editing Draft'
+            $scope.draftText = trans('entities.pages_editing_draft');
         } else {
-            $scope.draftText = 'Editing Page'
+            $scope.draftText = trans('entities.pages_editing_page');
         }
 
         let autoSave = false;
@@ -388,7 +387,7 @@ export default function (ngApp, events) {
                 lastSave = Date.now();
             }, errorRes => {
                 if (draftErroring) return;
-                events.emit('error', 'Failed to save draft. Ensure you have internet connection before saving this page.')
+                events.emit('error', trans('errors.page_draft_autosave_fail'));
                 draftErroring = true;
             });
         }
@@ -421,7 +420,7 @@ export default function (ngApp, events) {
             let url = window.baseUrl('/ajax/page/' + pageId);
             $http.get(url).then((responseData) => {
                 if (autoSave) $interval.cancel(autoSave);
-                $scope.draftText = 'Editing Page';
+                $scope.draftText = trans('entities.pages_editing_page');
                 $scope.isUpdateDraft = false;
                 $scope.$broadcast('html-update', responseData.data.html);
                 $scope.$broadcast('markdown-update', responseData.data.markdown || responseData.data.html);
@@ -429,7 +428,7 @@ export default function (ngApp, events) {
                 $timeout(() => {
                     startAutoSave();
                 }, 1000);
-                events.emit('success', 'Draft discarded, The editor has been updated with the current page content');
+                events.emit('success', trans('entities.pages_draft_discarded'));
             });
         };
 
@@ -594,7 +593,7 @@ export default function (ngApp, events) {
                 $scope.$apply(() => {
                     $scope.files.push(data);
                 });
-                events.emit('success', 'File uploaded');
+                events.emit('success', trans('entities.attachments_file_uploaded'));
             };
 
             /**
@@ -612,7 +611,7 @@ export default function (ngApp, events) {
                         data.link = '';
                     }
                 });
-                events.emit('success', 'File updated');
+                events.emit('success', trans('entities.attachments_file_updated'));
             };
 
             /**
@@ -638,7 +637,7 @@ export default function (ngApp, events) {
                 file.uploaded_to = pageId;
                 $http.post(window.baseUrl('/attachments/link'), file).then(resp => {
                     $scope.files.push(resp.data);
-                    events.emit('success', 'Link attached');
+                    events.emit('success', trans('entities.attachments_link_attached'));
                     $scope.file = getCleanFile();
                 }, checkError('link'));
             };
@@ -672,7 +671,7 @@ export default function (ngApp, events) {
                         $scope.editFile.link = '';
                     }
                     $scope.editFile = false;
-                    events.emit('success', resp.headers('message-success'));
+                    events.emit('success', trans('entities.attachments_updated_success'));
                 }, checkError('edit'));
             };
 
index 68c3150e02a48fb4f6ee5c1829bc421cb41bc7be..ef8bcd85c6c5ee8e80111a6303e1e7b2a3984d6d 100644 (file)
@@ -102,7 +102,7 @@ export default function (ngApp, events) {
                                 $(file.previewElement).find('[data-dz-errormessage]').text(message);
                             }
 
-                            if (xhr.status === 413) setMessage('The server does not allow uploads of this size. Please try a smaller file.');
+                            if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
                             if (errorMessage.file) setMessage(errorMessage.file[0]);
 
                         });
@@ -261,15 +261,21 @@ export default function (ngApp, events) {
             link: function (scope, element, attrs) {
 
                 // Elements
-                const input = element.find('[markdown-input] textarea').first();
-                const display = element.find('.markdown-display').first();
-                const insertImage = element.find('button[data-action="insertImage"]');
-                const insertEntityLink = element.find('button[data-action="insertEntityLink"]')
+                const $input = element.find('[markdown-input] textarea').first();
+                const $display = element.find('.markdown-display').first();
+                const $insertImage = element.find('button[data-action="insertImage"]');
+                const $insertEntityLink = element.find('button[data-action="insertEntityLink"]');
+
+                // Prevent markdown display link click redirect
+                $display.on('click', 'a', function(event) {
+                    event.preventDefault();
+                    window.open(this.getAttribute('href'));
+                });
 
                 let currentCaretPos = 0;
 
-                input.blur(event => {
-                    currentCaretPos = input[0].selectionStart;
+                $input.blur(event => {
+                    currentCaretPos = $input[0].selectionStart;
                 });
 
                 // Scroll sync
@@ -279,10 +285,10 @@ export default function (ngApp, events) {
                     displayHeight;
 
                 function setScrollHeights() {
-                    inputScrollHeight = input[0].scrollHeight;
-                    inputHeight = input.height();
-                    displayScrollHeight = display[0].scrollHeight;
-                    displayHeight = display.height();
+                    inputScrollHeight = $input[0].scrollHeight;
+                    inputHeight = $input.height();
+                    displayScrollHeight = $display[0].scrollHeight;
+                    displayHeight = $display.height();
                 }
 
                 setTimeout(() => {
@@ -291,29 +297,29 @@ export default function (ngApp, events) {
                 window.addEventListener('resize', setScrollHeights);
                 let scrollDebounceTime = 800;
                 let lastScroll = 0;
-                input.on('scroll', event => {
+                $input.on('scroll', event => {
                     let now = Date.now();
                     if (now - lastScroll > scrollDebounceTime) {
                         setScrollHeights()
                     }
-                    let scrollPercent = (input.scrollTop() / (inputScrollHeight - inputHeight));
+                    let scrollPercent = ($input.scrollTop() / (inputScrollHeight - inputHeight));
                     let displayScrollY = (displayScrollHeight - displayHeight) * scrollPercent;
-                    display.scrollTop(displayScrollY);
+                    $display.scrollTop(displayScrollY);
                     lastScroll = now;
                 });
 
                 // Editor key-presses
-                input.keydown(event => {
+                $input.keydown(event => {
                     // Insert image shortcut
                     if (event.which === 73 && event.ctrlKey && event.shiftKey) {
                         event.preventDefault();
-                        let caretPos = input[0].selectionStart;
-                        let currentContent = input.val();
+                        let caretPos = $input[0].selectionStart;
+                        let currentContent = $input.val();
                         const mdImageText = "![](http://)";
-                        input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
-                        input.focus();
-                        input[0].selectionStart = caretPos + ("![](".length);
-                        input[0].selectionEnd = caretPos + ('![](http://'.length);
+                        $input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
+                        $input.focus();
+                        $input[0].selectionStart = caretPos + ("![](".length);
+                        $input[0].selectionEnd = caretPos + ('![](http://'.length);
                         return;
                     }
 
@@ -328,35 +334,35 @@ export default function (ngApp, events) {
                 });
 
                 // Insert image from image manager
-                insertImage.click(event => {
+                $insertImage.click(event => {
                     window.ImageManager.showExternal(image => {
                         let caretPos = currentCaretPos;
-                        let currentContent = input.val();
+                        let currentContent = $input.val();
                         let mdImageText = "![" + image.name + "](" + image.thumbs.display + ")";
-                        input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
-                        input.change();
+                        $input.val(currentContent.substring(0, caretPos) + mdImageText + currentContent.substring(caretPos));
+                        $input.change();
                     });
                 });
 
                 function showLinkSelector() {
                     window.showEntityLinkSelector((entity) => {
                         let selectionStart = currentCaretPos;
-                        let selectionEnd = input[0].selectionEnd;
+                        let selectionEnd = $input[0].selectionEnd;
                         let textSelected = (selectionEnd !== selectionStart);
-                        let currentContent = input.val();
+                        let currentContent = $input.val();
 
                         if (textSelected) {
                             let selectedText = currentContent.substring(selectionStart, selectionEnd);
                             let linkText = `[${selectedText}](${entity.link})`;
-                            input.val(currentContent.substring(0, selectionStart) + linkText + currentContent.substring(selectionEnd));
+                            $input.val(currentContent.substring(0, selectionStart) + linkText + currentContent.substring(selectionEnd));
                         } else {
                             let linkText = ` [${entity.name}](${entity.link}) `;
-                            input.val(currentContent.substring(0, selectionStart) + linkText + currentContent.substring(selectionStart))
+                            $input.val(currentContent.substring(0, selectionStart) + linkText + currentContent.substring(selectionStart))
                         }
-                        input.change();
+                        $input.change();
                     });
                 }
-                insertEntityLink.click(showLinkSelector);
+                $insertEntityLink.click(showLinkSelector);
 
                 // Upload and insert image on paste
                 function editorPaste(e) {
@@ -369,7 +375,7 @@ export default function (ngApp, events) {
                     }
                 }
 
-                input.on('paste', editorPaste);
+                $input.on('paste', editorPaste);
 
                 // Handle image drop, Uploads images to BookStack.
                 function handleImageDrop(event) {
@@ -381,7 +387,7 @@ export default function (ngApp, events) {
                     }
                 }
 
-                input.on('drop', handleImageDrop);
+                $input.on('drop', handleImageDrop);
 
                 // Handle image upload and add image into markdown content
                 function uploadImage(file) {
@@ -399,17 +405,17 @@ export default function (ngApp, events) {
 
                     // Insert image into markdown
                     let id = "image-" + Math.random().toString(16).slice(2);
-                    let selectStart = input[0].selectionStart;
-                    let selectEnd = input[0].selectionEnd;
-                    let content = input[0].value;
+                    let selectStart = $input[0].selectionStart;
+                    let selectEnd = $input[0].selectionEnd;
+                    let content = $input[0].value;
                     let selectText = content.substring(selectStart, selectEnd);
                     let placeholderImage = window.baseUrl(`/loading.gif#upload${id}`);
                     let innerContent = ((selectEnd > selectStart) ? `![${selectText}]` : '![]') + `(${placeholderImage})`;
-                    input[0].value = content.substring(0, selectStart) +  innerContent + content.substring(selectEnd);
+                    $input[0].value = content.substring(0, selectStart) +  innerContent + content.substring(selectEnd);
 
-                    input.focus();
-                    input[0].selectionStart = selectStart;
-                    input[0].selectionEnd = selectStart;
+                    $input.focus();
+                    $input[0].selectionStart = selectStart;
+                    $input[0].selectionEnd = selectStart;
 
                     let remoteFilename = "image-" + Date.now() + "." + ext;
                     formData.append('file', file, remoteFilename);
@@ -417,20 +423,20 @@ export default function (ngApp, events) {
 
                     xhr.open('POST', window.baseUrl('/images/gallery/upload'));
                     xhr.onload = function () {
-                        let selectStart = input[0].selectionStart;
+                        let selectStart = $input[0].selectionStart;
                         if (xhr.status === 200 || xhr.status === 201) {
                             let result = JSON.parse(xhr.responseText);
-                            input[0].value = input[0].value.replace(placeholderImage, result.thumbs.display);
-                            input.change();
+                            $input[0].value = $input[0].value.replace(placeholderImage, result.thumbs.display);
+                            $input.change();
                         } else {
-                            console.log('An error occurred uploading the image');
+                            console.log(trans('errors.image_upload_error'));
                             console.log(xhr.responseText);
-                            input[0].value = input[0].value.replace(innerContent, '');
-                            input.change();
+                            $input[0].value = $input[0].value.replace(innerContent, '');
+                            $input.change();
                         }
-                        input.focus();
-                        input[0].selectionStart = selectStart;
-                        input[0].selectionEnd = selectStart;
+                        $input.focus();
+                        $input[0].selectionStart = selectStart;
+                        $input[0].selectionEnd = selectStart;
                     };
                     xhr.send(formData);
                 }
@@ -568,8 +574,7 @@ export default function (ngApp, events) {
                     }
                     // Enter or tab key
                     else if ((event.keyCode === 13 || event.keyCode === 9) && !event.shiftKey) {
-                        let text = suggestionElems[active].textContent;
-                        currentInput[0].value = text;
+                        currentInput[0].value = suggestionElems[active].textContent;
                         currentInput.focus();
                         $suggestionBox.hide();
                         isShowing = false;
@@ -665,7 +670,7 @@ export default function (ngApp, events) {
 
     ngApp.directive('entityLinkSelector', [function($http) {
         return {
-            restict: 'A',
+            restrict: 'A',
             link: function(scope, element, attrs) {
 
                 const selectButton = element.find('.entity-link-selector-confirm');
index 7a7cc592efca1eb1963ecd6f0e659ec27be23246..94462ed6483de9ba64f2035c33facd642dda5308 100644 (file)
@@ -17,6 +17,12 @@ window.baseUrl = function(path) {
 
 let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']);
 
+// Translation setup
+// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
+import Translations from "./translations"
+let translator = new Translations(window.translations);
+window.trans = translator.get.bind(translator);
+
 // Global Event System
 class EventManager {
     constructor() {
@@ -70,150 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) {
 });
 
 // Global jQuery Elements
-$(function () {
-
-    let notifications = $('.notification');
-    let successNotification = notifications.filter('.pos');
-    let errorNotification = notifications.filter('.neg');
-    let warningNotification = notifications.filter('.warning');
-    // Notification Events
-    window.Events.listen('success', function (text) {
-        successNotification.hide();
-        successNotification.find('span').text(text);
-        setTimeout(() => {
-            successNotification.show();
-        }, 1);
-    });
-    window.Events.listen('warning', function (text) {
-        warningNotification.find('span').text(text);
-        warningNotification.show();
-    });
-    window.Events.listen('error', function (text) {
-        errorNotification.find('span').text(text);
-        errorNotification.show();
-    });
-
-    // Notification hiding
-    notifications.click(function () {
-        $(this).fadeOut(100);
-    });
-
-    // Chapter page list toggles
-    $('.chapter-toggle').click(function (e) {
-        e.preventDefault();
-        $(this).toggleClass('open');
-        $(this).closest('.chapter').find('.inset-list').slideToggle(180);
-    });
-
-    // Back to top button
-    $('#back-to-top').click(function() {
-         $('#header').smoothScrollTo();
-    });
-    let scrollTopShowing = false;
-    let scrollTop = document.getElementById('back-to-top');
-    let scrollTopBreakpoint = 1200;
-    window.addEventListener('scroll', function() {
-        let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
-        if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
-            scrollTop.style.display = 'block';
-            scrollTopShowing = true;
-            setTimeout(() => {
-                scrollTop.style.opacity = 0.4;
-            }, 1);
-        } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
-            scrollTop.style.opacity = 0;
-            scrollTopShowing = false;
-            setTimeout(() => {
-                scrollTop.style.display = 'none';
-            }, 500);
-        }
-    });
-
-    // Common jQuery actions
-    $('[data-action="expand-entity-list-details"]').click(function() {
-        $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
-    });
-
-    // Popup close
-    $('.popup-close').click(function() {
-        $(this).closest('.overlay').fadeOut(240);
-    });
-    $('.overlay').click(function(event) {
-        if (!$(event.target).hasClass('overlay')) return;
-        $(this).fadeOut(240);
-    });
-
-    // Prevent markdown display link click redirect
-    $('.markdown-display').on('click', 'a', function(event) {
-        event.preventDefault();
-        window.open($(this).attr('href'));
-    });
-
-    // Toggle Switches
-    let $switches = $('[toggle-switch]');
-    if ($switches.length > 0) {
-        $switches.click(event => {
-           let $switch = $(event.target);
-           let input = $switch.find('input').first()[0];
-           let checked = input.value !== 'true';
-           input.value = checked ? 'true' : 'false';
-           $switch.toggleClass('active', checked);
-        });
-    }
-
-    // Image pickers
-    $('.image-picker').on('click', 'button', event => {
-        let button = event.target;
-        let picker = $(button).closest('.image-picker')[0];
-        let action = button.getAttribute('data-action');
-        let resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
-        let usingIds = picker.getAttribute('data-current-id') !== '';
-        let resizeCrop = picker.getAttribute('data-resize-crop') !== '';
-        let imageElem = picker.querySelector('img');
-        let input = picker.querySelector('input');
-
-        function setImage(image) {
-
-            if (image === 'none') {
-                imageElem.src = picker.getAttribute('data-default-image');
-                imageElem.classList.add('none');
-                input.value = 'none';
-                return;
-            }
-
-            imageElem.src = image.url;
-            input.value = usingIds ? image.id : image.url;
-            imageElem.classList.remove('none');
-        }
+let notifications = $('.notification');
+let successNotification = notifications.filter('.pos');
+let errorNotification = notifications.filter('.neg');
+let warningNotification = notifications.filter('.warning');
+// Notification Events
+window.Events.listen('success', function (text) {
+    successNotification.hide();
+    successNotification.find('span').text(text);
+    setTimeout(() => {
+        successNotification.show();
+    }, 1);
+});
+window.Events.listen('warning', function (text) {
+    warningNotification.find('span').text(text);
+    warningNotification.show();
+});
+window.Events.listen('error', function (text) {
+    errorNotification.find('span').text(text);
+    errorNotification.show();
+});
 
-        if (action === 'show-image-manager') {
-            window.ImageManager.showExternal((image) => {
-                if (!resize) {
-                    setImage(image);
-                    return;
-                }
-                let requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
-                $.get(window.baseUrl(requestString), resp => {
-                    image.url = resp.url;
-                    setImage(image);
-                });
-            });
-        } else if (action === 'reset-image') {
-            setImage({id: 0, url: picker.getAttribute('data-default-image')});
-        } else if (action === 'remove-image') {
-            setImage('none');
-        }
+// Notification hiding
+notifications.click(function () {
+    $(this).fadeOut(100);
+});
 
-    });
+// Chapter page list toggles
+$('.chapter-toggle').click(function (e) {
+    e.preventDefault();
+    $(this).toggleClass('open');
+    $(this).closest('.chapter').find('.inset-list').slideToggle(180);
+});
 
-    // Detect IE for css
-    if(navigator.userAgent.indexOf('MSIE')!==-1
-        || navigator.appVersion.indexOf('Trident/') > 0
-        || navigator.userAgent.indexOf('Safari') !== -1){
-        $('body').addClass('flexbox-support');
+// Back to top button
+$('#back-to-top').click(function() {
+     $('#header').smoothScrollTo();
+});
+let scrollTopShowing = false;
+let scrollTop = document.getElementById('back-to-top');
+let scrollTopBreakpoint = 1200;
+window.addEventListener('scroll', function() {
+    let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
+    if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
+        scrollTop.style.display = 'block';
+        scrollTopShowing = true;
+        setTimeout(() => {
+            scrollTop.style.opacity = 0.4;
+        }, 1);
+    } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
+        scrollTop.style.opacity = 0;
+        scrollTopShowing = false;
+        setTimeout(() => {
+            scrollTop.style.display = 'none';
+        }, 500);
     }
+});
 
+// Common jQuery actions
+$('[data-action="expand-entity-list-details"]').click(function() {
+    $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
 });
 
+// Popup close
+$('.popup-close').click(function() {
+    $(this).closest('.overlay').fadeOut(240);
+});
+$('.overlay').click(function(event) {
+    if (!$(event.target).hasClass('overlay')) return;
+    $(this).fadeOut(240);
+});
+
+// Detect IE for css
+if(navigator.userAgent.indexOf('MSIE')!==-1
+    || navigator.appVersion.indexOf('Trident/') > 0
+    || navigator.userAgent.indexOf('Safari') !== -1){
+    $('body').addClass('flexbox-support');
+}
+
 // Page specific items
 import "./pages/page-show";
diff --git a/resources/assets/js/translations.js b/resources/assets/js/translations.js
new file mode 100644 (file)
index 0000000..306c696
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ *  Translation Manager
+ *  Handles the JavaScript side of translating strings
+ *  in a way which fits with Laravel.
+ */
+class Translator {
+
+    /**
+     * Create an instance, Passing in the required translations
+     * @param translations
+     */
+    constructor(translations) {
+        this.store = translations;
+    }
+
+    /**
+     * Get a translation, Same format as laravel's 'trans' helper
+     * @param key
+     * @param replacements
+     * @returns {*}
+     */
+    get(key, replacements) {
+        let splitKey = key.split('.');
+        let value = splitKey.reduce((a, b) => {
+            return a != undefined ? a[b] : a;
+        }, this.store);
+
+        if (value === undefined) {
+            console.log(`Translation with key "${key}" does not exist`);
+            value = key;
+        }
+
+        if (replacements === undefined) return value;
+
+        let replaceMatches = value.match(/:([\S]+)/g);
+        if (replaceMatches === null) return value;
+        replaceMatches.forEach(match => {
+            let key = match.substring(1);
+            if (typeof replacements[key] === 'undefined') return;
+            value = value.replace(match, replacements[key]);
+        });
+        return value;
+    }
+
+}
+
+export default Translator
index 37c61159db077488d7b7d9ed3988bf81a20aa7f2..21553b8394d01f76392132b1fbeb3b2c269bbeed 100644 (file)
@@ -35,6 +35,12 @@ table.table {
   tr:hover {
     background-color: #EEE;
   }
+  .text-right {
+    text-align: right;
+  }
+  .text-center {
+    text-align: center;
+  }
 }
 
 table.no-style {
index 183480faa1338110759f496a9c9c3d3e0faed8e5..0017acd1d2df801b84c548034fb1cef26bc359fe 100644 (file)
@@ -16,7 +16,7 @@ return [
     'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.',
     'app_name_header' => 'Anwendungsname im Header anzeigen?',
     'app_public_viewing' => '&Ouml;ffentliche Ansicht erlauben?',
-    'app_secure_images' => 'Erh&oml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
+    'app_secure_images' => 'Erh&ouml;hte Sicherheit f&uuml;r Bilduploads aktivieren?',
     'app_secure_images_desc' => 'Aus Leistungsgr&uuml;nden sind alle Bilder &ouml;ffentlich sichtbar. Diese Option f&uuml;gt zuf&auml;llige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.',
     'app_editor' => 'Seiteneditor',
     'app_editor_desc' => 'W&auml;hlen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.',
index cc023799a9cf4eb6093c0611e9c69aef6de198cb..b9108702a288902c7e49ad46a9e42a023baf5389 100644 (file)
@@ -4,18 +4,21 @@ return [
     /**
      * Image Manager
      */
-    'imagem_select' => 'Image Select',
-    'imagem_all' => 'All',
-    'imagem_all_title' => 'View all images',
-    'imagem_book_title' => 'View images uploaded to this book',
-    'imagem_page_title' => 'View images uploaded to this page',
-    'imagem_search_hint' => 'Search by image name',
-    'imagem_uploaded' => 'Uploaded :uploadedDate',
-    'imagem_load_more' => 'Load More',
-    'imagem_image_name' => 'Image Name',
-    'imagem_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.',
-    'imagem_select_image' => 'Select Image',
-    'imagem_dropzone' => 'Drop images or click here to upload',
+    'image_select' => 'Image Select',
+    'image_all' => 'All',
+    'image_all_title' => 'View all images',
+    'image_book_title' => 'View images uploaded to this book',
+    'image_page_title' => 'View images uploaded to this page',
+    'image_search_hint' => 'Search by image name',
+    'image_uploaded' => 'Uploaded :uploadedDate',
+    'image_load_more' => 'Load More',
+    'image_image_name' => 'Image Name',
+    'image_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.',
+    'image_select_image' => 'Select Image',
+    'image_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
     'image_preview' => 'Image Preview',
+    'image_upload_success' => 'Image uploaded successfully',
+    'image_update_success' => 'Image details successfully updated',
+    'image_delete_success' => 'Image successfully deleted'
 ];
\ No newline at end of file
index 87ffb49856b003351bfb43aa1bfcff5a82179b75..033d9614ebcf12a20065a488a54e4f0debbbe8f9 100644 (file)
@@ -129,6 +129,8 @@ return [
     'pages_edit_toggle_header' => 'Toggle header',
     'pages_edit_save_draft' => 'Save Draft',
     'pages_edit_draft' => 'Edit Page Draft',
+    'pages_editing_draft' => 'Editing Draft',
+    'pages_editing_page' => 'Editing Page',
     'pages_edit_draft_save_at' => 'Draft saved at ',
     'pages_edit_delete_draft' => 'Delete Draft',
     'pages_edit_discard_draft' => 'Discard Draft',
@@ -175,6 +177,7 @@ return [
         'time_b' => 'in the last :minCount minutes',
         'message' => ':start :time. Take care not to overwrite each other\'s updates!',
     ],
+    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
 
     /**
      * Editor sidebar
@@ -207,6 +210,9 @@ return [
     'attachments_order_updated' => 'Attachment order updated',
     'attachments_updated_success' => 'Attachment details updated',
     'attachments_deleted' => 'Attachment deleted',
+    'attachments_file_uploaded' => 'File successfully uploaded',
+    'attachments_file_updated' => 'File successfully updated',
+    'attachments_link_attached' => 'Link successfully attached to page',
 
     /**
      * Profile View
index d36f3993257bd2110b875799fc65d4682d490c38..c4578a37a11d91c1eee8c03c3c1e4f3b08c99963 100644 (file)
@@ -33,10 +33,15 @@ return [
     'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
     'cannot_get_image_from_url' => 'Cannot get image from :url',
     'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
 
     // Attachments
     'attachment_page_mismatch' => 'Page mismatch during attachment update',
 
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+
     // Entities
     'entity_not_found' => 'Entity not found',
     'book_not_found' => 'Book not found',
index b1c74fbd01d66b2c7cc40fba094702126cddf33f..43f22d89a117cac1a6921058b2006c32292755b2 100644 (file)
@@ -17,6 +17,7 @@
     <!-- Scripts -->
     <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script>
     <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script>
+    <script src="{{ baseUrl('/translations.js') }}"></script>
 
     @yield('head')
 
index a28d61d41d11a58fed253fa1b1da0ded95eaebbe..9e6ddb52138b1aa165ba8d231bca5b807ffebfbf 100644 (file)
@@ -19,7 +19,7 @@
             {!! csrf_field() !!}
             <input type="hidden" name="_method" value="PUT">
 
-            @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
+            @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book'])
 
             <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
             <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button>
similarity index 87%
rename from resources/views/partials/entity-selector-popup.blade.php
rename to resources/views/components/entity-selector-popup.blade.php
index a758cc0216ca94ac79e4a2ef0b62f7ec493d9eb3..1c4d1fadba9da58e41c8aa229ccfaab14e6f0c9b 100644 (file)
@@ -5,7 +5,7 @@
                 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
                 <button type="button" class="corner-button neg button popup-close">x</button>
             </div>
-            @include('partials/entity-selector', ['name' => 'entity-selector'])
+            @include('components.entity-selector', ['name' => 'entity-selector'])
             <div class="popup-footer">
                 <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button>
             </div>
similarity index 92%
rename from resources/views/partials/entity-selector.blade.php
rename to resources/views/components/entity-selector.blade.php
index 8f3d82f6749d4f022d837fe78031ba5e9ed662c2..8fb2187e6ff7395e8264c657813990fb79472baf 100644 (file)
@@ -2,7 +2,7 @@
     <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}">
         <input type="hidden" entity-selector-input name="{{$name}}" value="">
         <input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()">
-        <div class="text-center loading" ng-show="loading">@include('partials/loading-icon')</div>
+        <div class="text-center loading" ng-show="loading">@include('partials.loading-icon')</div>
         <div ng-show="!loading" ng-bind-html="entityResults"></div>
     </div>
 </div>
\ No newline at end of file
similarity index 80%
rename from resources/views/partials/image-manager.blade.php
rename to resources/views/components/image-manager.blade.php
index 0d3626f5eeb32febe4e3deb0795e7b4839dcb121..39f3bcd3c1a27f2072d7357a9f7a69bf705741b0 100644 (file)
@@ -3,7 +3,7 @@
         <div class="popup-body" ng-click="$event.stopPropagation()">
 
             <div class="popup-header primary-background">
-                <div class="popup-title">{{ trans('components.imagem_select') }}</div>
+                <div class="popup-title">{{ trans('components.image_select') }}</div>
                 <button class="popup-close neg corner-button button">x</button>
             </div>
 
                 <div class="image-manager-content">
                     <div ng-if="imageType === 'gallery'" class="container">
                         <div class="image-manager-header row faded-small nav-tabs">
-                            <div class="col-xs-4 tab-item" title="{{ trans('components.imagem_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.imagem_all') }}</div>
-                            <div class="col-xs-4 tab-item" title="{{ trans('components.imagem_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
-                            <div class="col-xs-4 tab-item" title="{{ trans('components.imagem_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
+                            <div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
+                            <div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
+                            <div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
                         </div>
                     </div>
                     <div ng-show="view === 'all'" >
                         <form ng-submit="searchImages()" class="contained-search-box">
-                            <input type="text" placeholder="{{ trans('components.imagem_search_hint') }}" ng-model="searchTerm">
+                            <input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm">
                             <button ng-class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
                             <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
                         </form>
                                 <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
                                 <div class="image-meta">
                                     <span class="name" ng-bind="image.name"></span>
-                                    <span class="date">{{ trans('components.imagem_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
+                                    <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
                                 </div>
                             </div>
                         </div>
-                        <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.imagem_load_more') }}</div>
+                        <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div>
                     </div>
                 </div>
 
                                     </a>
                                 </div>
                                 <div class="form-group">
-                                    <label for="name">{{ trans('components.imagem_image_name') }}</label>
+                                    <label for="name">{{ trans('components.image_image_name') }}</label>
                                     <input type="text" id="name" name="name" ng-model="selectedImage.name">
                                 </div>
                             </form>
 
                             <div ng-show="dependantPages">
                                 <p class="text-neg text-small">
-                                    {{ trans('components.imagem_delete_confirm') }}
+                                    {{ trans('components.image_delete_confirm') }}
                                 </p>
                                 <ul class="text-neg">
                                     <li ng-repeat="page in dependantPages">
                                     <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
                                 </form>
                                 <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
-                                    <i class="zmdi zmdi-square-right"></i>{{ trans('components.imagem_select_image') }}
+                                    <i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
                                 </button>
                             </div>
 
                         </div>
 
-                        <drop-zone message="{{ trans('components.imagem_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
+                        <drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
 
 
                     </div>
index c2c56ee9fbd930be3f3e1688dcaf3609b3b7bdb1..47fb2b8b7a26f615ec70b3721d6216a9bd48a9a8 100644 (file)
@@ -1,10 +1,10 @@
-<div class="image-picker" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}">
+<div class="image-picker" image-picker="{{$name}}" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}">
 
     <div>
         <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif  class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
     </div>
 
-    <button class="button" type="button" data-action="show-image-manager">{{ trans('components.imagem_select_image') }}</button>
+    <button class="button" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button>
     <br>
     <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
 
         <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
     @endif
 
-    <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{$currentId or $currentImage or ''}}">
-</div>
\ No newline at end of file
+    <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{ isset($currentId) && ($currentId !== '' && $currentId !== false) ? $currentId : $currentImage}}">
+</div>
+
+<script>
+    (function(){
+
+        var picker = document.querySelector('[image-picker="{{$name}}"]');
+        picker.addEventListener('click', function(event) {
+            if (event.target.nodeName.toLowerCase() !== 'button') return;
+             var button = event.target;
+             var action = button.getAttribute('data-action');
+             var resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width');
+             var usingIds = picker.getAttribute('data-current-id') !== '';
+             var resizeCrop = picker.getAttribute('data-resize-crop') !== '';
+             var imageElem = picker.querySelector('img');
+             var input = picker.querySelector('input');
+
+             function setImage(image) {
+                 if (image === 'none') {
+                     imageElem.src = picker.getAttribute('data-default-image');
+                     imageElem.classList.add('none');
+                     input.value = 'none';
+                     return;
+                 }
+                 imageElem.src = image.url;
+                 input.value = usingIds ? image.id : image.url;
+                 imageElem.classList.remove('none');
+             }
+
+             if (action === 'show-image-manager') {
+                 window.ImageManager.showExternal((image) => {
+                     if (!resize) {
+                         setImage(image);
+                         return;
+                     }
+                     var requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false');
+                     $.get(window.baseUrl(requestString), resp => {
+                         image.url = resp.url;
+                         setImage(image);
+                     });
+                 });
+             } else if (action === 'reset-image') {
+                 setImage({id: 0, url: picker.getAttribute('data-default-image')});
+             } else if (action === 'remove-image') {
+                 setImage('none');
+             }
+
+            });
+
+    })();
+</script>
\ No newline at end of file
index b20b74d5ec90d17a8462610894a8db1013c78b72..ad54d5ab1f965eef8f435e66098c3abab5684f68 100644 (file)
@@ -1,4 +1,15 @@
-<div toggle-switch class="toggle-switch @if($value) active @endif">
+<div toggle-switch="{{$name}}" class="toggle-switch @if($value) active @endif">
     <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/>
     <div class="switch-handle"></div>
-</div>
\ No newline at end of file
+</div>
+<script>
+    (function() {
+       var toggle = document.querySelector('[toggle-switch="{{$name}}"]');
+       var toggleInput = toggle.querySelector('input');
+       toggle.onclick = function(event) {
+           var checked = toggleInput.value !== 'true';
+           toggleInput.value = checked ? 'true' : 'false';
+           checked ? toggle.classList.add('active') : toggle.classList.remove('active');
+       };
+    })()
+</script>
\ No newline at end of file
index e50cc7c5bb2dea098b3d1c2f7f5685e62fd2da30..e1c0a169d6f4661f9a6205091594bd9c2429b287 100644 (file)
@@ -20,7 +20,7 @@
 
     </div>
     
-    @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
-    @include('partials/entity-selector-popup')
+    @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
+    @include('components.entity-selector-popup')
 
 @stop
\ No newline at end of file
index 2bf2260471840d970068716d4ae31ee6abd63f3a..a9b6d69d7b4454b012b24856afe322a78abd4182 100644 (file)
@@ -19,7 +19,7 @@
             {!! csrf_field() !!}
             <input type="hidden" name="_method" value="PUT">
 
-            @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
+            @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter'])
 
             <a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
             <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button>
index 885cc2729c9c1a2386fe65e69a89c37a14be4ee8..62bcc881fc3206f005f3bdad614cdf87e4235aa1 100644 (file)
@@ -1,4 +1,4 @@
-<style>
+<style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
     header, #back-to-top, .primary-background {
         background-color: {{ setting('app-color') }} !important;
     }
index 1645fdfd0e063f8cc29533bc00cba4a9b975fc32..c736bc24eb9520fce4089c51f2c5610d5c6c761f 100644 (file)
@@ -56,7 +56,8 @@
                         'defaultImage' => baseUrl('/logo.png'),
                         'currentImage' => setting('app-logo'),
                         'name' => 'setting-app-logo',
-                        'imageClass' => 'logo-image'
+                        'imageClass' => 'logo-image',
+                        'currentId' => false
                     ])
 
                 </div>
 
 </div>
 
-@include('partials/image-manager', ['imageType' => 'system'])
+@include('components.image-manager', ['imageType' => 'system'])
 
 @stop
 
                 var isEmpty = $.trim($elm.val()).length === 0;
                 if (!isEmpty) $elm.val(hexVal);
                 $('#setting-app-color-light').val(isEmpty ? '' : rgbLightVal);
-                // Set page elements to provide preview
-                $('#header, .image-picker .button').attr('style', 'background-color:'+ hexVal+'!important;');
-                $('.faded-small').css('background-color', rgbLightVal);
-                $('.setting-nav a.selected').css('border-bottom-color', hexVal  + '!important');
+
+                var customStyles = document.getElementById('custom-styles');
+                var oldColor = customStyles.getAttribute('data-color');
+                var oldColorLight = customStyles.getAttribute('data-color-light');
+
+                customStyles.innerHTML = customStyles.innerHTML.split(oldColor).join(hexVal);
+                customStyles.innerHTML = customStyles.innerHTML.split(oldColorLight).join(rgbLightVal);
+
+                customStyles.setAttribute('data-color', hexVal);
+                customStyles.setAttribute('data-color-light', rgbLightVal);
             }
         });
     </script>
index 7925ef04209615c877045c3d1cce313b051988f2..c24254946d6fc34f6b974439219c8acb741d32d6 100644 (file)
             <tr>
                 <th>{{ trans('settings.role_name') }}</th>
                 <th></th>
-                <th class="text-right">{{ trans('settings.users') }}</th>
+                <th class="text-center">{{ trans('settings.users') }}</th>
             </tr>
             @foreach($roles as $role)
                 <tr>
                     <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td>
                     <td>{{ $role->description }}</td>
-                    <td class="text-right">{{ $role->users->count() }}</td>
+                    <td class="text-center">{{ $role->users->count() }}</td>
                 </tr>
             @endforeach
         </table>
index 5b55a228395590c19a445d3342a01f4f6324f3bb..4db00e31f932fb8157fa63d8c085b25ead1a2b24 100644 (file)
@@ -85,5 +85,5 @@
     </div>
 
     <p class="margin-top large"><br></p>
-    @include('partials/image-manager', ['imageType' => 'user'])
+    @include('components.image-manager', ['imageType' => 'user'])
 @stop
index 3cd26e62027ac4e6c16d0c52f11f166ee0cd56e7..3b311d3f80287f155b92e9edaa6f965a168cacc6 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+Route::get('/translations.js', 'HomeController@getTranslations');
+
 // Authenticated routes...
 Route::group(['middleware' => 'auth'], function () {