diff --git a/controllers/BaseController.php b/controllers/BaseController.php index 4219e7be..8a84b68c 100644 --- a/controllers/BaseController.php +++ b/controllers/BaseController.php @@ -197,7 +197,7 @@ class BaseController return $this->View->render($response, $page, $data); } - protected function renderPage($response, $page, $data = []) + protected function renderPage($request, $response, $page, $data = []) { $this->View->set('userentitiesForSidebar', $this->getDatabase()->userentities()->where('show_in_sidebar_menu = 1')->orderBy('name')); try @@ -217,7 +217,7 @@ class BaseController // Happens when database is not initialised or migrated... } - return $this->render($response, $page, $data); + return $this->render($request, $response, $page, $data); } private static $htmlPurifierInstance = null; diff --git a/controllers/OpenApiController.php b/controllers/OpenApiController.php index e3c35159..cd4d19e2 100644 --- a/controllers/OpenApiController.php +++ b/controllers/OpenApiController.php @@ -68,7 +68,7 @@ class OpenApiController extends BaseApiController public function DocumentationUi(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { - return $this->render($response, 'openapiui'); + return $this->render($request, $response, 'openapiui'); } public function __construct(\DI\Container $container) diff --git a/gulpfile.babel.js b/gulpfile.babel.js index b35b6b65..3ce7d0cd 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -88,7 +88,6 @@ view_eslint_config.globals = eslint_config.globals.concat([ // viewjs handling var files = glob.sync('./js/viewjs/*.js'); -var components = glob.sync('./js/viewjs/components/*.js'); var viewJStasks = []; @@ -111,32 +110,10 @@ files.forEach(function(target) .pipe(gulpif(minify, uglify())) .pipe(buffer()) .pipe(sourcemaps.init({ loadMaps: true })) - .pipe(sourcemaps.write('.')) + .pipe(sourcemaps.write('.', { sourceMappingURLPrefix: '/viewjs' })) .pipe(dest('./public/viewjs'))); viewJStasks.push(target); }); -components.forEach(function(target) -{ - task(target, cb => rollup({ - input: target, - output: { - format: 'umd', - name: path.basename(target), - sourcemap: 'inline', - }, - plugins: [resolve(), rollupCss({ - dest: path.resolve('./public/css/viewcss/' + path.basename(target).replace(".js", ".css")), - }), commonjs(), eslint(view_eslint_config)], - }) - .pipe(source(path.basename(target), "./js/viewjs/components")) - .pipe(buffer()) - .pipe(gulpif(minify, uglify())) - .pipe(buffer()) - .pipe(sourcemaps.init({ loadMaps: true })) - .pipe(sourcemaps.write('.')) - .pipe(dest('./public/viewjs/components'))); - viewJStasks.push(target); -}); // The `clean` function is not exported so it can be considered a private task. // It can still be used within the `series()` composition. diff --git a/js/grocy.js b/js/grocy.js index 6f272818..fe11f001 100644 --- a/js/grocy.js +++ b/js/grocy.js @@ -107,6 +107,21 @@ class GrocyClass } }); + window.addEventListener('load', function() + { + if (self.documentReady) return; + + // preload views + self.documentReady = true; + var element = self.preloadViews.pop(); + while (element !== undefined) + { + self.PreloadView(element.viewName, element.loadCss, element.cb); + + element = self.preloadViews.pop(); + } + }); + // save the config this.config = config; @@ -118,18 +133,6 @@ class GrocyClass } }); } - - $(document).on('ready', () => - { - this.documentReady = true; - var element = self.preloadViews.pop(); - while (element !== undefined) - { - self.preloadViews(element.viewName, element.loadCss, element.cb); - - element = self.preloadViews.pop(); - } - }) } static createSingleton(config, view) @@ -280,7 +283,7 @@ class GrocyClass { if (!this.documentReady) { - this.preloadViews.push({ viewName: viewName, loadCss: loadCss, cb: cb }); + this.preloadViews.push({ viewName: viewName, loadCss: loadCSS, cb: cb }); return; } @@ -297,7 +300,7 @@ class GrocyClass $("", { rel: "stylesheet", type: "text/css", - href: this.FormatUrl('/css/viewcss/' + viewName + '.cs') + href: this.FormatUrl('/css/viewcss/' + viewName + '.css') }).appendTo("head"); } } @@ -310,16 +313,51 @@ class GrocyClass OpenSubView(link) { var self = this; + console.log("loading subview " + link); $.ajax({ dataType: "json", - link, + url: link, success: (data) => { let scopeId = uuid.v4() - var proxy = new GrocyProxy(this, "#" + scopeId, data.config, link); + var grocyProxy = new GrocyProxy(this, "#" + scopeId, data.config, link); + var proxy = new Proxy(grocyProxy, { + get: function(target, prop, receiver) + { + if (prop in grocyProxy) + { + return grocyProxy[prop]; + } + else + { + return self[prop]; + } + }, + apply: function(target, thisArg, args) + { + if (target in grocyProxy) + { + return grocyProxy[target](...args); + } + else + { + return self[target](...args); + } + }, + ownKeys: function(oTarget, sKey) + { + let root = Reflect.ownKeys(self); + Array.concat(root, Reflect.ownKeys(grocyProxy)); + return root; + }, + has: function(oTarget, sKey) + { + return sKey in self || sKey in grocyProxy; + }, + }); bootbox.dialog({ - message: '
' + data.template + '
', + message: '
' + data.template + '
', size: 'large', backdrop: true, closeButton: false, @@ -329,7 +367,6 @@ class GrocyClass className: 'btn-secondary responsive-button', callback: function() { - proxy.Unload(); bootbox.hideAll(); } } @@ -338,10 +375,12 @@ class GrocyClass { // dialog div is alive, init view. // this occurs before the view is shown. + grocyProxy.Initialize(proxy); self.LoadView(data.viewJsName, "#" + scopeId, proxy); } }); - } + }, + error: (xhr, text, data) => { console.error(text); } }) diff --git a/js/helpers/frontend.js b/js/helpers/frontend.js index e1c20378..d6037b06 100644 --- a/js/helpers/frontend.js +++ b/js/helpers/frontend.js @@ -5,11 +5,11 @@ class GrocyFrontendHelpers { this.Grocy = Grocy; this.Api = Api; - var self = this; if (scope != null) { this.scope = $(scope); - this.$scope = (selector) => self.scope.find(selector); + var jScope = this.scope; + this.$scope = (selector) => jScope.find(selector); this.scopeSelector = scope; } else diff --git a/js/lib/proxy.js b/js/lib/proxy.js index ddc5600b..820bef69 100644 --- a/js/lib/proxy.js +++ b/js/lib/proxy.js @@ -4,14 +4,13 @@ import * as components from '../components'; class GrocyProxy { - constructor(RootGrocy, scopeSelector, config, url) { this.RootGrocy = RootGrocy; // proxy-local members, because they might not be set globally. this.QuantityUnits = config.QuantityUnits; - this.QuantityUnitConversionsResolved = config.QuantityUnitConversionsResolved || []; + this.QuantityUnitConversionsResolved = config.QuantityUnitConversionsResolved; this.QuantityUnitEditFormRedirectUri = config.QuantityUnitEditFormRedirectUri; this.MealPlanFirstDayOfWeek = config.MealPlanFirstDayOfWeek; this.EditMode = config.EditMode; @@ -31,9 +30,9 @@ class GrocyProxy // scoping this.scopeSelector = scopeSelector; - this.scope = $(scopeSelector); - var jScope = this.scope; - this.$scope = (selector) => jScope.find(selector); + this.scope = null; + this.$scope = null; + var queryString = url.split('?', 2); this.virtualUrl = queryString.length == 2 ? queryString[1] : ""; // maximum two parts @@ -45,52 +44,19 @@ class GrocyProxy Object.assign(this.config.UserSettings, RootGrocy.UserSettings); this.UserSettings = config.UserSettings; } - - this.configProxy = Proxy.revocable(this.config, { - get: function(target, prop, receiver) - { - if (Object.prototype.hasOwnProperty.call(target, prop)) - { - return Reflect.get(...arguments); - } - else - { - return Reflect.get(RootGrocy.config, prop, target); - } - } - }) - - // This is where the magic happens! - // basically, this Proxy object checks if a member is defined in this proxy class, - // and returns it if so. - // If not, the prop is handed over to the root grocy instance. - this.grocyProxy = Proxy.revocable(this, { - get: function(target, prop, receiver) - { - if (Object.prototype.hasOwnProperty.call(target, prop)) - { - return Reflect.get(...arguments) - } - else - { - return Reflect.get(RootGrocy, prop, receiver); - } - } - }); - - // scoped variants of some helpers - this.FrontendHelpers = new GrocyFrontendHelpers(this, RootGrocy.Api, this.scopeSelector); } - Unload() + Initialize(proxy) { - this.grocyProxy.revoke(); - this.configProxy.revoke(); + this.scope = $(this.scopeSelector); + var jScope = this.scope; + this.$scope = (selector) => jScope.find(selector); + this.FrontendHelpers = new GrocyFrontendHelpers(proxy, this.RootGrocy.Api, this.scopeSelector); } Use(componentName, scope = null) { - let scopeName = scope || ""; + let scopeName = scope || this.scopeSelector; // initialize Components only once per scope if (this.initComponents.find(elem => elem == componentName + scopeName)) return this.Components[componentName + scopeName]; @@ -109,14 +75,6 @@ class GrocyProxy } } - LoadView(viewName) - { - if (Object.prototype.hasOwnProperty.call(window, viewName + "View")) - { - window[viewName + "View"](this, this.scopeSelector); - } - } - // URI params on integrated components don't work because they // don't have an URL. So let's fake it. GetUriParam(key) diff --git a/js/viewjs/batteries.js b/js/viewjs/batteries.js index 48e104a9..9ba4be21 100644 --- a/js/viewjs/batteries.js +++ b/js/viewjs/batteries.js @@ -7,10 +7,8 @@ } // preload some views. - top.on('load', () => - { - Grocy.PreloadView("batteryform"); - }); + Grocy.PreloadView("batteryform"); + var batteriesTable = $scope('#batteries-table').DataTable({ 'order': [[1, 'asc']], diff --git a/js/viewjs/batteriesoverview.js b/js/viewjs/batteriesoverview.js index ad7641c5..d7bd8b4a 100644 --- a/js/viewjs/batteriesoverview.js +++ b/js/viewjs/batteriesoverview.js @@ -9,11 +9,9 @@ var batterycard = Grocy.Use("batterycard"); // preload some views. - top.on('load', () => - { - Grocy.PreloadView("batteriesjournal"); - Grocy.PreloadView("batteryform"); - }); + Grocy.PreloadView("batteriesjournal"); + Grocy.PreloadView("batteryform"); + var batteriesOverviewTable = $scope('#batteries-overview-table').DataTable({ 'order': [[4, 'asc']], diff --git a/js/viewjs/choresoverview.js b/js/viewjs/choresoverview.js index 345ab438..7f492a39 100644 --- a/js/viewjs/choresoverview.js +++ b/js/viewjs/choresoverview.js @@ -11,10 +11,8 @@ var chorecard = Grocy.Use("chorecard"); // preload some views. - top.on('load', () => - { - Grocy.PreloadView("choresjournal"); - }); + Grocy.PreloadView("choresjournal"); + var choresOverviewTable = $scope('#chores-overview-table').DataTable({ 'order': [[2, 'asc']], diff --git a/js/viewjs/locations.js b/js/viewjs/locations.js index 552d3d3b..bd893656 100644 --- a/js/viewjs/locations.js +++ b/js/viewjs/locations.js @@ -7,10 +7,7 @@ } // preload some views. - top.on('load', () => - { - Grocy.PreloadView("locationform"); - }); + Grocy.PreloadView("locationform"); var locationsTable = $scope('#locations-table').DataTable({ 'order': [[1, 'asc']], diff --git a/js/viewjs/productform.js b/js/viewjs/productform.js index 2c0149cd..b7e8bc8d 100644 --- a/js/viewjs/productform.js +++ b/js/viewjs/productform.js @@ -13,10 +13,7 @@ function productformView(Grocy, scope = null) Grocy.Use("numberpicker"); // preload some views. - top.on('load', () => - { - Grocy.PreloadView("productgroupform"); - }); + Grocy.PreloadView("productgroupform"); var shoppinglocationpicker = Grocy.Use("shoppinglocationpicker"); var userfields = Grocy.Use("userfieldsform"); diff --git a/js/viewjs/stockentries.js b/js/viewjs/stockentries.js index ede85e9b..d42054bc 100644 --- a/js/viewjs/stockentries.js +++ b/js/viewjs/stockentries.js @@ -15,7 +15,7 @@ Grocy.PreloadView("stockentryform"); Grocy.PreloadView("shoppinglistitemform"); Grocy.PreloadView("purchase"); - Grocy.PreloadView("conusme"); + Grocy.PreloadView("consume"); Grocy.PreloadView("inventory"); Grocy.PreloadView("stockjournal"); Grocy.PreloadView("stockjournalsummary"); diff --git a/js/viewjs/stockoverview.js b/js/viewjs/stockoverview.js index 250bbfd7..42f00b9f 100755 --- a/js/viewjs/stockoverview.js +++ b/js/viewjs/stockoverview.js @@ -14,7 +14,7 @@ Grocy.PreloadView("stockentries"); Grocy.PreloadView("shoppinglistitemform"); Grocy.PreloadView("purchase"); - Grocy.PreloadView("conusme"); + Grocy.PreloadView("consume"); Grocy.PreloadView("inventory"); Grocy.PreloadView("stockjournal"); Grocy.PreloadView("stockjournalsummary"); diff --git a/views/layout/json.blade.php b/views/layout/json.blade.php index c006ea1f..abde07a1 100644 --- a/views/layout/json.blade.php +++ b/views/layout/json.blade.php @@ -1,5 +1,28 @@ @php // this is a shoe-horned method to generate a "proper" subview as JSON. +$content = $__env->yieldContent('content'); +// Javascript is not JSON, so we need to do some trickery +// to reencode stuff like additional , at the end or +// ' instead of " as field delimter. +$config = "{\n" . $__env->yieldContent('grocyConfigProps') . '}'; +$config = preg_replace('/(\n[\t ]*)([a-zA-Z0-9]+):/','${1}"${2}":', $config); +$config = preg_replace('/: *\'(.*?)\',?\n/', ':"${1}",', $config); +$config = preg_replace('/,}$/', '}', $config); +$grocy_options = json_decode($config, true); + +$usersettings = $__env->yieldContent('forceUserSettings'); +$usersettings = json_decode('{' . $usersettings . '}'); +if($usersettings != null) + $grocy_options["UserSettings"] = $usersettings; + +// worst case this burns on the front end. +$viewname = trim($__env->yieldContent('viewJsName')); + +echo json_encode([ + 'template' => $content, + 'config' => $grocy_options, + 'viewJsName' => $viewname, + ]); @endphp \ No newline at end of file diff --git a/views/purchase.blade.php b/views/purchase.blade.php index 2820ce8e..e66cd69c 100644 --- a/views/purchase.blade.php +++ b/views/purchase.blade.php @@ -17,8 +17,12 @@ @endsection @section('content') +@php +$classes = $embedded ? '' : 'col-md-6 col-xl-4'; +@endphp +
-
+