diff --git a/js/components/BasePicker.js b/js/components/BasePicker.js
new file mode 100644
index 00000000..63661b17
--- /dev/null
+++ b/js/components/BasePicker.js
@@ -0,0 +1,99 @@
+class BasePicker
+{
+
+ constructor(Grocy, basename, scopeSelector = null)
+ {
+ this.Grocy = Grocy;
+
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+
+ this.picker = null;
+ this.input_element;
+
+ this.basename = basename;
+ }
+
+ prefill()
+ {
+ if (this.picker == null)
+ {
+ console.error("Cannot prefill " + this.basename + ", picker not set.");
+ return;
+ }
+
+ var doFokus = false;
+ var prefillByName = this.picker.parent().data('prefill-by-name').toString();
+ if (typeof prefillByName !== "undefined")
+ {
+ var possibleOptionElement = $(this.basename + " option:contains(\"" + prefillByName + "\")").first();
+
+ if (possibleOptionElement.length > 0)
+ {
+ doFokus = true;
+ this.picker.val(possibleOptionElement.val());
+ }
+ }
+
+ var prefillById = this.picker.parent().data('prefill-by-id').toString();
+ if (typeof prefillById !== "undefined")
+ {
+ doFokus = true;
+ this.picker.val(prefillById);
+ }
+
+ if (doFokus)
+ {
+ this.picker.data('combobox').refresh();
+ this.picker.trigger('change');
+
+ this.$(this.picker.parent().data('next-input-selector').toString()).focus();
+ }
+ }
+
+ initCombobox(selector)
+ {
+ this.$(selector).combobox({
+ appendId: '_text_input',
+ bsVersion: '4',
+ clearIfNoMatch: false
+ });
+ }
+
+ GetPicker()
+ {
+ return this.picker;
+ }
+
+ GetInputElement()
+ {
+ return this.input_element;
+ }
+
+ GetValue()
+ {
+ return this.picker.val();
+ }
+
+ SetValue(value)
+ {
+ this.input_element.val(value);
+ this.input_element.trigger('change');
+ }
+
+ SetId(value)
+ {
+ this.picker.val(value);
+ this.picker.data('combobox').refresh();
+ this.input_element.trigger('change');
+ }
+
+ Clear()
+ {
+ this.SetValue('');
+ this.SetId(null);
+ }
+}
+
+export default BasePicker;
\ No newline at end of file
diff --git a/js/components/barcodescanner.js b/js/components/barcodescanner.js
index 0e90875d..9adc91ec 100644
--- a/js/components/barcodescanner.js
+++ b/js/components/barcodescanner.js
@@ -1,11 +1,159 @@
import Quagga from '@ericblade/quagga2/dist/quagga';
-function barcodescanner(Grocy)
+class barcodescanner
{
- Grocy.Components.BarcodeScanner = {};
+ constructor(Grocy, scopeSelector = null)
+ {
+ this.Grocy = Grocy;
- Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = false;
- Grocy.Components.BarcodeScanner.CheckCapabilities = async function()
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+
+ // init component
+ this.LiveVideoSizeAdjusted = false;
+
+ var self = this;
+
+ Quagga.onDetected(function(result)
+ {
+ $.each(result.codeResult.decodedCodes, function(id, error)
+ {
+ if (error.error != undefined)
+ {
+ self.DecodedCodesCount++;
+ self.DecodedCodesErrorCount += parseFloat(error.error);
+ }
+ });
+
+ if (self.DecodedCodesErrorCount / self.DecodedCodesCount < 0.15)
+ {
+ self.StopScanning();
+ $(document).trigger("Grocy.BarcodeScanned", [result.codeResult.code, self.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 });
+ }
+ }
+ });
+
+ this.scope.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;
+ }
+
+ self.CurrentTarget = inputElement.attr("data-target");
+
+ var dialog = bootbox.dialog({
+ message: '
',
+ title: Grocy.translate('Scan a barcode'),
+ onEscape: function()
+ {
+ self.StopScanning();
+ },
+ size: 'big',
+ backdrop: true,
+ closeButton: true,
+ buttons: {
+ torch: {
+ label: '',
+ className: 'btn-warning responsive-button torch',
+ callback: function()
+ {
+ self.TorchOn(Quagga.CameraAccess.getActiveTrack());
+ return false;
+ }
+ },
+ cancel: {
+ label: Grocy.translate('Cancel'),
+ className: 'btn-secondary responsive-button',
+ callback: function()
+ {
+ self.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();
+ self.StartScanning();
+ };
+ }
+
+ self.StartScanning();
+ });
+
+ setTimeout(function()
+ {
+ this.$scope(".barcodescanner-input:visible").each(function()
+ {
+ if ($(this).hasAttr("disabled"))
+ {
+ $(this).after('');
+ }
+ else
+ {
+ $(this).after('');
+ }
+ });
+ }, 50);
+ }
+
+ async CheckCapabilities()
{
var track = Quagga.CameraAccess.getActiveTrack();
var capabilities = {};
@@ -16,61 +164,64 @@ function barcodescanner(Grocy)
// 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)
+ var cameraSelect = this.$('.cameraSelect-wrapper');
+ if (cameraSelect.length)
{
- cameraSelect.style.display = cameras.length > 1 ? 'inline-block' : 'none';
+ cameraSelect[0].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)
+ var node = this.$('.torch');
+ if (node.length)
{
- node.style.display = canTorch && !Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA ? 'inline-block' : 'none';
+ node[0].style.display = canTorch && !this.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)
+ if (canTorch && this.Grocy.FeatureFlags.GROCY_FEATURE_FLAG_AUTO_TORCH_ON_WITH_CAMERA)
{
- Grocy.Components.BarcodeScanner.TorchOn(track);
+ this.TorchOn(track);
}
// Reduce the height of the video, if it's higher than then the viewport
- if (!Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted)
+ if (!this.LiveVideoSizeAdjusted)
{
- var bc = document.getElementById('barcodescanner-container');
- if (bc)
+ var bc = this.$('barcodescanner-container');
+ if (bc.length)
{
+ bc = bc[0]
var bcAspectRatio = bc.offsetWidth / bc.offsetHeight;
var settings = track.getSettings();
if (bcAspectRatio > settings.aspectRatio)
{
- var v = document.querySelector('#barcodescanner-livestream video')
- if (v)
+ var v = this.$('#barcodescanner-livestream video')
+ if (v.length)
{
- var c = document.querySelector('#barcodescanner-livestream canvas')
+ v = v[0];
+ var c = this.$('#barcodescanner-livestream canvas')[0]
var newWidth = v.clientWidth / bcAspectRatio * settings.aspectRatio + 'px';
v.style.width = newWidth;
c.style.width = newWidth;
}
}
- Grocy.Components.BarcodeScanner.LiveVideoSizeAdjusted = true;
+ this.LiveVideoSizeAdjusted = true;
}
}
}
- Grocy.Components.BarcodeScanner.StartScanning = function()
+ StartScanning()
{
- Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
- Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
+ this.DecodedCodesCount = 0;
+ this.DecodedCodesErrorCount = 0;
+ var self = this;
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
- target: document.querySelector("#barcodescanner-livestream"),
+ target: this.$("#barcodescanner-livestream")[0],
constraints: {
facingMode: "environment",
...(window.localStorage.getItem('cameraId') && { deviceId: window.localStorage.getItem('cameraId') }) // If preferred cameraId is set, request to use that specific camera
@@ -124,8 +275,8 @@ function barcodescanner(Grocy)
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"));
+ self.Grocy.FrontendHelpers.ShowGenericError("Error while initializing the barcode scanning library", error.message);
+ toastr.info(self.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()
{
@@ -134,23 +285,23 @@ function barcodescanner(Grocy)
return;
}
- Grocy.Components.BarcodeScanner.CheckCapabilities();
+ this.CheckCapabilities();
Quagga.start();
});
}
- Grocy.Components.BarcodeScanner.StopScanning = function()
+ StopScanning()
{
Quagga.stop();
- Grocy.Components.BarcodeScanner.DecodedCodesCount = 0;
- Grocy.Components.BarcodeScanner.DecodedCodesErrorCount = 0;
+ this.DecodedCodesCount = 0;
+ this.DecodedCodesErrorCount = 0;
bootbox.hideAll();
}
- Grocy.Components.BarcodeScanner.TorchOn = function(track)
+ TorchOn(track)
{
if (track)
{
@@ -163,144 +314,6 @@ function barcodescanner(Grocy)
});
}
}
-
- 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
index f2159d19..0c264b62 100644
--- a/js/components/batterycard.js
+++ b/js/components/batterycard.js
@@ -1,28 +1,37 @@
import { EmptyElementWhenMatches } from '../helpers/extensions'
import { RefreshContextualTimeago } from '../configs/timeago'
-function batterycard(Grocy)
+class batterycard
{
-
- Grocy.Components.BatteryCard = {};
-
- Grocy.Components.BatteryCard.Refresh = function(batteryId)
+ constructor(Grocy, scopeSelector = null)
{
- Grocy.Api.Get('batteries/' + batteryId,
+ this.Grocy = Grocy;
+
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+ }
+
+ Refresh(batteryId)
+ {
+ var self = this;
+ this.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'));
+ self.$('#batterycard-battery-name').text(batteryDetails.battery.name);
+ self.$('#batterycard-battery-used_in').text(batteryDetails.battery.used_in);
+ self.$('#batterycard-battery-last-charged').text((batteryDetails.last_charged || self.Grocy.translate('never')));
+ self.$('#batterycard-battery-last-charged-timeago').attr("datetime", batteryDetails.last_charged || '');
+ self.$('#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");
+ self.$('#batterycard-battery-edit-button').attr("href", self.Grocy.FormatUrl("/battery/" + batteryDetails.battery.id.toString()));
+ self.$('#batterycard-battery-journal-button').attr("href", self.Grocy.FormatUrl("/batteriesjournal?embedded&battery=" + batteryDetails.battery.id.toString()));
+ self.$('#batterycard-battery-edit-button').removeClass("disabled");
+ self.$('#batterycard-battery-journal-button').removeClass("disabled");
- EmptyElementWhenMatches('#batterycard-battery-last-charged-timeago', Grocy.translate('timeago_nan'));
+ EmptyElementWhenMatches(self.$('#batterycard-battery-last-charged-timeago'), self.Grocy.translate('timeago_nan'));
+
+ // ToDo: Unscoped
RefreshContextualTimeago(".batterycard");
},
function(xhr)
diff --git a/js/components/calendarcard.js b/js/components/calendarcard.js
index 11a16131..c7444e81 100644
--- a/js/components/calendarcard.js
+++ b/js/components/calendarcard.js
@@ -1,45 +1,54 @@
-function calendarcard(Grocy)
+class calendarcard
{
- $('#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) { }
- }
- });
+ constructor(Grocy, scopeSelector = null)
+ {
+ this.Grocy = Grocy;
- $('#calendar').datetimepicker('show');
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+
+ this.$('#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) { }
+ }
+ });
+
+ this.$('#calendar').datetimepicker('show');
+ }
}
export { calendarcard }
\ No newline at end of file
diff --git a/js/components/chorecard.js b/js/components/chorecard.js
index 05595a12..abe929a0 100644
--- a/js/components/chorecard.js
+++ b/js/components/chorecard.js
@@ -1,36 +1,44 @@
import { EmptyElementWhenMatches } from '../helpers/extensions'
import { RefreshContextualTimeago } from '../configs/timeago'
-function chorecard(Grocy)
+class chorecard
{
- Grocy.Components.ChoreCard = {};
-
- Grocy.Components.ChoreCard.Refresh = function(choreId)
+ constructor(Grocy, scopeSelector = null)
{
- Grocy.Api.Get('chores/' + choreId,
+ this.Grocy = Grocy;
+
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+ }
+
+ Refresh(choreId)
+ {
+ var self = this;
+ this.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')));
+ self.$('#chorecard-chore-name').text(choreDetails.chore.name);
+ self.$('#chorecard-chore-last-tracked').text((choreDetails.last_tracked || self.Grocy.translate('never')));
+ self.$('#chorecard-chore-last-tracked-timeago').attr("datetime", choreDetails.last_tracked || '');
+ self.$('#chorecard-chore-tracked-count').text((choreDetails.tracked_count || '0'));
+ self.$('#chorecard-chore-last-done-by').text((choreDetails.last_done_by.display_name || self.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");
+ self.$('#chorecard-chore-edit-button').attr("href", self.Grocy.FormatUrl("/chore/" + choreDetails.chore.id.toString()));
+ self.$('#chorecard-chore-journal-button').attr("href", self.Grocy.FormatUrl("/choresjournal?embedded&chore=" + choreDetails.chore.id.toString()));
+ self.$('#chorecard-chore-edit-button').removeClass("disabled");
+ self.$('#chorecard-chore-journal-button').removeClass("disabled");
if (choreDetails.chore.track_date_only == 1)
{
- $("#chorecard-chore-last-tracked-timeago").addClass("timeago-date-only");
+ self.$("#chorecard-chore-last-tracked-timeago").addClass("timeago-date-only");
}
else
{
- $("#chorecard-chore-last-tracked-timeago").removeClass("timeago-date-only");
+ self.$("#chorecard-chore-last-tracked-timeago").removeClass("timeago-date-only");
}
- EmptyElementWhenMatches('#chorecard-chore-last-tracked-timeago', Grocy.translate('timeago_nan'));
+ EmptyElementWhenMatches(self.$('#chorecard-chore-last-tracked-timeago'), self.Grocy.translate('timeago_nan'));
RefreshContextualTimeago(".chorecard");
},
function(xhr)
diff --git a/js/components/datetimepicker.js b/js/components/datetimepicker.js
index 06b3caec..73310226 100644
--- a/js/components/datetimepicker.js
+++ b/js/components/datetimepicker.js
@@ -1,100 +1,119 @@
import { EmptyElementWhenMatches } from '../helpers/extensions'
import { RefreshContextualTimeago } from '../configs/timeago'
-function datetimepicker(Grocy)
+class datetimepicker
{
- Grocy.Components.DateTimePicker = {};
-
- Grocy.Components.DateTimePicker.GetInputElement = function()
+ constructor(Grocy, scopeSelector = null, basename = "datetimepicker")
{
- return $('.datetimepicker').find('input').not(".form-check-input");
+ this.Grocy = Grocy;
+
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+
+ this.basename = basename;
+
+ var inputElement = this.GetInputElement();
+ var self = this;
+
+
+ this.startDate = null;
+ if (inputElement.data('init-with-now') === true)
+ {
+ this.startDate = moment().format(inputElement.data('format'));
+ }
+ if (inputElement.data('init-value').length > 0)
+ {
+ this.startDate = moment(inputElement.data('init-value')).format(inputElement.data('format'));
+ }
+
+ this.limitDate = moment('2999-12-31 23:59:59');
+ if (inputElement.data('limit-end-to-now') === true)
+ {
+ this.limitDate = moment();
+ }
+
+ // set some event handlers
+ inputElement.on('keyup', (e) => self.keyupHandler(this, e));
+ inputElement.on('input', (e) => self.inputHandler(this, e));
+
+ this.$('.' + this.basename).on('update.datetimepicker', () => self.stateTrigger());
+ this.$('.' + this.basename).on('hide.datetimepicker', () => self.stateTrigger());
+
+ this.$("#" + this.basename + "-shortcut").on("click", () => self.handleShortcut(this));
+
+ this.Init()
}
- Grocy.Components.DateTimePicker.GetValue = function()
+ GetInputElement()
{
- return Grocy.Components.DateTimePicker.GetInputElement().val();
+ return this.$('.' + this.basename).find('input').not(".form-check-input");
}
- Grocy.Components.DateTimePicker.SetValue = function(value)
+ GetValue()
+ {
+ return this.GetInputElement().val();
+ }
+
+ SetValue(value, triggerEvents = true)
{
// "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"))
+ var shortcutValue = this.$("#" + this.basename + "-shortcut").data(this.basename + "-shortcut-value");
+ if (value != shortcutValue && this.$("#" + this.basename + "-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();
+ this.$("#" + this.basename + "-shortcut").click();
}
- $('#datetimepicker-timeago').text('');
+ var inputElement = this.GetInputElement();
+ inputElement.val(value);
+ if (triggerEvents)
+ {
+ inputElement.trigger('change');
+
+ inputElement.keyup();
+ }
}
- Grocy.Components.DateTimePicker.ChangeFormat = function(format)
+ Clear()
{
- $(".datetimepicker").datetimepicker("destroy");
- Grocy.Components.DateTimePicker.GetInputElement().data("format", format);
- Grocy.Components.DateTimePicker.Init();
+ this.$("." + this.basename).datetimepicker("destroy");
+ this.Init();
+ this.SetValue("", false);
+
+ this.$('#' + this.basename + '-timeago').text('');
+ }
+
+ ChangeFormat(format)
+ {
+ this.$("." + this.basename).datetimepicker("destroy");
+ var elem = this.GetInputElement();
+ elem.data("format", format);
+ this.Init();
if (format == "YYYY-MM-DD")
{
- Grocy.Components.DateTimePicker.GetInputElement().addClass("date-only-datetimepicker");
+ elem.addClass("date-only-datetimepicker");
}
else
{
- Grocy.Components.DateTimePicker.GetInputElement().removeClass("date-only-datetimepicker");
+ elem.removeClass("date-only-datetimepicker");
}
}
- var startDate = null;
- var inputElement = Grocy.Components.DateTimePicker.GetInputElement();
- if (inputElement.data('init-with-now') === true)
+ Init()
{
- 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(
+ this.$('.' + this.basename).datetimepicker(
{
- format: Grocy.Components.DateTimePicker.GetInputElement().data('format'),
+ format: this.GetInputElement().data('format'),
buttons: {
showToday: true,
showClose: true
},
- calendarWeeks: Grocy.CalendarShowWeekNumbers,
- maxDate: limitDate,
+ calendarWeeks: this.Grocy.CalendarShowWeekNumbers,
+ maxDate: this.limitDate,
locale: moment.locale(),
- defaultDate: startDate,
+ defaultDate: this.startDate,
useCurrent: false,
icons: {
time: 'far fa-clock',
@@ -125,28 +144,30 @@ function datetimepicker(Grocy)
}
});
}
- Grocy.Components.DateTimePicker.Init();
- Grocy.Components.DateTimePicker.GetInputElement().on('keyup', function(e)
+
+ keyupHandler(_this, e)
{
- $('.datetimepicker').datetimepicker('hide');
+ this.$('.' + this.basename).datetimepicker('hide');
- var value = Grocy.Components.DateTimePicker.GetValue();
+ var inputElement = this.GetInputElement();
+
+ var value = this.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'));
+ var format = inputElement.data('format');
+ var nextInputElement = this.$(inputElement.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));
+ this.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));
+ this.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))
@@ -156,18 +177,18 @@ function datetimepicker(Grocy)
{
date.add(1, "year");
}
- Grocy.Components.DateTimePicker.SetValue(date.format(format));
+ this.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'));
+ this.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));
+ this.SetValue(endOfMonth.format(format));
nextInputElement.focus();
}
else
@@ -221,23 +242,23 @@ function datetimepicker(Grocy)
}
//Custom validation
- value = Grocy.Components.DateTimePicker.GetValue();
+ value = this.GetValue();
dateObj = moment(value, format, true);
- var element = Grocy.Components.DateTimePicker.GetInputElement()[0];
+ var element = inputElement[0];
if (!dateObj.isValid())
{
- if ($(element).hasAttr("required"))
+ if (inputElement.hasAttr("required"))
{
element.setCustomValidity("error");
}
}
else
{
- if (Grocy.Components.DateTimePicker.GetInputElement().data('limit-end-to-now') === true && dateObj.isAfter(moment()))
+ if (inputElement.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()))
+ else if (inputElement.data('limit-start-to-now') === true && dateObj.isBefore(moment()))
{
element.setCustomValidity("error");
}
@@ -246,72 +267,68 @@ function datetimepicker(Grocy)
element.setCustomValidity("");
}
- var earlierThanLimit = Grocy.Components.DateTimePicker.GetInputElement().data("earlier-than-limit");
+ var earlierThanLimit = inputElement.data("earlier-than-limit");
if (!earlierThanLimit.isEmpty())
{
if (moment(value).isBefore(moment(earlierThanLimit)))
{
- $("#datetimepicker-earlier-than-info").removeClass("d-none");
+ this.$("#" + this.basename + "-earlier-than-info").removeClass("d-none");
}
else
{
- $("#datetimepicker-earlier-than-info").addClass("d-none");
+ this.$("#" + this.basename + "-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"))
+ var shortcutValue = this.$("#" + this.basename + "-shortcut").data(this.basename + "-shortcut-value");
+ if (value == shortcutValue && !this.$("#" + this.basename + "-shortcut").is(":checked"))
{
- $("#datetimepicker-shortcut").click();
+ this.$("#" + this.basename + "-shortcut").click();
}
- });
+ }
- Grocy.Components.DateTimePicker.GetInputElement().on('input', function(e)
+ inputHandler(_this, e)
{
- $('#datetimepicker-timeago').attr("datetime", Grocy.Components.DateTimePicker.GetValue());
- EmptyElementWhenMatches('#datetimepicker-timeago', Grocy.translate('timeago_nan'));
- RefreshContextualTimeago("#datetimepicker-wrapper");
- });
+ this.$('#' + this.basename + '-timeago').attr("datetime", this.GetValue());
+ EmptyElementWhenMatches(this.$('#' + this.basename + '-timeago'), this.Grocy.translate('timeago_nan'));
- $('.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');
- });
+ // TODO: scoping
+ RefreshContextualTimeago("#" + this.basename + "-wrapper");
+ }
- $('.datetimepicker').on('hide.datetimepicker', function(e)
+ stateTrigger()
{
- Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
- Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
- Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
- Grocy.Components.DateTimePicker.GetInputElement().trigger('keyup');
- });
+ var linputElement = this.GetInputElement();
+ linputElement.trigger('input')
+ .trigger('change')
+ .trigger('keypress')
+ .trigger('keyup');
+ }
- $("#datetimepicker-shortcut").on("click", function()
+ handleShortcut(_this)
{
- 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();
- }
+ var linputElement = this.GetInputElement();
+ if (_this.checked)
+ {
+ var value = this.$("#" + this.basename + "-shortcut").data(this.basename + "-shortcut-value");
+ this.SetValue(value);
+ this.GetInputElement().attr("readonly", "");
+ this.$(linputElement.data('next-input-selector')).focus();
+ }
+ else
+ {
+ this.SetValue("");
+ linputElement.removeAttr("readonly");
+ linputElement.focus();
+ }
- Grocy.Components.DateTimePicker.GetInputElement().trigger('input');
- Grocy.Components.DateTimePicker.GetInputElement().trigger('change');
- Grocy.Components.DateTimePicker.GetInputElement().trigger('keypress');
- });
+ this.stateTrigger();
+ }
+ }
}
export { datetimepicker }
\ No newline at end of file
diff --git a/js/components/datetimepicker2.js b/js/components/datetimepicker2.js
index 5b781354..454be5c1 100644
--- a/js/components/datetimepicker2.js
+++ b/js/components/datetimepicker2.js
@@ -1,317 +1,13 @@
import { EmptyElementWhenMatches } from '../helpers/extensions'
import { RefreshContextualTimeago } from '../configs/timeago'
+import { datetimepicker } from './datetimepicker'
-function datetimepicker2(Grocy)
+class datetimepicker2 extends datetimepicker
{
- Grocy.Components.DateTimePicker2 = {};
-
- Grocy.Components.DateTimePicker2.GetInputElement = function()
+ constructor(Grocy, scopeSelector = null)
{
- return $('.datetimepicker2').find('input').not(".form-check-input");
+ super(Grocy, scopeSelector, "datetimepicker2");
}
-
- 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/locationpicker.js b/js/components/locationpicker.js
index 66e0b912..48053200 100644
--- a/js/components/locationpicker.js
+++ b/js/components/locationpicker.js
@@ -1,79 +1,17 @@
-function locationpicker(Grocy)
+import BasePicker from "./BasePicker";
+
+class locationpicker extends BasePicker
{
-
- Grocy.Components.LocationPicker = {};
-
- Grocy.Components.LocationPicker.GetPicker = function()
+ constructor(Grocy, scopeSelector = null)
{
- return $('#location_id');
- }
+ super(Grocy, "#location_id", scopeSelector);
- Grocy.Components.LocationPicker.GetInputElement = function()
- {
- return $('#location_id_text_input');
- }
+ this.picker = this.$(this.basename);
+ this.inputElement = this.$('#location_id_text_input');
- Grocy.Components.LocationPicker.GetValue = function()
- {
- return $('#location_id').val();
- }
+ this.initCombobox('.location-combobox');
- 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();
+ this.prefill();
}
}
diff --git a/js/components/numberpicker.js b/js/components/numberpicker.js
index f60d2cfd..98da2a69 100644
--- a/js/components/numberpicker.js
+++ b/js/components/numberpicker.js
@@ -1,99 +1,164 @@
-function numberpicker(Grocy)
+class numberpicker
{
- $(".numberpicker-down-button").unbind('click').on("click", function()
+ constructor(Grocy, scopeSelector = null)
{
- var inputElement = $(this).parent().parent().find('input[type="number"]');
- inputElement.val(parseFloat(inputElement.val() || 1) - 1);
- inputElement.trigger('keyup');
- inputElement.trigger('change');
- });
+ this.Grocy = Grocy;
- $(".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');
- });
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+ var self = this;
- $(".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("");
- }
- });
+ this.$(".numberpicker-down-button").unbind('click').on("click", () => self.valueDownHandler(this));
+ this.$(".numberpicker-up-button").unbind('click').on("click", () => self.valueUpHandler(this));
- $(".numberpicker").each(function()
- {
- new MutationObserver(function(mutations)
+ this.$(".numberpicker").on("keyup", function()
{
- mutations.forEach(function(mutation)
+ $this = $(this)
+ if ($this.attr("data-not-equal") && !$this.attr("data-not-equal").toString().isEmpty() && $this.attr("data-not-equal") == $this.val())
{
- if (mutation.type == "attributes" && (mutation.attributeName == "min" || mutation.attributeName == "max" || mutation.attributeName == "data-not-equal" || mutation.attributeName == "data-initialised"))
+ $this[0].setCustomValidity("error");
+ }
+ else
+ {
+ $this[0].setCustomValidity("");
+ }
+ });
+
+ this.$(".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();
+ }
+ });
+
+ var observer = new MutationObserver((mutations) => self.handleObservedChange(mutations));
+
+ this.$(".numberpicker").each(() => observer.observe(this, {
+ attributes: true
+ }));
+
+ this.$(".numberpicker").attr("data-initialised", "true"); // Dummy change to trigger MutationObserver above once
+ }
+
+ modifyValueHandler(_this, newValue)
+ {
+ var inputElement = this.$(_this).parent().parent().find('input[type="number"]');
+
+ if (newValue instanceof Function)
+ newValue = newValue(_this, inputElement);
+
+ inputElement.val(newValue);
+
+ inputElement.trigger('keyup').trigger('change');
+ }
+
+ valueUpHandler(_this)
+ {
+ this.modifyValueHandler(_this, (_this, inputElement) => { return parseFloat(inputElement.val() || 0) + 1; });
+ }
+
+ valueDownHandler(_this)
+ {
+ this.modifyValueHandler(_this, (_this, inputElement) => { return parseFloat(inputElement.val() || 1) - 1; });
+ }
+
+ handleObservedChange(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")
{
- 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));
+ element.parent().find(".invalid-feedback").text(
+ this.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 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));
+ element.parent().find(".invalid-feedback").text(
+ this.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)
+ );
}
- }
- });
- }).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();
+ 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)
+ );
+ }
}
- 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
index d0131939..d40e6e58 100644
--- a/js/components/productamountpicker.js
+++ b/js/components/productamountpicker.js
@@ -1,22 +1,37 @@
-function productamountpicker(Grocy)
+class productamountpicker
{
- Grocy.Use("numberpicker");
-
- Grocy.Components.ProductAmountPicker = {};
- Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = false;
-
- Grocy.Components.ProductAmountPicker.Reload = function(productId, destinationQuId, forceInitialDisplayQu = false)
+ constructor(Grocy, scopeSelector = null)
{
- var conversionsForProduct = Grocy.QuantityUnitConversionsResolved.filter(elem => elem.product_id == productId);
+ this.Grocy = Grocy;
- if (!Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled)
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+
+ Grocy.Use("numberpicker");
+ this.AllowAnyQuEnabled = false;
+
+ this.qu_id = this.$("#qu_id");
+ this.display_amount = this.$("#display_amount");
+
+ var self = this;
+
+ this.$(".input-group-productamountpicker").on("change", () => self.onChangeHandler(this));
+ this.$("#display_amount").on("keyup", () => self.$(".input-group-productamountpicker").trigger("change"));
+ }
+
+ Reload(productId, destinationQuId, forceInitialDisplayQu = false)
+ {
+ var conversionsForProduct = this.Grocy.QuantityUnitConversionsResolved.filter(elem => elem.product_id == productId);
+
+ if (!this.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);
+ var qu = thisGrocy.QuantityUnits.find(elem => elem.id == destinationQuId);
+ this.qu_id.find("option").remove().end();
+ this.qu_id.attr("data-destination-qu-name", qu.name);
+ this.qu_id.attr("data-destination-qu-name-plural", qu.name_plural);
- conversionsForProduct.forEach(conversion =>
+ for (let conversion of conversionsForProduct)
{
var factor = parseFloat(conversion.factor);
if (conversion.to_qu_id == destinationQuId)
@@ -24,100 +39,115 @@ function productamountpicker(Grocy)
factor = 1;
}
- if (!$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times
+ if (!this.$('#qu_id option[value="' + conversion.to_qu_id + '"]').length) // Don't add the destination QU multiple times
{
- $("#qu_id").append('');
+ this.qu_id.append('');
}
- });
+ };
}
- if (!Grocy.Components.ProductAmountPicker.InitialValueSet || forceInitialDisplayQu)
+ if (!this.InitialValueSet || forceInitialDisplayQu)
{
- $("#qu_id").val($("#qu_id").attr("data-initial-qu-id"));
+ this.qu_id.val(this.qu_id.attr("data-initial-qu-id"));
}
- if (!Grocy.Components.ProductAmountPicker.InitialValueSet)
+ if (!this.InitialValueSet)
{
- var convertedAmount = $("#display_amount").val() * $("#qu_id option:selected").attr("data-qu-factor");
- $("#display_amount").val(convertedAmount);
+ var convertedAmount = this.display_amount.val() * this.$("#qu_id option:selected").attr("data-qu-factor");
+ this.display_amount.val(convertedAmount);
- Grocy.Components.ProductAmountPicker.InitialValueSet = true;
+ this.InitialValueSet = true;
}
if (conversionsForProduct.length === 1 && !forceInitialDisplayQu)
{
- $("#qu_id").val($("#qu_id option:first").val());
+ this.qu_id.val(this.$("#qu_id option:first").val());
}
- if ($('#qu_id option').length == 1)
+ if (this.$('#qu_id option').length == 1)
{
- $("#qu_id").attr("disabled", "");
+ this.qu_id.attr("disabled", "");
}
else
{
- $("#qu_id").removeAttr("disabled");
+ this.qu_id.removeAttr("disabled");
}
- $(".input-group-productamountpicker").trigger("change");
+ this.$(".input-group-productamountpicker").trigger("change");
+
+
}
- Grocy.Components.ProductAmountPicker.SetQuantityUnit = function(quId)
+ SetQuantityUnit(quId)
{
- $("#qu_id").val(quId);
+ this.qu_id.val(quId);
}
- Grocy.Components.ProductAmountPicker.AllowAnyQu = function(keepInitialQu = false)
+ AllowAnyQu(keepInitialQu = false)
{
- Grocy.Components.ProductAmountPicker.AllowAnyQuEnabled = true;
+ this.AllowAnyQuEnabled = true;
- $("#qu_id").find("option").remove().end();
- Grocy.QuantityUnits.forEach(qu =>
+ this.qu_id.find("option").remove().end();
+ for (let qu of this.Grocy.QuantityUnits)
{
- $("#qu_id").append('');
- });
+ this.qu_id.append('');
+ }
if (keepInitialQu)
{
- Grocy.Components.ProductAmountPicker.SetQuantityUnit($("#qu_id").attr("data-initial-qu-id"));
+ this.SetQuantityUnit(this.qu_id.attr("data-initial-qu-id"));
}
- $("#qu_id").removeAttr("disabled");
+ this.qu_id.removeAttr("disabled");
- $(".input-group-productamountpicker").trigger("change");
+ this.$(".input-group-productamountpicker").trigger("change");
}
- Grocy.Components.ProductAmountPicker.Reset = function()
+ Reset()
{
- $("#qu_id").find("option").remove();
- $("#qu-conversion-info").addClass("d-none");
- $("#qu-display_amount-info").val("");
+ this.qu_id.find("option").remove();
+ this.$("#qu-conversion-info").addClass("d-none");
+ this.$("#qu-display_amount-info").val("");
}
- $(".input-group-productamountpicker").on("change", function()
+ onChangeHandler(_this)
{
- var selectedQuName = $("#qu_id option:selected").text();
- var quFactor = $("#qu_id option:selected").attr("data-qu-factor");
- var amount = $("#display_amount").val();
+ var selectedQuName = this.$("#qu_id option:selected").text();
+ var quFactor = this.$("#qu_id option:selected").attr("data-qu-factor");
+ var amount = this.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"))
+ var destinationQuName = this.Grocy.translaten(destinationAmount,
+ this.qu_id.attr("data-destination-qu-name"),
+ this.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())
+ var conversionInfo = this.$("#qu-conversion-info");
+
+ if (this.qu_id.attr("data-destination-qu-name") == selectedQuName ||
+ this.AllowAnyQuEnabled ||
+ amount.toString().isEmpty() ||
+ selectedQuName.toString().isEmpty())
{
- $("#qu-conversion-info").addClass("d-none");
+ conversionInfo.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));
+ conversionInfo.removeClass("d-none");
+ conversionInfo.text(this.Grocy.translate("This equals %1$s %2$s",
+ destinationAmount.toLocaleString({
+ minimumFractionDigits: 0,
+ maximumFractionDigits: this.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");
- });
+ $("#amount").val(
+ destinationAmount
+ .toFixed(this.Grocy.UserSettings.stock_decimal_places_amounts)
+ .replace(/0*$/g, '')
+ );
+ }
}
export { productamountpicker }
\ No newline at end of file
diff --git a/js/components/productcard.js b/js/components/productcard.js
index 0d1e6711..e8f2bd69 100644
--- a/js/components/productcard.js
+++ b/js/components/productcard.js
@@ -2,123 +2,146 @@ import Chart from 'chart.js';
import { EmptyElementWhenMatches } from '../helpers/extensions'
import { RefreshContextualTimeago } from '../configs/timeago'
-function productcard(Grocy)
+class productcard
{
-
- Grocy.Components.ProductCard = {};
-
- Grocy.Components.ProductCard.Refresh = function(productId)
+ constructor(Grocy, scopeSelector = null)
{
- Grocy.Api.Get('stock/products/' + productId,
+ this.Grocy = Grocy;
+
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+ this.PriceHistoryChart = null;
+ var self = this;
+
+ this.$("#productcard-product-description").on("shown.bs.collapse", function()
+ {
+ self.$(".expandable-text")
+ .find("a[data-toggle='collapse']")
+ .text(self.Grocy.translate("Show less"));
+ })
+
+ this.$("#productcard-product-description").on("hidden.bs.collapse", function()
+ {
+ self.$(".expandable-text")
+ .find("a[data-toggle='collapse']")
+ .text(self.Grocy.translate("Show more"));
+ })
+ }
+
+ Refresh(productId)
+ {
+ var self = this;
+ this.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');
+ self.$('#productcard-product-name').text(productDetails.product.name);
+ self.$('#productcard-product-description').html(productDetails.product.description);
+ self.$('#productcard-product-stock-amount').text(stockAmount);
+ self.$('#productcard-product-stock-qu-name').text(self.Grocy.translaten(stockAmount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural));
+ self.$('#productcard-product-stock-value').text(stockValue + ' ' + self.Grocy.Currency);
+ self.$('#productcard-product-last-purchased').text((productDetails.last_purchased || '2999-12-31').substring(0, 10));
+ self.$('#productcard-product-last-purchased-timeago').attr("datetime", productDetails.last_purchased || '2999-12-31');
+ self.$('#productcard-product-last-used').text((productDetails.last_used || '2999-12-31').substring(0, 10));
+ self.$('#productcard-product-last-used-timeago').attr("datetime", productDetails.last_used || '2999-12-31');
if (productDetails.location != null)
{
- $('#productcard-product-location').text(productDetails.location.name);
+ self.$('#productcard-product-location').text(productDetails.location.name);
}
- $('#productcard-product-spoil-rate').text((parseFloat(productDetails.spoil_rate_percent) / 100).toLocaleString(undefined, { style: "percent" }));
+ self.$('#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));
+ self.$('#productcard-product-stock-amount-aggregated').text(productDetails.stock_amount_aggregated);
+ self.$('#productcard-product-stock-qu-name-aggregated').text(self.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));
+ self.$('#productcard-product-stock-opened-amount-aggregated').text(self.Grocy.translate('%s opened', productDetails.stock_amount_opened_aggregated));
}
else
{
- $('#productcard-product-stock-opened-amount-aggregated').text("");
+ self.$('#productcard-product-stock-opened-amount-aggregated').text("");
}
- $("#productcard-aggregated-amounts").removeClass("d-none");
+ self.$("#productcard-aggregated-amounts").removeClass("d-none");
}
else
{
- $("#productcard-aggregated-amounts").addClass("d-none");
+ self.$("#productcard-aggregated-amounts").addClass("d-none");
}
if (productDetails.product.description != null && !productDetails.product.description.isEmpty())
{
- $("#productcard-product-description-wrapper").removeClass("d-none");
+ self.$("#productcard-product-description-wrapper").removeClass("d-none");
}
else
{
- $("#productcard-product-description-wrapper").addClass("d-none");
+ self.$("#productcard-product-description-wrapper").addClass("d-none");
}
if (productDetails.average_shelf_life_days == -1)
{
- $('#productcard-product-average-shelf-life').text(Grocy.translate("Unknown"));
+ self.$('#productcard-product-average-shelf-life').text(self.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"));
+ self.$('#productcard-product-average-shelf-life').text(self.Grocy.translate("Unlimited"));
}
else
{
- $('#productcard-product-average-shelf-life').text(moment.duration(productDetails.average_shelf_life_days, "days").humanize());
+ self.$('#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));
+ self.$('#productcard-product-stock-opened-amount').text(self.Grocy.translate('%s opened', stockAmountOpened));
}
else
{
- $('#productcard-product-stock-opened-amount').text("");
+ self.$('#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");
+ self.$('#productcard-product-edit-button').attr("href", self.Grocy.FormatUrl("/product/" + productDetails.product.id.toString() + '?' + 'returnto=' + encodeURIComponent(self.Grocy.CurrentUrlRelative)));
+ self.$('#productcard-product-journal-button').attr("href", self.Grocy.FormatUrl("/stockjournal?embedded&product=" + productDetails.product.id.toString()));
+ self.$('#productcard-product-stock-button').attr("href", self.Grocy.FormatUrl("/stockentries?embedded&product=" + productDetails.product.id.toString()));
+ self.$('#productcard-product-stock-button').removeClass("disabled");
+ self.$('#productcard-product-edit-button').removeClass("disabled");
+ self.$('#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);
+ self.$('#productcard-product-last-price').text(Number.parseFloat(productDetails.last_price).toLocaleString() + ' ' + self.Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name);
}
else
{
- $('#productcard-product-last-price').text(Grocy.translate('Unknown'));
+ self.$('#productcard-product-last-price').text(self.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);
+ self.$('#productcard-product-average-price').text(Number.parseFloat(productDetails.avg_price).toLocaleString() + ' ' + self.Grocy.Currency + ' per ' + productDetails.quantity_unit_stock.name);
}
else
{
- $('#productcard-product-average-price').text(Grocy.translate('Unknown'));
+ self.$('#productcard-product-average-price').text(self.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'));
+ self.$("#productcard-product-picture").removeClass("d-none");
+ self.$("#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");
+ self.$("#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'));
+ EmptyElementWhenMatches(self.$('#productcard-product-last-purchased-timeago'), self.Grocy.translate('timeago_nan'));
+ EmptyElementWhenMatches(self.$('#productcard-product-last-used-timeago'), self.Grocy.translate('timeago_nan'));
RefreshContextualTimeago(".productcard");
},
function(xhr)
@@ -127,22 +150,22 @@ function productcard(Grocy)
}
);
- if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
+ if (this.Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
{
- Grocy.Api.Get('stock/products/' + productId + '/price-history',
+ this.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");
+ self.$("#productcard-product-price-history-chart").removeClass("d-none");
+ self.$("#productcard-no-price-data-hint").addClass("d-none");
- Grocy.Components.ProductCard.ReInitPriceHistoryChart();
+ self.ReInitPriceHistoryChart();
var datasets = {};
- var chart = Grocy.Components.ProductCard.PriceHistoryChart.data;
- priceHistoryDataPoints.forEach((dataPoint) =>
+ var chart = self.PriceHistoryChart.data;
+ for (let dataPoint of priceHistoryDataPoints)
{
- var key = Grocy.translate("Unknown store");
+ let key = Grocy.translate("Unknown store");
if (dataPoint.shopping_location)
{
key = dataPoint.shopping_location.name
@@ -154,9 +177,9 @@ function productcard(Grocy)
}
chart.labels.push(moment(dataPoint.date).toDate());
datasets[key].push(dataPoint.price);
+ }
- });
- Object.keys(datasets).forEach((key) =>
+ for (let key of Object.keys(datasets))
{
chart.datasets.push({
data: datasets[key],
@@ -164,13 +187,13 @@ function productcard(Grocy)
borderColor: "HSL(" + (129 * chart.datasets.length) + ",100%,50%)",
label: key,
});
- });
- Grocy.Components.ProductCard.PriceHistoryChart.update();
+ }
+ self.PriceHistoryChart.update();
}
else
{
- $("#productcard-product-price-history-chart").addClass("d-none");
- $("#productcard-no-price-data-hint").removeClass("d-none");
+ self.$("#productcard-product-price-history-chart").addClass("d-none");
+ self.$("#productcard-no-price-data-hint").removeClass("d-none");
}
},
function(xhr)
@@ -179,17 +202,17 @@ function productcard(Grocy)
}
);
}
- };
+ }
- Grocy.Components.ProductCard.ReInitPriceHistoryChart = function()
+ ReInitPriceHistoryChart()
{
- if (typeof Grocy.Components.ProductCard.PriceHistoryChart !== "undefined")
+ if (this.PriceHistoryChart !== null)
{
- Grocy.Components.ProductCard.PriceHistoryChart.destroy();
+ this.PriceHistoryChart.destroy();
}
var format = 'YYYY-MM-DD';
- Grocy.Components.ProductCard.PriceHistoryChart = new Chart(document.getElementById("productcard-product-price-history-chart"), {
+ this.PriceHistoryChart = new Chart(this.$("#productcard-product-price-history-chart")[0], {
type: "line",
data: {
labels: [ //Date objects
@@ -230,16 +253,6 @@ function productcard(Grocy)
}
});
}
-
- $("#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
index 1e818335..e659d18d 100644
--- a/js/components/productpicker.js
+++ b/js/components/productpicker.js
@@ -1,159 +1,171 @@
-function productpicker(Grocy)
+import BasePicker from "./BasePicker";
+
+class productpicker extends BasePicker
{
- Grocy.Use('barcodescanner');
- Grocy.Components.ProductPicker = {};
- Grocy.Components.ProductPicker.GetPicker = function()
+ constructor(Grocy, scopeSelector = null)
{
- return $('#product_id');
+ super(Grocy, "#product_id", scopeSelector)
+
+ this.Grocy.Use('barcodescanner');
+
+ this.picker = this.$('#product_id');
+ this.inputElement = this.$('#product_id_text_input');
+
+ var self = this;
+
+ this.embedded = "";
+ if (this.Grocy.GetUriParam("embedded") !== undefined)
+ {
+ this.embedded = "embedded";
+ }
+
+ this.initCombobox('.product-combobox');
+
+ this.prefill();
+
+ if (this.Grocy.GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
+ {
+ this.$('#InplaceAddBarcodeToExistingProduct').text(this.Grocy.GetUriParam("barcode"));
+ this.$('#flow-info-InplaceAddBarcodeToExistingProduct').removeClass('d-none');
+ this.$('#barcode-lookup-disabled-hint').removeClass('d-none');
+ this.$('#barcode-lookup-hint').addClass('d-none');
+ }
+
+ this.PopupOpen = false;
+
+ this.$('#product_id_text_input').on('blur', (e) => self.onBlurHandler(this, e));
+ this.scope.on("Grocy.BarcodeScanned", (e, barcode, target) => self.onBarcodeScannedHandler(e, barcode, target));
+
+ $(document).on("shown.bs.modal", function(e)
+ {
+ self.$(".modal-footer").addClass("d-block").addClass("d-sm-flex");
+ self.$(".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)
+ this.$('#product_id_text_input').keydown(function(event)
+ {
+ if (event.keyCode === 13) // Enter
+ {
+ if (this.picker.hasClass("combobox-menu-visible"))
+ {
+ return;
+ }
+
+ self.$("#product_id_text_input").trigger("blur");
+ }
+ });
}
- Grocy.Components.ProductPicker.GetInputElement = function()
+ prefill()
{
- return $('#product_id_text_input');
+ var doFocus = false;
+
+ var prefillProduct = this.Grocy.GetUriParam('product-name');
+ var prefillProduct2 = this.picker.parent().data('prefill-by-name').toString();
+ if (!prefillProduct2.isEmpty())
+ {
+ prefillProduct = prefillProduct2;
+ }
+ if (typeof prefillProduct !== "undefined")
+ {
+ var possibleOptionElement = this.$("#product_id option[data-additional-searchdata*=\"" + prefillProduct + "\"]").first();
+ if (possibleOptionElement.length === 0)
+ {
+ possibleOptionElement = this.$("#product_id option:contains(\"" + prefillProduct + "\")").first();
+ }
+
+ if (possibleOptionElement.length > 0)
+ {
+ doFocus = true;
+ this.picker.val(possibleOptionElement.val());
+ }
+ }
+
+ var prefillProductId = this.Grocy.GetUriParam("product");
+ var prefillProductId2 = this.picker.parent().data('prefill-by-id').toString();
+ if (!prefillProductId2.isEmpty())
+ {
+ prefillProductId = prefillProductId2;
+ }
+ if (typeof prefillProductId !== "undefined")
+ {
+ this.picker.val(prefillProductId);
+ doFocus = true;
+ }
+
+ if (doFocus)
+ {
+ this.picker.data('combobox').refresh();
+ this.picker.trigger('change');
+
+ this.$(this.picker.parent().data('next-input-selector').toString())
+ .focus();
+ }
}
- Grocy.Components.ProductPicker.GetValue = function()
+ InProductAddWorkflow()
{
- return $('#product_id').val();
+ return this.Grocy.GetUriParam('flow') == "InplaceNewProductWithName";
}
- Grocy.Components.ProductPicker.SetValue = function(value)
+ InProductModifyWorkflow()
{
- Grocy.Components.ProductPicker.GetInputElement().val(value);
- Grocy.Components.ProductPicker.GetInputElement().trigger('change');
+ return this.Grocy.GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct";
}
- Grocy.Components.ProductPicker.SetId = function(value)
+ InAnyFlow()
{
- Grocy.Components.ProductPicker.GetPicker().val(value);
- Grocy.Components.ProductPicker.GetPicker().data('combobox').refresh();
- Grocy.Components.ProductPicker.GetInputElement().trigger('change');
+ return this.InProductAddWorkflow() || this.InProductModifyWorkflow();
}
- Grocy.Components.ProductPicker.Clear = function()
+ FinishFlow()
{
- Grocy.Components.ProductPicker.SetValue('');
- Grocy.Components.ProductPicker.SetId(null);
+ this.Grocy.RemoveUriParam("flow");
+ this.Grocy.RemoveUriParam("barcode");
+ this.Grocy.RemoveUriParam("product-name");
}
- Grocy.Components.ProductPicker.InProductAddWorkflow = function()
+ ShowCustomError(text)
{
- return Grocy.GetUriParam('flow') == "InplaceNewProductWithName";
- }
-
- Grocy.Components.ProductPicker.InProductModifyWorkflow = function()
- {
- return Grocy.GetUriParam('flow') == "InplaceAddBarcodeToExistingProduct";
- }
-
- Grocy.Components.ProductPicker.InAnyFlow = function()
- {
- return Grocy.Components.ProductPicker.InProductAddWorkflow() || Grocy.Components.ProductPicker.InProductModifyWorkflow();
- }
-
- Grocy.Components.ProductPicker.FinishFlow = function()
- {
- Grocy.RemoveUriParam("flow");
- Grocy.RemoveUriParam("barcode");
- Grocy.RemoveUriParam("product-name");
- }
-
- Grocy.Components.ProductPicker.ShowCustomError = function(text)
- {
- var element = $("#custom-productpicker-error");
+ var element = this.$("#custom-productpicker-error");
element.text(text);
element.removeClass("d-none");
}
- Grocy.Components.ProductPicker.HideCustomError = function()
+ HideCustomError()
{
- $("#custom-productpicker-error").addClass("d-none");
+ this.$("#custom-productpicker-error").addClass("d-none");
}
- Grocy.Components.ProductPicker.Disable = function()
+ Disable()
{
- Grocy.Components.ProductPicker.GetInputElement().attr("disabled", "");
- $("#barcodescanner-start-button").attr("disabled", "");
- $("#barcodescanner-start-button").addClass("disabled");
+ this.inputElement.attr("disabled", "");
+ this.$("#barcodescanner-start-button").attr("disabled", "");
+ this.$("#barcodescanner-start-button").addClass("disabled");
}
- Grocy.Components.ProductPicker.Enable = function()
+ Enable()
{
- Grocy.Components.ProductPicker.GetInputElement().removeAttr("disabled");
- $("#barcodescanner-start-button").removeAttr("disabled");
- $("#barcodescanner-start-button").removeClass("disabled");
+ this.inputElement.removeAttr("disabled");
+ this.$("#barcodescanner-start-button").removeAttr("disabled");
+ this.$("#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 = Grocy.GetUriParam('product-name');
- var prefillProduct2 = this_product_picker.parent().data('prefill-by-name').toString();
- if (!prefillProduct2.isEmpty())
+ onBlurHandler(_this, e)
{
- 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();
- }
+ var self = this;
- if (possibleOptionElement.length > 0)
- {
- productpicker_doFocus = true;
- this_product_picker.val(possibleOptionElement.val());
- }
- }
-
- var prefillProductId = Grocy.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 (Grocy.GetUriParam("flow") === "InplaceAddBarcodeToExistingProduct")
- {
- $('#InplaceAddBarcodeToExistingProduct').text(Grocy.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"))
+ if (this.picker.hasClass("combobox-menu-visible"))
{
return;
}
- $('#product_id').attr("barcode", "null");
- var input = $('#product_id_text_input').val().toString();
+ this.picker.attr("barcode", "null");
+
+ var input = this.$('#product_id_text_input').val().toString();
var possibleOptionElement = [];
// did we enter a grocycode?
@@ -163,62 +175,66 @@ function productpicker(Grocy)
if (gc[1] == "p")
{
// find product id
- possibleOptionElement = $("#product_id option[value=\"" + gc[2] + "\"]").first();
- $("#product_id").data("grocycode", true);
+ possibleOptionElement = this.$("#product_id option[value=\"" + gc[2] + "\"]").first();
+ this.picker.data("grocycode", true);
}
}
else // process barcode as usual
{
- possibleOptionElement = $("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();
+ possibleOptionElement = this.$("#product_id option[data-additional-searchdata*=\"" + input + ",\"]").first();
}
- if (Grocy.GetUriParam('flow') === undefined && input.length > 0 && possibleOptionElement.length > 0)
+ if (this.Grocy.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');
+ this.picker.val(possibleOptionElement.val());
+ this.picker.attr("barcode", input);
+ this.picker.data('combobox').refresh();
+ this.picker.trigger('change');
}
else
{
- if (Grocy.Components.ProductPicker.PopupOpen === true)
+ if (this.PopupOpen === true)
{
return;
}
- var optionElement = $("#product_id option:contains(\"" + input + "\")").first();
- if (input.length > 0 && optionElement.length === 0 && Grocy.GetUriParam('flow') === undefined && Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-all-product-workflows').toString() === "false")
+ var optionElement = this.$("#product_id option:contains(\"" + input + "\")").first();
+ if (input.length > 0 &&
+ optionElement.length === 0 &&
+ this.Grocy.GetUriParam('flow') === undefined &&
+ this.picker.parent().data('disallow-all-product-workflows').toString() === "false")
{
var addProductWorkflowsAdditionalCssClasses = "";
- if (Grocy.Components.ProductPicker.GetPicker().parent().data('disallow-add-product-workflows').toString() === "true")
+ if (this.picker.parent().data('disallow-add-product-workflows').toString() === "true")
{
addProductWorkflowsAdditionalCssClasses = "d-none";
}
- var embedded = "";
- if (Grocy.GetUriParam("embedded") !== undefined)
- {
- embedded = "embedded";
- }
-
var buttons = {
cancel: {
- label: Grocy.translate('Cancel'),
+ label: this.Grocy.translate('Cancel'),
className: 'btn-secondary responsive-button',
callback: function()
{
- Grocy.Components.ProductPicker.PopupOpen = false;
- Grocy.Components.ProductPicker.SetValue('');
+ self.PopupOpen = false;
+ self.SetValue('');
}
},
addnewproduct: {
- label: 'P ' + Grocy.translate('Add as new product'),
+ label: 'P ' + this.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);
+ self.PopupOpen = false;
+ window.location.href = self.Grocy.FormatUrl(
+ '/product/new?flow=InplaceNewProductWithName' +
+ '&name=' + encodeURIComponent(input) +
+ '&returnto=' + encodeURIComponent(
+ Grocy.CurrentUrlRelative +
+ "?flow=InplaceNewProductWithName" +
+ "&" + self.embedded) +
+ "&" + self.embedded);
}
},
addbarcode: {
@@ -226,8 +242,12 @@ function productpicker(Grocy)
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));
+ self.PopupOpen = false;
+ window.location.href = Grocy.FormatUrl(
+ self.Grocy.CurrentUrlRelative +
+ '?flow=InplaceAddBarcodeToExistingProduct' +
+ '&barcode=' + encodeURIComponent(input)
+ );
}
},
addnewproductwithbarcode: {
@@ -235,8 +255,18 @@ function productpicker(Grocy)
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);
+ self.PopupOpen = false;
+ window.location.href = Grocy.FormatUrl(
+ '/product/new' +
+ '?flow=InplaceNewProductWithBarcode' +
+ '&barcode=' + encodeURIComponent(input) +
+ '&returnto=' + encodeURIComponent(
+ self.Grocy.CurrentUrlRelative +
+ "?flow=InplaceAddBarcodeToExistingProduct" +
+ "&barcode=" + input +
+ "&" + self.embedded
+ ) +
+ "&" + self.embedded);
}
}
};
@@ -248,21 +278,21 @@ function productpicker(Grocy)
className: 'btn-primary responsive-button retry-camera-scanning-button',
callback: function()
{
- Grocy.Components.ProductPicker.PopupOpen = false;
- Grocy.Components.ProductPicker.SetValue('');
- $("#barcodescanner-start-button").click();
+ self.PopupOpen = false;
+ self.SetValue('');
+ self.$("#barcodescanner-start-button").click();
}
};
}
- Grocy.Components.ProductPicker.PopupOpen = true;
+ this.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'),
+ message: this.Grocy.translate('"%s" could not be resolved to a product, how do you want to proceed?', input),
+ title: this.Grocy.translate('Create or assign product'),
onEscape: function()
{
- Grocy.Components.ProductPicker.PopupOpen = false;
- Grocy.Components.ProductPicker.SetValue('');
+ self.PopupOpen = false;
+ self.SetValue('');
},
size: 'large',
backdrop: true,
@@ -272,66 +302,49 @@ function productpicker(Grocy)
{
if (e.key === 'B' || e.key === 'b')
{
- $('.add-new-barcode-dialog-button').not(".d-none").click();
+ self.$('.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();
+ self.$('.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();
+ self.$('.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();
+ self.$('.retry-camera-scanning-button').not(".d-none").click();
}
});
}
}
- });
+ }
- $(document).on("Grocy.BarcodeScanned", function(e, barcode, target)
+ onBarcodeScannedHandler(e, barcode, target)
{
+ var self = this;
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();
+ this.inputElement
+ .focusout()
+ .focus()
+ .blur();
- Grocy.Components.ProductPicker.GetInputElement().val(barcode);
+ this.inputElement.val(barcode);
setTimeout(function()
{
- Grocy.Components.ProductPicker.GetInputElement().focusout();
- Grocy.Components.ProductPicker.GetInputElement().focus();
- Grocy.Components.ProductPicker.GetInputElement().blur();
+ self.inputElement
+ .focusout()
+ .focus()
+ .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
index 082559f2..bcae1104 100644
--- a/js/components/recipepicker.js
+++ b/js/components/recipepicker.js
@@ -1,76 +1,17 @@
-function recipepicker(Grocy)
+import BasePicker from './BasePicker'
+
+class recipepicker extends BasePicker
{
- Grocy.Components.RecipePicker = {};
-
- Grocy.Components.RecipePicker.GetPicker = function()
+ constructor(Grocy, scopeSelector = null)
{
- return $('#recipe_id');
- }
+ super(Grocy, "#recipe_id", scopeSelector);
- Grocy.Components.RecipePicker.GetInputElement = function()
- {
- return $('#recipe_id_text_input');
- }
+ this.picker = this.$(this.basename);
+ this.input_element = this.$(this.basename + '_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();
+ this.initCombobox('.recipe-combobox');
+ this.prefill();
}
}
diff --git a/js/components/shoppinglocationpicker.js b/js/components/shoppinglocationpicker.js
index 0e4768c2..77441ca4 100644
--- a/js/components/shoppinglocationpicker.js
+++ b/js/components/shoppinglocationpicker.js
@@ -1,76 +1,17 @@
-function shoppinglocationpicker(Grocy)
+import BasePicker from "./BasePicker";
+
+class shoppinglocationpicker extends BasePicker
{
- Grocy.Components.ShoppingLocationPicker = {};
- Grocy.Components.ShoppingLocationPicker.GetPicker = function()
+ constructor(Grocy, scopeSelector = null)
{
- return $('#shopping_location_id');
- }
+ super(Grocy, "#shopping_location_id", scopeSelector);
- Grocy.Components.ShoppingLocationPicker.GetInputElement = function()
- {
- return $('#shopping_location_id_text_input');
- }
+ this.picker = this.$(this.basename);
+ this.input_element = this.$(this.basename + '_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();
+ this.initCombobox('.recipe-combobox');
+ this.prefill();
}
}
diff --git a/js/components/userfieldsform.js b/js/components/userfieldsform.js
index c0171129..3e025111 100644
--- a/js/components/userfieldsform.js
+++ b/js/components/userfieldsform.js
@@ -1,17 +1,28 @@
import { RandomString } from '../helpers/extensions';
import { LoadImagesLazy } from '../configs/lazy'
+import uuid from 'uuid';
+import { datetimepicker } from "./datetimepicker";
-function userfieldsform(Grocy)
+class userfieldsform
{
- if (document.querySelector('.datetimepicker-input') !== null)
+ constructor(Grocy, scopeSelector = null)
{
- Grocy.Use("datetimepicker");
- }
- Grocy.Components.UserfieldsForm = {};
+ this.Grocy = Grocy;
- Grocy.Components.UserfieldsForm.Save = function(success, error)
+ this.scopeSelector = scopeSelector;
+ this.scope = scopeSelector != null ? $(scope) : $(document);
+ this.$ = scopeSelector != null ? $(scope).find : $;
+
+ this.$(".userfield-link").keyup();
+
+ // We need to further scope some of the embedded components.
+ // Store those instances here.
+ this.components = []
+ }
+
+ Save(success, error)
{
- if (!$("#userfields-form").length)
+ if (!this.$("#userfields-form").length)
{
if (success)
{
@@ -22,8 +33,9 @@ function userfieldsform(Grocy)
}
var jsonData = {};
+ var self = this;
- $("#userfields-form .userfield-input").not("div").each(function()
+ this.$("#userfields-form .userfield-input").not("div").each(function()
{
var input = $(this);
var fieldName = input.attr("data-userfield-name");
@@ -42,10 +54,10 @@ function userfieldsform(Grocy)
var oldFile = input.data('old-file')
if (oldFile)
{
- Grocy.Api.Delete('files/userfiles/' + oldFile, null, null,
+ self.Grocy.Api.Delete('files/userfiles/' + oldFile, null, null,
function(xhr)
{
- Grocy.FrontendHelpers.ShowGenericError('Could not delete file', xhr);
+ self.Grocy.FrontendHelpers.ShowGenericError('Could not delete file', xhr);
});
jsonData[fieldName] = "";
}
@@ -56,7 +68,7 @@ function userfieldsform(Grocy)
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,
+ self.Grocy.Api.UploadFile(input[0].files[0], 'userfiles', fileName,
function(result)
{
},
@@ -78,7 +90,7 @@ function userfieldsform(Grocy)
}
});
- Grocy.Api.Put('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId, jsonData,
+ this.Grocy.Api.Put('userfields/' + $("#userfields-form").data("entity") + '/' + this.Grocy.EditObjectId, jsonData,
function(result)
{
if (success)
@@ -96,19 +108,20 @@ function userfieldsform(Grocy)
);
}
- Grocy.Components.UserfieldsForm.Load = function()
+ Load()
{
- if (!$("#userfields-form").length)
+ if (!this.$("#userfields-form").length)
{
return;
}
+ var self = this;
- Grocy.Api.Get('userfields/' + $("#userfields-form").data("entity") + '/' + Grocy.EditObjectId,
+ Grocy.Api.Get('userfields/' + this.$("#userfields-form").data("entity") + '/' + this.Grocy.EditObjectId,
function(result)
{
$.each(result, function(key, value)
{
- var input = $(".userfield-input[data-userfield-name='" + key + "']");
+ var input = self.$(".userfield-input[data-userfield-name='" + key + "']");
if (input.attr("type") == "checkbox" && value == 1)
{
@@ -117,7 +130,7 @@ function userfieldsform(Grocy)
else if (input.hasAttr("multiple"))
{
input.val(value.split(","));
- $(".selectpicker").selectpicker("render");
+ self.$(".selectpicker").selectpicker("render");
}
else if (input.attr('type') == "file")
{
@@ -128,16 +141,16 @@ function userfieldsform(Grocy)
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").attr('href', self.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'));
+ .attr('src', self.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("label.custom-file-label").text(self.Grocy.translate("No file selected"));
formGroup.find(".userfield-file-show").addClass('d-none');
input.attr('data-old-file', fileSrc);
}
@@ -167,6 +180,16 @@ function userfieldsform(Grocy)
input.val(value);
}
});
+
+ self.$('.datetimepicker-wrapper').each(() =>
+ {
+ let picker = $(this);
+
+ var scopeId = uuid.v4();
+ picker.attr('id', scopeId);
+
+ this.components.push(new datetimepicker(self.Grocy, "#" + scopeId));
+ });
},
function(xhr)
{
@@ -175,9 +198,10 @@ function userfieldsform(Grocy)
);
}
- $(".userfield-link").keyup(function(e)
+ onUserfieldInputKeyUp(_this)
{
- var formRow = $(this).parent().parent();
+
+ var formRow = this.$(_this).parent().parent();
var title = formRow.find(".userfield-link-title").val();
var link = formRow.find(".userfield-link-link").val();
@@ -187,7 +211,8 @@ function userfieldsform(Grocy)
};
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
index 8f96fe84..6762f345 100644
--- a/js/components/userpicker.js
+++ b/js/components/userpicker.js
@@ -1,86 +1,58 @@
-function userpicker(Grocy)
+import BasePicker from "./BasePicker";
+
+class userpicker extends BasePicker
{
-
- Grocy.Components.UserPicker = {};
-
- Grocy.Components.UserPicker.GetPicker = function()
+ constructor(Grocy, scopeSelector = null)
{
- return $('#user_id');
+ super(Grocy, "#user_id", scopeSelector);
+
+ this.picker = this.$(this.basename);
+ this.input_element = this.$(this.basename + '_text_input');
+
+ this.initCombobox('.recipe-combobox');
+ this.prefill();
}
- Grocy.Components.UserPicker.GetInputElement = function()
+ prefill()
{
- return $('#user_id_text_input');
- }
+ var doFocus = false;
+ var possibleOptionElement = null;
- 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)
+ var prefillUser = this.picker.parent().data('prefill-by-username').toString();
+ if (typeof prefillUser !== "undefined")
{
- possibleOptionElement = $("#user_id option:contains(\"" + prefillUser + "\")").first();
+ possibleOptionElement = this.$("#user_id option[data-additional-searchdata*=\"" + prefillUser + "\"]").first();
+ if (possibleOptionElement.length === 0)
+ {
+ possibleOptionElement = this.$("#user_id option:contains(\"" + prefillUser + "\")").first();
+ }
+
+ if (possibleOptionElement.length > 0)
+ {
+ doFocus = true;
+ this.picker.val(possibleOptionElement.val());
+ }
}
- if (possibleOptionElement.length > 0)
+ var prefillUserId = this.picker.parent().data('prefill-by-user-id').toString();
+ if (typeof prefillUserId !== "undefined")
{
- user_picker_doFocus = true;
- this_user_picker.val(possibleOptionElement.val());
-
+ possibleOptionElement = this.$("#user_id option[value='" + prefillUserId + "']").first();
+ if (possibleOptionElement.length > 0)
+ {
+ doFocus = true;
+ this.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)
+ if (doFocus)
{
- user_picker_doFocus = true;
- this_user_picker.val(possibleOptionElement.val());
+ this.picker.data('combobox').refresh();
+ this.picker.trigger('change');
+
+ this.$(this.picker.parent().data('next-input-selector').toString())
+ .focus();
}
}
-
- 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/grocy.js b/js/grocy.js
index 26df66fb..ac5ddd25 100644
--- a/js/grocy.js
+++ b/js/grocy.js
@@ -50,6 +50,8 @@ class GrocyClass
this.Components = {};
this.initComponents = [];
+ this.RootGrocy = null;
+
// Init some classes
this.Api = new GrocyApi(this);
@@ -217,18 +219,20 @@ class GrocyClass
this.IdleTime += 1;
}
- Use(componentName)
+ Use(componentName, scope = null)
{
- // initialize Components only once
- if (this.initComponents.find(elem => elem == componentName))
- return;
+ let scopeName = scope || "";
+ // initialize Components only once per scope
+ if (this.initComponents.find(elem => elem == componentName + scopeName))
+ return this.components[componentName + scopeName];
if (Object.prototype.hasOwnProperty.call(components, componentName))
{
// add-then-init to resolve circular dependencies
this.initComponents.push(componentName);
- var component = components[componentName](this);
- this.components[component.key] = component;
+ var component = components[componentName](this, scope);
+ this.components[componentName + scopeName] = component;
+ return component;
}
else
{
diff --git a/js/helpers/extensions.js b/js/helpers/extensions.js
index 951d6937..89765950 100644
--- a/js/helpers/extensions.js
+++ b/js/helpers/extensions.js
@@ -96,8 +96,5 @@ export
IsJsonString,
BoolVal,
GetFileNameFromPath,
- RemoveUriParam,
- UpdateUriParam,
- GetUriParam,
EmptyElementWhenMatches
}
\ No newline at end of file
diff --git a/js/lib/proxy.js b/js/lib/proxy.js
index 5df1156a..65eaf073 100644
--- a/js/lib/proxy.js
+++ b/js/lib/proxy.js
@@ -7,7 +7,7 @@ class GrocyProxy
constructor(RootGrocy, scopeSelector, config, url)
{
- this.rootGrocy = RootGrocy;
+ this.RootGrocy = RootGrocy;
// proxy-local members, because they might not be set globally.
this.QuantityUnits = config.QuantityUnits;
@@ -38,9 +38,6 @@ class GrocyProxy
this.config = config;
- // scoped variants of some helpers
- this.FrontendHelpers = new GrocyFrontendHelpers(this, this.Api, this.scopeSelector);
-
this.configProxy = Proxy.revocable(this.config, {
get: function(target, prop, receiver)
{
@@ -56,11 +53,9 @@ class GrocyProxy
})
// This is where the magic happens!
- // basically, this Proxy object checks if a member
- // is defined in this proxy class, and returns it
- // if so.
- // If not, the prop is handed over to the root
- // grocy instance.
+ // basically, this Proxy object checks if a member is defined in this proxy class,
+ // and returns it if so.
+ // If not, the prop is handed over to the root grocy instance.
this.grocyProxy = Proxy.revocable(this, {
get: function(target, prop, receiver)
{
@@ -74,6 +69,9 @@ class GrocyProxy
}
}
});
+
+ // scoped variants of some helpers
+ this.FrontendHelpers = new GrocyFrontendHelpers(this, RootGrocy.Api, this.scopeSelector);
}
Unload()
@@ -82,18 +80,20 @@ class GrocyProxy
this.configProxy.revoke();
}
- Use(componentName)
+ Use(componentName, scope = null)
{
- // initialize Components only once
- if (this.initComponents.find(elem => elem == componentName))
- return;
+ let scopeName = scope || "";
+ // initialize Components only once per scope
+ if (this.initComponents.find(elem => elem == componentName + scopeName))
+ return this.components[componentName + scopeName];
if (Object.prototype.hasOwnProperty.call(components, componentName))
{
// add-then-init to resolve circular dependencies
this.initComponents.push(componentName);
- var component = components[componentName](this, this.scopeSelector);
- this.components[component.key] = component;
+ var component = components[componentName](this, scope);
+ this.components[componentName + scopeName] = component;
+ return component;
}
else
{
@@ -125,6 +125,7 @@ class GrocyProxy
return currentParam[1] === undefined ? true : decodeURIComponent(currentParam[1]);
}
}
+ return undefined;
}
UpdateUriParam(key, value)
@@ -181,4 +182,6 @@ class GrocyProxy
}
this.virtualUrl = vurl.substring(1); // remove leading &
}
-}
\ No newline at end of file
+}
+
+export { GrocyProxy }
\ No newline at end of file
diff --git a/package.json b/package.json
index f5ee736a..6485aa69 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,8 @@
"swagger-ui-dist": "^3.32.1",
"tempusdominus-bootstrap-4": "https://github.com/mistressofjellyfish/bootstrap-4.git#master",
"timeago": "^1.6.7",
- "toastr": "^2.1.4"
+ "toastr": "^2.1.4",
+ "uuid": "^8.3.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^19.0.0",
diff --git a/yarn.lock b/yarn.lock
index ec1c1606..fb668165 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4755,6 +4755,7 @@ fsevents@~2.3.2:
tempusdominus-bootstrap-4: "https://github.com/mistressofjellyfish/bootstrap-4.git#master"
timeago: ^1.6.7
toastr: ^2.1.4
+ uuid: ^8.3.2
vinyl-buffer: ^1.0.1
vinyl-source-stream: ^2.0.0
languageName: unknown
@@ -10818,7 +10819,7 @@ fsevents@~2.3.2:
languageName: node
linkType: hard
-"uuid@npm:^8.2.0, uuid@npm:^8.3.0":
+"uuid@npm:^8.2.0, uuid@npm:^8.3.0, uuid@npm:^8.3.2":
version: 8.3.2
resolution: "uuid@npm:8.3.2"
bin: