Created solution for JS translations
Also tidied up existing components and JS
Showing
26 changed files
with
309 additions
and
210 deletions
| ... | @@ -117,7 +117,7 @@ class AttachmentController extends Controller | ... | @@ -117,7 +117,7 @@ class AttachmentController extends Controller |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | $attachment = $this->attachmentService->updateFile($attachment, $request->all()); | 119 | $attachment = $this->attachmentService->updateFile($attachment, $request->all()); |
| 120 | - return $this->jsonSuccess($attachment, trans('entities.attachments_updated_success')); | 120 | + return response()->json($attachment); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | /** | 123 | /** | ... | ... |
| ... | @@ -118,17 +118,6 @@ abstract class Controller extends BaseController | ... | @@ -118,17 +118,6 @@ abstract class Controller extends BaseController |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | /** | 120 | /** |
| 121 | - * Send a json respons with a message attached as a header. | ||
| 122 | - * @param $data | ||
| 123 | - * @param string $successMessage | ||
| 124 | - * @return $this | ||
| 125 | - */ | ||
| 126 | - protected function jsonSuccess($data, $successMessage = "") | ||
| 127 | - { | ||
| 128 | - return response()->json($data)->header('message-success', $successMessage); | ||
| 129 | - } | ||
| 130 | - | ||
| 131 | - /** | ||
| 132 | * Send back a json error message. | 121 | * Send back a json error message. |
| 133 | * @param string $messageText | 122 | * @param string $messageText |
| 134 | * @param int $statusCode | 123 | * @param int $statusCode | ... | ... |
| ... | @@ -43,4 +43,39 @@ class HomeController extends Controller | ... | @@ -43,4 +43,39 @@ class HomeController extends Controller |
| 43 | ]); | 43 | ]); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | + /** | ||
| 47 | + * Get a js representation of the current translations | ||
| 48 | + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response | ||
| 49 | + */ | ||
| 50 | + public function getTranslations() { | ||
| 51 | + $locale = trans()->getLocale(); | ||
| 52 | + $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale; | ||
| 53 | + if (cache()->has($cacheKey) && config('app.env') !== 'development') { | ||
| 54 | + $resp = cache($cacheKey); | ||
| 55 | + } else { | ||
| 56 | + $translations = [ | ||
| 57 | + // Get only translations which might be used in JS | ||
| 58 | + 'common' => trans('common'), | ||
| 59 | + 'components' => trans('components'), | ||
| 60 | + 'entities' => trans('entities'), | ||
| 61 | + 'errors' => trans('errors') | ||
| 62 | + ]; | ||
| 63 | + if ($locale !== 'en') { | ||
| 64 | + $enTrans = [ | ||
| 65 | + 'common' => trans('common', [], null, 'en'), | ||
| 66 | + 'components' => trans('components', [], null, 'en'), | ||
| 67 | + 'entities' => trans('entities', [], null, 'en'), | ||
| 68 | + 'errors' => trans('errors', [], null, 'en') | ||
| 69 | + ]; | ||
| 70 | + $translations = array_replace_recursive($enTrans, $translations); | ||
| 71 | + } | ||
| 72 | + $resp = 'window.translations = ' . json_encode($translations); | ||
| 73 | + cache()->put($cacheKey, $resp, 120); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + return response($resp, 200, [ | ||
| 77 | + 'Content-Type' => 'application/javascript' | ||
| 78 | + ]); | ||
| 79 | + } | ||
| 80 | + | ||
| 46 | } | 81 | } | ... | ... |
| ... | @@ -64,7 +64,7 @@ export default function (ngApp, events) { | ... | @@ -64,7 +64,7 @@ export default function (ngApp, events) { |
| 64 | $scope.$apply(() => { | 64 | $scope.$apply(() => { |
| 65 | $scope.images.unshift(data); | 65 | $scope.images.unshift(data); |
| 66 | }); | 66 | }); |
| 67 | - events.emit('success', 'Image uploaded'); | 67 | + events.emit('success', trans('components.image_upload_success')); |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | /** | 70 | /** |
| ... | @@ -151,10 +151,9 @@ export default function (ngApp, events) { | ... | @@ -151,10 +151,9 @@ export default function (ngApp, events) { |
| 151 | if ($scope.searching) components['term'] = $scope.searchTerm; | 151 | if ($scope.searching) components['term'] = $scope.searchTerm; |
| 152 | 152 | ||
| 153 | 153 | ||
| 154 | - let urlQueryString = Object.keys(components).map((key) => { | 154 | + url += Object.keys(components).map((key) => { |
| 155 | return key + '=' + encodeURIComponent(components[key]); | 155 | return key + '=' + encodeURIComponent(components[key]); |
| 156 | }).join('&'); | 156 | }).join('&'); |
| 157 | - url += urlQueryString; | ||
| 158 | 157 | ||
| 159 | $http.get(url).then((response) => { | 158 | $http.get(url).then((response) => { |
| 160 | $scope.images = $scope.images.concat(response.data.images); | 159 | $scope.images = $scope.images.concat(response.data.images); |
| ... | @@ -209,7 +208,7 @@ export default function (ngApp, events) { | ... | @@ -209,7 +208,7 @@ export default function (ngApp, events) { |
| 209 | event.preventDefault(); | 208 | event.preventDefault(); |
| 210 | let url = window.baseUrl('/images/update/' + $scope.selectedImage.id); | 209 | let url = window.baseUrl('/images/update/' + $scope.selectedImage.id); |
| 211 | $http.put(url, this.selectedImage).then(response => { | 210 | $http.put(url, this.selectedImage).then(response => { |
| 212 | - events.emit('success', 'Image details updated'); | 211 | + events.emit('success', trans('components.image_update_success')); |
| 213 | }, (response) => { | 212 | }, (response) => { |
| 214 | if (response.status === 422) { | 213 | if (response.status === 422) { |
| 215 | let errors = response.data; | 214 | let errors = response.data; |
| ... | @@ -238,7 +237,7 @@ export default function (ngApp, events) { | ... | @@ -238,7 +237,7 @@ export default function (ngApp, events) { |
| 238 | $http.delete(url).then((response) => { | 237 | $http.delete(url).then((response) => { |
| 239 | $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1); | 238 | $scope.images.splice($scope.images.indexOf($scope.selectedImage), 1); |
| 240 | $scope.selectedImage = false; | 239 | $scope.selectedImage = false; |
| 241 | - events.emit('success', 'Image successfully deleted'); | 240 | + events.emit('success', trans('components.image_delete_success')); |
| 242 | }, (response) => { | 241 | }, (response) => { |
| 243 | // Pages failure | 242 | // Pages failure |
| 244 | if (response.status === 400) { | 243 | if (response.status === 400) { |
| ... | @@ -309,9 +308,9 @@ export default function (ngApp, events) { | ... | @@ -309,9 +308,9 @@ export default function (ngApp, events) { |
| 309 | 308 | ||
| 310 | // Set initial header draft text | 309 | // Set initial header draft text |
| 311 | if ($scope.isUpdateDraft || $scope.isNewPageDraft) { | 310 | if ($scope.isUpdateDraft || $scope.isNewPageDraft) { |
| 312 | - $scope.draftText = 'Editing Draft' | 311 | + $scope.draftText = trans('entities.pages_editing_draft'); |
| 313 | } else { | 312 | } else { |
| 314 | - $scope.draftText = 'Editing Page' | 313 | + $scope.draftText = trans('entities.pages_editing_page'); |
| 315 | } | 314 | } |
| 316 | 315 | ||
| 317 | let autoSave = false; | 316 | let autoSave = false; |
| ... | @@ -388,7 +387,7 @@ export default function (ngApp, events) { | ... | @@ -388,7 +387,7 @@ export default function (ngApp, events) { |
| 388 | lastSave = Date.now(); | 387 | lastSave = Date.now(); |
| 389 | }, errorRes => { | 388 | }, errorRes => { |
| 390 | if (draftErroring) return; | 389 | if (draftErroring) return; |
| 391 | - events.emit('error', 'Failed to save draft. Ensure you have internet connection before saving this page.') | 390 | + events.emit('error', trans('errors.page_draft_autosave_fail')); |
| 392 | draftErroring = true; | 391 | draftErroring = true; |
| 393 | }); | 392 | }); |
| 394 | } | 393 | } |
| ... | @@ -421,7 +420,7 @@ export default function (ngApp, events) { | ... | @@ -421,7 +420,7 @@ export default function (ngApp, events) { |
| 421 | let url = window.baseUrl('/ajax/page/' + pageId); | 420 | let url = window.baseUrl('/ajax/page/' + pageId); |
| 422 | $http.get(url).then((responseData) => { | 421 | $http.get(url).then((responseData) => { |
| 423 | if (autoSave) $interval.cancel(autoSave); | 422 | if (autoSave) $interval.cancel(autoSave); |
| 424 | - $scope.draftText = 'Editing Page'; | 423 | + $scope.draftText = trans('entities.pages_editing_page'); |
| 425 | $scope.isUpdateDraft = false; | 424 | $scope.isUpdateDraft = false; |
| 426 | $scope.$broadcast('html-update', responseData.data.html); | 425 | $scope.$broadcast('html-update', responseData.data.html); |
| 427 | $scope.$broadcast('markdown-update', responseData.data.markdown || responseData.data.html); | 426 | $scope.$broadcast('markdown-update', responseData.data.markdown || responseData.data.html); |
| ... | @@ -429,7 +428,7 @@ export default function (ngApp, events) { | ... | @@ -429,7 +428,7 @@ export default function (ngApp, events) { |
| 429 | $timeout(() => { | 428 | $timeout(() => { |
| 430 | startAutoSave(); | 429 | startAutoSave(); |
| 431 | }, 1000); | 430 | }, 1000); |
| 432 | - events.emit('success', 'Draft discarded, The editor has been updated with the current page content'); | 431 | + events.emit('success', trans('entities.pages_draft_discarded')); |
| 433 | }); | 432 | }); |
| 434 | }; | 433 | }; |
| 435 | 434 | ||
| ... | @@ -594,7 +593,7 @@ export default function (ngApp, events) { | ... | @@ -594,7 +593,7 @@ export default function (ngApp, events) { |
| 594 | $scope.$apply(() => { | 593 | $scope.$apply(() => { |
| 595 | $scope.files.push(data); | 594 | $scope.files.push(data); |
| 596 | }); | 595 | }); |
| 597 | - events.emit('success', 'File uploaded'); | 596 | + events.emit('success', trans('entities.attachments_file_uploaded')); |
| 598 | }; | 597 | }; |
| 599 | 598 | ||
| 600 | /** | 599 | /** |
| ... | @@ -612,7 +611,7 @@ export default function (ngApp, events) { | ... | @@ -612,7 +611,7 @@ export default function (ngApp, events) { |
| 612 | data.link = ''; | 611 | data.link = ''; |
| 613 | } | 612 | } |
| 614 | }); | 613 | }); |
| 615 | - events.emit('success', 'File updated'); | 614 | + events.emit('success', trans('entities.attachments_file_updated')); |
| 616 | }; | 615 | }; |
| 617 | 616 | ||
| 618 | /** | 617 | /** |
| ... | @@ -638,7 +637,7 @@ export default function (ngApp, events) { | ... | @@ -638,7 +637,7 @@ export default function (ngApp, events) { |
| 638 | file.uploaded_to = pageId; | 637 | file.uploaded_to = pageId; |
| 639 | $http.post(window.baseUrl('/attachments/link'), file).then(resp => { | 638 | $http.post(window.baseUrl('/attachments/link'), file).then(resp => { |
| 640 | $scope.files.push(resp.data); | 639 | $scope.files.push(resp.data); |
| 641 | - events.emit('success', 'Link attached'); | 640 | + events.emit('success', trans('entities.attachments_link_attached')); |
| 642 | $scope.file = getCleanFile(); | 641 | $scope.file = getCleanFile(); |
| 643 | }, checkError('link')); | 642 | }, checkError('link')); |
| 644 | }; | 643 | }; |
| ... | @@ -672,7 +671,7 @@ export default function (ngApp, events) { | ... | @@ -672,7 +671,7 @@ export default function (ngApp, events) { |
| 672 | $scope.editFile.link = ''; | 671 | $scope.editFile.link = ''; |
| 673 | } | 672 | } |
| 674 | $scope.editFile = false; | 673 | $scope.editFile = false; |
| 675 | - events.emit('success', resp.headers('message-success')); | 674 | + events.emit('success', trans('entities.attachments_updated_success')); |
| 676 | }, checkError('edit')); | 675 | }, checkError('edit')); |
| 677 | }; | 676 | }; |
| 678 | 677 | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -17,6 +17,12 @@ window.baseUrl = function(path) { | ... | @@ -17,6 +17,12 @@ window.baseUrl = function(path) { |
| 17 | 17 | ||
| 18 | let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']); | 18 | let ngApp = angular.module('bookStack', ['ngResource', 'ngAnimate', 'ngSanitize', 'ui.sortable']); |
| 19 | 19 | ||
| 20 | +// Translation setup | ||
| 21 | +// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system | ||
| 22 | +import Translations from "./translations" | ||
| 23 | +let translator = new Translations(window.translations); | ||
| 24 | +window.trans = translator.get.bind(translator); | ||
| 25 | + | ||
| 20 | // Global Event System | 26 | // Global Event System |
| 21 | class EventManager { | 27 | class EventManager { |
| 22 | constructor() { | 28 | constructor() { |
| ... | @@ -70,150 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) { | ... | @@ -70,150 +76,83 @@ jQuery.expr[":"].contains = $.expr.createPseudo(function (arg) { |
| 70 | }); | 76 | }); |
| 71 | 77 | ||
| 72 | // Global jQuery Elements | 78 | // Global jQuery Elements |
| 73 | -$(function () { | 79 | +let notifications = $('.notification'); |
| 74 | - | 80 | +let successNotification = notifications.filter('.pos'); |
| 75 | - let notifications = $('.notification'); | 81 | +let errorNotification = notifications.filter('.neg'); |
| 76 | - let successNotification = notifications.filter('.pos'); | 82 | +let warningNotification = notifications.filter('.warning'); |
| 77 | - let errorNotification = notifications.filter('.neg'); | 83 | +// Notification Events |
| 78 | - let warningNotification = notifications.filter('.warning'); | 84 | +window.Events.listen('success', function (text) { |
| 79 | - // Notification Events | 85 | + successNotification.hide(); |
| 80 | - window.Events.listen('success', function (text) { | 86 | + successNotification.find('span').text(text); |
| 81 | - successNotification.hide(); | 87 | + setTimeout(() => { |
| 82 | - successNotification.find('span').text(text); | 88 | + successNotification.show(); |
| 83 | - setTimeout(() => { | 89 | + }, 1); |
| 84 | - successNotification.show(); | 90 | +}); |
| 85 | - }, 1); | 91 | +window.Events.listen('warning', function (text) { |
| 86 | - }); | 92 | + warningNotification.find('span').text(text); |
| 87 | - window.Events.listen('warning', function (text) { | 93 | + warningNotification.show(); |
| 88 | - warningNotification.find('span').text(text); | 94 | +}); |
| 89 | - warningNotification.show(); | 95 | +window.Events.listen('error', function (text) { |
| 90 | - }); | 96 | + errorNotification.find('span').text(text); |
| 91 | - window.Events.listen('error', function (text) { | 97 | + errorNotification.show(); |
| 92 | - errorNotification.find('span').text(text); | 98 | +}); |
| 93 | - errorNotification.show(); | ||
| 94 | - }); | ||
| 95 | - | ||
| 96 | - // Notification hiding | ||
| 97 | - notifications.click(function () { | ||
| 98 | - $(this).fadeOut(100); | ||
| 99 | - }); | ||
| 100 | - | ||
| 101 | - // Chapter page list toggles | ||
| 102 | - $('.chapter-toggle').click(function (e) { | ||
| 103 | - e.preventDefault(); | ||
| 104 | - $(this).toggleClass('open'); | ||
| 105 | - $(this).closest('.chapter').find('.inset-list').slideToggle(180); | ||
| 106 | - }); | ||
| 107 | - | ||
| 108 | - // Back to top button | ||
| 109 | - $('#back-to-top').click(function() { | ||
| 110 | - $('#header').smoothScrollTo(); | ||
| 111 | - }); | ||
| 112 | - let scrollTopShowing = false; | ||
| 113 | - let scrollTop = document.getElementById('back-to-top'); | ||
| 114 | - let scrollTopBreakpoint = 1200; | ||
| 115 | - window.addEventListener('scroll', function() { | ||
| 116 | - let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0; | ||
| 117 | - if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) { | ||
| 118 | - scrollTop.style.display = 'block'; | ||
| 119 | - scrollTopShowing = true; | ||
| 120 | - setTimeout(() => { | ||
| 121 | - scrollTop.style.opacity = 0.4; | ||
| 122 | - }, 1); | ||
| 123 | - } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) { | ||
| 124 | - scrollTop.style.opacity = 0; | ||
| 125 | - scrollTopShowing = false; | ||
| 126 | - setTimeout(() => { | ||
| 127 | - scrollTop.style.display = 'none'; | ||
| 128 | - }, 500); | ||
| 129 | - } | ||
| 130 | - }); | ||
| 131 | - | ||
| 132 | - // Common jQuery actions | ||
| 133 | - $('[data-action="expand-entity-list-details"]').click(function() { | ||
| 134 | - $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240); | ||
| 135 | - }); | ||
| 136 | - | ||
| 137 | - // Popup close | ||
| 138 | - $('.popup-close').click(function() { | ||
| 139 | - $(this).closest('.overlay').fadeOut(240); | ||
| 140 | - }); | ||
| 141 | - $('.overlay').click(function(event) { | ||
| 142 | - if (!$(event.target).hasClass('overlay')) return; | ||
| 143 | - $(this).fadeOut(240); | ||
| 144 | - }); | ||
| 145 | - | ||
| 146 | - // Prevent markdown display link click redirect | ||
| 147 | - $('.markdown-display').on('click', 'a', function(event) { | ||
| 148 | - event.preventDefault(); | ||
| 149 | - window.open($(this).attr('href')); | ||
| 150 | - }); | ||
| 151 | - | ||
| 152 | - // Toggle Switches | ||
| 153 | - let $switches = $('[toggle-switch]'); | ||
| 154 | - if ($switches.length > 0) { | ||
| 155 | - $switches.click(event => { | ||
| 156 | - let $switch = $(event.target); | ||
| 157 | - let input = $switch.find('input').first()[0]; | ||
| 158 | - let checked = input.value !== 'true'; | ||
| 159 | - input.value = checked ? 'true' : 'false'; | ||
| 160 | - $switch.toggleClass('active', checked); | ||
| 161 | - }); | ||
| 162 | - } | ||
| 163 | - | ||
| 164 | - // Image pickers | ||
| 165 | - $('.image-picker').on('click', 'button', event => { | ||
| 166 | - let button = event.target; | ||
| 167 | - let picker = $(button).closest('.image-picker')[0]; | ||
| 168 | - let action = button.getAttribute('data-action'); | ||
| 169 | - let resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width'); | ||
| 170 | - let usingIds = picker.getAttribute('data-current-id') !== ''; | ||
| 171 | - let resizeCrop = picker.getAttribute('data-resize-crop') !== ''; | ||
| 172 | - let imageElem = picker.querySelector('img'); | ||
| 173 | - let input = picker.querySelector('input'); | ||
| 174 | - | ||
| 175 | - function setImage(image) { | ||
| 176 | - | ||
| 177 | - if (image === 'none') { | ||
| 178 | - imageElem.src = picker.getAttribute('data-default-image'); | ||
| 179 | - imageElem.classList.add('none'); | ||
| 180 | - input.value = 'none'; | ||
| 181 | - return; | ||
| 182 | - } | ||
| 183 | - | ||
| 184 | - imageElem.src = image.url; | ||
| 185 | - input.value = usingIds ? image.id : image.url; | ||
| 186 | - imageElem.classList.remove('none'); | ||
| 187 | - } | ||
| 188 | 99 | ||
| 189 | - if (action === 'show-image-manager') { | 100 | +// Notification hiding |
| 190 | - window.ImageManager.showExternal((image) => { | 101 | +notifications.click(function () { |
| 191 | - if (!resize) { | 102 | + $(this).fadeOut(100); |
| 192 | - setImage(image); | 103 | +}); |
| 193 | - return; | ||
| 194 | - } | ||
| 195 | - let requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false'); | ||
| 196 | - $.get(window.baseUrl(requestString), resp => { | ||
| 197 | - image.url = resp.url; | ||
| 198 | - setImage(image); | ||
| 199 | - }); | ||
| 200 | - }); | ||
| 201 | - } else if (action === 'reset-image') { | ||
| 202 | - setImage({id: 0, url: picker.getAttribute('data-default-image')}); | ||
| 203 | - } else if (action === 'remove-image') { | ||
| 204 | - setImage('none'); | ||
| 205 | - } | ||
| 206 | 104 | ||
| 207 | - }); | 105 | +// Chapter page list toggles |
| 106 | +$('.chapter-toggle').click(function (e) { | ||
| 107 | + e.preventDefault(); | ||
| 108 | + $(this).toggleClass('open'); | ||
| 109 | + $(this).closest('.chapter').find('.inset-list').slideToggle(180); | ||
| 110 | +}); | ||
| 208 | 111 | ||
| 209 | - // Detect IE for css | 112 | +// Back to top button |
| 210 | - if(navigator.userAgent.indexOf('MSIE')!==-1 | 113 | +$('#back-to-top').click(function() { |
| 211 | - || navigator.appVersion.indexOf('Trident/') > 0 | 114 | + $('#header').smoothScrollTo(); |
| 212 | - || navigator.userAgent.indexOf('Safari') !== -1){ | 115 | +}); |
| 213 | - $('body').addClass('flexbox-support'); | 116 | +let scrollTopShowing = false; |
| 117 | +let scrollTop = document.getElementById('back-to-top'); | ||
| 118 | +let scrollTopBreakpoint = 1200; | ||
| 119 | +window.addEventListener('scroll', function() { | ||
| 120 | + let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0; | ||
| 121 | + if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) { | ||
| 122 | + scrollTop.style.display = 'block'; | ||
| 123 | + scrollTopShowing = true; | ||
| 124 | + setTimeout(() => { | ||
| 125 | + scrollTop.style.opacity = 0.4; | ||
| 126 | + }, 1); | ||
| 127 | + } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) { | ||
| 128 | + scrollTop.style.opacity = 0; | ||
| 129 | + scrollTopShowing = false; | ||
| 130 | + setTimeout(() => { | ||
| 131 | + scrollTop.style.display = 'none'; | ||
| 132 | + }, 500); | ||
| 214 | } | 133 | } |
| 134 | +}); | ||
| 215 | 135 | ||
| 136 | +// Common jQuery actions | ||
| 137 | +$('[data-action="expand-entity-list-details"]').click(function() { | ||
| 138 | + $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240); | ||
| 216 | }); | 139 | }); |
| 217 | 140 | ||
| 141 | +// Popup close | ||
| 142 | +$('.popup-close').click(function() { | ||
| 143 | + $(this).closest('.overlay').fadeOut(240); | ||
| 144 | +}); | ||
| 145 | +$('.overlay').click(function(event) { | ||
| 146 | + if (!$(event.target).hasClass('overlay')) return; | ||
| 147 | + $(this).fadeOut(240); | ||
| 148 | +}); | ||
| 149 | + | ||
| 150 | +// Detect IE for css | ||
| 151 | +if(navigator.userAgent.indexOf('MSIE')!==-1 | ||
| 152 | + || navigator.appVersion.indexOf('Trident/') > 0 | ||
| 153 | + || navigator.userAgent.indexOf('Safari') !== -1){ | ||
| 154 | + $('body').addClass('flexbox-support'); | ||
| 155 | +} | ||
| 156 | + | ||
| 218 | // Page specific items | 157 | // Page specific items |
| 219 | import "./pages/page-show"; | 158 | import "./pages/page-show"; | ... | ... |
resources/assets/js/translations.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Translation Manager | ||
| 3 | + * Handles the JavaScript side of translating strings | ||
| 4 | + * in a way which fits with Laravel. | ||
| 5 | + */ | ||
| 6 | +class Translator { | ||
| 7 | + | ||
| 8 | + /** | ||
| 9 | + * Create an instance, Passing in the required translations | ||
| 10 | + * @param translations | ||
| 11 | + */ | ||
| 12 | + constructor(translations) { | ||
| 13 | + this.store = translations; | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * Get a translation, Same format as laravel's 'trans' helper | ||
| 18 | + * @param key | ||
| 19 | + * @param replacements | ||
| 20 | + * @returns {*} | ||
| 21 | + */ | ||
| 22 | + get(key, replacements) { | ||
| 23 | + let splitKey = key.split('.'); | ||
| 24 | + let value = splitKey.reduce((a, b) => { | ||
| 25 | + return a != undefined ? a[b] : a; | ||
| 26 | + }, this.store); | ||
| 27 | + | ||
| 28 | + if (value === undefined) { | ||
| 29 | + console.log(`Translation with key "${key}" does not exist`); | ||
| 30 | + value = key; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + if (replacements === undefined) return value; | ||
| 34 | + | ||
| 35 | + let replaceMatches = value.match(/:([\S]+)/g); | ||
| 36 | + if (replaceMatches === null) return value; | ||
| 37 | + replaceMatches.forEach(match => { | ||
| 38 | + let key = match.substring(1); | ||
| 39 | + if (typeof replacements[key] === 'undefined') return; | ||
| 40 | + value = value.replace(match, replacements[key]); | ||
| 41 | + }); | ||
| 42 | + return value; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +export default Translator |
| ... | @@ -35,6 +35,12 @@ table.table { | ... | @@ -35,6 +35,12 @@ table.table { |
| 35 | tr:hover { | 35 | tr:hover { |
| 36 | background-color: #EEE; | 36 | background-color: #EEE; |
| 37 | } | 37 | } |
| 38 | + .text-right { | ||
| 39 | + text-align: right; | ||
| 40 | + } | ||
| 41 | + .text-center { | ||
| 42 | + text-align: center; | ||
| 43 | + } | ||
| 38 | } | 44 | } |
| 39 | 45 | ||
| 40 | table.no-style { | 46 | table.no-style { | ... | ... |
| ... | @@ -16,7 +16,7 @@ return [ | ... | @@ -16,7 +16,7 @@ return [ |
| 16 | 'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.', | 16 | 'app_name_desc' => 'Dieser Name wird im Header und E-Mails angezeigt.', |
| 17 | 'app_name_header' => 'Anwendungsname im Header anzeigen?', | 17 | 'app_name_header' => 'Anwendungsname im Header anzeigen?', |
| 18 | 'app_public_viewing' => 'Öffentliche Ansicht erlauben?', | 18 | 'app_public_viewing' => 'Öffentliche Ansicht erlauben?', |
| 19 | - 'app_secure_images' => 'Erh&oml;hte Sicherheit für Bilduploads aktivieren?', | 19 | + 'app_secure_images' => 'Erhöhte Sicherheit für Bilduploads aktivieren?', |
| 20 | 'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.', | 20 | 'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu eratene, Zeichenketten vor die Bild-URLs hinzu. Stellen sie sicher, dass Verzeichnindexes deaktiviert sind, um einen einfachen Zugrif zu verhindern.', |
| 21 | 'app_editor' => 'Seiteneditor', | 21 | 'app_editor' => 'Seiteneditor', |
| 22 | 'app_editor_desc' => 'Wählen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.', | 22 | 'app_editor_desc' => 'Wählen sie den Editor aus, der von allen Benutzern genutzt werden soll, um Seiten zu editieren.', | ... | ... |
| ... | @@ -4,18 +4,21 @@ return [ | ... | @@ -4,18 +4,21 @@ return [ |
| 4 | /** | 4 | /** |
| 5 | * Image Manager | 5 | * Image Manager |
| 6 | */ | 6 | */ |
| 7 | - 'imagem_select' => 'Image Select', | 7 | + 'image_select' => 'Image Select', |
| 8 | - 'imagem_all' => 'All', | 8 | + 'image_all' => 'All', |
| 9 | - 'imagem_all_title' => 'View all images', | 9 | + 'image_all_title' => 'View all images', |
| 10 | - 'imagem_book_title' => 'View images uploaded to this book', | 10 | + 'image_book_title' => 'View images uploaded to this book', |
| 11 | - 'imagem_page_title' => 'View images uploaded to this page', | 11 | + 'image_page_title' => 'View images uploaded to this page', |
| 12 | - 'imagem_search_hint' => 'Search by image name', | 12 | + 'image_search_hint' => 'Search by image name', |
| 13 | - 'imagem_uploaded' => 'Uploaded :uploadedDate', | 13 | + 'image_uploaded' => 'Uploaded :uploadedDate', |
| 14 | - 'imagem_load_more' => 'Load More', | 14 | + 'image_load_more' => 'Load More', |
| 15 | - 'imagem_image_name' => 'Image Name', | 15 | + 'image_image_name' => 'Image Name', |
| 16 | - 'imagem_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.', | 16 | + 'image_delete_confirm' => 'This image is used in the pages below, Click delete again to confirm you want to delete this image.', |
| 17 | - 'imagem_select_image' => 'Select Image', | 17 | + 'image_select_image' => 'Select Image', |
| 18 | - 'imagem_dropzone' => 'Drop images or click here to upload', | 18 | + 'image_dropzone' => 'Drop images or click here to upload', |
| 19 | 'images_deleted' => 'Images Deleted', | 19 | 'images_deleted' => 'Images Deleted', |
| 20 | 'image_preview' => 'Image Preview', | 20 | 'image_preview' => 'Image Preview', |
| 21 | + 'image_upload_success' => 'Image uploaded successfully', | ||
| 22 | + 'image_update_success' => 'Image details successfully updated', | ||
| 23 | + 'image_delete_success' => 'Image successfully deleted' | ||
| 21 | ]; | 24 | ]; |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -129,6 +129,8 @@ return [ | ... | @@ -129,6 +129,8 @@ return [ |
| 129 | 'pages_edit_toggle_header' => 'Toggle header', | 129 | 'pages_edit_toggle_header' => 'Toggle header', |
| 130 | 'pages_edit_save_draft' => 'Save Draft', | 130 | 'pages_edit_save_draft' => 'Save Draft', |
| 131 | 'pages_edit_draft' => 'Edit Page Draft', | 131 | 'pages_edit_draft' => 'Edit Page Draft', |
| 132 | + 'pages_editing_draft' => 'Editing Draft', | ||
| 133 | + 'pages_editing_page' => 'Editing Page', | ||
| 132 | 'pages_edit_draft_save_at' => 'Draft saved at ', | 134 | 'pages_edit_draft_save_at' => 'Draft saved at ', |
| 133 | 'pages_edit_delete_draft' => 'Delete Draft', | 135 | 'pages_edit_delete_draft' => 'Delete Draft', |
| 134 | 'pages_edit_discard_draft' => 'Discard Draft', | 136 | 'pages_edit_discard_draft' => 'Discard Draft', |
| ... | @@ -175,6 +177,7 @@ return [ | ... | @@ -175,6 +177,7 @@ return [ |
| 175 | 'time_b' => 'in the last :minCount minutes', | 177 | 'time_b' => 'in the last :minCount minutes', |
| 176 | 'message' => ':start :time. Take care not to overwrite each other\'s updates!', | 178 | 'message' => ':start :time. Take care not to overwrite each other\'s updates!', |
| 177 | ], | 179 | ], |
| 180 | + 'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content', | ||
| 178 | 181 | ||
| 179 | /** | 182 | /** |
| 180 | * Editor sidebar | 183 | * Editor sidebar |
| ... | @@ -207,6 +210,9 @@ return [ | ... | @@ -207,6 +210,9 @@ return [ |
| 207 | 'attachments_order_updated' => 'Attachment order updated', | 210 | 'attachments_order_updated' => 'Attachment order updated', |
| 208 | 'attachments_updated_success' => 'Attachment details updated', | 211 | 'attachments_updated_success' => 'Attachment details updated', |
| 209 | 'attachments_deleted' => 'Attachment deleted', | 212 | 'attachments_deleted' => 'Attachment deleted', |
| 213 | + 'attachments_file_uploaded' => 'File successfully uploaded', | ||
| 214 | + 'attachments_file_updated' => 'File successfully updated', | ||
| 215 | + 'attachments_link_attached' => 'Link successfully attached to page', | ||
| 210 | 216 | ||
| 211 | /** | 217 | /** |
| 212 | * Profile View | 218 | * Profile View | ... | ... |
| ... | @@ -33,10 +33,15 @@ return [ | ... | @@ -33,10 +33,15 @@ return [ |
| 33 | 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', | 33 | 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', |
| 34 | 'cannot_get_image_from_url' => 'Cannot get image from :url', | 34 | 'cannot_get_image_from_url' => 'Cannot get image from :url', |
| 35 | 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', | 35 | 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', |
| 36 | + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', | ||
| 37 | + 'image_upload_error' => 'An error occurred uploading the image', | ||
| 36 | 38 | ||
| 37 | // Attachments | 39 | // Attachments |
| 38 | 'attachment_page_mismatch' => 'Page mismatch during attachment update', | 40 | 'attachment_page_mismatch' => 'Page mismatch during attachment update', |
| 39 | 41 | ||
| 42 | + // Pages | ||
| 43 | + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', | ||
| 44 | + | ||
| 40 | // Entities | 45 | // Entities |
| 41 | 'entity_not_found' => 'Entity not found', | 46 | 'entity_not_found' => 'Entity not found', |
| 42 | 'book_not_found' => 'Book not found', | 47 | 'book_not_found' => 'Book not found', | ... | ... |
| ... | @@ -17,6 +17,7 @@ | ... | @@ -17,6 +17,7 @@ |
| 17 | <!-- Scripts --> | 17 | <!-- Scripts --> |
| 18 | <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script> | 18 | <script src="{{ baseUrl('/libs/jquery/jquery.min.js?version=2.1.4') }}"></script> |
| 19 | <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script> | 19 | <script src="{{ baseUrl('/libs/jquery/jquery-ui.min.js?version=1.11.4') }}"></script> |
| 20 | + <script src="{{ baseUrl('/translations.js') }}"></script> | ||
| 20 | 21 | ||
| 21 | @yield('head') | 22 | @yield('head') |
| 22 | 23 | ... | ... |
| ... | @@ -19,7 +19,7 @@ | ... | @@ -19,7 +19,7 @@ |
| 19 | {!! csrf_field() !!} | 19 | {!! csrf_field() !!} |
| 20 | <input type="hidden" name="_method" value="PUT"> | 20 | <input type="hidden" name="_method" value="PUT"> |
| 21 | 21 | ||
| 22 | - @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book']) | 22 | + @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book']) |
| 23 | 23 | ||
| 24 | <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a> | 24 | <a href="{{ $chapter->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a> |
| 25 | <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button> | 25 | <button type="submit" class="button pos">{{ trans('entities.chapters_move') }}</button> | ... | ... |
| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | <div class="popup-title">{{ trans('entities.entity_select') }}</div> | 5 | <div class="popup-title">{{ trans('entities.entity_select') }}</div> |
| 6 | <button type="button" class="corner-button neg button popup-close">x</button> | 6 | <button type="button" class="corner-button neg button popup-close">x</button> |
| 7 | </div> | 7 | </div> |
| 8 | - @include('partials/entity-selector', ['name' => 'entity-selector']) | 8 | + @include('components.entity-selector', ['name' => 'entity-selector']) |
| 9 | <div class="popup-footer"> | 9 | <div class="popup-footer"> |
| 10 | <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button> | 10 | <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button> |
| 11 | </div> | 11 | </div> | ... | ... |
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}"> | 2 | <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}"> |
| 3 | <input type="hidden" entity-selector-input name="{{$name}}" value=""> | 3 | <input type="hidden" entity-selector-input name="{{$name}}" value=""> |
| 4 | <input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()"> | 4 | <input type="text" placeholder="{{ trans('common.search') }}" ng-model="search" ng-model-options="{debounce: 200}" ng-change="searchEntities()"> |
| 5 | - <div class="text-center loading" ng-show="loading">@include('partials/loading-icon')</div> | 5 | + <div class="text-center loading" ng-show="loading">@include('partials.loading-icon')</div> |
| 6 | <div ng-show="!loading" ng-bind-html="entityResults"></div> | 6 | <div ng-show="!loading" ng-bind-html="entityResults"></div> |
| 7 | </div> | 7 | </div> |
| 8 | </div> | 8 | </div> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | <div class="popup-body" ng-click="$event.stopPropagation()"> | 3 | <div class="popup-body" ng-click="$event.stopPropagation()"> |
| 4 | 4 | ||
| 5 | <div class="popup-header primary-background"> | 5 | <div class="popup-header primary-background"> |
| 6 | - <div class="popup-title">{{ trans('components.imagem_select') }}</div> | 6 | + <div class="popup-title">{{ trans('components.image_select') }}</div> |
| 7 | <button class="popup-close neg corner-button button">x</button> | 7 | <button class="popup-close neg corner-button button">x</button> |
| 8 | </div> | 8 | </div> |
| 9 | 9 | ||
| ... | @@ -12,14 +12,14 @@ | ... | @@ -12,14 +12,14 @@ |
| 12 | <div class="image-manager-content"> | 12 | <div class="image-manager-content"> |
| 13 | <div ng-if="imageType === 'gallery'" class="container"> | 13 | <div ng-if="imageType === 'gallery'" class="container"> |
| 14 | <div class="image-manager-header row faded-small nav-tabs"> | 14 | <div class="image-manager-header row faded-small nav-tabs"> |
| 15 | - <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> | 15 | + <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> |
| 16 | - <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> | 16 | + <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> |
| 17 | - <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> | 17 | + <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> |
| 18 | </div> | 18 | </div> |
| 19 | </div> | 19 | </div> |
| 20 | <div ng-show="view === 'all'" > | 20 | <div ng-show="view === 'all'" > |
| 21 | <form ng-submit="searchImages()" class="contained-search-box"> | 21 | <form ng-submit="searchImages()" class="contained-search-box"> |
| 22 | - <input type="text" placeholder="{{ trans('components.imagem_search_hint') }}" ng-model="searchTerm"> | 22 | + <input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm"> |
| 23 | <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> | 23 | <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> |
| 24 | <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button> | 24 | <button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button> |
| 25 | </form> | 25 | </form> |
| ... | @@ -31,11 +31,11 @@ | ... | @@ -31,11 +31,11 @@ |
| 31 | <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}"> | 31 | <img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}"> |
| 32 | <div class="image-meta"> | 32 | <div class="image-meta"> |
| 33 | <span class="name" ng-bind="image.name"></span> | 33 | <span class="name" ng-bind="image.name"></span> |
| 34 | - <span class="date">{{ trans('components.imagem_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span> | 34 | + <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span> |
| 35 | </div> | 35 | </div> |
| 36 | </div> | 36 | </div> |
| 37 | </div> | 37 | </div> |
| 38 | - <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.imagem_load_more') }}</div> | 38 | + <div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div> |
| 39 | </div> | 39 | </div> |
| 40 | </div> | 40 | </div> |
| 41 | 41 | ||
| ... | @@ -51,14 +51,14 @@ | ... | @@ -51,14 +51,14 @@ |
| 51 | </a> | 51 | </a> |
| 52 | </div> | 52 | </div> |
| 53 | <div class="form-group"> | 53 | <div class="form-group"> |
| 54 | - <label for="name">{{ trans('components.imagem_image_name') }}</label> | 54 | + <label for="name">{{ trans('components.image_image_name') }}</label> |
| 55 | <input type="text" id="name" name="name" ng-model="selectedImage.name"> | 55 | <input type="text" id="name" name="name" ng-model="selectedImage.name"> |
| 56 | </div> | 56 | </div> |
| 57 | </form> | 57 | </form> |
| 58 | 58 | ||
| 59 | <div ng-show="dependantPages"> | 59 | <div ng-show="dependantPages"> |
| 60 | <p class="text-neg text-small"> | 60 | <p class="text-neg text-small"> |
| 61 | - {{ trans('components.imagem_delete_confirm') }} | 61 | + {{ trans('components.image_delete_confirm') }} |
| 62 | </p> | 62 | </p> |
| 63 | <ul class="text-neg"> | 63 | <ul class="text-neg"> |
| 64 | <li ng-repeat="page in dependantPages"> | 64 | <li ng-repeat="page in dependantPages"> |
| ... | @@ -72,13 +72,13 @@ | ... | @@ -72,13 +72,13 @@ |
| 72 | <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button> | 72 | <button class="button icon neg"><i class="zmdi zmdi-delete"></i></button> |
| 73 | </form> | 73 | </form> |
| 74 | <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()"> | 74 | <button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()"> |
| 75 | - <i class="zmdi zmdi-square-right"></i>{{ trans('components.imagem_select_image') }} | 75 | + <i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }} |
| 76 | </button> | 76 | </button> |
| 77 | </div> | 77 | </div> |
| 78 | 78 | ||
| 79 | </div> | 79 | </div> |
| 80 | 80 | ||
| 81 | - <drop-zone message="{{ trans('components.imagem_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone> | 81 | + <drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone> |
| 82 | 82 | ||
| 83 | 83 | ||
| 84 | </div> | 84 | </div> | ... | ... |
| 1 | -<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 '' }}"> | 1 | +<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 '' }}"> |
| 2 | 2 | ||
| 3 | <div> | 3 | <div> |
| 4 | <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}"> | 4 | <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}"> |
| 5 | </div> | 5 | </div> |
| 6 | 6 | ||
| 7 | - <button class="button" type="button" data-action="show-image-manager">{{ trans('components.imagem_select_image') }}</button> | 7 | + <button class="button" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button> |
| 8 | <br> | 8 | <br> |
| 9 | <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button> | 9 | <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button> |
| 10 | 10 | ||
| ... | @@ -13,5 +13,54 @@ | ... | @@ -13,5 +13,54 @@ |
| 13 | <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button> | 13 | <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button> |
| 14 | @endif | 14 | @endif |
| 15 | 15 | ||
| 16 | - <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{$currentId or $currentImage or ''}}"> | ||
| 17 | -</div> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 16 | + <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{ isset($currentId) && ($currentId !== '' && $currentId !== false) ? $currentId : $currentImage}}"> | ||
| 17 | +</div> | ||
| 18 | + | ||
| 19 | +<script> | ||
| 20 | + (function(){ | ||
| 21 | + | ||
| 22 | + var picker = document.querySelector('[image-picker="{{$name}}"]'); | ||
| 23 | + picker.addEventListener('click', function(event) { | ||
| 24 | + if (event.target.nodeName.toLowerCase() !== 'button') return; | ||
| 25 | + var button = event.target; | ||
| 26 | + var action = button.getAttribute('data-action'); | ||
| 27 | + var resize = picker.getAttribute('data-resize-height') && picker.getAttribute('data-resize-width'); | ||
| 28 | + var usingIds = picker.getAttribute('data-current-id') !== ''; | ||
| 29 | + var resizeCrop = picker.getAttribute('data-resize-crop') !== ''; | ||
| 30 | + var imageElem = picker.querySelector('img'); | ||
| 31 | + var input = picker.querySelector('input'); | ||
| 32 | + | ||
| 33 | + function setImage(image) { | ||
| 34 | + if (image === 'none') { | ||
| 35 | + imageElem.src = picker.getAttribute('data-default-image'); | ||
| 36 | + imageElem.classList.add('none'); | ||
| 37 | + input.value = 'none'; | ||
| 38 | + return; | ||
| 39 | + } | ||
| 40 | + imageElem.src = image.url; | ||
| 41 | + input.value = usingIds ? image.id : image.url; | ||
| 42 | + imageElem.classList.remove('none'); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + if (action === 'show-image-manager') { | ||
| 46 | + window.ImageManager.showExternal((image) => { | ||
| 47 | + if (!resize) { | ||
| 48 | + setImage(image); | ||
| 49 | + return; | ||
| 50 | + } | ||
| 51 | + var requestString = '/images/thumb/' + image.id + '/' + picker.getAttribute('data-resize-width') + '/' + picker.getAttribute('data-resize-height') + '/' + (resizeCrop ? 'true' : 'false'); | ||
| 52 | + $.get(window.baseUrl(requestString), resp => { | ||
| 53 | + image.url = resp.url; | ||
| 54 | + setImage(image); | ||
| 55 | + }); | ||
| 56 | + }); | ||
| 57 | + } else if (action === 'reset-image') { | ||
| 58 | + setImage({id: 0, url: picker.getAttribute('data-default-image')}); | ||
| 59 | + } else if (action === 'remove-image') { | ||
| 60 | + setImage('none'); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + }); | ||
| 64 | + | ||
| 65 | + })(); | ||
| 66 | +</script> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | -<div toggle-switch class="toggle-switch @if($value) active @endif"> | 1 | +<div toggle-switch="{{$name}}" class="toggle-switch @if($value) active @endif"> |
| 2 | <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/> | 2 | <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/> |
| 3 | <div class="switch-handle"></div> | 3 | <div class="switch-handle"></div> |
| 4 | -</div> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 4 | +</div> | ||
| 5 | +<script> | ||
| 6 | + (function() { | ||
| 7 | + var toggle = document.querySelector('[toggle-switch="{{$name}}"]'); | ||
| 8 | + var toggleInput = toggle.querySelector('input'); | ||
| 9 | + toggle.onclick = function(event) { | ||
| 10 | + var checked = toggleInput.value !== 'true'; | ||
| 11 | + toggleInput.value = checked ? 'true' : 'false'; | ||
| 12 | + checked ? toggle.classList.add('active') : toggle.classList.remove('active'); | ||
| 13 | + }; | ||
| 14 | + })() | ||
| 15 | +</script> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -20,7 +20,7 @@ | ... | @@ -20,7 +20,7 @@ |
| 20 | 20 | ||
| 21 | </div> | 21 | </div> |
| 22 | 22 | ||
| 23 | - @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id]) | 23 | + @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id]) |
| 24 | - @include('partials/entity-selector-popup') | 24 | + @include('components.entity-selector-popup') |
| 25 | 25 | ||
| 26 | @stop | 26 | @stop |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -19,7 +19,7 @@ | ... | @@ -19,7 +19,7 @@ |
| 19 | {!! csrf_field() !!} | 19 | {!! csrf_field() !!} |
| 20 | <input type="hidden" name="_method" value="PUT"> | 20 | <input type="hidden" name="_method" value="PUT"> |
| 21 | 21 | ||
| 22 | - @include('partials/entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter']) | 22 | + @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter']) |
| 23 | 23 | ||
| 24 | <a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a> | 24 | <a href="{{ $page->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a> |
| 25 | <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button> | 25 | <button type="submit" class="button pos">{{ trans('entities.pages_move') }}</button> | ... | ... |
| 1 | -<style> | 1 | +<style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}"> |
| 2 | header, #back-to-top, .primary-background { | 2 | header, #back-to-top, .primary-background { |
| 3 | background-color: {{ setting('app-color') }} !important; | 3 | background-color: {{ setting('app-color') }} !important; |
| 4 | } | 4 | } | ... | ... |
| ... | @@ -56,7 +56,8 @@ | ... | @@ -56,7 +56,8 @@ |
| 56 | 'defaultImage' => baseUrl('/logo.png'), | 56 | 'defaultImage' => baseUrl('/logo.png'), |
| 57 | 'currentImage' => setting('app-logo'), | 57 | 'currentImage' => setting('app-logo'), |
| 58 | 'name' => 'setting-app-logo', | 58 | 'name' => 'setting-app-logo', |
| 59 | - 'imageClass' => 'logo-image' | 59 | + 'imageClass' => 'logo-image', |
| 60 | + 'currentId' => false | ||
| 60 | ]) | 61 | ]) |
| 61 | 62 | ||
| 62 | </div> | 63 | </div> |
| ... | @@ -125,7 +126,7 @@ | ... | @@ -125,7 +126,7 @@ |
| 125 | 126 | ||
| 126 | </div> | 127 | </div> |
| 127 | 128 | ||
| 128 | -@include('partials/image-manager', ['imageType' => 'system']) | 129 | +@include('components.image-manager', ['imageType' => 'system']) |
| 129 | 130 | ||
| 130 | @stop | 131 | @stop |
| 131 | 132 | ||
| ... | @@ -142,10 +143,16 @@ | ... | @@ -142,10 +143,16 @@ |
| 142 | var isEmpty = $.trim($elm.val()).length === 0; | 143 | var isEmpty = $.trim($elm.val()).length === 0; |
| 143 | if (!isEmpty) $elm.val(hexVal); | 144 | if (!isEmpty) $elm.val(hexVal); |
| 144 | $('#setting-app-color-light').val(isEmpty ? '' : rgbLightVal); | 145 | $('#setting-app-color-light').val(isEmpty ? '' : rgbLightVal); |
| 145 | - // Set page elements to provide preview | 146 | + |
| 146 | - $('#header, .image-picker .button').attr('style', 'background-color:'+ hexVal+'!important;'); | 147 | + var customStyles = document.getElementById('custom-styles'); |
| 147 | - $('.faded-small').css('background-color', rgbLightVal); | 148 | + var oldColor = customStyles.getAttribute('data-color'); |
| 148 | - $('.setting-nav a.selected').css('border-bottom-color', hexVal + '!important'); | 149 | + var oldColorLight = customStyles.getAttribute('data-color-light'); |
| 150 | + | ||
| 151 | + customStyles.innerHTML = customStyles.innerHTML.split(oldColor).join(hexVal); | ||
| 152 | + customStyles.innerHTML = customStyles.innerHTML.split(oldColorLight).join(rgbLightVal); | ||
| 153 | + | ||
| 154 | + customStyles.setAttribute('data-color', hexVal); | ||
| 155 | + customStyles.setAttribute('data-color-light', rgbLightVal); | ||
| 149 | } | 156 | } |
| 150 | }); | 157 | }); |
| 151 | </script> | 158 | </script> | ... | ... |
| ... | @@ -20,13 +20,13 @@ | ... | @@ -20,13 +20,13 @@ |
| 20 | <tr> | 20 | <tr> |
| 21 | <th>{{ trans('settings.role_name') }}</th> | 21 | <th>{{ trans('settings.role_name') }}</th> |
| 22 | <th></th> | 22 | <th></th> |
| 23 | - <th class="text-right">{{ trans('settings.users') }}</th> | 23 | + <th class="text-center">{{ trans('settings.users') }}</th> |
| 24 | </tr> | 24 | </tr> |
| 25 | @foreach($roles as $role) | 25 | @foreach($roles as $role) |
| 26 | <tr> | 26 | <tr> |
| 27 | <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td> | 27 | <td><a href="{{ baseUrl("/settings/roles/{$role->id}") }}">{{ $role->display_name }}</a></td> |
| 28 | <td>{{ $role->description }}</td> | 28 | <td>{{ $role->description }}</td> |
| 29 | - <td class="text-right">{{ $role->users->count() }}</td> | 29 | + <td class="text-center">{{ $role->users->count() }}</td> |
| 30 | </tr> | 30 | </tr> |
| 31 | @endforeach | 31 | @endforeach |
| 32 | </table> | 32 | </table> | ... | ... |
| ... | @@ -85,5 +85,5 @@ | ... | @@ -85,5 +85,5 @@ |
| 85 | </div> | 85 | </div> |
| 86 | 86 | ||
| 87 | <p class="margin-top large"><br></p> | 87 | <p class="margin-top large"><br></p> |
| 88 | - @include('partials/image-manager', ['imageType' => 'user']) | 88 | + @include('components.image-manager', ['imageType' => 'user']) |
| 89 | @stop | 89 | @stop | ... | ... |
-
Please register or sign in to post a comment