L = function(text, ...placeholderValues) { var localizedText = Grocy.LocalizationStrings[text]; if (localizedText === undefined) { if (Grocy.Mode === 'dev') { jsonData = {}; jsonData.text = text; Grocy.Api.Post('system/log-missing-localization', jsonData, function(result) { // Nothing to do... }, function(xhr) { Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response) } ); } localizedText = text; } for (var i = 0; i < placeholderValues.length; i++) { localizedText = localizedText.replace('#' + (i + 1), placeholderValues[i]); } return localizedText; } U = function(relativePath) { return Grocy.BaseUrl.replace(/\/$/, '') + relativePath; } Pluralize = function(number, singularForm, pluralForm) { var text = singularForm; if (number != 1 && pluralForm !== null && !pluralForm.isEmpty()) { text = pluralForm; } return text; } if (!Grocy.ActiveNav.isEmpty()) { var menuItem = $('#sidebarResponsive').find("[data-nav-for-page='" + Grocy.ActiveNav + "']"); menuItem.addClass('active-page'); var parentMenuSelector = menuItem.data("sub-menu-of"); if (typeof parentMenuSelector !== "undefined") { $(parentMenuSelector).collapse("show"); $(parentMenuSelector).prev(".nav-link-collapse").addClass("active-page"); } } var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.attributeName === "class") { var attributeValue = $(mutation.target).prop(mutation.attributeName); if (attributeValue.contains("sidenav-toggled")) { window.localStorage.setItem("sidebar_state", "collapsed"); } else { window.localStorage.setItem("sidebar_state", "expanded"); } } }); }); observer.observe(document.body, { attributes: true }); if (window.localStorage.getItem("sidebar_state") === "collapsed") { $("#sidenavToggler").click(); } $.timeago.settings.allowFuture = true; RefreshContextualTimeago = function() { $("time.timeago").each(function() { var element = $(this); var timestamp = element.attr("datetime"); element.timeago("update", timestamp); }); } RefreshContextualTimeago(); toastr.options = { toastClass: 'alert', closeButton: true, timeOut: 20000, extendedTimeOut: 5000 }; window.FontAwesomeConfig = { searchPseudoElements: true } // Don't show tooltips on touch input devices if (IsTouchInputDevice()) { var css = document.createElement("style"); css.innerHTML = ".tooltip { display: none; }"; document.body.appendChild(css); } Grocy.Api = { }; Grocy.Api.Get = function(apiFunction, success, error) { var xhr = new XMLHttpRequest(); var url = U('/api/' + apiFunction); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 204) { if (success) { if (xhr.status === 200) { success(JSON.parse(xhr.responseText)); } else { success({ }); } } } else { if (error) { error(xhr); } } } }; xhr.open('GET', url, true); xhr.send(); }; Grocy.Api.Post = function(apiFunction, jsonData, success, error) { var xhr = new XMLHttpRequest(); var url = U('/api/' + apiFunction); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 204) { if (success) { if (xhr.status === 200) { success(JSON.parse(xhr.responseText)); } else { success({ }); } } } else { if (error) { error(xhr); } } } }; xhr.open('POST', url, true); xhr.setRequestHeader('Content-type', 'application/json'); xhr.send(JSON.stringify(jsonData)); }; Grocy.Api.Put = function(apiFunction, jsonData, success, error) { var xhr = new XMLHttpRequest(); var url = U('/api/' + apiFunction); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 204) { if (success) { if (xhr.status === 200) { success(JSON.parse(xhr.responseText)); } else { success({ }); } } } else { if (error) { error(xhr); } } } }; xhr.open('PUT', url, true); xhr.setRequestHeader('Content-type', 'application/json'); xhr.send(JSON.stringify(jsonData)); }; Grocy.Api.Delete = function(apiFunction, jsonData, success, error) { var xhr = new XMLHttpRequest(); var url = U('/api/' + apiFunction); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200 || xhr.status === 204) { if (success) { if (xhr.status === 200) { success(JSON.parse(xhr.responseText)); } else { success({ }); } } } else { if (error) { error(xhr); } } } }; xhr.open('DELETE', url, true); xhr.setRequestHeader('Content-type', 'application/json'); xhr.send(JSON.stringify(jsonData)); }; Grocy.Api.UploadFile = function(file, group, fileName, success, error) { var xhr = new XMLHttpRequest(); var url = U('/api/file/' + group + '?file_name=' + encodeURIComponent(fileName)); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { if (success) { success(JSON.parse(xhr.responseText)); } } else { if (error) { error(xhr); } } } }; xhr.open('PUT', url, true); xhr.setRequestHeader('Content-type', 'application/octet-stream'); xhr.send(file); }; Grocy.Api.DeleteFile = function(fileName, group, success, error) { var xhr = new XMLHttpRequest(); var url = U('/api/file/' + group + '?file_name=' + encodeURIComponent(fileName)); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { if (success) { success(JSON.parse(xhr.responseText)); } } else { if (error) { error(xhr); } } } }; xhr.open('DELETE', url, true); xhr.setRequestHeader('Content-type', 'application/json'); xhr.send(); }; Grocy.FrontendHelpers = { }; Grocy.FrontendHelpers.ValidateForm = function(formId) { var form = document.getElementById(formId); if (form.checkValidity() === true) { $(form).find(':submit').removeClass('disabled'); } else { $(form).find(':submit').addClass('disabled'); } $(form).addClass('was-validated'); } Grocy.FrontendHelpers.BeginUiBusy = function(formId = null) { $("body").addClass("cursor-busy"); if (formId !== null) { $("#" + formId + " :input").attr("disabled", true); } } Grocy.FrontendHelpers.EndUiBusy = function(formId = null) { $("body").removeClass("cursor-busy"); if (formId !== null) { $("#" + formId + " :input").attr("disabled", false); } } Grocy.FrontendHelpers.ShowGenericError = function(message, exception) { toastr.error(L(message) + '

' + L('Click to show technical details'), '', { onclick: function() { bootbox.alert({ title: L('Error details'), message: JSON.stringify(exception, null, 4) }); } }); console.error(exception); } $("form").on("keyup paste", "input, textarea", function() { $(this).closest("form").addClass("is-dirty"); }); $("form").on("click", "select", function() { $(this).closest("form").addClass("is-dirty"); }); // Auto saving user setting controls $(".user-setting-control").on("change", function() { var element = $(this); var settingKey = element.attr("data-setting-key"); var inputType = "unknown"; if (typeof element.attr("type") !== typeof undefined && element.attr("type") !== false) { inputType = element.attr("type").toLowerCase(); } if (inputType === "checkbox") { value = element.is(":checked"); } else { var value = element.val(); } Grocy.UserSettings[settingKey] = value; jsonData = { }; jsonData.value = value; Grocy.Api.Put('user/settings/' + settingKey, jsonData, function(result) { // Nothing to do... }, function(xhr) { Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response) } ); }); // Show file name Bootstrap custom file input $('input.custom-file-input').on('change', function() { $(this).next('.custom-file-label').html(GetFileNameFromPath($(this).val())); }); // Translation of "Browse"-button of Bootstrap custom file input if ($(".custom-file-label").length > 0) { $("