diff --git a/js/components/barcodescanner.js b/js/components/barcodescanner.js new file mode 100644 index 00000000..0e90875d --- /dev/null +++ b/js/components/barcodescanner.js @@ -0,0 +1,306 @@ +import Quagga from '@ericblade/quagga2/dist/quagga'; + +function barcodescanner(Grocy) +{ + Grocy.Components.BarcodeScanner = {}; + + Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = false; + Grocy.Components.BarcodeScanner.CheckCapabilities = async function() + { + var track = Quagga.CameraAccess.getActiveTrack(); + var capabilities = {}; + if (typeof track.getCapabilities === 'function') + { + capabilities = track.getCapabilities(); + } + + // If there is more than 1 camera, show the camera selection + var cameras = await Quagga.CameraAccess.enumerateVideoDevices(); + var cameraSelect = document.querySelector('.cameraSelect-wrapper'); + if (cameraSelect) + { + cameraSelect.style.display = cameras.length > 1 ? 'inline-block' : 'none'; + } + + // Check if the camera is capable to turn on a torch. + var canTorch = typeof capabilities.torch === 'boolean' && capabilities.torch + // Remove the torch button, if either the device can not torch or AutoTorchOn is set. + var node = document.querySelector('.torch'); + if (node) + { + node.style.display = canTorch && !Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA ? 'inline-block' : 'none'; + } + // If AutoTorchOn is set, turn on the torch. + if (canTorch && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA) + { + Grocy.Components.BarcodeScanner.TorchOn(track); + } + + // Reduce the height of the video, if it's higher than then the viewport + if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted) + { + var bc = document.getElementById('barcodescanner-container'); + if (bc) + { + var bcAspectRatio = bc.offsetWidth / bc.offsetHeight; + var settings = track.getSettings(); + if (bcAspectRatio > settings.aspectRatio) + { + var v = document.querySelector('#barcodescanner-livestream video') + if (v) + { + var c = document.querySelector('#barcodescanner-livestream canvas') + var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px'; + v.style.width = newWidth; + c.style.width = newWidth; + } + } + + Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = true; + } + } + } + + Grocy.Components.BarcodeScanner.StartScanning = function() + { + Grocy.Components.BarcodeScanner.DecodedCodesCount = 0; + Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0; + + Quagga.init({ + inputStream: { + name: "Live", + type: "LiveStream", + target: document.querySelector("#barcodescanner-livestream"), + constraints: { + facingMode: "environment", + ...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }) // If preferred cameraId is set, request to use that specific camera + } + }, + locator: { + patchSize: Grocy.UserSettings.quagga2_patchsize, + halfSample: Grocy.UserSettings.quagga2_halfsample, + debug: { + showCanvas: Grocy.UserSettings.quagga2_debug, + showPatches: Grocy.UserSettings.quagga2_debug, + showFoundPatches: Grocy.UserSettings.quagga2_debug, + showSkeleton: Grocy.UserSettings.quagga2_debug, + showLabels: Grocy.UserSettings.quagga2_debug, + showPatchLabels: Grocy.UserSettings.quagga2_debug, + showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug, + boxFromPatches: { + showTransformed: Grocy.UserSettings.quagga2_debug, + showTransformedBox: Grocy.UserSettings.quagga2_debug, + showBB: Grocy.UserSettings.quagga2_debug + } + } + }, + numOfWorkers: Grocy.UserSettings.quagga2_numofworkers, + frequency: Grocy.UserSettings.quagga2_frequency, + decoder: { + readers: [ + "ean_reader", + "ean_8_reader", + "code_128_reader" + ], + debug: { + showCanvas: Grocy.UserSettings.quagga2_debug, + showPatches: Grocy.UserSettings.quagga2_debug, + showFoundPatches: Grocy.UserSettings.quagga2_debug, + showSkeleton: Grocy.UserSettings.quagga2_debug, + showLabels: Grocy.UserSettings.quagga2_debug, + showPatchLabels: Grocy.UserSettings.quagga2_debug, + showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug, + boxFromPatches: { + showTransformed: Grocy.UserSettings.quagga2_debug, + showTransformedBox: Grocy.UserSettings.quagga2_debug, + showBB: Grocy.UserSettings.quagga2_debug + } + } + }, + locate: true + }, function(error) + { + // error *needs* to be logged here, otherwise the stack trace is lying. + console.error(error); + if (error) + { + Grocy.FrontendHelpers.ShowGenericError("Error while initializing the barcode scanning library", error.message); + toastr.info(Grocy.translate("Camera access is only possible when supported and allowed by your browser and when grocy is served via a secure (https://) connection")); + window.localStorage.removeItem("cameraId"); + setTimeout(function() + { + bootbox.hideAll(); + }, 500); + return; + } + + Grocy.Components.BarcodeScanner.CheckCapabilities(); + + Quagga.start(); + }); + } + + Grocy.Components.BarcodeScanner.StopScanning = function() + { + Quagga.stop(); + + Grocy.Components.BarcodeScanner.DecodedCodesCount = 0; + Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0; + + bootbox.hideAll(); + } + + Grocy.Components.BarcodeScanner.TorchOn = function(track) + { + if (track) + { + track.applyConstraints({ + advanced: [ + { + torch: true + } + ] + }); + } + } + + Quagga.onDetected(function(result) + { + $.each(result.codeResult.decodedCodes, function(id, error) + { + if (error.error != undefined) + { + Grocy.Components.BarcodeScanner.DecodedCodesCount++; + Grocy.Components.BarcodeScanner.DecodedCodesErrorCount += parseFloat(error.error); + } + }); + + if (Grocy.Components.BarcodeScanner.DecodedCodesErrorCount / Grocy.Components.BarcodeScanner.DecodedCodesCount < 0.15) + { + Grocy.Components.BarcodeScanner.StopScanning(); + $(document).trigger("Grocy.BarcodeScanned", [result.codeResult.code, Grocy.Components.BarcodeScanner.CurrentTarget]); + } + }); + + Quagga.onProcessed(function(result) + { + var drawingCtx = Quagga.canvas.ctx.overlay; + var drawingCanvas = Quagga.canvas.dom.overlay; + + if (result) + { + if (result.boxes) + { + drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height"))); + result.boxes.filter(function(box) + { + return box !== result.box; + }).forEach(function(box) + { + Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "yellow", lineWidth: 4 }); + }); + } + + if (result.box) + { + Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 4 }); + } + + if (result.codeResult && result.codeResult.code) + { + Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: "red", lineWidth: 4 }); + } + } + }); + + $(document).on("click", "#barcodescanner-start-button", async function(e) + { + e.preventDefault(); + var inputElement = $(e.currentTarget).prev(); + if (inputElement.hasAttr("disabled")) + { + // Do nothing and disable the barcode scanner start button + $(e.currentTarget).addClass("disabled"); + return; + } + + Grocy.Components.BarcodeScanner.CurrentTarget = inputElement.attr("data-target"); + + var dialog = bootbox.dialog({ + message: '
', + title: Grocy.translate('Scan a barcode'), + onEscape: function() + { + Grocy.Components.BarcodeScanner.StopScanning(); + }, + size: 'big', + backdrop: true, + closeButton: true, + buttons: { + torch: { + label: '', + className: 'btn-warning responsive-button torch', + callback: function() + { + Grocy.Components.BarcodeScanner.TorchOn(Quagga.CameraAccess.getActiveTrack()); + return false; + } + }, + cancel: { + label: Grocy.translate('Cancel'), + className: 'btn-secondary responsive-button', + callback: function() + { + Grocy.Components.BarcodeScanner.StopScanning(); + } + } + } + }); + + // Add camera select to existing dialog + dialog.find('.bootbox-body').append('
'); + var cameraSelect = document.querySelector('.cameraSelect'); + + if (cameraSelect != null) + { + var cameras = await Quagga.CameraAccess.enumerateVideoDevices(); + cameras.forEach(camera => + { + var option = document.createElement("option"); + option.text = camera.label ? camera.label : camera.deviceId; // Use camera label if it exists, else show device id + option.value = camera.deviceId; + cameraSelect.appendChild(option); + }); + + // Set initial value to preferred camera if one exists - and if not, start out empty + cameraSelect.value = window.localStorage.getItem('cameraId'); + + cameraSelect.onchange = function() + { + window.localStorage.setItem('cameraId', cameraSelect.value); + Quagga.stop(); + Grocy.Components.BarcodeScanner.StartScanning(); + }; + } + + Grocy.Components.BarcodeScanner.StartScanning(); + }); + + setTimeout(function() + { + $(".barcodescanner-input:visible").each(function() + { + if ($(this).hasAttr("disabled")) + { + $(this).after(''); + } + else + { + $(this).after(''); + } + }); + }, 50); + +} + +export { barcodescanner } \ No newline at end of file diff --git a/js/components/batterycard.js b/js/components/batterycard.js new file mode 100644 index 00000000..f2159d19 --- /dev/null +++ b/js/components/batterycard.js @@ -0,0 +1,36 @@ +import { EmptyElementWhenMatches } from '../helpers/extensions' +import { RefreshContextualTimeago } from '../configs/timeago' + +function batterycard(Grocy) +{ + + Grocy.Components.BatteryCard = {}; + + Grocy.Components.BatteryCard.Refresh = function(batteryId) + { + Grocy.Api.Get('batteries/' + batteryId, + function(batteryDetails) + { + $('#batterycard-battery-name').text(batteryDetails.battery.name); + $('#batterycard-battery-used_in').text(batteryDetails.battery.used_in); + $('#batterycard-battery-last-charged').text((batteryDetails.last_charged || Grocy.translate('never'))); + $('#batterycard-battery-last-charged-timeago').attr("datetime", batteryDetails.last_charged || ''); + $('#batterycard-battery-charge-cycles-count').text((batteryDetails.charge_cycles_count || '0')); + + $('#batterycard-battery-edit-button').attr("href", Grocy.FormatUrl("/battery/" + batteryDetails.battery.id.toString())); + $('#batterycard-battery-journal-button').attr("href", Grocy.FormatUrl("/batteriesjournal?embedded&battery=" + batteryDetails.battery.id.toString())); + $('#batterycard-battery-edit-button').removeClass("disabled"); + $('#batterycard-battery-journal-button').removeClass("disabled"); + + EmptyElementWhenMatches('#batterycard-battery-last-charged-timeago', Grocy.translate('timeago_nan')); + RefreshContextualTimeago(".batterycard"); + }, + function(xhr) + { + console.error(xhr); + } + ); + }; +} + +export { batterycard } \ No newline at end of file diff --git a/js/components/calendarcard.js b/js/components/calendarcard.js new file mode 100644 index 00000000..11a16131 --- /dev/null +++ b/js/components/calendarcard.js @@ -0,0 +1,45 @@ +function calendarcard(Grocy) +{ + $('#calendar').datetimepicker( + { + format: 'L', + buttons: { + showToday: true, + showClose: false + }, + calendarWeeks: true, + locale: moment.locale(), + icons: { + time: 'far fa-clock', + date: 'far fa-calendar', + up: 'fas fa-arrow-up', + down: 'fas fa-arrow-down', + previous: 'fas fa-chevron-left', + next: 'fas fa-chevron-right', + today: 'fas fa-calendar-check', + clear: 'far fa-trash-alt', + close: 'far fa-times-circle' + }, + keepOpen: true, + inline: true, + keyBinds: { + up: function(widget) { }, + down: function(widget) { }, + 'control up': function(widget) { }, + 'control down': function(widget) { }, + left: function(widget) { }, + right: function(widget) { }, + pageUp: function(widget) { }, + pageDown: function(widget) { }, + enter: function(widget) { }, + escape: function(widget) { }, + 'control space': function(widget) { }, + t: function(widget) { }, + 'delete': function(widget) { } + } + }); + + $('#calendar').datetimepicker('show'); +} + +export { calendarcard } \ No newline at end of file diff --git a/js/components/chorecard.js b/js/components/chorecard.js new file mode 100644 index 00000000..05595a12 --- /dev/null +++ b/js/components/chorecard.js @@ -0,0 +1,43 @@ +import { EmptyElementWhenMatches } from '../helpers/extensions' +import { RefreshContextualTimeago } from '../configs/timeago' + +function chorecard(Grocy) +{ + Grocy.Components.ChoreCard = {}; + + Grocy.Components.ChoreCard.Refresh = function(choreId) + { + Grocy.Api.Get('chores/' + choreId, + function(choreDetails) + { + $('#chorecard-chore-name').text(choreDetails.chore.name); + $('#chorecard-chore-last-tracked').text((choreDetails.last_tracked || Grocy.translate('never'))); + $('#chorecard-chore-last-tracked-timeago').attr("datetime", choreDetails.last_tracked || ''); + $('#chorecard-chore-tracked-count').text((choreDetails.tracked_count || '0')); + $('#chorecard-chore-last-done-by').text((choreDetails.last_done_by.display_name || Grocy.translate('Unknown'))); + + $('#chorecard-chore-edit-button').attr("href", Grocy.FormatUrl("/chore/" + choreDetails.chore.id.toString())); + $('#chorecard-chore-journal-button').attr("href", Grocy.FormatUrl("/choresjournal?embedded&chore=" + choreDetails.chore.id.toString())); + $('#chorecard-chore-edit-button').removeClass("disabled"); + $('#chorecard-chore-journal-button').removeClass("disabled"); + + if (choreDetails.chore.track_date_only == 1) + { + $("#chorecard-chore-last-tracked-timeago").addClass("timeago-date-only"); + } + else + { + $("#chorecard-chore-last-tracked-timeago").removeClass("timeago-date-only"); + } + + EmptyElementWhenMatches('#chorecard-chore-last-tracked-timeago', Grocy.translate('timeago_nan')); + RefreshContextualTimeago(".chorecard"); + }, + function(xhr) + { + console.error(xhr); + } + ); + }; +} +export { chorecard } \ No newline at end of file diff --git a/js/components/datetimepicker.js b/js/components/datetimepicker.js new file mode 100644 index 00000000..06b3caec --- /dev/null +++ b/js/components/datetimepicker.js @@ -0,0 +1,317 @@ +import { EmptyElementWhenMatches } from '../helpers/extensions' +import { RefreshContextualTimeago } from '../configs/timeago' + +function datetimepicker(Grocy) +{ + Grocy.Components.DateTimePicker = {}; + + Grocy.Components.DateTimePicker.GetInputElement = function() + { + return $('.datetimepicker').find('input').not(".form-check-input"); + } + + Grocy.Components.DateTimePicker.GetValue = function() + { + return Grocy.Components.DateTimePicker.GetInputElement().val(); + } + + Grocy.Components.DateTimePicker.SetValue = function(value) + { + // "Click" the shortcut checkbox when the desired value is + // not the shortcut value and it is currently set + var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); + if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked")) + { + $("#datetimepicker-shortcut").click(); + } + Grocy.Components.DateTimePicker.GetInputElement().val(value); + Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); + + Grocy.Components.DateTimePicker.GetInputElement().keyup(); + } + + Grocy.Components.DateTimePicker.Clear = function() + { + $(".datetimepicker").datetimepicker("destroy"); + Grocy.Components.DateTimePicker.Init(); + + Grocy.Components.DateTimePicker.GetInputElement().val(""); + + // "Click" the shortcut checkbox when the desired value is + // not the shortcut value and it is currently set + var value = ""; + var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); + if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked")) + { + $("#datetimepicker-shortcut").click(); + } + + $('#datetimepicker-timeago').text(''); + } + + Grocy.Components.DateTimePicker.ChangeFormat = function(format) + { + $(".datetimepicker").datetimepicker("destroy"); + Grocy.Components.DateTimePicker.GetInputElement().data("format", format); + Grocy.Components.DateTimePicker.Init(); + + if (format == "YYYY-MM-DD") + { + Grocy.Components.DateTimePicker.GetInputElement().addClass("date-only-datetimepicker"); + } + else + { + Grocy.Components.DateTimePicker.GetInputElement().removeClass("date-only-datetimepicker"); + } + } + + var startDate = null; + var inputElement = Grocy.Components.DateTimePicker.GetInputElement(); + if (inputElement.data('init-with-now') === true) + { + startDate = moment().format(inputElement.data('format')); + } + if (inputElement.data('init-value').length > 0) + { + startDate = moment(inputElement.data('init-value')).format(Grocy.Components.DateTimePicker.GetInputElement().data('format')); + } + + var limitDate = moment('2999-12-31 23:59:59'); + if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true) + { + limitDate = moment(); + } + + Grocy.Components.DateTimePicker.Init = function() + { + $('.datetimepicker').datetimepicker( + { + format: Grocy.Components.DateTimePicker.GetInputElement().data('format'), + buttons: { + showToday: true, + showClose: true + }, + calendarWeeks: Grocy.CalendarShowWeekNumbers, + maxDate: limitDate, + locale: moment.locale(), + defaultDate: startDate, + useCurrent: false, + icons: { + time: 'far fa-clock', + date: 'far fa-calendar', + up: 'fas fa-arrow-up', + down: 'fas fa-arrow-down', + previous: 'fas fa-chevron-left', + next: 'fas fa-chevron-right', + today: 'fas fa-calendar-check', + clear: 'far fa-trash-alt', + close: 'far fa-times-circle' + }, + sideBySide: true, + keyBinds: { + up: function(widget) { }, + down: function(widget) { }, + 'control up': function(widget) { }, + 'control down': function(widget) { }, + left: function(widget) { }, + right: function(widget) { }, + pageUp: function(widget) { }, + pageDown: function(widget) { }, + enter: function(widget) { }, + escape: function(widget) { }, + 'control space': function(widget) { }, + t: function(widget) { }, + 'delete': function(widget) { } + } + }); + } + Grocy.Components.DateTimePicker.Init(); + + Grocy.Components.DateTimePicker.GetInputElement().on('keyup', function(e) + { + $('.datetimepicker').datetimepicker('hide'); + + var value = Grocy.Components.DateTimePicker.GetValue(); + var now = new Date(); + var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00'); + var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99'); + var format = Grocy.Components.DateTimePicker.GetInputElement().data('format'); + var nextInputElement = $(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector')); + + //If input is empty and any arrow key is pressed, set date to today + if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39)) + { + Grocy.Components.DateTimePicker.SetValue(moment(new Date(), format, true).format(format)); + nextInputElement.focus(); + } + else if (value === 'x' || value === 'X') + { + Grocy.Components.DateTimePicker.SetValue(moment('2999-12-31 23:59:59').format(format)); + nextInputElement.focus(); + } + else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd)) + { + var date = moment((new Date()).getFullYear().toString() + value); + if (date.isBefore(moment())) + { + date.add(1, "year"); + } + Grocy.Components.DateTimePicker.SetValue(date.format(format)); + nextInputElement.focus(); + } + else if (value.length === 8 && $.isNumeric(value)) + { + Grocy.Components.DateTimePicker.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3')); + nextInputElement.focus(); + } + else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+")) + { + var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month"); + Grocy.Components.DateTimePicker.SetValue(endOfMonth.format(format)); + nextInputElement.focus(); + } + else + { + var dateObj = moment(value, format, true); + if (dateObj.isValid()) + { + if (e.shiftKey) + { + // WITH shift modifier key + + if (e.keyCode === 38) // Up + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'months').format(format)); + } + else if (e.keyCode === 40) // Down + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'months').format(format)); + } + else if (e.keyCode === 37) // Left + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'years').format(format)); + } + else if (e.keyCode === 39) // Right + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'years').format(format)); + } + } + else + { + // WITHOUT shift modifier key + + if (e.keyCode === 38) // Up + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'days').format(format)); + } + else if (e.keyCode === 40) // Down + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'days').format(format)); + } + else if (e.keyCode === 37) // Left + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'weeks').format(format)); + } + else if (e.keyCode === 39) // Right + { + Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'weeks').format(format)); + } + } + } + } + + //Custom validation + value = Grocy.Components.DateTimePicker.GetValue(); + dateObj = moment(value, format, true); + var element = Grocy.Components.DateTimePicker.GetInputElement()[0]; + if (!dateObj.isValid()) + { + if ($(element).hasAttr("required")) + { + element.setCustomValidity("error"); + } + } + else + { + if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment())) + { + element.setCustomValidity("error"); + } + else if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment())) + { + element.setCustomValidity("error"); + } + else + { + element.setCustomValidity(""); + } + + var earlierThanLimit = Grocy.Components.DateTimePicker.GetInputElement().data("earlier-than-limit"); + if (!earlierThanLimit.isEmpty()) + { + if (moment(value).isBefore(moment(earlierThanLimit))) + { + $("#datetimepicker-earlier-than-info").removeClass("d-none"); + } + else + { + $("#datetimepicker-earlier-than-info").addClass("d-none"); + } + } + } + + // "Click" the shortcut checkbox when the shortcut value was + // entered manually and it is currently not set + var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); + if (value == shortcutValue && !$("#datetimepicker-shortcut").is(":checked")) + { + $("#datetimepicker-shortcut").click(); + } + }); + + Grocy.Components.DateTimePicker.GetInputElement().on('input', function(e) + { + $('#datetimepicker-timeago').attr("datetime", Grocy.Components.DateTimePicker.GetValue()); + EmptyElementWhenMatches('#datetimepicker-timeago', Grocy.translate('timeago_nan')); + RefreshContextualTimeago("#datetimepicker-wrapper"); + }); + + $('.datetimepicker').on('update.datetimepicker', function(e) + { + Grocy.Components.DateTimePicker.GetInputElement().trigger('input'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup'); + }); + + $('.datetimepicker').on('hide.datetimepicker', function(e) + { + Grocy.Components.DateTimePicker.GetInputElement().trigger('input'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup'); + }); + + $("#datetimepicker-shortcut").on("click", function() + { + if (this.checked) + { + var value = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); + Grocy.Components.DateTimePicker.SetValue(value); + Grocy.Components.DateTimePicker.GetInputElement().attr("readonly", ""); + $(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector')).focus(); + } + else + { + Grocy.Components.DateTimePicker.SetValue(""); + Grocy.Components.DateTimePicker.GetInputElement().removeAttr("readonly"); + Grocy.Components.DateTimePicker.GetInputElement().focus(); + } + + Grocy.Components.DateTimePicker.GetInputElement().trigger('input'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); + Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress'); + }); +} + +export { datetimepicker } \ No newline at end of file diff --git a/js/components/datetimepicker2.js b/js/components/datetimepicker2.js new file mode 100644 index 00000000..5b781354 --- /dev/null +++ b/js/components/datetimepicker2.js @@ -0,0 +1,317 @@ +import { EmptyElementWhenMatches } from '../helpers/extensions' +import { RefreshContextualTimeago } from '../configs/timeago' + +function datetimepicker2(Grocy) +{ + Grocy.Components.DateTimePicker2 = {}; + + Grocy.Components.DateTimePicker2.GetInputElement = function() + { + return $('.datetimepicker2').find('input').not(".form-check-input"); + } + + Grocy.Components.DateTimePicker2.GetValue = function() + { + return Grocy.Components.DateTimePicker2.GetInputElement().val(); + } + + Grocy.Components.DateTimePicker2.SetValue = function(value) + { + // "Click" the shortcut checkbox when the desired value is + // not the shortcut value and it is currently set + var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); + if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked")) + { + $("#datetimepicker2-shortcut").click(); + } + + Grocy.Components.DateTimePicker2.GetInputElement().val(value); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); + + Grocy.Components.DateTimePicker2.GetInputElement().keyup(); + } + + Grocy.Components.DateTimePicker2.Clear = function() + { + $(".datetimepicker2").datetimepicker("destroy"); + Grocy.Components.DateTimePicker2.Init(); + + Grocy.Components.DateTimePicker2.GetInputElement().val(""); + + // "Click" the shortcut checkbox when the desired value is + // not the shortcut value and it is currently set + var value = ""; + var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); + if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked")) + { + $("#datetimepicker2-shortcut").click(); + } + + $('#datetimepicker2-timeago').text(''); + } + + Grocy.Components.DateTimePicker2.ChangeFormat = function(format) + { + $(".datetimepicker2").datetimepicker("destroy"); + Grocy.Components.DateTimePicker2.GetInputElement().data("format", format); + Grocy.Components.DateTimePicker2.Init(); + + if (format == "YYYY-MM-DD") + { + Grocy.Components.DateTimePicker2.GetInputElement().addClass("date-only-datetimepicker"); + } + else + { + Grocy.Components.DateTimePicker2.GetInputElement().removeClass("date-only-datetimepicker"); + } + } + + var startDate = null; + if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-with-now') === true) + { + startDate = moment().format(Grocy.Components.DateTimePicker2.GetInputElement().data('format')); + } + if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-value').length > 0) + { + startDate = moment(Grocy.Components.DateTimePicker2.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker2.GetInputElement().data('format')); + } + + var limitDate = moment('2999-12-31 23:59:59'); + if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true) + { + limitDate = moment(); + } + + Grocy.Components.DateTimePicker2.Init = function() + { + $('.datetimepicker2').datetimepicker( + { + format: Grocy.Components.DateTimePicker2.GetInputElement().data('format'), + buttons: { + showToday: true, + showClose: true + }, + calendarWeeks: Grocy.CalendarShowWeekNumbers, + maxDate: limitDate, + locale: moment.locale(), + defaultDate: startDate, + useCurrent: false, + icons: { + time: 'far fa-clock', + date: 'far fa-calendar', + up: 'fas fa-arrow-up', + down: 'fas fa-arrow-down', + previous: 'fas fa-chevron-left', + next: 'fas fa-chevron-right', + today: 'fas fa-calendar-check', + clear: 'far fa-trash-alt', + close: 'far fa-times-circle' + }, + sideBySide: true, + keyBinds: { + up: function(widget) { }, + down: function(widget) { }, + 'control up': function(widget) { }, + 'control down': function(widget) { }, + left: function(widget) { }, + right: function(widget) { }, + pageUp: function(widget) { }, + pageDown: function(widget) { }, + enter: function(widget) { }, + escape: function(widget) { }, + 'control space': function(widget) { }, + t: function(widget) { }, + 'delete': function(widget) { } + } + }); + } + Grocy.Components.DateTimePicker2.Init(); + + Grocy.Components.DateTimePicker2.GetInputElement().on('keyup', function(e) + { + $('.datetimepicker2').datetimepicker('hide'); + + var value = Grocy.Components.DateTimePicker2.GetValue(); + var now = new Date(); + var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00'); + var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99'); + var format = Grocy.Components.DateTimePicker2.GetInputElement().data('format'); + var nextInputElement = $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')); + + //If input is empty and any arrow key is pressed, set date to today + if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39)) + { + Grocy.Components.DateTimePicker2.SetValue(moment(new Date(), format, true).format(format)); + nextInputElement.focus(); + } + else if (value === 'x' || value === 'X') + { + Grocy.Components.DateTimePicker2.SetValue(moment('2999-12-31 23:59:59').format(format)); + nextInputElement.focus(); + } + else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd)) + { + var date = moment((new Date()).getFullYear().toString() + value); + if (date.isBefore(moment())) + { + date.add(1, "year"); + } + Grocy.Components.DateTimePicker2.SetValue(date.format(format)); + nextInputElement.focus(); + } + else if (value.length === 8 && $.isNumeric(value)) + { + Grocy.Components.DateTimePicker2.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3')); + nextInputElement.focus(); + } + else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+")) + { + var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month"); + Grocy.Components.DateTimePicker2.SetValue(endOfMonth.format(format)); + nextInputElement.focus(); + } + else + { + var dateObj = moment(value, format, true); + if (dateObj.isValid()) + { + if (e.shiftKey) + { + // WITH shift modifier key + + if (e.keyCode === 38) // Up + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'months').format(format)); + } + else if (e.keyCode === 40) // Down + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'months').format(format)); + } + else if (e.keyCode === 37) // Left + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'years').format(format)); + } + else if (e.keyCode === 39) // Right + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'years').format(format)); + } + } + else + { + // WITHOUT shift modifier key + + if (e.keyCode === 38) // Up + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'days').format(format)); + } + else if (e.keyCode === 40) // Down + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'days').format(format)); + } + else if (e.keyCode === 37) // Left + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'weeks').format(format)); + } + else if (e.keyCode === 39) // Right + { + Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'weeks').format(format)); + } + } + } + } + + //Custom validation + value = Grocy.Components.DateTimePicker2.GetValue(); + dateObj = moment(value, format, true); + var element = Grocy.Components.DateTimePicker2.GetInputElement()[0]; + if (!dateObj.isValid()) + { + if ($(element).hasAttr("required")) + { + element.setCustomValidity("error"); + } + } + else + { + if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment())) + { + element.setCustomValidity("error"); + } + else if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment())) + { + element.setCustomValidity("error"); + } + else + { + element.setCustomValidity(""); + } + + var earlierThanLimit = Grocy.Components.DateTimePicker2.GetInputElement().data("earlier-than-limit"); + if (!earlierThanLimit.isEmpty()) + { + if (moment(value).isBefore(moment(earlierThanLimit))) + { + $("#datetimepicker-earlier-than-info").removeClass("d-none"); + } + else + { + $("#datetimepicker-earlier-than-info").addClass("d-none"); + } + } + } + + // "Click" the shortcut checkbox when the shortcut value was + // entered manually and it is currently not set + var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); + if (value == shortcutValue && !$("#datetimepicker2-shortcut").is(":checked")) + { + $("#datetimepicker2-shortcut").click(); + } + }); + + Grocy.Components.DateTimePicker2.GetInputElement().on('input', function(e) + { + $('#datetimepicker2-timeago').attr("datetime", Grocy.Components.DateTimePicker2.GetValue()); + EmptyElementWhenMatches('#datetimepicker2-timeago', Grocy.translate('timeago_nan')); + RefreshContextualTimeago("#datetimepicker2-wrapper"); + }); + + $('.datetimepicker2').on('update.datetimepicker', function(e) + { + Grocy.Components.DateTimePicker2.GetInputElement().trigger('input'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup'); + }); + + $('.datetimepicker2').on('hide.datetimepicker', function(e) + { + Grocy.Components.DateTimePicker2.GetInputElement().trigger('input'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup'); + }); + + $("#datetimepicker2-shortcut").on("click", function() + { + if (this.checked) + { + var value = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); + Grocy.Components.DateTimePicker2.SetValue(value); + Grocy.Components.DateTimePicker2.GetInputElement().attr("readonly", ""); + $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')).focus(); + } + else + { + Grocy.Components.DateTimePicker2.SetValue(""); + Grocy.Components.DateTimePicker2.GetInputElement().removeAttr("readonly"); + Grocy.Components.DateTimePicker2.GetInputElement().focus(); + } + + Grocy.Components.DateTimePicker2.GetInputElement().trigger('input'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); + Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress'); + }); +} + +export { datetimepicker2 } \ No newline at end of file diff --git a/js/components/index.js b/js/components/index.js new file mode 100644 index 00000000..ce706056 --- /dev/null +++ b/js/components/index.js @@ -0,0 +1,15 @@ +export * from './barcodescanner'; +export * from './batterycard'; +export * from './calendarcard'; +export * from './chorecard'; +export * from './datetimepicker'; +export * from './datetimepicker2'; +export * from './locationpicker'; +export * from './numberpicker'; +export * from './productamountpicker'; +export * from './productcard'; +export * from './productpicker'; +export * from './recipepicker'; +export * from './shoppinglocationpicker'; +export * from './userfieldsform'; +export * from './userpicker'; \ No newline at end of file diff --git a/js/components/locationpicker.js b/js/components/locationpicker.js new file mode 100644 index 00000000..66e0b912 --- /dev/null +++ b/js/components/locationpicker.js @@ -0,0 +1,80 @@ +function locationpicker(Grocy) +{ + + Grocy.Components.LocationPicker = {}; + + Grocy.Components.LocationPicker.GetPicker = function() + { + return $('#location_id'); + } + + Grocy.Components.LocationPicker.GetInputElement = function() + { + return $('#location_id_text_input'); + } + + Grocy.Components.LocationPicker.GetValue = function() + { + return $('#location_id').val(); + } + + Grocy.Components.LocationPicker.SetValue = function(value) + { + Grocy.Components.LocationPicker.GetInputElement().val(value); + Grocy.Components.LocationPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.LocationPicker.SetId = function(value) + { + Grocy.Components.LocationPicker.GetPicker().val(value); + Grocy.Components.LocationPicker.GetPicker().data('combobox').refresh(); + Grocy.Components.LocationPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.LocationPicker.Clear = function() + { + Grocy.Components.LocationPicker.SetValue(''); + Grocy.Components.LocationPicker.SetId(null); + } + + $('.location-combobox').combobox({ + appendId: '_text_input', + bsVersion: '4', + clearIfNoMatch: true + }); + + // these names seem a bit long, but as they live in global space + // and this is a component, they need to be unique. + var locationpicker_doFocus = false; + var this_location_picker = Grocy.Components.LocationPicker.GetPicker(); + + var prefillByName = this_location_picker.parent().data('prefill-by-name').toString(); + if (typeof prefillByName !== "undefined") + { + var possibleOptionElement = $("#location_id option:contains(\"" + prefillByName + "\")").first(); + + if (possibleOptionElement.length > 0) + { + locationpicker_doFocus = true; + this_location_picker.val(possibleOptionElement.val()); + } + } + + var prefillById = this_location_picker.parent().data('prefill-by-id').toString(); + if (typeof prefillById !== "undefined") + { + locationpicker_doFocus = true; + this_location_picker.val(prefillById); + } + + if (locationpicker_doFocus) + { + this_location_picker.data('combobox').refresh(); + this_location_picker.trigger('change'); + + $(this_location_picker.parent().data('next-input-selector').toString()) + .focus(); + } +} + +export { locationpicker } \ No newline at end of file diff --git a/js/components/numberpicker.js b/js/components/numberpicker.js new file mode 100644 index 00000000..f60d2cfd --- /dev/null +++ b/js/components/numberpicker.js @@ -0,0 +1,99 @@ +function numberpicker(Grocy) +{ + $(".numberpicker-down-button").unbind('click').on("click", function() + { + var inputElement = $(this).parent().parent().find('input[type="number"]'); + inputElement.val(parseFloat(inputElement.val() || 1) - 1); + inputElement.trigger('keyup'); + inputElement.trigger('change'); + }); + + $(".numberpicker-up-button").unbind('click').on("click", function() + { + var inputElement = $(this).parent().parent().find('input[type="number"]'); + inputElement.val(parseFloat(inputElement.val() || 0) + 1); + inputElement.trigger('keyup'); + inputElement.trigger('change'); + }); + + $(".numberpicker").on("keyup", function() + { + if ($(this).attr("data-not-equal") && !$(this).attr("data-not-equal").toString().isEmpty() && $(this).attr("data-not-equal") == $(this).val()) + { + $(this)[0].setCustomValidity("error"); + } + else + { + $(this)[0].setCustomValidity(""); + } + }); + + $(".numberpicker").each(function() + { + new MutationObserver(function(mutations) + { + mutations.forEach(function(mutation) + { + if (mutation.type == "attributes" && (mutation.attributeName == "min" || mutation.attributeName == "max" || mutation.attributeName == "data-not-equal" || mutation.attributeName == "data-initialised")) + { + var element = $(mutation.target); + var min = element.attr("min"); + var decimals = element.attr("data-decimals"); + + var max = ""; + if (element.hasAttr("max")) + { + max = element.attr("max"); + } + + if (element.hasAttr("data-not-equal")) + { + var notEqual = element.attr("data-not-equal"); + + if (notEqual != "NaN") + { + if (max.isEmpty()) + { + element.parent().find(".invalid-feedback").text(Grocy.translate("This cannot be lower than %1$s or equal %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals)); + } + else + { + element.parent().find(".invalid-feedback").text(Grocy.translate("This must be between %1$s and %2$s, cannot equal %3$s and needs to be a valid number with max. %4$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }), decimals)); + } + + return; + } + } + + if (max.isEmpty()) + { + element.parent().find(".invalid-feedback").text(Grocy.translate("This cannot be lower than %1$s and needs to be a valid number with max. %2$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals)); + } + else + { + element.parent().find(".invalid-feedback").text(Grocy.translate("This must between %1$s and %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals)); + } + } + }); + }).observe(this, { + attributes: true + }); + }); + $(".numberpicker").attr("data-initialised", "true"); // Dummy change to trigger MutationObserver above once + + $(".numberpicker").on("keydown", function(e) + { + if (e.key == "ArrowUp") + { + e.preventDefault(); + $(this).parent().find(".numberpicker-up-button").click(); + } + else if (e.key == "ArrowDown") + { + e.preventDefault(); + $(this).parent().find(".numberpicker-down-button").click(); + } + }); +} + +export { numberpicker } \ No newline at end of file diff --git a/js/components/productamountpicker.js b/js/components/productamountpicker.js new file mode 100644 index 00000000..d0131939 --- /dev/null +++ b/js/components/productamountpicker.js @@ -0,0 +1,123 @@ +function productamountpicker(Grocy) +{ + Grocy.Use("numberpicker"); + + Grocy.Components.ProductAmountPicker = {}; + Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false; + + Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuId, forceInitialDisplayQu = false) + { + var conversionsForProduct = Grocy.QuantityUnitConversionsResolved.filter(elem => elem.product_id == productId); + + if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled) + { + var qu = Grocy.QuantityUnits.find(elem => elem.id == destinationQuId); + $("#qu_id").find("option").remove().end(); + $("#qu_id").attr("data-destination-qu-name", qu.name); + $("#qu_id").attr("data-destination-qu-name-plural", qu.name_plural); + + conversionsForProduct.forEach(conversion => + { + var factor = parseFloat(conversion.factor); + if (conversion.to_qu_id == destinationQuId) + { + factor = 1; + } + + if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times + { + $("#qu_id").append(''); + } + }); + } + + if (!Grocy.Components.ProductAmountPicker.InitialValueSet || forceInitialDisplayQu) + { + $("#qu_id").val($("#qu_id").attr("data-initial-qu-id")); + } + + if (!Grocy.Components.ProductAmountPicker.InitialValueSet) + { + var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor"); + $("#display_amount").val(convertedAmount); + + Grocy.Components.ProductAmountPicker.InitialValueSet = true; + } + + if (conversionsForProduct.length === 1 && !forceInitialDisplayQu) + { + $("#qu_id").val($("#qu_id option:first").val()); + } + + if ($('#qu_id option').length == 1) + { + $("#qu_id").attr("disabled", ""); + } + else + { + $("#qu_id").removeAttr("disabled"); + } + + $(".input-group-productamountpicker").trigger("change"); + } + + Grocy.Components.ProductAmountPicker.SetQuantityUnit = function(quId) + { + $("#qu_id").val(quId); + } + + Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false) + { + Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true; + + $("#qu_id").find("option").remove().end(); + Grocy.QuantityUnits.forEach(qu => + { + $("#qu_id").append(''); + }); + + if (keepInitialQu) + { + Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu_id").attr("data-initial-qu-id")); + } + + $("#qu_id").removeAttr("disabled"); + + $(".input-group-productamountpicker").trigger("change"); + } + + Grocy.Components.ProductAmountPicker.Reset = function() + { + $("#qu_id").find("option").remove(); + $("#qu-conversion-info").addClass("d-none"); + $("#qu-display_amount-info").val(""); + } + + $(".input-group-productamountpicker").on("change", function() + { + var selectedQuName = $("#qu_id option:selected").text(); + var quFactor = $("#qu_id option:selected").attr("data-qu-factor"); + var amount = $("#display_amount").val(); + var destinationAmount = amount / quFactor; + var destinationQuName = Grocy.translaten(destinationAmount, $("#qu_id").attr("data-destination-qu-name"), $("#qu_id").attr("data-destination-qu-name-plural")) + + if ($("#qu_id").attr("data-destination-qu-name") == selectedQuName || Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled || amount.toString().isEmpty() || selectedQuName.toString().isEmpty()) + { + $("#qu-conversion-info").addClass("d-none"); + } + else + { + $("#qu-conversion-info").removeClass("d-none"); + $("#qu-conversion-info").text(Grocy.translate("This equals %1$s %2$s", destinationAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), destinationQuName)); + } + + $("#amount").val(destinationAmount.toFixed(Grocy.UserSettings.stock_decimal_places_amounts).replace(/0*$/g, '')); + }); + + $("#display_amount").on("keyup", function() + { + $(".input-group-productamountpicker").trigger("change"); + }); +} + +export { productamountpicker } \ No newline at end of file diff --git a/js/components/productcard.js b/js/components/productcard.js new file mode 100644 index 00000000..0d1e6711 --- /dev/null +++ b/js/components/productcard.js @@ -0,0 +1,245 @@ +import Chart from 'chart.js'; +import { EmptyElementWhenMatches } from '../helpers/extensions' +import { RefreshContextualTimeago } from '../configs/timeago' + +function productcard(Grocy) +{ + + Grocy.Components.ProductCard = {}; + + Grocy.Components.ProductCard.Refresh = function(productId) + { + Grocy.Api.Get('stock/products/' + productId, + function(productDetails) + { + var stockAmount = productDetails.stock_amount || '0'; + var stockValue = productDetails.stock_value || '0'; + var stockAmountOpened = productDetails.stock_amount_opened || '0'; + $('#productcard-product-name').text(productDetails.product.name); + $('#productcard-product-description').html(productDetails.product.description); + $('#productcard-product-stock-amount').text(stockAmount); + $('#productcard-product-stock-qu-name').text(Grocy.translaten(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)); + $('#productcard-product-stock-value').text(stockValue + ' ' + Grocy.Currency); + $('#productcard-product-last-purchased').text((productDetails.last_purchased || '2999-12-31').substring(0, 10)); + $('#productcard-product-last-purchased-timeago').attr("datetime", productDetails.last_purchased || '2999-12-31'); + $('#productcard-product-last-used').text((productDetails.last_used || '2999-12-31').substring(0, 10)); + $('#productcard-product-last-used-timeago').attr("datetime", productDetails.last_used || '2999-12-31'); + if (productDetails.location != null) + { + $('#productcard-product-location').text(productDetails.location.name); + } + $('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" })); + + if (productDetails.is_aggregated_amount == 1) + { + $('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated); + $('#productcard-product-stock-qu-name-aggregated').text(Grocy.translaten(productDetails.stock_amount_aggregated, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)); + + if (productDetails.stock_amount_opened_aggregated > 0) + { + $('#productcard-product-stock-opened-amount-aggregated').text(Grocy.translate('%s opened', productDetails.stock_amount_opened_aggregated)); + } + else + { + $('#productcard-product-stock-opened-amount-aggregated').text(""); + } + + $("#productcard-aggregated-amounts").removeClass("d-none"); + } + else + { + $("#productcard-aggregated-amounts").addClass("d-none"); + } + + if (productDetails.product.description != null && !productDetails.product.description.isEmpty()) + { + $("#productcard-product-description-wrapper").removeClass("d-none"); + } + else + { + $("#productcard-product-description-wrapper").addClass("d-none"); + } + + if (productDetails.average_shelf_life_days == -1) + { + $('#productcard-product-average-shelf-life').text(Grocy.translate("Unknown")); + } + else if (parseInt(productDetails.average_shelf_life_days) > 73000) // > 200 years aka forever + { + $('#productcard-product-average-shelf-life').text(Grocy.translate("Unlimited")); + } + else + { + $('#productcard-product-average-shelf-life').text(moment.duration(productDetails.average_shelf_life_days, "days").humanize()); + } + + if (stockAmountOpened > 0) + { + $('#productcard-product-stock-opened-amount').text(Grocy.translate('%s opened', stockAmountOpened)); + } + else + { + $('#productcard-product-stock-opened-amount').text(""); + } + + $('#productcard-product-edit-button').attr("href", Grocy.FormatUrl("/product/" + productDetails.product.id.toString() + '?' + 'returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative))); + $('#productcard-product-journal-button').attr("href", Grocy.FormatUrl("/stockjournal?embedded&product=" + productDetails.product.id.toString())); + $('#productcard-product-stock-button').attr("href", Grocy.FormatUrl("/stockentries?embedded&product=" + productDetails.product.id.toString())); + $('#productcard-product-stock-button').removeClass("disabled"); + $('#productcard-product-edit-button').removeClass("disabled"); + $('#productcard-product-journal-button').removeClass("disabled"); + + if (productDetails.last_price !== null) + { + $('#productcard-product-last-price').text(Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name); + } + else + { + $('#productcard-product-last-price').text(Grocy.translate('Unknown')); + } + + if (productDetails.avg_price !== null) + { + $('#productcard-product-average-price').text(Number.parseFloat(productDetails.avg_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name); + } + else + { + $('#productcard-product-average-price').text(Grocy.translate('Unknown')); + } + + if (productDetails.product.picture_file_name !== null && !productDetails.product.picture_file_name.isEmpty()) + { + $("#productcard-product-picture").removeClass("d-none"); + $("#productcard-product-picture").attr("src", Grocy.FormatUrl('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400')); + } + else + { + $("#productcard-product-picture").addClass("d-none"); + } + + EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', Grocy.translate('timeago_nan')); + EmptyElementWhenMatches('#productcard-product-last-used-timeago', Grocy.translate('timeago_nan')); + RefreshContextualTimeago(".productcard"); + }, + function(xhr) + { + console.error(xhr); + } + ); + + if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) + { + Grocy.Api.Get('stock/products/' + productId + '/price-history', + function(priceHistoryDataPoints) + { + if (priceHistoryDataPoints.length > 0) + { + $("#productcard-product-price-history-chart").removeClass("d-none"); + $("#productcard-no-price-data-hint").addClass("d-none"); + + Grocy.Components.ProductCard.ReInitPriceHistoryChart(); + var datasets = {}; + var chart = Grocy.Components.ProductCard.PriceHistoryChart.data; + priceHistoryDataPoints.forEach((dataPoint) => + { + var key = Grocy.translate("Unknown store"); + if (dataPoint.shopping_location) + { + key = dataPoint.shopping_location.name + } + + if (!datasets[key]) + { + datasets[key] = [] + } + chart.labels.push(moment(dataPoint.date).toDate()); + datasets[key].push(dataPoint.price); + + }); + Object.keys(datasets).forEach((key) => + { + chart.datasets.push({ + data: datasets[key], + fill: false, + borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)", + label: key, + }); + }); + Grocy.Components.ProductCard.PriceHistoryChart.update(); + } + else + { + $("#productcard-product-price-history-chart").addClass("d-none"); + $("#productcard-no-price-data-hint").removeClass("d-none"); + } + }, + function(xhr) + { + console.error(xhr); + } + ); + } + }; + + Grocy.Components.ProductCard.ReInitPriceHistoryChart = function() + { + if (typeof Grocy.Components.ProductCard.PriceHistoryChart !== "undefined") + { + Grocy.Components.ProductCard.PriceHistoryChart.destroy(); + } + + var format = 'YYYY-MM-DD'; + Grocy.Components.ProductCard.PriceHistoryChart = new Chart(document.getElementById("productcard-product-price-history-chart"), { + type: "line", + data: { + labels: [ //Date objects + // Will be populated in Grocy.Components.ProductCard.Refresh + ], + datasets: [ //Datasets + // Will be populated in Grocy.Components.ProductCard.Refresh + ] + }, + options: { + scales: { + xAxes: [{ + type: 'time', + time: { + parser: format, + round: 'day', + tooltipFormat: format, + unit: 'day', + unitStepSize: 10, + displayFormats: { + 'day': format + } + }, + ticks: { + autoSkip: true, + maxRotation: 0 + } + }], + yAxes: [{ + ticks: { + beginAtZero: true + } + }] + }, + legend: { + display: true + } + } + }); + } + + $("#productcard-product-description").on("shown.bs.collapse", function() + { + $(".expandable-text").find("a[data-toggle='collapse']").text(Grocy.translate("Show less")); + }) + + $("#productcard-product-description").on("hidden.bs.collapse", function() + { + $(".expandable-text").find("a[data-toggle='collapse']").text(Grocy.translate("Show more")); + }) +} + +export { productcard } \ No newline at end of file diff --git a/js/components/productpicker.js b/js/components/productpicker.js new file mode 100644 index 00000000..1c24e775 --- /dev/null +++ b/js/components/productpicker.js @@ -0,0 +1,339 @@ +import { GetUriParam, RemoveUriParam } from '../helpers/extensions'; + +function productpicker(Grocy) +{ + Grocy.Use('barcodescanner'); + Grocy.Components.ProductPicker = {}; + + Grocy.Components.ProductPicker.GetPicker = function() + { + return $('#product_id'); + } + + Grocy.Components.ProductPicker.GetInputElement = function() + { + return $('#product_id_text_input'); + } + + Grocy.Components.ProductPicker.GetValue = function() + { + return $('#product_id').val(); + } + + Grocy.Components.ProductPicker.SetValue = function(value) + { + Grocy.Components.ProductPicker.GetInputElement().val(value); + Grocy.Components.ProductPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.ProductPicker.SetId = function(value) + { + Grocy.Components.ProductPicker.GetPicker().val(value); + Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh(); + Grocy.Components.ProductPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.ProductPicker.Clear = function() + { + Grocy.Components.ProductPicker.SetValue(''); + Grocy.Components.ProductPicker.SetId(null); + } + + Grocy.Components.ProductPicker.InProductAddWorkflow = function() + { + return GetUriParam('flow') == "InplaceNewProductWithName"; + } + + Grocy.Components.ProductPicker.InProductModifyWorkflow = function() + { + return GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct"; + } + + Grocy.Components.ProductPicker.InAnyFlow = function() + { + return Grocy.Components.ProductPicker.InProductAddWorkflow() || Grocy.Components.ProductPicker.InProductModifyWorkflow(); + } + + Grocy.Components.ProductPicker.FinishFlow = function() + { + RemoveUriParam("flow"); + RemoveUriParam("barcode"); + RemoveUriParam("product-name"); + } + + Grocy.Components.ProductPicker.ShowCustomError = function(text) + { + var element = $("#custom-productpicker-error"); + element.text(text); + element.removeClass("d-none"); + } + + Grocy.Components.ProductPicker.HideCustomError = function() + { + $("#custom-productpicker-error").addClass("d-none"); + } + + Grocy.Components.ProductPicker.Disable = function() + { + Grocy.Components.ProductPicker.GetInputElement().attr("disabled", ""); + $("#barcodescanner-start-button").attr("disabled", ""); + $("#barcodescanner-start-button").addClass("disabled"); + } + + Grocy.Components.ProductPicker.Enable = function() + { + Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled"); + $("#barcodescanner-start-button").removeAttr("disabled"); + $("#barcodescanner-start-button").removeClass("disabled"); + } + + $('.product-combobox').combobox({ + appendId: '_text_input', + bsVersion: '4', + clearIfNoMatch: false + }); + + var this_product_picker = Grocy.Components.ProductPicker.GetPicker(); + var productpicker_doFocus = false; + + var prefillProduct = GetUriParam('product-name'); + var prefillProduct2 = this_product_picker.parent().data('prefill-by-name').toString(); + if (!prefillProduct2.isEmpty()) + { + prefillProduct = prefillProduct2; + } + if (typeof prefillProduct !== "undefined") + { + var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first(); + if (possibleOptionElement.length === 0) + { + possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first(); + } + + if (possibleOptionElement.length > 0) + { + productpicker_doFocus = true; + this_product_picker.val(possibleOptionElement.val()); + } + } + + var prefillProductId = GetUriParam("product"); + var prefillProductId2 = this_product_picker.parent().data('prefill-by-id').toString(); + if (!prefillProductId2.isEmpty()) + { + prefillProductId = prefillProductId2; + } + if (typeof prefillProductId !== "undefined") + { + this_product_picker.val(prefillProductId); + productpicker_doFocus = true; + } + + if (productpicker_doFocus) + { + this_product_picker.data('combobox').refresh(); + this_product_picker.trigger('change'); + + $(this_product_picker.parent().data('next-input-selector').toString()) + .focus(); + } + + if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") + { + $('#InplaceAddBarcodeToExistingProduct').text(GetUriParam("barcode")); + $('#flow-info-InplaceAddBarcodeToExistingProduct').removeClass('d-none'); + $('#barcode-lookup-disabled-hint').removeClass('d-none'); + $('#barcode-lookup-hint').addClass('d-none'); + } + + Grocy.Components.ProductPicker.PopupOpen = false; + $('#product_id_text_input').on('blur', function(e) + { + if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible")) + { + return; + } + $('#product_id').attr("barcode", "null"); + + var input = $('#product_id_text_input').val().toString(); + var possibleOptionElement = []; + + // did we enter a grocycode? + if (input.startsWith("grcy")) + { + var gc = input.split(":"); + if (gc[1] == "p") + { + // find product id + possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first(); + $("#product_id").data("grocycode", true); + } + } + else // process barcode as usual + { + possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first(); + } + + if (GetUriParam('flow') === undefined && input.length > 0 && possibleOptionElement.length > 0) + { + $('#product_id').val(possibleOptionElement.val()); + $('#product_id').attr("barcode", input); + $('#product_id').data('combobox').refresh(); + $('#product_id').trigger('change'); + } + else + { + if (Grocy.Components.ProductPicker.PopupOpen === true) + { + return; + } + + var optionElement = $("#product_id option:contains(\"" + input + "\")").first(); + if (input.length > 0 && optionElement.length === 0 && GetUriParam('flow') === undefined && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false") + { + var addProductWorkflowsAdditionalCssClasses = ""; + if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true") + { + addProductWorkflowsAdditionalCssClasses = "d-none"; + } + + var embedded = ""; + if (GetUriParam("embedded") !== undefined) + { + embedded = "embedded"; + } + + var buttons = { + cancel: { + label: Grocy.translate('Cancel'), + className: 'btn-secondary responsive-button', + callback: function() + { + Grocy.Components.ProductPicker.PopupOpen = false; + Grocy.Components.ProductPicker.SetValue(''); + } + }, + addnewproduct: { + label: 'P ' + Grocy.translate('Add as new product'), + className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses, + callback: function() + { + + Grocy.Components.ProductPicker.PopupOpen = false; + window.location.href = Grocy.FormatUrl('/product/new?flow=InplaceNewProductWithName&name=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceNewProductWithName&" + embedded) + "&" + embedded); + } + }, + addbarcode: { + label: 'B ' + Grocy.translate('Add as barcode to existing product'), + className: 'btn-info add-new-barcode-dialog-button responsive-button', + callback: function() + { + Grocy.Components.ProductPicker.PopupOpen = false; + window.location.href = Grocy.FormatUrl(Grocy.CurrentUrlRelative + '?flow=InplaceAddBarcodeToExistingProduct&barcode=' + encodeURIComponent(input)); + } + }, + addnewproductwithbarcode: { + label: 'A ' + Grocy.translate('Add as new product and prefill barcode'), + className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses, + callback: function() + { + Grocy.Components.ProductPicker.PopupOpen = false; + window.location.href = Grocy.FormatUrl('/product/new?flow=InplaceNewProductWithBarcode&barcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceAddBarcodeToExistingProduct&barcode=" + input + "&" + embedded) + "&" + embedded); + } + } + }; + + if (!Grocy.FeatureFlags.DISABLE_BROWSER_BARCODE_CAMERA_SCANNING) + { + buttons.retrycamerascanning = { + label: 'C ', + className: 'btn-primary responsive-button retry-camera-scanning-button', + callback: function() + { + Grocy.Components.ProductPicker.PopupOpen = false; + Grocy.Components.ProductPicker.SetValue(''); + $("#barcodescanner-start-button").click(); + } + }; + } + + Grocy.Components.ProductPicker.PopupOpen = true; + bootbox.dialog({ + message: Grocy.translate('"%s" could not be resolved to a product, how do you want to proceed?', input), + title: Grocy.translate('Create or assign product'), + onEscape: function() + { + Grocy.Components.ProductPicker.PopupOpen = false; + Grocy.Components.ProductPicker.SetValue(''); + }, + size: 'large', + backdrop: true, + closeButton: false, + buttons: buttons + }).on('keypress', function(e) + { + if (e.key === 'B' || e.key === 'b') + { + $('.add-new-barcode-dialog-button').not(".d-none").click(); + } + if (e.key === 'p' || e.key === 'P') + { + $('.add-new-product-dialog-button').not(".d-none").click(); + } + if (e.key === 'a' || e.key === 'A') + { + $('.add-new-product-with-barcode-dialog-button').not(".d-none").click(); + } + if (e.key === 'c' || e.key === 'C') + { + $('.retry-camera-scanning-button').not(".d-none").click(); + } + }); + } + } + }); + + $(document).on("Grocy.BarcodeScanned", function(e, barcode, target) + { + if (!(target == "@productpicker" || target == "undefined" || target == undefined)) // Default target + { + return; + } + + // Don't know why the blur event does not fire immediately ... this works... + Grocy.Components.ProductPicker.GetInputElement().focusout(); + Grocy.Components.ProductPicker.GetInputElement().focus(); + Grocy.Components.ProductPicker.GetInputElement().blur(); + + Grocy.Components.ProductPicker.GetInputElement().val(barcode); + + setTimeout(function() + { + Grocy.Components.ProductPicker.GetInputElement().focusout(); + Grocy.Components.ProductPicker.GetInputElement().focus(); + Grocy.Components.ProductPicker.GetInputElement().blur(); + }, 200); + }); + + $(document).on("shown.bs.modal", function(e) + { + $(".modal-footer").addClass("d-block").addClass("d-sm-flex"); + $(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0"); + }) + + // Make that ENTER behaves the same like TAB (trigger blur to start workflows, but only when the dropdown is not opened) + $('#product_id_text_input').keydown(function(event) + { + if (event.keyCode === 13) // Enter + { + if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible")) + { + return; + } + + $("#product_id_text_input").trigger("blur"); + } + }); +} + +export { productpicker } \ No newline at end of file diff --git a/js/components/recipepicker.js b/js/components/recipepicker.js new file mode 100644 index 00000000..082559f2 --- /dev/null +++ b/js/components/recipepicker.js @@ -0,0 +1,77 @@ +function recipepicker(Grocy) +{ + + Grocy.Components.RecipePicker = {}; + + Grocy.Components.RecipePicker.GetPicker = function() + { + return $('#recipe_id'); + } + + Grocy.Components.RecipePicker.GetInputElement = function() + { + return $('#recipe_id_text_input'); + } + + Grocy.Components.RecipePicker.GetValue = function() + { + return $('#recipe_id').val(); + } + + Grocy.Components.RecipePicker.SetValue = function(value) + { + Grocy.Components.RecipePicker.GetInputElement().val(value); + Grocy.Components.RecipePicker.GetInputElement().trigger('change'); + } + + Grocy.Components.RecipePicker.SetId = function(value) + { + Grocy.Components.RecipePicker.GetPicker().val(value); + Grocy.Components.RecipePicker.GetPicker().data('combobox').refresh(); + Grocy.Components.RecipePicker.GetInputElement().trigger('change'); + } + + Grocy.Components.RecipePicker.Clear = function() + { + Grocy.Components.RecipePicker.SetValue(''); + Grocy.Components.RecipePicker.SetId(null); + } + + $('.recipe-combobox').combobox({ + appendId: '_text_input', + bsVersion: '4', + clearIfNoMatch: true + }); + + var this_recipe_picker = Grocy.Components.RecipePicker.GetPicker(); + var recipe_picker_doFocus = false; + + var prefillByName = this_recipe_picker.parent().data('prefill-by-name').toString(); + if (typeof prefillByName !== "undefined") + { + var possibleOptionElement = $("#recipe_id option:contains(\"" + prefillByName + "\")").first(); + + if (possibleOptionElement.length > 0) + { + recipe_picker_doFocus = true; + this_recipe_picker.val(possibleOptionElement.val()); + } + } + + var prefillById = this_recipe_picker.parent().data('prefill-by-id').toString(); + if (typeof prefillById !== "undefined") + { + recipe_picker_doFocus = true; + this_recipe_picker.val(prefillById); + } + + if (recipe_picker_doFocus) + { + this_recipe_picker.data('combobox').refresh(); + this_recipe_picker.trigger('change'); + + $(this_recipe_picker.parent().data('next-input-selector').toString()).focus(); + } +} + +export { recipepicker } \ No newline at end of file diff --git a/js/components/shoppinglocationpicker.js b/js/components/shoppinglocationpicker.js new file mode 100644 index 00000000..0e4768c2 --- /dev/null +++ b/js/components/shoppinglocationpicker.js @@ -0,0 +1,77 @@ +function shoppinglocationpicker(Grocy) +{ + Grocy.Components.ShoppingLocationPicker = {}; + + Grocy.Components.ShoppingLocationPicker.GetPicker = function() + { + return $('#shopping_location_id'); + } + + Grocy.Components.ShoppingLocationPicker.GetInputElement = function() + { + return $('#shopping_location_id_text_input'); + } + + Grocy.Components.ShoppingLocationPicker.GetValue = function() + { + return $('#shopping_location_id').val(); + } + + Grocy.Components.ShoppingLocationPicker.SetValue = function(value) + { + Grocy.Components.ShoppingLocationPicker.GetInputElement().val(value); + Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.ShoppingLocationPicker.SetId = function(value) + { + Grocy.Components.ShoppingLocationPicker.GetPicker().val(value); + Grocy.Components.ShoppingLocationPicker.GetPicker().data('combobox').refresh(); + Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.ShoppingLocationPicker.Clear = function() + { + Grocy.Components.ShoppingLocationPicker.SetValue(''); + Grocy.Components.ShoppingLocationPicker.SetId(null); + } + + $('.shopping-location-combobox').combobox({ + appendId: '_text_input', + bsVersion: '4', + clearIfNoMatch: true + }); + + var shoppinglocationpicker_doFocus = false; + var this_shoppinglocation_picker = $('#shopping_location_id'); + + var prefillByName = this_shoppinglocation_picker.parent().data('prefill-by-name').toString(); + if (typeof prefillByName !== "undefined") + { + var possibleOptionElement = $("#shopping_location_id option:contains(\"" + prefillByName + "\")").first(); + + if (possibleOptionElement.length > 0) + { + this_shoppinglocation_picker.val(possibleOptionElement.val()); + shoppinglocationpicker_doFocus = true; + } + } + + var prefillById = this_shoppinglocation_picker.parent().data('prefill-by-id').toString(); + if (typeof prefillById !== "undefined") + { + this_shoppinglocation_picker.val(prefillById); + shoppinglocationpicker_doFocus = true; + } + + if (shoppinglocationpicker_doFocus) + { + this_shoppinglocation_picker.data('combobox').refresh(); + this_shoppinglocation_picker.trigger('change'); + + $(this_shoppinglocation_picker.parent().data('next-input-selector').toString()) + .focus(); + } +} + +export { shoppinglocationpicker } \ No newline at end of file diff --git a/js/components/userfieldsform.js b/js/components/userfieldsform.js new file mode 100644 index 00000000..ad18fb98 --- /dev/null +++ b/js/components/userfieldsform.js @@ -0,0 +1,191 @@ +import { RandomString } from '../helpers/extensions'; +import { LoadImagesLazy } from '../configs/lazy' + +function userfieldsform(Grocy) +{ + Grocy.Use("datetimepicker"); + + Grocy.Components.UserfieldsForm = {}; + + Grocy.Components.UserfieldsForm.Save = function(success, error) + { + if (!$("#userfields-form").length) + { + if (success) + { + success(); + } + + return; + } + + var jsonData = {}; + + $("#userfields-form .userfield-input").not("div").each(function() + { + var input = $(this); + var fieldName = input.attr("data-userfield-name"); + var fieldValue = input.val(); + + if (input.attr("type") == "checkbox") + { + jsonData[fieldName] = "0"; + if (input.is(":checked")) + { + jsonData[fieldName] = "1"; + } + } + else if (input.attr("type") == "file") + { + var oldFile = input.data('old-file') + if (oldFile) + { + Grocy.Api.Delete('files/userfiles/' + oldFile, null, null, + function(xhr) + { + Grocy.FrontendHelpers.ShowGenericError('Could not delete file', xhr); + }); + jsonData[fieldName] = ""; + } + + if (input[0].files.length > 0) + { + // Files service requires an extension + var fileName = RandomString() + '.' + input[0].files[0].name.split('.').reverse()[0]; + + jsonData[fieldName] = btoa(fileName) + '_' + btoa(input[0].files[0].name); + Grocy.Api.UploadFile(input[0].files[0], 'userfiles', fileName, + function(result) + { + }, + function(xhr) + { + // When navigating away immediately from the current page, this is maybe a false positive - so ignore this for now + // Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response) + } + ); + } + } + else if ($(this).hasAttr("multiple")) + { + jsonData[fieldName] = $(this).val().join(","); + } + else + { + jsonData[fieldName] = fieldValue; + } + }); + + Grocy.Api.Put('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, jsonData, + function(result) + { + if (success) + { + success(); + } + }, + function(xhr) + { + if (error) + { + error(); + } + } + ); + } + + Grocy.Components.UserfieldsForm.Load = function() + { + if (!$("#userfields-form").length) + { + return; + } + + Grocy.Api.Get('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, + function(result) + { + $.each(result, function(key, value) + { + var input = $(".userfield-input[data-userfield-name='" + key + "']"); + + if (input.attr("type") == "checkbox" && value == 1) + { + input.prop("checked", true); + } + else if (input.hasAttr("multiple")) + { + input.val(value.split(",")); + $(".selectpicker").selectpicker("render"); + } + else if (input.attr('type') == "file") + { + if (value != null && !value.isEmpty()) + { + var fileName = atob(value.split('_')[1]); + var fileSrc = value.split('_')[0]; + var formGroup = input.parent().parent().parent(); + + formGroup.find("label.custom-file-label").text(fileName); + formGroup.find(".userfield-file-show").attr('href', Grocy.FormatUrl('/files/userfiles/' + value)); + formGroup.find('.userfield-file-show').removeClass('d-none'); + formGroup.find('img.userfield-current-file') + .attr('src', Grocy.FormatUrl('/files/userfiles/' + value + '?force_serve_as=picture&best_fit_width=250&best_fit_height=250')); + LoadImagesLazy(); + + formGroup.find('.userfield-file-delete').click( + function() + { + formGroup.find("label.custom-file-label").text(Grocy.translate("No file selected")); + formGroup.find(".userfield-file-show").addClass('d-none'); + input.attr('data-old-file', fileSrc); + } + ); + + input.on("change", function(e) + { + formGroup.find(".userfield-file-show").addClass('d-none'); + }); + } + } + else if (input.attr("data-userfield-type") == "link") + { + if (!value.isEmpty()) + { + var data = JSON.parse(value); + + var formRow = input.parent().parent(); + formRow.find(".userfield-link-title").val(data.title); + formRow.find(".userfield-link-link").val(data.link); + + input.val(value); + } + } + else + { + input.val(value); + } + }); + }, + function(xhr) + { + console.error(xhr); + } + ); + } + + $(".userfield-link").keyup(function(e) + { + var formRow = $(this).parent().parent(); + var title = formRow.find(".userfield-link-title").val(); + var link = formRow.find(".userfield-link-link").val(); + + var value = { + "title": title, + "link": link + }; + + formRow.find(".userfield-input").val(JSON.stringify(value)); + }); +} + +export { userfieldsform } \ No newline at end of file diff --git a/js/components/userpicker.js b/js/components/userpicker.js new file mode 100644 index 00000000..8f96fe84 --- /dev/null +++ b/js/components/userpicker.js @@ -0,0 +1,86 @@ +function userpicker(Grocy) +{ + + Grocy.Components.UserPicker = {}; + + Grocy.Components.UserPicker.GetPicker = function() + { + return $('#user_id'); + } + + Grocy.Components.UserPicker.GetInputElement = function() + { + return $('#user_id_text_input'); + } + + Grocy.Components.UserPicker.GetValue = function() + { + return $('#user_id').val(); + } + + Grocy.Components.UserPicker.SetValue = function(value) + { + Grocy.Components.UserPicker.GetInputElement().val(value); + Grocy.Components.UserPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.UserPicker.SetId = function(value) + { + Grocy.Components.UserPicker.GetPicker().val(value); + Grocy.Components.UserPicker.GetPicker().data('combobox').refresh(); + Grocy.Components.UserPicker.GetInputElement().trigger('change'); + } + + Grocy.Components.UserPicker.Clear = function() + { + Grocy.Components.UserPicker.SetValue(''); + Grocy.Components.UserPicker.SetId(null); + } + + $('.user-combobox').combobox({ + appendId: '_text_input', + bsVersion: '4' + }); + + var this_user_picker = Grocy.Components.UserPicker.GetPicker(); + var user_picker_doFocus = false; + var possibleOptionElement = null; + + var prefillUser = this_user_picker.parent().data('prefill-by-username').toString(); + if (typeof prefillUser !== "undefined") + { + possibleOptionElement = $("#user_id option[data-additional-searchdata*=\"" + prefillUser + "\"]").first(); + if (possibleOptionElement.length === 0) + { + possibleOptionElement = $("#user_id option:contains(\"" + prefillUser + "\")").first(); + } + + if (possibleOptionElement.length > 0) + { + user_picker_doFocus = true; + this_user_picker.val(possibleOptionElement.val()); + + } + } + + var prefillUserId = this_user_picker.parent().data('prefill-by-user-id').toString(); + if (typeof prefillUserId !== "undefined") + { + possibleOptionElement = $("#user_id option[value='" + prefillUserId + "']").first(); + if (possibleOptionElement.length > 0) + { + user_picker_doFocus = true; + this_user_picker.val(possibleOptionElement.val()); + } + } + + if (user_picker_doFocus) + { + this_user_picker.data('combobox').refresh(); + this_user_picker.trigger('change'); + + $(this_user_picker.parent().data('next-input-selector').toString()) + .focus(); + } +} +export { userpicker } \ No newline at end of file diff --git a/js/configs/globalstate.js b/js/configs/globalstate.js index 9ac9f9e5..68177374 100644 --- a/js/configs/globalstate.js +++ b/js/configs/globalstate.js @@ -18,10 +18,11 @@ function setInitialGlobalState(Grocy) var parentMenuSelector = menuItem.data("sub-menu-of"); if (typeof parentMenuSelector !== "undefined") { - $(parentMenuSelector).collapse("show"); - $(parentMenuSelector).prev(".nav-link-collapse").addClass("active-page"); + var pMenu = $(parentMenuSelector); + pMenu.collapse("show"); + pMenu.prev(".nav-link-collapse").addClass("active-page"); - $(parentMenuSelector).on("shown.bs.collapse", function() + pMenu.on("shown.bs.collapse", function() { if (!menuItem.isVisibleInViewport(75)) { diff --git a/js/grocy.js b/js/grocy.js index d4c0f345..d86406ad 100644 --- a/js/grocy.js +++ b/js/grocy.js @@ -12,6 +12,7 @@ import { HeaderClock } from "./helpers/clock"; import { animateCSS, BoolVal, EmptyElementWhenMatches, GetUriParam, RemoveUriParam, UpdateUriParam } from "./helpers/extensions"; import Translator from "gettext-translator"; import { WindowMessageBag } from './helpers/messagebag'; +import * as components from './components'; import "./helpers/string"; @@ -47,6 +48,7 @@ class GrocyClass this.Locale = config.Locale; this.Components = {}; + this.initComponents = []; // Init some classes this.Api = new GrocyApi(this); @@ -150,21 +152,21 @@ class GrocyClass translate(text, ...placeholderValues) { - if (this.Mode === "dev") + /*if (this.Mode === "dev") { var text2 = text; this.Api.Post('system/log-missing-localization', { "text": text2 }); - } + }*/ return this.Translator.__(text, ...placeholderValues) } translaten(number, singularForm, pluralForm) { - if (this.Mode === "dev") + /*if (this.Mode === "dev") { var singularForm2 = singularForm; this.Api.Post('system/log-missing-localization', { "text": singularForm2 }); - } + }*/ return this.Translator.n__(singularForm, pluralForm, number, number) } @@ -212,6 +214,24 @@ class GrocyClass this.IdleTime += 1; } + Use(componentName) + { + // initialize Components only once + if (this.initComponents.find(elem => elem == componentName)) + return; + + if (Object.prototype.hasOwnProperty.call(components, componentName)) + { + // add-then-init to resolve circular dependencies + this.initComponents.push(componentName); + components[componentName](this); + } + else + { + console.error("Unable to find component " + componentName); + } + } + UndoStockBooking(bookingId) { var self = this; diff --git a/js/viewjs/barcodescannertesting.js b/js/viewjs/barcodescannertesting.js index bf630cbf..d208867e 100644 --- a/js/viewjs/barcodescannertesting.js +++ b/js/viewjs/barcodescannertesting.js @@ -1,4 +1,5 @@ -Grocy.BarCodeScannerTestingHitCount = 0; +Grocy.Use("barcodescanner"); +Grocy.BarCodeScannerTestingHitCount = 0; Grocy.BarCodeScannerTestingMissCount = 0; $("#scanned_barcode").on("blur", function(e) diff --git a/js/viewjs/batteriesoverview.js b/js/viewjs/batteriesoverview.js index ec8f5321..b891ca41 100644 --- a/js/viewjs/batteriesoverview.js +++ b/js/viewjs/batteriesoverview.js @@ -1,4 +1,6 @@ -var batteriesOverviewTable = $('#batteries-overview-table').DataTable({ +Grocy.Use("batterycard"); + +var batteriesOverviewTable = $('#batteries-overview-table').DataTable({ 'order': [[4, 'asc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, diff --git a/js/viewjs/batteriessettings.js b/js/viewjs/batteriessettings.js index 792c3d93..b0b44a17 100644 --- a/js/viewjs/batteriessettings.js +++ b/js/viewjs/batteriessettings.js @@ -1,3 +1,5 @@ -$("#batteries_due_soon_days").val(Grocy.UserSettings.batteries_due_soon_days); +Grocy.Use("numberpicker"); + +$("#batteries_due_soon_days").val(Grocy.UserSettings.batteries_due_soon_days); RefreshLocaleNumberInput(); diff --git a/js/viewjs/batteryform.js b/js/viewjs/batteryform.js index 074ddea9..ebbfe45e 100644 --- a/js/viewjs/batteryform.js +++ b/js/viewjs/batteryform.js @@ -1,5 +1,8 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("numberpicker"); +Grocy.Use("userfieldsform"); + $('#save-battery-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/batterytracking.js b/js/viewjs/batterytracking.js index c2a249bb..901ee912 100644 --- a/js/viewjs/batterytracking.js +++ b/js/viewjs/batterytracking.js @@ -1,4 +1,7 @@ -$('#save-batterytracking-button').on('click', function(e) +Grocy.Use("batterycard"); +Grocy.Use("datetimepicker"); + +$('#save-batterytracking-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/choreform.js b/js/viewjs/choreform.js index 538e6d28..c562dc9d 100644 --- a/js/viewjs/choreform.js +++ b/js/viewjs/choreform.js @@ -1,4 +1,7 @@ -$('#save-chore-button').on('click', function(e) +Grocy.Use("numberpicker"); +Grocy.Use("userfieldsform"); + +$('#save-chore-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/choresoverview.js b/js/viewjs/choresoverview.js index 99f124ea..82adff67 100644 --- a/js/viewjs/choresoverview.js +++ b/js/viewjs/choresoverview.js @@ -1,4 +1,6 @@ -var choresOverviewTable = $('#chores-overview-table').DataTable({ +Grocy.Use("chorecard"); + +var choresOverviewTable = $('#chores-overview-table').DataTable({ 'order': [[2, 'asc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, diff --git a/js/viewjs/choressettings.js b/js/viewjs/choressettings.js index 52967a6a..2c0360d8 100644 --- a/js/viewjs/choressettings.js +++ b/js/viewjs/choressettings.js @@ -1,3 +1,5 @@ -$("#chores_due_soon_days").val(Grocy.UserSettings.chores_due_soon_days); +Grocy.Use("numberpicker"); + +$("#chores_due_soon_days").val(Grocy.UserSettings.chores_due_soon_days); RefreshLocaleNumberInput(); diff --git a/js/viewjs/choretracking.js b/js/viewjs/choretracking.js index b58c1eda..e5c7ecb6 100644 --- a/js/viewjs/choretracking.js +++ b/js/viewjs/choretracking.js @@ -1,4 +1,8 @@ -$('#save-choretracking-button').on('click', function(e) +Grocy.Use("chorecard"); +Grocy.Use("datetimepicker"); +Grocy.Use("userpicker"); + +$('#save-choretracking-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/components/barcodescanner.js b/js/viewjs/components/barcodescanner.js deleted file mode 100644 index 9477e3e7..00000000 --- a/js/viewjs/components/barcodescanner.js +++ /dev/null @@ -1,304 +0,0 @@ -/* global Quagga2DatamatrixReader */ -import Quagga from '@ericblade/quagga2/dist/quagga'; - -Grocy.Components.BarcodeScanner = {}; - -Quagga.registerReader("datamatrix", Quagga2DatamatrixReader); - -Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = false; -Grocy.Components.BarcodeScanner.CheckCapabilities = async function() -{ - var track = Quagga.CameraAccess.getActiveTrack(); - var capabilities = {}; - if (typeof track.getCapabilities === 'function') - { - capabilities = track.getCapabilities(); - } - - // If there is more than 1 camera, show the camera selection - var cameras = await Quagga.CameraAccess.enumerateVideoDevices(); - var cameraSelect = document.querySelector('.cameraSelect-wrapper'); - if (cameraSelect) - { - cameraSelect.style.display = cameras.length > 1 ? 'inline-block' : 'none'; - } - - // Check if the camera is capable to turn on a torch. - var canTorch = typeof capabilities.torch === 'boolean' && capabilities.torch - // Remove the torch button, if either the device can not torch or AutoTorchOn is set. - var node = document.querySelector('.torch'); - if (node) - { - node.style.display = canTorch && !Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA ? 'inline-block' : 'none'; - } - // If AutoTorchOn is set, turn on the torch. - if (canTorch && Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA) - { - Grocy.Components.BarcodeScanner.TorchOn(track); - } - - // Reduce the height of the video, if it's higher than then the viewport - if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted) - { - var bc = document.getElementById('barcodescanner-container'); - if (bc) - { - var bcAspectRatio = bc.offsetWidth / bc.offsetHeight; - var settings = track.getSettings(); - if (bcAspectRatio > settings.aspectRatio) - { - var v = document.querySelector('#barcodescanner-livestream video') - if (v) - { - var c = document.querySelector('#barcodescanner-livestream canvas') - var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px'; - v.style.width = newWidth; - c.style.width = newWidth; - } - } - - Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = true; - } - } -} - -Grocy.Components.BarcodeScanner.StartScanning = function() -{ - Grocy.Components.BarcodeScanner.DecodedCodesCount = 0; - Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0; - - Quagga.init({ - inputStream: { - name: "Live", - type: "LiveStream", - target: document.querySelector("#barcodescanner-livestream"), - constraints: { - facingMode: "environment", - ...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }) // If preferred cameraId is set, request to use that specific camera - } - }, - locator: { - patchSize: Grocy.UserSettings.quagga2_patchsize, - halfSample: Grocy.UserSettings.quagga2_halfsample, - debug: { - showCanvas: Grocy.UserSettings.quagga2_debug, - showPatches: Grocy.UserSettings.quagga2_debug, - showFoundPatches: Grocy.UserSettings.quagga2_debug, - showSkeleton: Grocy.UserSettings.quagga2_debug, - showLabels: Grocy.UserSettings.quagga2_debug, - showPatchLabels: Grocy.UserSettings.quagga2_debug, - showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug, - boxFromPatches: { - showTransformed: Grocy.UserSettings.quagga2_debug, - showTransformedBox: Grocy.UserSettings.quagga2_debug, - showBB: Grocy.UserSettings.quagga2_debug - } - } - }, - numOfWorkers: Grocy.UserSettings.quagga2_numofworkers, - frequency: Grocy.UserSettings.quagga2_frequency, - decoder: { - readers: [ - "ean_reader", - "ean_8_reader", - "code_128_reader", - "datamatrix" - ], - debug: { - showCanvas: Grocy.UserSettings.quagga2_debug, - showPatches: Grocy.UserSettings.quagga2_debug, - showFoundPatches: Grocy.UserSettings.quagga2_debug, - showSkeleton: Grocy.UserSettings.quagga2_debug, - showLabels: Grocy.UserSettings.quagga2_debug, - showPatchLabels: Grocy.UserSettings.quagga2_debug, - showRemainingPatchLabels: Grocy.UserSettings.quagga2_debug, - boxFromPatches: { - showTransformed: Grocy.UserSettings.quagga2_debug, - showTransformedBox: Grocy.UserSettings.quagga2_debug, - showBB: Grocy.UserSettings.quagga2_debug - } - } - }, - locate: true - }, function(error) - { - // error *needs* to be logged here, otherwise the stack trace is lying. - console.error(error); - if (error) - { - Grocy.FrontendHelpers.ShowGenericError("Error while initializing the barcode scanning library", error.message); - toastr.info(__t("Camera access is only possible when supported and allowed by your browser and when grocy is served via a secure (https://) connection")); - window.localStorage.removeItem("cameraId"); - setTimeout(function() - { - bootbox.hideAll(); - }, 500); - return; - } - - Grocy.Components.BarcodeScanner.CheckCapabilities(); - - Quagga.start(); - }); -} - -Grocy.Components.BarcodeScanner.StopScanning = function() -{ - Quagga.stop(); - - Grocy.Components.BarcodeScanner.DecodedCodesCount = 0; - Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0; - - bootbox.hideAll(); -} - -Grocy.Components.BarcodeScanner.TorchOn = function(track) -{ - if (track) - { - track.applyConstraints({ - advanced: [ - { - torch: true - } - ] - }); - } -} - -Quagga.onDetected(function(result) -{ - $.each(result.codeResult.decodedCodes, function(id, error) - { - if (error.error != undefined) - { - Grocy.Components.BarcodeScanner.DecodedCodesCount++; - Grocy.Components.BarcodeScanner.DecodedCodesErrorCount += parseFloat(error.error); - } - }); - - if (Grocy.Components.BarcodeScanner.DecodedCodesErrorCount / Grocy.Components.BarcodeScanner.DecodedCodesCount < 0.15) - { - Grocy.Components.BarcodeScanner.StopScanning(); - $(document).trigger("Grocy.BarcodeScanned", [result.codeResult.code, Grocy.Components.BarcodeScanner.CurrentTarget]); - } -}); - -Quagga.onProcessed(function(result) -{ - var drawingCtx = Quagga.canvas.ctx.overlay; - var drawingCanvas = Quagga.canvas.dom.overlay; - - if (result) - { - if (result.boxes) - { - drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height"))); - result.boxes.filter(function(box) - { - return box !== result.box; - }).forEach(function(box) - { - Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "yellow", lineWidth: 4 }); - }); - } - - if (result.box) - { - Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 4 }); - } - - if (result.codeResult && result.codeResult.code) - { - Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: "red", lineWidth: 4 }); - } - } -}); - -$(document).on("click", "#barcodescanner-start-button", async function(e) -{ - e.preventDefault(); - var inputElement = $(e.currentTarget).prev(); - if (inputElement.hasAttr("disabled")) - { - // Do nothing and disable the barcode scanner start button - $(e.currentTarget).addClass("disabled"); - return; - } - - Grocy.Components.BarcodeScanner.CurrentTarget = inputElement.attr("data-target"); - - var dialog = bootbox.dialog({ - message: '
', - title: __t('Scan a barcode'), - onEscape: function() - { - Grocy.Components.BarcodeScanner.StopScanning(); - }, - size: 'big', - backdrop: true, - closeButton: true, - buttons: { - torch: { - label: '', - className: 'btn-warning responsive-button torch', - callback: function() - { - Grocy.Components.BarcodeScanner.TorchOn(Quagga.CameraAccess.getActiveTrack()); - return false; - } - }, - cancel: { - label: __t('Cancel'), - className: 'btn-secondary responsive-button', - callback: function() - { - Grocy.Components.BarcodeScanner.StopScanning(); - } - } - } - }); - - // Add camera select to existing dialog - dialog.find('.bootbox-body').append('
'); - var cameraSelect = document.querySelector('.cameraSelect'); - - if (cameraSelect != null) - { - var cameras = await Quagga.CameraAccess.enumerateVideoDevices(); - cameras.forEach(camera => - { - var option = document.createElement("option"); - option.text = camera.label ? camera.label : camera.deviceId; // Use camera label if it exists, else show device id - option.value = camera.deviceId; - cameraSelect.appendChild(option); - }); - - // Set initial value to preferred camera if one exists - and if not, start out empty - cameraSelect.value = window.localStorage.getItem('cameraId'); - - cameraSelect.onchange = function() - { - window.localStorage.setItem('cameraId', cameraSelect.value); - Quagga.stop(); - Grocy.Components.BarcodeScanner.StartScanning(); - }; - } - - Grocy.Components.BarcodeScanner.StartScanning(); -}); - -setTimeout(function() -{ - $(".barcodescanner-input:visible").each(function() - { - if ($(this).hasAttr("disabled")) - { - $(this).after(''); - } - else - { - $(this).after(''); - } - }); -}, 50); diff --git a/js/viewjs/components/batterycard.js b/js/viewjs/components/batterycard.js deleted file mode 100644 index e4ad85ab..00000000 --- a/js/viewjs/components/batterycard.js +++ /dev/null @@ -1,27 +0,0 @@ -Grocy.Components.BatteryCard = {}; - -Grocy.Components.BatteryCard.Refresh = function(batteryId) -{ - Grocy.Api.Get('batteries/' + batteryId, - function(batteryDetails) - { - $('#batterycard-battery-name').text(batteryDetails.battery.name); - $('#batterycard-battery-used_in').text(batteryDetails.battery.used_in); - $('#batterycard-battery-last-charged').text((batteryDetails.last_charged || __t('never'))); - $('#batterycard-battery-last-charged-timeago').attr("datetime", batteryDetails.last_charged || ''); - $('#batterycard-battery-charge-cycles-count').text((batteryDetails.charge_cycles_count || '0')); - - $('#batterycard-battery-edit-button').attr("href", U("/battery/" + batteryDetails.battery.id.toString())); - $('#batterycard-battery-journal-button').attr("href", U("/batteriesjournal?embedded&battery=" + batteryDetails.battery.id.toString())); - $('#batterycard-battery-edit-button').removeClass("disabled"); - $('#batterycard-battery-journal-button').removeClass("disabled"); - - EmptyElementWhenMatches('#batterycard-battery-last-charged-timeago', __t('timeago_nan')); - RefreshContextualTimeago(".batterycard"); - }, - function(xhr) - { - console.error(xhr); - } - ); -}; diff --git a/js/viewjs/components/calendarcard.js b/js/viewjs/components/calendarcard.js deleted file mode 100644 index 36cc3425..00000000 --- a/js/viewjs/components/calendarcard.js +++ /dev/null @@ -1,40 +0,0 @@ -$('#calendar').datetimepicker( - { - format: 'L', - buttons: { - showToday: true, - showClose: false - }, - calendarWeeks: true, - locale: moment.locale(), - icons: { - time: 'far fa-clock', - date: 'far fa-calendar', - up: 'fas fa-arrow-up', - down: 'fas fa-arrow-down', - previous: 'fas fa-chevron-left', - next: 'fas fa-chevron-right', - today: 'fas fa-calendar-check', - clear: 'far fa-trash-alt', - close: 'far fa-times-circle' - }, - keepOpen: true, - inline: true, - keyBinds: { - up: function(widget) { }, - down: function(widget) { }, - 'control up': function(widget) { }, - 'control down': function(widget) { }, - left: function(widget) { }, - right: function(widget) { }, - pageUp: function(widget) { }, - pageDown: function(widget) { }, - enter: function(widget) { }, - escape: function(widget) { }, - 'control space': function(widget) { }, - t: function(widget) { }, - 'delete': function(widget) { } - } - }); - -$('#calendar').datetimepicker('show'); diff --git a/js/viewjs/components/chorecard.js b/js/viewjs/components/chorecard.js deleted file mode 100644 index 861eaa51..00000000 --- a/js/viewjs/components/chorecard.js +++ /dev/null @@ -1,36 +0,0 @@ -Grocy.Components.ChoreCard = {}; - -Grocy.Components.ChoreCard.Refresh = function(choreId) -{ - Grocy.Api.Get('chores/' + choreId, - function(choreDetails) - { - $('#chorecard-chore-name').text(choreDetails.chore.name); - $('#chorecard-chore-last-tracked').text((choreDetails.last_tracked || __t('never'))); - $('#chorecard-chore-last-tracked-timeago').attr("datetime", choreDetails.last_tracked || ''); - $('#chorecard-chore-tracked-count').text((choreDetails.tracked_count || '0')); - $('#chorecard-chore-last-done-by').text((choreDetails.last_done_by.display_name || __t('Unknown'))); - - $('#chorecard-chore-edit-button').attr("href", U("/chore/" + choreDetails.chore.id.toString())); - $('#chorecard-chore-journal-button').attr("href", U("/choresjournal?embedded&chore=" + choreDetails.chore.id.toString())); - $('#chorecard-chore-edit-button').removeClass("disabled"); - $('#chorecard-chore-journal-button').removeClass("disabled"); - - if (choreDetails.chore.track_date_only == 1) - { - $("#chorecard-chore-last-tracked-timeago").addClass("timeago-date-only"); - } - else - { - $("#chorecard-chore-last-tracked-timeago").removeClass("timeago-date-only"); - } - - EmptyElementWhenMatches('#chorecard-chore-last-tracked-timeago', __t('timeago_nan')); - RefreshContextualTimeago(".chorecard"); - }, - function(xhr) - { - console.error(xhr); - } - ); -}; diff --git a/js/viewjs/components/datetimepicker.js b/js/viewjs/components/datetimepicker.js deleted file mode 100644 index d6dc97a5..00000000 --- a/js/viewjs/components/datetimepicker.js +++ /dev/null @@ -1,308 +0,0 @@ -Grocy.Components.DateTimePicker = {}; - -Grocy.Components.DateTimePicker.GetInputElement = function() -{ - return $('.datetimepicker').find('input').not(".form-check-input"); -} - -Grocy.Components.DateTimePicker.GetValue = function() -{ - return Grocy.Components.DateTimePicker.GetInputElement().val(); -} - -Grocy.Components.DateTimePicker.SetValue = function(value) -{ - // "Click" the shortcut checkbox when the desired value is - // not the shortcut value and it is currently set - var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); - if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked")) - { - $("#datetimepicker-shortcut").click(); - } - Grocy.Components.DateTimePicker.GetInputElement().val(value); - Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); - - Grocy.Components.DateTimePicker.GetInputElement().keyup(); -} - -Grocy.Components.DateTimePicker.Clear = function() -{ - $(".datetimepicker").datetimepicker("destroy"); - Grocy.Components.DateTimePicker.Init(); - - Grocy.Components.DateTimePicker.GetInputElement().val(""); - - // "Click" the shortcut checkbox when the desired value is - // not the shortcut value and it is currently set - var value = ""; - var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); - if (value != shortcutValue && $("#datetimepicker-shortcut").is(":checked")) - { - $("#datetimepicker-shortcut").click(); - } - - $('#datetimepicker-timeago').text(''); -} - -Grocy.Components.DateTimePicker.ChangeFormat = function(format) -{ - $(".datetimepicker").datetimepicker("destroy"); - Grocy.Components.DateTimePicker.GetInputElement().data("format", format); - Grocy.Components.DateTimePicker.Init(); - - if (format == "YYYY-MM-DD") - { - Grocy.Components.DateTimePicker.GetInputElement().addClass("date-only-datetimepicker"); - } - else - { - Grocy.Components.DateTimePicker.GetInputElement().removeClass("date-only-datetimepicker"); - } -} - -var startDate = null; -if (Grocy.Components.DateTimePicker.GetInputElement().data('init-with-now') === true) -{ - startDate = moment().format(Grocy.Components.DateTimePicker.GetInputElement().data('format')); -} -if (Grocy.Components.DateTimePicker.GetInputElement().data('init-value').length > 0) -{ - startDate = moment(Grocy.Components.DateTimePicker.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker.GetInputElement().data('format')); -} - -var limitDate = moment('2999-12-31 23:59:59'); -if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true) -{ - limitDate = moment(); -} - -Grocy.Components.DateTimePicker.Init = function() -{ - $('.datetimepicker').datetimepicker( - { - format: Grocy.Components.DateTimePicker.GetInputElement().data('format'), - buttons: { - showToday: true, - showClose: true - }, - calendarWeeks: Grocy.CalendarShowWeekNumbers, - maxDate: limitDate, - locale: moment.locale(), - defaultDate: startDate, - useCurrent: false, - icons: { - time: 'far fa-clock', - date: 'far fa-calendar', - up: 'fas fa-arrow-up', - down: 'fas fa-arrow-down', - previous: 'fas fa-chevron-left', - next: 'fas fa-chevron-right', - today: 'fas fa-calendar-check', - clear: 'far fa-trash-alt', - close: 'far fa-times-circle' - }, - sideBySide: true, - keyBinds: { - up: function(widget) { }, - down: function(widget) { }, - 'control up': function(widget) { }, - 'control down': function(widget) { }, - left: function(widget) { }, - right: function(widget) { }, - pageUp: function(widget) { }, - pageDown: function(widget) { }, - enter: function(widget) { }, - escape: function(widget) { }, - 'control space': function(widget) { }, - t: function(widget) { }, - 'delete': function(widget) { } - } - }); -} -Grocy.Components.DateTimePicker.Init(); - -Grocy.Components.DateTimePicker.GetInputElement().on('keyup', function(e) -{ - $('.datetimepicker').datetimepicker('hide'); - - var value = Grocy.Components.DateTimePicker.GetValue(); - var now = new Date(); - var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00'); - var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99'); - var format = Grocy.Components.DateTimePicker.GetInputElement().data('format'); - var nextInputElement = $(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector')); - - //If input is empty and any arrow key is pressed, set date to today - if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39)) - { - Grocy.Components.DateTimePicker.SetValue(moment(new Date(), format, true).format(format)); - nextInputElement.focus(); - } - else if (value === 'x' || value === 'X') - { - Grocy.Components.DateTimePicker.SetValue(moment('2999-12-31 23:59:59').format(format)); - nextInputElement.focus(); - } - else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd)) - { - var date = moment((new Date()).getFullYear().toString() + value); - if (date.isBefore(moment())) - { - date.add(1, "year"); - } - Grocy.Components.DateTimePicker.SetValue(date.format(format)); - nextInputElement.focus(); - } - else if (value.length === 8 && $.isNumeric(value)) - { - Grocy.Components.DateTimePicker.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3')); - nextInputElement.focus(); - } - else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+")) - { - var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month"); - Grocy.Components.DateTimePicker.SetValue(endOfMonth.format(format)); - nextInputElement.focus(); - } - else - { - var dateObj = moment(value, format, true); - if (dateObj.isValid()) - { - if (e.shiftKey) - { - // WITH shift modifier key - - if (e.keyCode === 38) // Up - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'months').format(format)); - } - else if (e.keyCode === 40) // Down - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'months').format(format)); - } - else if (e.keyCode === 37) // Left - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'years').format(format)); - } - else if (e.keyCode === 39) // Right - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'years').format(format)); - } - } - else - { - // WITHOUT shift modifier key - - if (e.keyCode === 38) // Up - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'days').format(format)); - } - else if (e.keyCode === 40) // Down - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'days').format(format)); - } - else if (e.keyCode === 37) // Left - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(-1, 'weeks').format(format)); - } - else if (e.keyCode === 39) // Right - { - Grocy.Components.DateTimePicker.SetValue(dateObj.add(1, 'weeks').format(format)); - } - } - } - } - - //Custom validation - value = Grocy.Components.DateTimePicker.GetValue(); - dateObj = moment(value, format, true); - var element = Grocy.Components.DateTimePicker.GetInputElement()[0]; - if (!dateObj.isValid()) - { - if ($(element).hasAttr("required")) - { - element.setCustomValidity("error"); - } - } - else - { - if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment())) - { - element.setCustomValidity("error"); - } - else if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment())) - { - element.setCustomValidity("error"); - } - else - { - element.setCustomValidity(""); - } - - var earlierThanLimit = Grocy.Components.DateTimePicker.GetInputElement().data("earlier-than-limit"); - if (!earlierThanLimit.isEmpty()) - { - if (moment(value).isBefore(moment(earlierThanLimit))) - { - $("#datetimepicker-earlier-than-info").removeClass("d-none"); - } - else - { - $("#datetimepicker-earlier-than-info").addClass("d-none"); - } - } - } - - // "Click" the shortcut checkbox when the shortcut value was - // entered manually and it is currently not set - var shortcutValue = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); - if (value == shortcutValue && !$("#datetimepicker-shortcut").is(":checked")) - { - $("#datetimepicker-shortcut").click(); - } -}); - -Grocy.Components.DateTimePicker.GetInputElement().on('input', function(e) -{ - $('#datetimepicker-timeago').attr("datetime", Grocy.Components.DateTimePicker.GetValue()); - EmptyElementWhenMatches('#datetimepicker-timeago', __t('timeago_nan')); - RefreshContextualTimeago("#datetimepicker-wrapper"); -}); - -$('.datetimepicker').on('update.datetimepicker', function(e) -{ - Grocy.Components.DateTimePicker.GetInputElement().trigger('input'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup'); -}); - -$('.datetimepicker').on('hide.datetimepicker', function(e) -{ - Grocy.Components.DateTimePicker.GetInputElement().trigger('input'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup'); -}); - -$("#datetimepicker-shortcut").on("click", function() -{ - if (this.checked) - { - var value = $("#datetimepicker-shortcut").data("datetimepicker-shortcut-value"); - Grocy.Components.DateTimePicker.SetValue(value); - Grocy.Components.DateTimePicker.GetInputElement().attr("readonly", ""); - $(Grocy.Components.DateTimePicker.GetInputElement().data('next-input-selector')).focus(); - } - else - { - Grocy.Components.DateTimePicker.SetValue(""); - Grocy.Components.DateTimePicker.GetInputElement().removeAttr("readonly"); - Grocy.Components.DateTimePicker.GetInputElement().focus(); - } - - Grocy.Components.DateTimePicker.GetInputElement().trigger('input'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('change'); - Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress'); -}); diff --git a/js/viewjs/components/datetimepicker2.js b/js/viewjs/components/datetimepicker2.js deleted file mode 100644 index 5fcc4209..00000000 --- a/js/viewjs/components/datetimepicker2.js +++ /dev/null @@ -1,309 +0,0 @@ -Grocy.Components.DateTimePicker2 = {}; - -Grocy.Components.DateTimePicker2.GetInputElement = function() -{ - return $('.datetimepicker2').find('input').not(".form-check-input"); -} - -Grocy.Components.DateTimePicker2.GetValue = function() -{ - return Grocy.Components.DateTimePicker2.GetInputElement().val(); -} - -Grocy.Components.DateTimePicker2.SetValue = function(value) -{ - // "Click" the shortcut checkbox when the desired value is - // not the shortcut value and it is currently set - var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); - if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked")) - { - $("#datetimepicker2-shortcut").click(); - } - - Grocy.Components.DateTimePicker2.GetInputElement().val(value); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); - - Grocy.Components.DateTimePicker2.GetInputElement().keyup(); -} - -Grocy.Components.DateTimePicker2.Clear = function() -{ - $(".datetimepicker2").datetimepicker("destroy"); - Grocy.Components.DateTimePicker2.Init(); - - Grocy.Components.DateTimePicker2.GetInputElement().val(""); - - // "Click" the shortcut checkbox when the desired value is - // not the shortcut value and it is currently set - var value = ""; - var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); - if (value != shortcutValue && $("#datetimepicker2-shortcut").is(":checked")) - { - $("#datetimepicker2-shortcut").click(); - } - - $('#datetimepicker2-timeago').text(''); -} - -Grocy.Components.DateTimePicker2.ChangeFormat = function(format) -{ - $(".datetimepicker2").datetimepicker("destroy"); - Grocy.Components.DateTimePicker2.GetInputElement().data("format", format); - Grocy.Components.DateTimePicker2.Init(); - - if (format == "YYYY-MM-DD") - { - Grocy.Components.DateTimePicker2.GetInputElement().addClass("date-only-datetimepicker"); - } - else - { - Grocy.Components.DateTimePicker2.GetInputElement().removeClass("date-only-datetimepicker"); - } -} - -var startDate = null; -if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-with-now') === true) -{ - startDate = moment().format(Grocy.Components.DateTimePicker2.GetInputElement().data('format')); -} -if (Grocy.Components.DateTimePicker2.GetInputElement().data('init-value').length > 0) -{ - startDate = moment(Grocy.Components.DateTimePicker2.GetInputElement().data('init-value')).format(Grocy.Components.DateTimePicker2.GetInputElement().data('format')); -} - -var limitDate = moment('2999-12-31 23:59:59'); -if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true) -{ - limitDate = moment(); -} - -Grocy.Components.DateTimePicker2.Init = function() -{ - $('.datetimepicker2').datetimepicker( - { - format: Grocy.Components.DateTimePicker2.GetInputElement().data('format'), - buttons: { - showToday: true, - showClose: true - }, - calendarWeeks: Grocy.CalendarShowWeekNumbers, - maxDate: limitDate, - locale: moment.locale(), - defaultDate: startDate, - useCurrent: false, - icons: { - time: 'far fa-clock', - date: 'far fa-calendar', - up: 'fas fa-arrow-up', - down: 'fas fa-arrow-down', - previous: 'fas fa-chevron-left', - next: 'fas fa-chevron-right', - today: 'fas fa-calendar-check', - clear: 'far fa-trash-alt', - close: 'far fa-times-circle' - }, - sideBySide: true, - keyBinds: { - up: function(widget) { }, - down: function(widget) { }, - 'control up': function(widget) { }, - 'control down': function(widget) { }, - left: function(widget) { }, - right: function(widget) { }, - pageUp: function(widget) { }, - pageDown: function(widget) { }, - enter: function(widget) { }, - escape: function(widget) { }, - 'control space': function(widget) { }, - t: function(widget) { }, - 'delete': function(widget) { } - } - }); -} -Grocy.Components.DateTimePicker2.Init(); - -Grocy.Components.DateTimePicker2.GetInputElement().on('keyup', function(e) -{ - $('.datetimepicker2').datetimepicker('hide'); - - var value = Grocy.Components.DateTimePicker2.GetValue(); - var now = new Date(); - var centuryStart = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '00'); - var centuryEnd = Number.parseInt(now.getFullYear().toString().substring(0, 2) + '99'); - var format = Grocy.Components.DateTimePicker2.GetInputElement().data('format'); - var nextInputElement = $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')); - - //If input is empty and any arrow key is pressed, set date to today - if (value.length === 0 && (e.keyCode === 38 || e.keyCode === 40 || e.keyCode === 37 || e.keyCode === 39)) - { - Grocy.Components.DateTimePicker2.SetValue(moment(new Date(), format, true).format(format)); - nextInputElement.focus(); - } - else if (value === 'x' || value === 'X') - { - Grocy.Components.DateTimePicker2.SetValue(moment('2999-12-31 23:59:59').format(format)); - nextInputElement.focus(); - } - else if (value.length === 4 && !(Number.parseInt(value) > centuryStart && Number.parseInt(value) < centuryEnd)) - { - var date = moment((new Date()).getFullYear().toString() + value); - if (date.isBefore(moment())) - { - date.add(1, "year"); - } - Grocy.Components.DateTimePicker2.SetValue(date.format(format)); - nextInputElement.focus(); - } - else if (value.length === 8 && $.isNumeric(value)) - { - Grocy.Components.DateTimePicker2.SetValue(value.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3')); - nextInputElement.focus(); - } - else if (value.length === 7 && $.isNumeric(value.substring(0, 6)) && (value.substring(6, 7).toLowerCase() === "e" || value.substring(6, 7).toLowerCase() === "+")) - { - var endOfMonth = moment(value.substring(0, 4) + "-" + value.substring(4, 6) + "-01").endOf("month"); - Grocy.Components.DateTimePicker2.SetValue(endOfMonth.format(format)); - nextInputElement.focus(); - } - else - { - var dateObj = moment(value, format, true); - if (dateObj.isValid()) - { - if (e.shiftKey) - { - // WITH shift modifier key - - if (e.keyCode === 38) // Up - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'months').format(format)); - } - else if (e.keyCode === 40) // Down - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'months').format(format)); - } - else if (e.keyCode === 37) // Left - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'years').format(format)); - } - else if (e.keyCode === 39) // Right - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'years').format(format)); - } - } - else - { - // WITHOUT shift modifier key - - if (e.keyCode === 38) // Up - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'days').format(format)); - } - else if (e.keyCode === 40) // Down - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'days').format(format)); - } - else if (e.keyCode === 37) // Left - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(-1, 'weeks').format(format)); - } - else if (e.keyCode === 39) // Right - { - Grocy.Components.DateTimePicker2.SetValue(dateObj.add(1, 'weeks').format(format)); - } - } - } - } - - //Custom validation - value = Grocy.Components.DateTimePicker2.GetValue(); - dateObj = moment(value, format, true); - var element = Grocy.Components.DateTimePicker2.GetInputElement()[0]; - if (!dateObj.isValid()) - { - if ($(element).hasAttr("required")) - { - element.setCustomValidity("error"); - } - } - else - { - if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment())) - { - element.setCustomValidity("error"); - } - else if (Grocy.Components.DateTimePicker2.GetInputElement().data('limit-start-to-now') === true && dateObj.isBefore(moment())) - { - element.setCustomValidity("error"); - } - else - { - element.setCustomValidity(""); - } - - var earlierThanLimit = Grocy.Components.DateTimePicker2.GetInputElement().data("earlier-than-limit"); - if (!earlierThanLimit.isEmpty()) - { - if (moment(value).isBefore(moment(earlierThanLimit))) - { - $("#datetimepicker-earlier-than-info").removeClass("d-none"); - } - else - { - $("#datetimepicker-earlier-than-info").addClass("d-none"); - } - } - } - - // "Click" the shortcut checkbox when the shortcut value was - // entered manually and it is currently not set - var shortcutValue = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); - if (value == shortcutValue && !$("#datetimepicker2-shortcut").is(":checked")) - { - $("#datetimepicker2-shortcut").click(); - } -}); - -Grocy.Components.DateTimePicker2.GetInputElement().on('input', function(e) -{ - $('#datetimepicker2-timeago').attr("datetime", Grocy.Components.DateTimePicker2.GetValue()); - EmptyElementWhenMatches('#datetimepicker2-timeago', __t('timeago_nan')); - RefreshContextualTimeago("#datetimepicker2-wrapper"); -}); - -$('.datetimepicker2').on('update.datetimepicker', function(e) -{ - Grocy.Components.DateTimePicker2.GetInputElement().trigger('input'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup'); -}); - -$('.datetimepicker2').on('hide.datetimepicker', function(e) -{ - Grocy.Components.DateTimePicker2.GetInputElement().trigger('input'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('keyup'); -}); - -$("#datetimepicker2-shortcut").on("click", function() -{ - if (this.checked) - { - var value = $("#datetimepicker2-shortcut").data("datetimepicker2-shortcut-value"); - Grocy.Components.DateTimePicker2.SetValue(value); - Grocy.Components.DateTimePicker2.GetInputElement().attr("readonly", ""); - $(Grocy.Components.DateTimePicker2.GetInputElement().data('next-input-selector')).focus(); - } - else - { - Grocy.Components.DateTimePicker2.SetValue(""); - Grocy.Components.DateTimePicker2.GetInputElement().removeAttr("readonly"); - Grocy.Components.DateTimePicker2.GetInputElement().focus(); - } - - Grocy.Components.DateTimePicker2.GetInputElement().trigger('input'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('change'); - Grocy.Components.DateTimePicker2.GetInputElement().trigger('keypress'); -}); diff --git a/js/viewjs/components/locationpicker.js b/js/viewjs/components/locationpicker.js deleted file mode 100644 index 7ed5f7fd..00000000 --- a/js/viewjs/components/locationpicker.js +++ /dev/null @@ -1,74 +0,0 @@ -Grocy.Components.LocationPicker = {}; - -Grocy.Components.LocationPicker.GetPicker = function() -{ - return $('#location_id'); -} - -Grocy.Components.LocationPicker.GetInputElement = function() -{ - return $('#location_id_text_input'); -} - -Grocy.Components.LocationPicker.GetValue = function() -{ - return $('#location_id').val(); -} - -Grocy.Components.LocationPicker.SetValue = function(value) -{ - Grocy.Components.LocationPicker.GetInputElement().val(value); - Grocy.Components.LocationPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.LocationPicker.SetId = function(value) -{ - Grocy.Components.LocationPicker.GetPicker().val(value); - Grocy.Components.LocationPicker.GetPicker().data('combobox').refresh(); - Grocy.Components.LocationPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.LocationPicker.Clear = function() -{ - Grocy.Components.LocationPicker.SetValue(''); - Grocy.Components.LocationPicker.SetId(null); -} - -$('.location-combobox').combobox({ - appendId: '_text_input', - bsVersion: '4', - clearIfNoMatch: true -}); - -// these names seem a bit long, but as they live in global space -// and this is a component, they need to be unique. -var locationpicker_doFocus = false; -var this_location_picker = Grocy.Components.LocationPicker.GetPicker(); - -var prefillByName = this_location_picker.parent().data('prefill-by-name').toString(); -if (typeof prefillByName !== "undefined") -{ - var possibleOptionElement = $("#location_id option:contains(\"" + prefillByName + "\")").first(); - - if (possibleOptionElement.length > 0) - { - locationpicker_doFocus = true; - this_location_picker.val(possibleOptionElement.val()); - } -} - -var prefillById = this_location_picker.parent().data('prefill-by-id').toString(); -if (typeof prefillById !== "undefined") -{ - locationpicker_doFocus = true; - this_location_picker.val(prefillById); -} - -if (locationpicker_doFocus) -{ - this_location_picker.data('combobox').refresh(); - this_location_picker.trigger('change'); - - $(this_location_picker.parent().data('next-input-selector').toString()) - .focus(); -} diff --git a/js/viewjs/components/numberpicker.js b/js/viewjs/components/numberpicker.js deleted file mode 100644 index 249e2b85..00000000 --- a/js/viewjs/components/numberpicker.js +++ /dev/null @@ -1,94 +0,0 @@ -$(".numberpicker-down-button").unbind('click').on("click", function() -{ - var inputElement = $(this).parent().parent().find('input[type="number"]'); - inputElement.val(parseFloat(inputElement.val() || 1) - 1); - inputElement.trigger('keyup'); - inputElement.trigger('change'); -}); - -$(".numberpicker-up-button").unbind('click').on("click", function() -{ - var inputElement = $(this).parent().parent().find('input[type="number"]'); - inputElement.val(parseFloat(inputElement.val() || 0) + 1); - inputElement.trigger('keyup'); - inputElement.trigger('change'); -}); - -$(".numberpicker").on("keyup", function() -{ - if ($(this).attr("data-not-equal") && !$(this).attr("data-not-equal").toString().isEmpty() && $(this).attr("data-not-equal") == $(this).val()) - { - $(this)[0].setCustomValidity("error"); - } - else - { - $(this)[0].setCustomValidity(""); - } -}); - -$(".numberpicker").each(function() -{ - new MutationObserver(function(mutations) - { - mutations.forEach(function(mutation) - { - if (mutation.type == "attributes" && (mutation.attributeName == "min" || mutation.attributeName == "max" || mutation.attributeName == "data-not-equal" || mutation.attributeName == "data-initialised")) - { - var element = $(mutation.target); - var min = element.attr("min"); - var decimals = element.attr("data-decimals"); - - var max = ""; - if (element.hasAttr("max")) - { - max = element.attr("max"); - } - - if (element.hasAttr("data-not-equal")) - { - var notEqual = element.attr("data-not-equal"); - - if (notEqual != "NaN") - { - if (max.isEmpty()) - { - element.parent().find(".invalid-feedback").text(__t("This cannot be lower than %1$s or equal %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals)); - } - else - { - element.parent().find(".invalid-feedback").text(__t("This must be between %1$s and %2$s, cannot equal %3$s and needs to be a valid number with max. %4$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(notEqual).toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }), decimals)); - } - - return; - } - } - - if (max.isEmpty()) - { - element.parent().find(".invalid-feedback").text(__t("This cannot be lower than %1$s and needs to be a valid number with max. %2$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals)); - } - else - { - element.parent().find(".invalid-feedback").text(__t("This must between %1$s and %2$s and needs to be a valid number with max. %3$s decimal places", parseFloat(min).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), parseFloat(max).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: decimals }), decimals)); - } - } - }); - }).observe(this, { - attributes: true - }); -}); -$(".numberpicker").attr("data-initialised", "true"); // Dummy change to trigger MutationObserver above once - -$(".numberpicker").on("keydown", function(e) -{ - if (e.key == "ArrowUp") - { - e.preventDefault(); - $(this).parent().find(".numberpicker-up-button").click(); - } - else if (e.key == "ArrowDown") - { - e.preventDefault(); - $(this).parent().find(".numberpicker-down-button").click(); - } -}); diff --git a/js/viewjs/components/productamountpicker.js b/js/viewjs/components/productamountpicker.js deleted file mode 100644 index 1008267f..00000000 --- a/js/viewjs/components/productamountpicker.js +++ /dev/null @@ -1,116 +0,0 @@ -Grocy.Components.ProductAmountPicker = {}; -Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false; - -Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuId, forceInitialDisplayQu = false) -{ - var conversionsForProduct = Grocy.QuantityUnitConversionsResolved.filter(elem => elem.product_id == productId); - - if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled) - { - var qu = Grocy.QuantityUnits.find(elem => elem.id == destinationQuId); - $("#qu_id").find("option").remove().end(); - $("#qu_id").attr("data-destination-qu-name", qu.name); - $("#qu_id").attr("data-destination-qu-name-plural", qu.name_plural); - - conversionsForProduct.forEach(conversion => - { - var factor = parseFloat(conversion.factor); - if (conversion.to_qu_id == destinationQuId) - { - factor = 1; - } - - if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times - { - $("#qu_id").append(''); - } - }); - } - - if (!Grocy.Components.ProductAmountPicker.InitialValueSet || forceInitialDisplayQu) - { - $("#qu_id").val($("#qu_id").attr("data-initial-qu-id")); - } - - if (!Grocy.Components.ProductAmountPicker.InitialValueSet) - { - var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor"); - $("#display_amount").val(convertedAmount); - - Grocy.Components.ProductAmountPicker.InitialValueSet = true; - } - - if (conversionsForProduct.length === 1 && !forceInitialDisplayQu) - { - $("#qu_id").val($("#qu_id option:first").val()); - } - - if ($('#qu_id option').length == 1) - { - $("#qu_id").attr("disabled", ""); - } - else - { - $("#qu_id").removeAttr("disabled"); - } - - $(".input-group-productamountpicker").trigger("change"); -} - -Grocy.Components.ProductAmountPicker.SetQuantityUnit = function(quId) -{ - $("#qu_id").val(quId); -} - -Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false) -{ - Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true; - - $("#qu_id").find("option").remove().end(); - Grocy.QuantityUnits.forEach(qu => - { - $("#qu_id").append(''); - }); - - if (keepInitialQu) - { - Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu_id").attr("data-initial-qu-id")); - } - - $("#qu_id").removeAttr("disabled"); - - $(".input-group-productamountpicker").trigger("change"); -} - -Grocy.Components.ProductAmountPicker.Reset = function() -{ - $("#qu_id").find("option").remove(); - $("#qu-conversion-info").addClass("d-none"); - $("#qu-display_amount-info").val(""); -} - -$(".input-group-productamountpicker").on("change", function() -{ - var selectedQuName = $("#qu_id option:selected").text(); - var quFactor = $("#qu_id option:selected").attr("data-qu-factor"); - var amount = $("#display_amount").val(); - var destinationAmount = amount / quFactor; - var destinationQuName = __n(destinationAmount, $("#qu_id").attr("data-destination-qu-name"), $("#qu_id").attr("data-destination-qu-name-plural")) - - if ($("#qu_id").attr("data-destination-qu-name") == selectedQuName || Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled || amount.toString().isEmpty() || selectedQuName.toString().isEmpty()) - { - $("#qu-conversion-info").addClass("d-none"); - } - else - { - $("#qu-conversion-info").removeClass("d-none"); - $("#qu-conversion-info").text(__t("This equals %1$s %2$s", destinationAmount.toLocaleString({ minimumFractionDigits: 0, maximumFractionDigits: Grocy.UserSettings.stock_decimal_places_amounts }), destinationQuName)); - } - - $("#amount").val(destinationAmount.toFixed(Grocy.UserSettings.stock_decimal_places_amounts).replace(/0*$/g, '')); -}); - -$("#display_amount").on("keyup", function() -{ - $(".input-group-productamountpicker").trigger("change"); -}); diff --git a/js/viewjs/components/productcard.js b/js/viewjs/components/productcard.js deleted file mode 100644 index 4f1cdc20..00000000 --- a/js/viewjs/components/productcard.js +++ /dev/null @@ -1,237 +0,0 @@ -import Chart from 'chart.js'; - -Grocy.Components.ProductCard = {}; - -Grocy.Components.ProductCard.Refresh = function(productId) -{ - Grocy.Api.Get('stock/products/' + productId, - function(productDetails) - { - var stockAmount = productDetails.stock_amount || '0'; - var stockValue = productDetails.stock_value || '0'; - var stockAmountOpened = productDetails.stock_amount_opened || '0'; - $('#productcard-product-name').text(productDetails.product.name); - $('#productcard-product-description').html(productDetails.product.description); - $('#productcard-product-stock-amount').text(stockAmount); - $('#productcard-product-stock-qu-name').text(__n(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)); - $('#productcard-product-stock-value').text(stockValue + ' ' + Grocy.Currency); - $('#productcard-product-last-purchased').text((productDetails.last_purchased || '2999-12-31').substring(0, 10)); - $('#productcard-product-last-purchased-timeago').attr("datetime", productDetails.last_purchased || '2999-12-31'); - $('#productcard-product-last-used').text((productDetails.last_used || '2999-12-31').substring(0, 10)); - $('#productcard-product-last-used-timeago').attr("datetime", productDetails.last_used || '2999-12-31'); - if (productDetails.location != null) - { - $('#productcard-product-location').text(productDetails.location.name); - } - $('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" })); - - if (productDetails.is_aggregated_amount == 1) - { - $('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated); - $('#productcard-product-stock-qu-name-aggregated').text(__n(productDetails.stock_amount_aggregated, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural)); - - if (productDetails.stock_amount_opened_aggregated > 0) - { - $('#productcard-product-stock-opened-amount-aggregated').text(__t('%s opened', productDetails.stock_amount_opened_aggregated)); - } - else - { - $('#productcard-product-stock-opened-amount-aggregated').text(""); - } - - $("#productcard-aggregated-amounts").removeClass("d-none"); - } - else - { - $("#productcard-aggregated-amounts").addClass("d-none"); - } - - if (productDetails.product.description != null && !productDetails.product.description.isEmpty()) - { - $("#productcard-product-description-wrapper").removeClass("d-none"); - } - else - { - $("#productcard-product-description-wrapper").addClass("d-none"); - } - - if (productDetails.average_shelf_life_days == -1) - { - $('#productcard-product-average-shelf-life').text(__t("Unknown")); - } - else if (parseInt(productDetails.average_shelf_life_days) > 73000) // > 200 years aka forever - { - $('#productcard-product-average-shelf-life').text(__t("Unlimited")); - } - else - { - $('#productcard-product-average-shelf-life').text(moment.duration(productDetails.average_shelf_life_days, "days").humanize()); - } - - if (stockAmountOpened > 0) - { - $('#productcard-product-stock-opened-amount').text(__t('%s opened', stockAmountOpened)); - } - else - { - $('#productcard-product-stock-opened-amount').text(""); - } - - $('#productcard-product-edit-button').attr("href", U("/product/" + productDetails.product.id.toString() + '?' + 'returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative))); - $('#productcard-product-journal-button').attr("href", U("/stockjournal?embedded&product=" + productDetails.product.id.toString())); - $('#productcard-product-stock-button').attr("href", U("/stockentries?embedded&product=" + productDetails.product.id.toString())); - $('#productcard-product-stock-button').removeClass("disabled"); - $('#productcard-product-edit-button').removeClass("disabled"); - $('#productcard-product-journal-button').removeClass("disabled"); - - if (productDetails.last_price !== null) - { - $('#productcard-product-last-price').text(Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name); - } - else - { - $('#productcard-product-last-price').text(__t('Unknown')); - } - - if (productDetails.avg_price !== null) - { - $('#productcard-product-average-price').text(Number.parseFloat(productDetails.avg_price).toLocaleString() + ' ' + Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name); - } - else - { - $('#productcard-product-average-price').text(__t('Unknown')); - } - - if (productDetails.product.picture_file_name !== null && !productDetails.product.picture_file_name.isEmpty()) - { - $("#productcard-product-picture").removeClass("d-none"); - $("#productcard-product-picture").attr("src", U('/api/files/productpictures/' + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400')); - } - else - { - $("#productcard-product-picture").addClass("d-none"); - } - - EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', __t('timeago_nan')); - EmptyElementWhenMatches('#productcard-product-last-used-timeago', __t('timeago_nan')); - RefreshContextualTimeago(".productcard"); - }, - function(xhr) - { - console.error(xhr); - } - ); - - if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING) - { - Grocy.Api.Get('stock/products/' + productId + '/price-history', - function(priceHistoryDataPoints) - { - if (priceHistoryDataPoints.length > 0) - { - $("#productcard-product-price-history-chart").removeClass("d-none"); - $("#productcard-no-price-data-hint").addClass("d-none"); - - Grocy.Components.ProductCard.ReInitPriceHistoryChart(); - var datasets = {}; - var chart = Grocy.Components.ProductCard.PriceHistoryChart.data; - priceHistoryDataPoints.forEach((dataPoint) => - { - var key = __t("Unknown store"); - if (dataPoint.shopping_location) - { - key = dataPoint.shopping_location.name - } - - if (!datasets[key]) - { - datasets[key] = [] - } - chart.labels.push(moment(dataPoint.date).toDate()); - datasets[key].push(dataPoint.price); - - }); - Object.keys(datasets).forEach((key) => - { - chart.datasets.push({ - data: datasets[key], - fill: false, - borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)", - label: key, - }); - }); - Grocy.Components.ProductCard.PriceHistoryChart.update(); - } - else - { - $("#productcard-product-price-history-chart").addClass("d-none"); - $("#productcard-no-price-data-hint").removeClass("d-none"); - } - }, - function(xhr) - { - console.error(xhr); - } - ); - } -}; - -Grocy.Components.ProductCard.ReInitPriceHistoryChart = function() -{ - if (typeof Grocy.Components.ProductCard.PriceHistoryChart !== "undefined") - { - Grocy.Components.ProductCard.PriceHistoryChart.destroy(); - } - - var format = 'YYYY-MM-DD'; - Grocy.Components.ProductCard.PriceHistoryChart = new Chart(document.getElementById("productcard-product-price-history-chart"), { - type: "line", - data: { - labels: [ //Date objects - // Will be populated in Grocy.Components.ProductCard.Refresh - ], - datasets: [ //Datasets - // Will be populated in Grocy.Components.ProductCard.Refresh - ] - }, - options: { - scales: { - xAxes: [{ - type: 'time', - time: { - parser: format, - round: 'day', - tooltipFormat: format, - unit: 'day', - unitStepSize: 10, - displayFormats: { - 'day': format - } - }, - ticks: { - autoSkip: true, - maxRotation: 0 - } - }], - yAxes: [{ - ticks: { - beginAtZero: true - } - }] - }, - legend: { - display: true - } - } - }); -} - -$("#productcard-product-description").on("shown.bs.collapse", function() -{ - $(".expandable-text").find("a[data-toggle='collapse']").text(__t("Show less")); -}) - -$("#productcard-product-description").on("hidden.bs.collapse", function() -{ - $(".expandable-text").find("a[data-toggle='collapse']").text(__t("Show more")); -}) diff --git a/js/viewjs/components/productpicker.js b/js/viewjs/components/productpicker.js deleted file mode 100644 index 6081e392..00000000 --- a/js/viewjs/components/productpicker.js +++ /dev/null @@ -1,331 +0,0 @@ -Grocy.Components.ProductPicker = {}; - -Grocy.Components.ProductPicker.GetPicker = function() -{ - return $('#product_id'); -} - -Grocy.Components.ProductPicker.GetInputElement = function() -{ - return $('#product_id_text_input'); -} - -Grocy.Components.ProductPicker.GetValue = function() -{ - return $('#product_id').val(); -} - -Grocy.Components.ProductPicker.SetValue = function(value) -{ - Grocy.Components.ProductPicker.GetInputElement().val(value); - Grocy.Components.ProductPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.ProductPicker.SetId = function(value) -{ - Grocy.Components.ProductPicker.GetPicker().val(value); - Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh(); - Grocy.Components.ProductPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.ProductPicker.Clear = function() -{ - Grocy.Components.ProductPicker.SetValue(''); - Grocy.Components.ProductPicker.SetId(null); -} - -Grocy.Components.ProductPicker.InProductAddWorkflow = function() -{ - return GetUriParam('flow') == "InplaceNewProductWithName"; -} - -Grocy.Components.ProductPicker.InProductModifyWorkflow = function() -{ - return GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct"; -} - -Grocy.Components.ProductPicker.InAnyFlow = function() -{ - return Grocy.Components.ProductPicker.InProductAddWorkflow() || Grocy.Components.ProductPicker.InProductModifyWorkflow(); -} - -Grocy.Components.ProductPicker.FinishFlow = function() -{ - RemoveUriParam("flow"); - RemoveUriParam("barcode"); - RemoveUriParam("product-name"); -} - -Grocy.Components.ProductPicker.ShowCustomError = function(text) -{ - var element = $("#custom-productpicker-error"); - element.text(text); - element.removeClass("d-none"); -} - -Grocy.Components.ProductPicker.HideCustomError = function() -{ - $("#custom-productpicker-error").addClass("d-none"); -} - -Grocy.Components.ProductPicker.Disable = function() -{ - Grocy.Components.ProductPicker.GetInputElement().attr("disabled", ""); - $("#barcodescanner-start-button").attr("disabled", ""); - $("#barcodescanner-start-button").addClass("disabled"); -} - -Grocy.Components.ProductPicker.Enable = function() -{ - Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled"); - $("#barcodescanner-start-button").removeAttr("disabled"); - $("#barcodescanner-start-button").removeClass("disabled"); -} - -$('.product-combobox').combobox({ - appendId: '_text_input', - bsVersion: '4', - clearIfNoMatch: false -}); - -var this_product_picker = Grocy.Components.ProductPicker.GetPicker(); -var productpicker_doFocus = false; - -var prefillProduct = GetUriParam('product-name'); -var prefillProduct2 = this_product_picker.parent().data('prefill-by-name').toString(); -if (!prefillProduct2.isEmpty()) -{ - prefillProduct = prefillProduct2; -} -if (typeof prefillProduct !== "undefined") -{ - var possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first(); - if (possibleOptionElement.length === 0) - { - possibleOptionElement = $("#product_id option:contains(\"" + prefillProduct + "\")").first(); - } - - if (possibleOptionElement.length > 0) - { - productpicker_doFocus = true; - this_product_picker.val(possibleOptionElement.val()); - } -} - -var prefillProductId = GetUriParam("product"); -var prefillProductId2 = this_product_picker.parent().data('prefill-by-id').toString(); -if (!prefillProductId2.isEmpty()) -{ - prefillProductId = prefillProductId2; -} -if (typeof prefillProductId !== "undefined") -{ - this_product_picker.val(prefillProductId); - productpicker_doFocus = true; -} - -if (productpicker_doFocus) -{ - this_product_picker.data('combobox').refresh(); - this_product_picker.trigger('change'); - - $(this_product_picker.parent().data('next-input-selector').toString()) - .focus(); -} - -if (GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct") -{ - $('#InplaceAddBarcodeToExistingProduct').text(GetUriParam("barcode")); - $('#flow-info-InplaceAddBarcodeToExistingProduct').removeClass('d-none'); - $('#barcode-lookup-disabled-hint').removeClass('d-none'); - $('#barcode-lookup-hint').addClass('d-none'); -} - -Grocy.Components.ProductPicker.PopupOpen = false; -$('#product_id_text_input').on('blur', function(e) -{ - if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible")) - { - return; - } - $('#product_id').attr("barcode", "null"); - - var input = $('#product_id_text_input').val().toString(); - var possibleOptionElement = []; - - // did we enter a grocycode? - if (input.startsWith("grcy")) - { - var gc = input.split(":"); - if (gc[1] == "p") - { - // find product id - possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first(); - $("#product_id").data("grocycode", true); - } - } - else // process barcode as usual - { - possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first(); - } - - if (GetUriParam('flow') === undefined && input.length > 0 && possibleOptionElement.length > 0) - { - $('#product_id').val(possibleOptionElement.val()); - $('#product_id').attr("barcode", input); - $('#product_id').data('combobox').refresh(); - $('#product_id').trigger('change'); - } - else - { - if (Grocy.Components.ProductPicker.PopupOpen === true) - { - return; - } - - var optionElement = $("#product_id option:contains(\"" + input + "\")").first(); - if (input.length > 0 && optionElement.length === 0 && GetUriParam('flow') === undefined && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false") - { - var addProductWorkflowsAdditionalCssClasses = ""; - if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true") - { - addProductWorkflowsAdditionalCssClasses = "d-none"; - } - - var embedded = ""; - if (GetUriParam("embedded") !== undefined) - { - embedded = "embedded"; - } - - var buttons = { - cancel: { - label: __t('Cancel'), - className: 'btn-secondary responsive-button', - callback: function() - { - Grocy.Components.ProductPicker.PopupOpen = false; - Grocy.Components.ProductPicker.SetValue(''); - } - }, - addnewproduct: { - label: 'P ' + __t('Add as new product'), - className: 'btn-success add-new-product-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses, - callback: function() - { - - Grocy.Components.ProductPicker.PopupOpen = false; - window.location.href = U('/product/new?flow=InplaceNewProductWithName&name=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceNewProductWithName&" + embedded) + "&" + embedded); - } - }, - addbarcode: { - label: 'B ' + __t('Add as barcode to existing product'), - className: 'btn-info add-new-barcode-dialog-button responsive-button', - callback: function() - { - Grocy.Components.ProductPicker.PopupOpen = false; - window.location.href = U(Grocy.CurrentUrlRelative + '?flow=InplaceAddBarcodeToExistingProduct&barcode=' + encodeURIComponent(input)); - } - }, - addnewproductwithbarcode: { - label: 'A ' + __t('Add as new product and prefill barcode'), - className: 'btn-warning add-new-product-with-barcode-dialog-button responsive-button ' + addProductWorkflowsAdditionalCssClasses, - callback: function() - { - Grocy.Components.ProductPicker.PopupOpen = false; - window.location.href = U('/product/new?flow=InplaceNewProductWithBarcode&barcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(Grocy.CurrentUrlRelative + "?flow=InplaceAddBarcodeToExistingProduct&barcode=" + input + "&" + embedded) + "&" + embedded); - } - } - }; - - if (!Grocy.FeatureFlags.DISABLE_BROWSER_BARCODE_CAMERA_SCANNING) - { - buttons.retrycamerascanning = { - label: 'C ', - className: 'btn-primary responsive-button retry-camera-scanning-button', - callback: function() - { - Grocy.Components.ProductPicker.PopupOpen = false; - Grocy.Components.ProductPicker.SetValue(''); - $("#barcodescanner-start-button").click(); - } - }; - } - - Grocy.Components.ProductPicker.PopupOpen = true; - bootbox.dialog({ - message: __t('"%s" could not be resolved to a product, how do you want to proceed?', input), - title: __t('Create or assign product'), - onEscape: function() - { - Grocy.Components.ProductPicker.PopupOpen = false; - Grocy.Components.ProductPicker.SetValue(''); - }, - size: 'large', - backdrop: true, - closeButton: false, - buttons: buttons - }).on('keypress', function(e) - { - if (e.key === 'B' || e.key === 'b') - { - $('.add-new-barcode-dialog-button').not(".d-none").click(); - } - if (e.key === 'p' || e.key === 'P') - { - $('.add-new-product-dialog-button').not(".d-none").click(); - } - if (e.key === 'a' || e.key === 'A') - { - $('.add-new-product-with-barcode-dialog-button').not(".d-none").click(); - } - if (e.key === 'c' || e.key === 'C') - { - $('.retry-camera-scanning-button').not(".d-none").click(); - } - }); - } - } -}); - -$(document).on("Grocy.BarcodeScanned", function(e, barcode, target) -{ - if (!(target == "@productpicker" || target == "undefined" || target == undefined)) // Default target - { - return; - } - - // Don't know why the blur event does not fire immediately ... this works... - Grocy.Components.ProductPicker.GetInputElement().focusout(); - Grocy.Components.ProductPicker.GetInputElement().focus(); - Grocy.Components.ProductPicker.GetInputElement().blur(); - - Grocy.Components.ProductPicker.GetInputElement().val(barcode); - - setTimeout(function() - { - Grocy.Components.ProductPicker.GetInputElement().focusout(); - Grocy.Components.ProductPicker.GetInputElement().focus(); - Grocy.Components.ProductPicker.GetInputElement().blur(); - }, 200); -}); - -$(document).on("shown.bs.modal", function(e) -{ - $(".modal-footer").addClass("d-block").addClass("d-sm-flex"); - $(".modal-footer").find("button").addClass("mt-2").addClass("mt-sm-0"); -}) - -// Make that ENTER behaves the same like TAB (trigger blur to start workflows, but only when the dropdown is not opened) -$('#product_id_text_input').keydown(function(event) -{ - if (event.keyCode === 13) // Enter - { - if (Grocy.Components.ProductPicker.GetPicker().hasClass("combobox-menu-visible")) - { - return; - } - - $("#product_id_text_input").trigger("blur"); - } -}); diff --git a/js/viewjs/components/recipepicker.js b/js/viewjs/components/recipepicker.js deleted file mode 100644 index 3e398eca..00000000 --- a/js/viewjs/components/recipepicker.js +++ /dev/null @@ -1,71 +0,0 @@ -Grocy.Components.RecipePicker = {}; - -Grocy.Components.RecipePicker.GetPicker = function() -{ - return $('#recipe_id'); -} - -Grocy.Components.RecipePicker.GetInputElement = function() -{ - return $('#recipe_id_text_input'); -} - -Grocy.Components.RecipePicker.GetValue = function() -{ - return $('#recipe_id').val(); -} - -Grocy.Components.RecipePicker.SetValue = function(value) -{ - Grocy.Components.RecipePicker.GetInputElement().val(value); - Grocy.Components.RecipePicker.GetInputElement().trigger('change'); -} - -Grocy.Components.RecipePicker.SetId = function(value) -{ - Grocy.Components.RecipePicker.GetPicker().val(value); - Grocy.Components.RecipePicker.GetPicker().data('combobox').refresh(); - Grocy.Components.RecipePicker.GetInputElement().trigger('change'); -} - -Grocy.Components.RecipePicker.Clear = function() -{ - Grocy.Components.RecipePicker.SetValue(''); - Grocy.Components.RecipePicker.SetId(null); -} - -$('.recipe-combobox').combobox({ - appendId: '_text_input', - bsVersion: '4', - clearIfNoMatch: true -}); - -var this_recipe_picker = Grocy.Components.RecipePicker.GetPicker(); -var recipe_picker_doFocus = false; - -var prefillByName = this_recipe_picker.parent().data('prefill-by-name').toString(); -if (typeof prefillByName !== "undefined") -{ - var possibleOptionElement = $("#recipe_id option:contains(\"" + prefillByName + "\")").first(); - - if (possibleOptionElement.length > 0) - { - recipe_picker_doFocus = true; - this_recipe_picker.val(possibleOptionElement.val()); - } -} - -var prefillById = this_recipe_picker.parent().data('prefill-by-id').toString(); -if (typeof prefillById !== "undefined") -{ - recipe_picker_doFocus = true; - this_recipe_picker.val(prefillById); -} - -if (recipe_picker_doFocus) -{ - this_recipe_picker.data('combobox').refresh(); - this_recipe_picker.trigger('change'); - - $(this_recipe_picker.parent().data('next-input-selector').toString()).focus(); -} \ No newline at end of file diff --git a/js/viewjs/components/shoppinglocationpicker.js b/js/viewjs/components/shoppinglocationpicker.js deleted file mode 100644 index 7f1de6d7..00000000 --- a/js/viewjs/components/shoppinglocationpicker.js +++ /dev/null @@ -1,72 +0,0 @@ -Grocy.Components.ShoppingLocationPicker = {}; - -Grocy.Components.ShoppingLocationPicker.GetPicker = function() -{ - return $('#shopping_location_id'); -} - -Grocy.Components.ShoppingLocationPicker.GetInputElement = function() -{ - return $('#shopping_location_id_text_input'); -} - -Grocy.Components.ShoppingLocationPicker.GetValue = function() -{ - return $('#shopping_location_id').val(); -} - -Grocy.Components.ShoppingLocationPicker.SetValue = function(value) -{ - Grocy.Components.ShoppingLocationPicker.GetInputElement().val(value); - Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.ShoppingLocationPicker.SetId = function(value) -{ - Grocy.Components.ShoppingLocationPicker.GetPicker().val(value); - Grocy.Components.ShoppingLocationPicker.GetPicker().data('combobox').refresh(); - Grocy.Components.ShoppingLocationPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.ShoppingLocationPicker.Clear = function() -{ - Grocy.Components.ShoppingLocationPicker.SetValue(''); - Grocy.Components.ShoppingLocationPicker.SetId(null); -} - -$('.shopping-location-combobox').combobox({ - appendId: '_text_input', - bsVersion: '4', - clearIfNoMatch: true -}); - -var shoppinglocationpicker_doFocus = false; -var this_shoppinglocation_picker = $('#shopping_location_id'); - -var prefillByName = this_shoppinglocation_picker.parent().data('prefill-by-name').toString(); -if (typeof prefillByName !== "undefined") -{ - var possibleOptionElement = $("#shopping_location_id option:contains(\"" + prefillByName + "\")").first(); - - if (possibleOptionElement.length > 0) - { - this_shoppinglocation_picker.val(possibleOptionElement.val()); - shoppinglocationpicker_doFocus = true; - } -} - -var prefillById = this_shoppinglocation_picker.parent().data('prefill-by-id').toString(); -if (typeof prefillById !== "undefined") -{ - this_shoppinglocation_picker.val(prefillById); - shoppinglocationpicker_doFocus = true; -} - -if (shoppinglocationpicker_doFocus) -{ - this_shoppinglocation_picker.data('combobox').refresh(); - this_shoppinglocation_picker.trigger('change'); - - $(this_shoppinglocation_picker.parent().data('next-input-selector').toString()) - .focus(); -} \ No newline at end of file diff --git a/js/viewjs/components/userfieldsform.js b/js/viewjs/components/userfieldsform.js deleted file mode 100644 index 43409bd6..00000000 --- a/js/viewjs/components/userfieldsform.js +++ /dev/null @@ -1,183 +0,0 @@ -import { RandomString } from '../../helpers/extensions'; - -Grocy.Components.UserfieldsForm = {}; - -Grocy.Components.UserfieldsForm.Save = function(success, error) -{ - if (!$("#userfields-form").length) - { - if (success) - { - success(); - } - - return; - } - - var jsonData = {}; - - $("#userfields-form .userfield-input").not("div").each(function() - { - var input = $(this); - var fieldName = input.attr("data-userfield-name"); - var fieldValue = input.val(); - - if (input.attr("type") == "checkbox") - { - jsonData[fieldName] = "0"; - if (input.is(":checked")) - { - jsonData[fieldName] = "1"; - } - } - else if (input.attr("type") == "file") - { - var oldFile = input.data('old-file') - if (oldFile) - { - Grocy.Api.Delete('files/userfiles/' + oldFile, null, null, - function(xhr) - { - Grocy.FrontendHelpers.ShowGenericError('Could not delete file', xhr); - }); - jsonData[fieldName] = ""; - } - - if (input[0].files.length > 0) - { - // Files service requires an extension - var fileName = RandomString() + '.' + input[0].files[0].name.split('.').reverse()[0]; - - jsonData[fieldName] = btoa(fileName) + '_' + btoa(input[0].files[0].name); - Grocy.Api.UploadFile(input[0].files[0], 'userfiles', fileName, - function(result) - { - }, - function(xhr) - { - // When navigating away immediately from the current page, this is maybe a false positive - so ignore this for now - // Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response) - } - ); - } - } - else if ($(this).hasAttr("multiple")) - { - jsonData[fieldName] = $(this).val().join(","); - } - else - { - jsonData[fieldName] = fieldValue; - } - }); - - Grocy.Api.Put('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, jsonData, - function(result) - { - if (success) - { - success(); - } - }, - function(xhr) - { - if (error) - { - error(); - } - } - ); -} - -Grocy.Components.UserfieldsForm.Load = function() -{ - if (!$("#userfields-form").length) - { - return; - } - - Grocy.Api.Get('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, - function(result) - { - $.each(result, function(key, value) - { - var input = $(".userfield-input[data-userfield-name='" + key + "']"); - - if (input.attr("type") == "checkbox" && value == 1) - { - input.prop("checked", true); - } - else if (input.hasAttr("multiple")) - { - input.val(value.split(",")); - $(".selectpicker").selectpicker("render"); - } - else if (input.attr('type') == "file") - { - if (value != null && !value.isEmpty()) - { - var fileName = atob(value.split('_')[1]); - var fileSrc = value.split('_')[0]; - var formGroup = input.parent().parent().parent(); - - formGroup.find("label.custom-file-label").text(fileName); - formGroup.find(".userfield-file-show").attr('href', U('/files/userfiles/' + value)); - formGroup.find('.userfield-file-show').removeClass('d-none'); - formGroup.find('img.userfield-current-file') - .attr('src', U('/files/userfiles/' + value + '?force_serve_as=picture&best_fit_width=250&best_fit_height=250')); - LoadImagesLazy(); - - formGroup.find('.userfield-file-delete').click( - function() - { - formGroup.find("label.custom-file-label").text(__t("No file selected")); - formGroup.find(".userfield-file-show").addClass('d-none'); - input.attr('data-old-file', fileSrc); - } - ); - - input.on("change", function(e) - { - formGroup.find(".userfield-file-show").addClass('d-none'); - }); - } - } - else if (input.attr("data-userfield-type") == "link") - { - if (!value.isEmpty()) - { - var data = JSON.parse(value); - - var formRow = input.parent().parent(); - formRow.find(".userfield-link-title").val(data.title); - formRow.find(".userfield-link-link").val(data.link); - - input.val(value); - } - } - else - { - input.val(value); - } - }); - }, - function(xhr) - { - console.error(xhr); - } - ); -} - -$(".userfield-link").keyup(function(e) -{ - var formRow = $(this).parent().parent(); - var title = formRow.find(".userfield-link-title").val(); - var link = formRow.find(".userfield-link-link").val(); - - var value = { - "title": title, - "link": link - }; - - formRow.find(".userfield-input").val(JSON.stringify(value)); -}); diff --git a/js/viewjs/components/userpicker.js b/js/viewjs/components/userpicker.js deleted file mode 100644 index abfc3a50..00000000 --- a/js/viewjs/components/userpicker.js +++ /dev/null @@ -1,81 +0,0 @@ -Grocy.Components.UserPicker = {}; - -Grocy.Components.UserPicker.GetPicker = function() -{ - return $('#user_id'); -} - -Grocy.Components.UserPicker.GetInputElement = function() -{ - return $('#user_id_text_input'); -} - -Grocy.Components.UserPicker.GetValue = function() -{ - return $('#user_id').val(); -} - -Grocy.Components.UserPicker.SetValue = function(value) -{ - Grocy.Components.UserPicker.GetInputElement().val(value); - Grocy.Components.UserPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.UserPicker.SetId = function(value) -{ - Grocy.Components.UserPicker.GetPicker().val(value); - Grocy.Components.UserPicker.GetPicker().data('combobox').refresh(); - Grocy.Components.UserPicker.GetInputElement().trigger('change'); -} - -Grocy.Components.UserPicker.Clear = function() -{ - Grocy.Components.UserPicker.SetValue(''); - Grocy.Components.UserPicker.SetId(null); -} - -$('.user-combobox').combobox({ - appendId: '_text_input', - bsVersion: '4' -}); - -var this_user_picker = Grocy.Components.UserPicker.GetPicker(); -var user_picker_doFocus = false; -var possibleOptionElement = null; - -var prefillUser = this_user_picker.parent().data('prefill-by-username').toString(); -if (typeof prefillUser !== "undefined") -{ - possibleOptionElement = $("#user_id option[data-additional-searchdata*=\"" + prefillUser + "\"]").first(); - if (possibleOptionElement.length === 0) - { - possibleOptionElement = $("#user_id option:contains(\"" + prefillUser + "\")").first(); - } - - if (possibleOptionElement.length > 0) - { - user_picker_doFocus = true; - this_user_picker.val(possibleOptionElement.val()); - - } -} - -var prefillUserId = this_user_picker.parent().data('prefill-by-user-id').toString(); -if (typeof prefillUserId !== "undefined") -{ - possibleOptionElement = $("#user_id option[value='" + prefillUserId + "']").first(); - if (possibleOptionElement.length > 0) - { - user_picker_doFocus = true; - this_user_picker.val(possibleOptionElement.val()); - } -} - -if (user_picker_doFocus) -{ - this_user_picker.data('combobox').refresh(); - this_user_picker.trigger('change'); - - $(this_user_picker.parent().data('next-input-selector').toString()) - .focus(); -} \ No newline at end of file diff --git a/js/viewjs/consume.js b/js/viewjs/consume.js index 8069c18c..28af6a55 100644 --- a/js/viewjs/consume.js +++ b/js/viewjs/consume.js @@ -1,6 +1,11 @@ import { BoolVal } from '../helpers/extensions'; import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("productamountpicker"); +Grocy.Use("productcard"); +Grocy.Use("productpicker"); +Grocy.Use("recipepicker"); + $('#save-consume-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/equipmentform.js b/js/viewjs/equipmentform.js index f442d666..31dfbd81 100644 --- a/js/viewjs/equipmentform.js +++ b/js/viewjs/equipmentform.js @@ -1,6 +1,8 @@ import { RandomString } from '../helpers/extensions'; import { ResizeResponsiveEmbeds } from '../helpers/embeds'; +Grocy.Use("userfieldsform"); + $('#save-equipment-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/inventory.js b/js/viewjs/inventory.js index 7eee6e47..09757fd7 100644 --- a/js/viewjs/inventory.js +++ b/js/viewjs/inventory.js @@ -1,5 +1,17 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("datetimepicker"); +if (Grocy.UserSettings.show_purchased_date_on_purchase) +{ + Grocy.Use("datetimepicker2"); +} +Grocy.Use("locationpicker"); +Grocy.Use("numberpicker"); +Grocy.Use("productpicker"); +Grocy.Use("productamountpicker"); +Grocy.Use("productcard"); +Grocy.Use("shoppinglocationpicker"); + $('#save-inventory-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/locationform.js b/js/viewjs/locationform.js index 55d819ed..2b626bc3 100644 --- a/js/viewjs/locationform.js +++ b/js/viewjs/locationform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("userfieldsform"); + $('#save-location-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/mealplan.js b/js/viewjs/mealplan.js index aee7b85d..c5790bc2 100644 --- a/js/viewjs/mealplan.js +++ b/js/viewjs/mealplan.js @@ -10,6 +10,10 @@ import '@fullcalendar/core/main.css'; import '@fullcalendar/daygrid/main.css'; import '@fullcalendar/bootstrap/main.css'; +Grocy.Use("numberpicker"); +Grocy.Use("productamountpicker"); +Grocy.Use("recipepicker"); + var setLocale = false; if (__t('fullcalendar_locale').replace(" ", "") !== "" && __t('fullcalendar_locale') != 'x') { diff --git a/js/viewjs/productbarcodeform.js b/js/viewjs/productbarcodeform.js index 1fbcb6f6..b9bd75c0 100644 --- a/js/viewjs/productbarcodeform.js +++ b/js/viewjs/productbarcodeform.js @@ -1,5 +1,9 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use('barcodescanner'); +Grocy.Use("productamountpicker"); +Grocy.Use("userfieldsform"); + $('#save-barcode-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/productform.js b/js/viewjs/productform.js index 480416dc..bc47bdb7 100644 --- a/js/viewjs/productform.js +++ b/js/viewjs/productform.js @@ -1,5 +1,9 @@ import { BoolVal } from '../helpers/extensions'; +Grocy.Use("numberpicker"); +Grocy.Use("shoppinglocationpicker"); +Grocy.Use("userfieldsform"); + function saveProductPicture(result, location, jsonData) { var productId = Grocy.EditObjectId || result.created_object_id; diff --git a/js/viewjs/productgroupform.js b/js/viewjs/productgroupform.js index 1c8acad5..8496e6c2 100644 --- a/js/viewjs/productgroupform.js +++ b/js/viewjs/productgroupform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("userfieldsform"); + $('#save-product-group-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/purchase.js b/js/viewjs/purchase.js index e5002052..f7c3427c 100644 --- a/js/viewjs/purchase.js +++ b/js/viewjs/purchase.js @@ -1,6 +1,17 @@ import { BoolVal } from '../helpers/extensions'; import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("datetimepicker"); +if (Grocy.UserSettings.show_purchased_date_on_purchase) +{ + Grocy.Use("datetimepicker2"); +} +Grocy.Use("locationpicker"); +Grocy.Use("numberpicker"); +Grocy.Use("productamountpicker"); +Grocy.Use("productcard"); +Grocy.Use("shoppinglocationpicker"); + var CurrentProductDetails; $('#save-purchase-button').on('click', function(e) diff --git a/js/viewjs/quantityunitconversionform.js b/js/viewjs/quantityunitconversionform.js index f64d9665..4b3d26bf 100644 --- a/js/viewjs/quantityunitconversionform.js +++ b/js/viewjs/quantityunitconversionform.js @@ -1,5 +1,8 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("numberpicker"); +Grocy.Use("userfieldsform"); + $('#save-quconversion-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/quantityunitform.js b/js/viewjs/quantityunitform.js index 583020eb..f752ceda 100644 --- a/js/viewjs/quantityunitform.js +++ b/js/viewjs/quantityunitform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("userfieldsform"); + $('.save-quantityunit-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/quantityunitpluraltesting.js b/js/viewjs/quantityunitpluraltesting.js index 1e4169af..742aa960 100644 --- a/js/viewjs/quantityunitpluraltesting.js +++ b/js/viewjs/quantityunitpluraltesting.js @@ -1,4 +1,6 @@ -$("#qu_id").change(function(event) +Grocy.Use("numberpicker"); + +$("#qu_id").change(function(event) { RefreshQuPluralTestingResult(); }); diff --git a/js/viewjs/recipeform.js b/js/viewjs/recipeform.js index b38674f9..cffa1f54 100644 --- a/js/viewjs/recipeform.js +++ b/js/viewjs/recipeform.js @@ -1,5 +1,9 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("numberpicker"); +Grocy.Use("recipepicker"); +Grocy.Use("userfieldsform"); + function saveRecipePicture(result, location, jsonData) { var recipeId = Grocy.EditObjectId || result.created_object_id; diff --git a/js/viewjs/recipeposform.js b/js/viewjs/recipeposform.js index cb94b2e1..67edb759 100644 --- a/js/viewjs/recipeposform.js +++ b/js/viewjs/recipeposform.js @@ -1,4 +1,9 @@ import { WindowMessageBag } from '../helpers/messagebag'; + +Grocy.Use("numberpicker"); +Grocy.Use("productamountpicker"); +Grocy.Use("productcard"); + Grocy.RecipePosFormInitialLoadDone = false; $('#save-recipe-pos-button').on('click', function(e) diff --git a/js/viewjs/recipes.js b/js/viewjs/recipes.js index 5d7a8a22..0abd9c66 100644 --- a/js/viewjs/recipes.js +++ b/js/viewjs/recipes.js @@ -1,4 +1,6 @@ -var recipesTables = $('#recipes-table').DataTable({ +Grocy.Use("numberpicker"); + +var recipesTables = $('#recipes-table').DataTable({ 'order': [[1, 'asc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, diff --git a/js/viewjs/shoppinglist.js b/js/viewjs/shoppinglist.js index b24ee4ac..43251b5b 100644 --- a/js/viewjs/shoppinglist.js +++ b/js/viewjs/shoppinglist.js @@ -3,6 +3,9 @@ import bwipjs from '../../node_modules/bwip-js/dist/bwip-js.mjs'; import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("calendarcard"); +Grocy.Use("productcard"); + var shoppingListTable = $('#shoppinglist-table').DataTable({ 'order': [[1, 'asc']], "orderFixed": [[3, 'asc']], diff --git a/js/viewjs/shoppinglistform.js b/js/viewjs/shoppinglistform.js index eeafdb9e..36b00a33 100644 --- a/js/viewjs/shoppinglistform.js +++ b/js/viewjs/shoppinglistform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("userfieldsform"); + $('#save-shopping-list-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/shoppinglistitemform.js b/js/viewjs/shoppinglistitemform.js index 74e48892..113e3d23 100644 --- a/js/viewjs/shoppinglistitemform.js +++ b/js/viewjs/shoppinglistitemform.js @@ -1,5 +1,8 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("productamountpicker"); +Grocy.Use("userfieldsform"); + Grocy.ShoppingListItemFormInitialLoadDone = false; $('#save-shoppinglist-button').on('click', function(e) diff --git a/js/viewjs/shoppinglocationform.js b/js/viewjs/shoppinglocationform.js index 0a0d9364..fc72bc6e 100644 --- a/js/viewjs/shoppinglocationform.js +++ b/js/viewjs/shoppinglocationform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("userfieldsform"); + $('#save-shopping-location-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/stockentries.js b/js/viewjs/stockentries.js index bcc3973a..41075476 100644 --- a/js/viewjs/stockentries.js +++ b/js/viewjs/stockentries.js @@ -1,4 +1,7 @@ -var stockEntriesTable = $('#stockentries-table').DataTable({ +Grocy.Use("productcard"); +Grocy.Use("productpicker"); + +var stockEntriesTable = $('#stockentries-table').DataTable({ 'order': [[2, 'asc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, diff --git a/js/viewjs/stockentryform.js b/js/viewjs/stockentryform.js index cfd1d79c..160cb7cf 100644 --- a/js/viewjs/stockentryform.js +++ b/js/viewjs/stockentryform.js @@ -1,5 +1,11 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("datetimepicker"); +Grocy.Use("datetimepicker2"); +Grocy.Use("locationpicker"); +Grocy.Use("numberpicker"); +Grocy.Use("shoppinglocationpicker"); + $('#save-stockentry-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/stockoverview.js b/js/viewjs/stockoverview.js index 5fa17c02..857afef8 100755 --- a/js/viewjs/stockoverview.js +++ b/js/viewjs/stockoverview.js @@ -1,4 +1,6 @@ -var stockOverviewTable = $('#stock-overview-table').DataTable({ +Grocy.Use("productcard"); + +var stockOverviewTable = $('#stock-overview-table').DataTable({ 'order': [[5, 'asc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, diff --git a/js/viewjs/stocksettings.js b/js/viewjs/stocksettings.js index 5b0a321f..bfc9e18a 100644 --- a/js/viewjs/stocksettings.js +++ b/js/viewjs/stocksettings.js @@ -1,5 +1,7 @@ import { BoolVal } from '../helpers/extensions'; +Grocy.Use("numberpicker"); + $("#product_presets_location_id").val(Grocy.UserSettings.product_presets_location_id); $("#product_presets_product_group_id").val(Grocy.UserSettings.product_presets_product_group_id); $("#product_presets_qu_id").val(Grocy.UserSettings.product_presets_qu_id); diff --git a/js/viewjs/taskform.js b/js/viewjs/taskform.js index 734969a4..baccc9c9 100644 --- a/js/viewjs/taskform.js +++ b/js/viewjs/taskform.js @@ -1,5 +1,8 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("datetimepicker"); +Grocy.Use("userfieldsform"); + $('#save-task-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/tasks.js b/js/viewjs/tasks.js index 85c27dcf..b37d78a5 100644 --- a/js/viewjs/tasks.js +++ b/js/viewjs/tasks.js @@ -1,4 +1,6 @@ -var tasksTable = $('#tasks-table').DataTable({ +Grocy.Use("userpicker"); + +var tasksTable = $('#tasks-table').DataTable({ 'order': [[2, 'asc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, diff --git a/js/viewjs/taskssettings.js b/js/viewjs/taskssettings.js index c9cf81d3..2498ce6a 100644 --- a/js/viewjs/taskssettings.js +++ b/js/viewjs/taskssettings.js @@ -1,3 +1,5 @@ -$("#tasks_due_soon_days").val(Grocy.UserSettings.tasks_due_soon_days); +Grocy.Use("numberpicker"); + +$("#tasks_due_soon_days").val(Grocy.UserSettings.tasks_due_soon_days); RefreshLocaleNumberInput(); diff --git a/js/viewjs/transfer.js b/js/viewjs/transfer.js index 94743352..dfd265c1 100644 --- a/js/viewjs/transfer.js +++ b/js/viewjs/transfer.js @@ -1,5 +1,9 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("productpicker"); +Grocy.Use("productamountpicker"); +Grocy.Use("productcard"); + $('#save-transfer-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/userfieldform.js b/js/viewjs/userfieldform.js index f834bb41..18776554 100644 --- a/js/viewjs/userfieldform.js +++ b/js/viewjs/userfieldform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("numberpicker"); + $('#save-userfield-button').on('click', function(e) { e.preventDefault(); diff --git a/js/viewjs/userform.js b/js/viewjs/userform.js index f8da5e7e..9b244b28 100644 --- a/js/viewjs/userform.js +++ b/js/viewjs/userform.js @@ -1,4 +1,6 @@ -function SaveUserPicture(result, jsonData) +Grocy.Use("userfieldsform"); + +function SaveUserPicture(result, jsonData) { Grocy.Components.UserfieldsForm.Save(() => { diff --git a/js/viewjs/userobjectform.js b/js/viewjs/userobjectform.js index 053c8b3f..0cb1ad09 100644 --- a/js/viewjs/userobjectform.js +++ b/js/viewjs/userobjectform.js @@ -1,5 +1,7 @@ import { WindowMessageBag } from '../helpers/messagebag'; +Grocy.Use("userfieldsform"); + $('#save-userobject-button').on('click', function(e) { e.preventDefault(); diff --git a/views/components/barcodescanner.blade.php b/views/components/barcodescanner.blade.php index f34bf0fe..0d34b69e 100644 --- a/views/components/barcodescanner.blade.php +++ b/views/components/barcodescanner.blade.php @@ -1,13 +1,5 @@ @if (!GROCY_DISABLE_BROWSER_BARCODE_CAMERA_SCANNING) -@push('componentScripts') - -@endpush - -@push('pageScripts') - -@endpush - @push('pageStyles')