$.expr[":"].containsNC = $.expr.createPseudo(function (arg) { return function (elem) { return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0; }; }); function returnToGarage() { window.location.href = '/Home'; } function successToast(message) { Swal.fire({ toast: true, position: "top-end", showConfirmButton: false, timer: 3000, title: message, timerProgressBar: true, icon: "success", didOpen: (toast) => { toast.onmouseenter = Swal.stopTimer; toast.onmouseleave = Swal.resumeTimer; } }) } function errorToast(message) { Swal.fire({ toast: true, position: "top-end", showConfirmButton: false, timer: 3000, title: message, timerProgressBar: true, icon: "error", didOpen: (toast) => { toast.onmouseenter = Swal.stopTimer; toast.onmouseleave = Swal.resumeTimer; } }) } function infoToast(message) { Swal.fire({ toast: true, position: "top-end", showConfirmButton: false, timer: 3000, title: message, timerProgressBar: true, icon: "info", didOpen: (toast) => { toast.onmouseenter = Swal.stopTimer; toast.onmouseleave = Swal.resumeTimer; } }) } function warnToast(message) { Swal.fire({ toast: true, position: "top-end", showConfirmButton: false, timer: 3000, title: message, timerProgressBar: true, icon: "warning", didOpen: (toast) => { toast.onmouseenter = Swal.stopTimer; toast.onmouseleave = Swal.resumeTimer; } }) } function viewVehicle(vehicleId) { window.location.href = `/Vehicle/Index?vehicleId=${vehicleId}`; } function saveVehicle(isEdit) { var vehicleId = getVehicleModelData().id; var vehicleYear = $("#inputYear").val(); var vehicleMake = $("#inputMake").val(); var vehicleModel = $("#inputModel").val(); var vehicleTags = $("#inputTag").val(); var vehiclePurchaseDate = $("#inputPurchaseDate").val(); var vehicleSoldDate = $("#inputSoldDate").val(); var vehicleLicensePlate = $("#inputLicensePlate").val(); var vehicleIsElectric = $("#inputFuelType").val() == 'Electric'; var vehicleIsDiesel = $("#inputFuelType").val() == 'Diesel'; var vehicleUseHours = $("#inputUseHours").is(":checked"); var vehicleOdometerOptional = $("#inputOdometerOptional").is(":checked"); var vehicleHasOdometerAdjustment = $("#inputHasOdometerAdjustment").is(':checked'); var vehicleOdometerMultiplier = $("#inputOdometerMultiplier").val(); var vehicleOdometerDifference = parseInt(globalParseFloat($("#inputOdometerDifference").val())).toString(); var vehiclePurchasePrice = $("#inputPurchasePrice").val(); var vehicleSoldPrice = $("#inputSoldPrice").val(); var vehicleIdentifier = $("#inputIdentifier").val(); var vehicleDashboardMetrics = $("#collapseMetricInfo :checked").map(function () { return this.value; }).toArray(); var extraFields = getAndValidateExtraFields(); //validate var hasError = false; if (extraFields.hasError) { hasError = true; } if (vehicleYear.trim() == '' || parseInt(vehicleYear) < 1900) { hasError = true; $("#inputYear").addClass("is-invalid"); } else { $("#inputYear").removeClass("is-invalid"); } if (vehicleMake.trim() == '') { hasError = true; $("#inputMake").addClass("is-invalid"); } else { $("#inputMake").removeClass("is-invalid"); } if (vehicleModel.trim() == '') { hasError = true; $("#inputModel").addClass("is-invalid"); } else { $("#inputModel").removeClass("is-invalid"); } if (vehicleIdentifier == "LicensePlate") { if (vehicleLicensePlate.trim() == '') { hasError = true; $("#inputLicensePlate").addClass("is-invalid"); } else { $("#inputLicensePlate").removeClass("is-invalid"); } } else { $("#inputLicensePlate").removeClass("is-invalid"); //check if extra fields have value. var vehicleIdentifierExtraField = extraFields.extraFields.filter(x => x.name == vehicleIdentifier); //check if extra field exists. if (vehicleIdentifierExtraField.length == 0) { $(".modal.fade.show").find(`.extra-field [placeholder='${vehicleIdentifier}']`).addClass("is-invalid"); hasError = true; } else { $(".modal.fade.show").find(`.extra-field [placeholder='${vehicleIdentifier}']`).removeClass("is-invalid"); } } if (vehicleHasOdometerAdjustment) { //validate odometer adjustments //validate multiplier if (vehicleOdometerMultiplier.trim() == '' || !isValidMoney(vehicleOdometerMultiplier)) { hasError = true; $("#inputOdometerMultiplier").addClass("is-invalid"); } else { $("#inputOdometerMultiplier").removeClass("is-invalid"); } //validate difference if (vehicleOdometerDifference.trim() == '' || isNaN(vehicleOdometerDifference)) { hasError = true; $("#inputOdometerDifference").addClass("is-invalid"); } else { $("#inputOdometerDifference").removeClass("is-invalid"); } } if (vehiclePurchasePrice.trim() != '' && !isValidMoney(vehiclePurchasePrice)) { hasError = true; $("#inputPurchasePrice").addClass("is-invalid"); $("#collapsePurchaseInfo").collapse('show'); } else { $("#inputPurchasePrice").removeClass("is-invalid"); } if (vehicleSoldPrice.trim() != '' && !isValidMoney(vehicleSoldPrice)) { hasError = true; $("#inputSoldPrice").addClass("is-invalid"); $("#collapsePurchaseInfo").collapse('show'); } else { $("#inputSoldPrice").removeClass("is-invalid"); } if (hasError) { return; } $.post('/Vehicle/SaveVehicle', { id: vehicleId, imageLocation: uploadedFile, mapLocation: uploadedMap, year: vehicleYear, make: vehicleMake, model: vehicleModel, licensePlate: vehicleLicensePlate, isElectric: vehicleIsElectric, isDiesel: vehicleIsDiesel, tags: vehicleTags, useHours: vehicleUseHours, extraFields: extraFields.extraFields, purchaseDate: vehiclePurchaseDate, soldDate: vehicleSoldDate, odometerOptional: vehicleOdometerOptional, hasOdometerAdjustment: vehicleHasOdometerAdjustment, odometerMultiplier: vehicleOdometerMultiplier, odometerDifference: vehicleOdometerDifference, purchasePrice: vehiclePurchasePrice, soldPrice: vehicleSoldPrice, dashboardMetrics: vehicleDashboardMetrics, vehicleIdentifier: vehicleIdentifier }, function (data) { if (data.success) { if (!isEdit) { successToast("Vehicle Added"); hideAddVehicleModal(); loadGarage(); } else { successToast("Vehicle Updated"); hideEditVehicleModal(); viewVehicle(vehicleId); } } else { errorToast(data.message); } }); } function toggleOdometerAdjustment() { var isChecked = $("#inputHasOdometerAdjustment").is(':checked'); if (isChecked) { $("#odometerAdjustments").collapse('show'); } else { $("#odometerAdjustments").collapse('hide'); } } function setUploadedFile(data) { uploadedFile = data; } function setUploadedMap(data) { uploadedMap = data; } function handleVehicleMapCheckChanged() { let vehicleHasMap = $("#inputHasVehicleMap").is(":checked"); if (vehicleHasMap) { $("#inputMap").off('cancel').on('cancel', function () { $("#inputHasVehicleMap").prop('checked', false); }); $("#inputMap").trigger('click'); } else { uploadedMap = ''; } } function uploadMap(event) { let selectedMapFile = event.files[0]; uploadFileAsync(selectedMapFile, setUploadedMap); } function uploadThumbnail(event) { var originalImage = event.files[0]; var maxHeight = 290; try { //load image and perform Hermite resize var img = new Image(); img.onload = function () { URL.revokeObjectURL(img.src); var imgWidth = img.width; var imgHeight = img.height; if (imgHeight > maxHeight) { //only scale if height is greater than threshold var imgScale = maxHeight / imgHeight; var newImgWidth = imgWidth * imgScale; var newImgHeight = imgHeight * imgScale; var resizedCanvas = hermiteResize(img, newImgWidth, newImgHeight); resizedCanvas.toBlob((blob) => { let file = new File([blob], originalImage.name, { type: "image/jpeg" }); uploadFileAsync(file, setUploadedFile); }, 'image/jpeg'); } else { uploadFileAsync(originalImage, setUploadedFile); } } img.src = URL.createObjectURL(originalImage); } catch (error) { console.log(`Error while attempting to upload and resize thumbnail - ${error}`); uploadFileAsync(originalImage, setUploadedFile); } } //Resize method using Hermite interpolation //JS implementation by viliusle function hermiteResize(origImg, width, height) { var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = origImg.width; canvas.height = origImg.height; ctx.drawImage(origImg, 0, 0); var width_source = canvas.width; var height_source = canvas.height; width = Math.round(width); height = Math.round(height); var ratio_w = width_source / width; var ratio_h = height_source / height; var ratio_w_half = Math.ceil(ratio_w / 2); var ratio_h_half = Math.ceil(ratio_h / 2); var img = ctx.getImageData(0, 0, width_source, height_source); var img2 = ctx.createImageData(width, height); var data = img.data; var data2 = img2.data; for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { var x2 = (i + j * width) * 4; var weight = 0; var weights = 0; var weights_alpha = 0; var gx_r = 0; var gx_g = 0; var gx_b = 0; var gx_a = 0; var center_y = (j + 0.5) * ratio_h; var yy_start = Math.floor(j * ratio_h); var yy_stop = Math.ceil((j + 1) * ratio_h); for (var yy = yy_start; yy < yy_stop; yy++) { var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; var center_x = (i + 0.5) * ratio_w; var w0 = dy * dy; //pre-calc part of w var xx_start = Math.floor(i * ratio_w); var xx_stop = Math.ceil((i + 1) * ratio_w); for (var xx = xx_start; xx < xx_stop; xx++) { var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; var w = Math.sqrt(w0 + dx * dx); if (w >= 1) { //pixel too far continue; } //hermite filter weight = 2 * w * w * w - 3 * w * w + 1; var pos_x = 4 * (xx + yy * width_source); //alpha gx_a += weight * data[pos_x + 3]; weights_alpha += weight; //colors if (data[pos_x + 3] < 255) weight = weight * data[pos_x + 3] / 250; gx_r += weight * data[pos_x]; gx_g += weight * data[pos_x + 1]; gx_b += weight * data[pos_x + 2]; weights += weight; } } data2[x2] = gx_r / weights; data2[x2 + 1] = gx_g / weights; data2[x2 + 2] = gx_b / weights; data2[x2 + 3] = gx_a / weights_alpha; } } //clear and resize canvas canvas.width = width; canvas.height = height; ctx.clearRect(0, 0, width, height); //draw ctx.putImageData(img2, 0, 0); return canvas; } function uploadFileAsync(event, callBack) { let formData = new FormData(); if (event.files != undefined && event.files.length > 0) { formData.append("file", event.files[0]); } else { formData.append("file", event); } sloader.show(); $.ajax({ url: "/Files/HandleFileUpload", data: formData, cache: false, processData: false, contentType: false, type: 'POST', success: function (response) { sloader.hide(); if (response.trim() != '') { callBack(response); } }, error: function () { sloader.hide(); errorToast("An error has occurred, please check the file size and try again later."); } }); } function isValidMoney(input) { const euRegex = /^\$?(?=\(.*\)|[^()]*$)\(?\d{1,3}((\.\d{3}){0,8}|(\d{3}){0,8})(,\d{1,3}?)?\)?$/; const usRegex = /^\$?(?=\(.*\)|[^()]*$)\(?\d{1,3}((,\d{3}){0,8}|(\d{3}){0,8})(\.\d{1,3}?)?\)?$/; return (euRegex.test(input) || usRegex.test(input)); } function initExtraFieldDatePicker(fieldName) { let inputField = $(`#${fieldName}`); if (inputField.length > 0) { inputField.datepicker({ format: getShortDatePattern().pattern, autoclose: true, weekStart: getGlobalConfig().firstDayOfWeek }); } } function initDatePicker(input, futureOnly) { if (futureOnly) { input.datepicker({ startDate: "+0d", format: getShortDatePattern().pattern, autoclose: true, weekStart: getGlobalConfig().firstDayOfWeek }); } else { input.datepicker({ endDate: "+0d", format: getShortDatePattern().pattern, autoclose: true, weekStart: getGlobalConfig().firstDayOfWeek }); } } function initTagSelector(input, noDataList) { if (noDataList) { input.tagsinput({ useDataList: false }); } else { input.tagsinput(); } } function getAndValidateSelectedVehicle() { var selectedVehiclesArray = []; $("#vehicleSelector :checked").map(function () { selectedVehiclesArray.push(this.value); }); if (selectedVehiclesArray.length == 0) { return { hasError: true, ids: [] } } else { return { hasError: false, ids: selectedVehiclesArray } } } function showMobileNav() { $(".lubelogger-mobile-nav").addClass("lubelogger-mobile-nav-show"); //hide body scrollbar $("body").css('overflow-y', 'hidden'); $("body").css('position', 'fixed'); //iOS SafariWebKit hack fix } function hideMobileNav() { $(".lubelogger-mobile-nav").removeClass("lubelogger-mobile-nav-show"); //re-enable scrollbar. $("body").css('overflow-y', 'auto'); $("body").css('position', ''); //iOS SafariWebKit hack fix } var windowWidthForCompare = 0; function bindWindowResize() { windowWidthForCompare = window.innerWidth; $(window).on('resize', function () { if (window.innerWidth != windowWidthForCompare) { hideMobileNav(); checkNavBarOverflow(); windowWidthForCompare = window.innerWidth; } }); } function encodeHTMLInput(input) { const encoded = document.createElement('div'); encoded.innerText = input; return encoded.innerHTML; } function decodeHTMLEntities(text) { return $("") .html(text) .text(); } var debounce = null; function setDebounce(callBack) { clearTimeout(debounce); debounce = setTimeout(function () { callBack(); }, 1000); } function toggleSort(tabName, sender) { var sortColumn = sender.textContent; var sortAscIcon = ''; var sortDescIcon = ''; sender = $(sender); //order of sort - asc, desc, reset if (sender.hasClass('sort-asc')) { sender.removeClass('sort-asc'); sender.addClass('sort-desc'); sender.html(`${sortColumn}${sortDescIcon}`); sortTable(tabName, sortColumn, true); } else if (sender.hasClass('sort-desc')) { //restore table sender.removeClass('sort-desc'); sender.html(`${sortColumn}`); resetSortTable(tabName); } else { //first time sorting. //check if table was sorted before by a different column(only relevant to fuel tab) if ($("[default-sort]").length > 0 && ($(".sort-asc").length > 0 || $(".sort-desc").length > 0)) { //restore table state. resetSortTable(tabName); //reset other sorted columns if ($(".sort-asc").length > 0) { $(".sort-asc").html($(".sort-asc").html().replace(sortAscIcon, "")); $(".sort-asc").removeClass("sort-asc"); } if ($(".sort-desc").length > 0) { $(".sort-desc").html($(".sort-desc").html().replace(sortDescIcon, "")); $(".sort-desc").removeClass("sort-desc"); } } sender.addClass('sort-asc'); sender.html(`${sortColumn}${sortAscIcon}`); //append sortRowId to the table rows if nothing has been appended yet. if ($("[default-sort]").length == 0) { $(`#${tabName} table tbody tr`).map((index, elem) => { $(elem).attr("default-sort", index); }); } sortTable(tabName, sortColumn, false); } } function sortTable(tabName, columnName, desc) { //get column index. var columns = $(`#${tabName} table th`).toArray().map(x => x.innerText); var colIndex = columns.findIndex(x => x == columnName); //get row data var rowData = $(`#${tabName} table tbody tr`); var sortedRow = rowData.toArray().sort((a, b) => { var currentVal = globalParseFloat(a.children[colIndex].textContent); var nextVal = globalParseFloat(b.children[colIndex].textContent); if (desc) { return nextVal - currentVal; } else { return currentVal - nextVal; } }); $(`#${tabName} table tbody`).html(sortedRow); } function resetSortTable(tabName) { var rowData = $(`#${tabName} table tbody tr`); var sortedRow = rowData.toArray().sort((a, b) => { var currentVal = $(a).attr('default-sort'); var nextVal = $(b).attr('default-sort'); return currentVal - nextVal; }); $(`#${tabName} table tbody`).html(sortedRow); } function filterTable(tabName, sender) { var rowData = $(`#${tabName} table tbody tr`); if (sender == undefined) { rowData.removeClass('override-hide'); return; } var tagName = sender.textContent; //check for other applied filters if ($(sender).hasClass("bg-primary")) { rowData.removeClass('override-hide'); $(sender).removeClass('bg-primary'); $(sender).addClass('bg-secondary'); updateAggregateLabels(); } else { //hide table rows. rowData.addClass('override-hide'); $(`[data-tags~='${tagName}']`).removeClass('override-hide'); updateAggregateLabels(); if ($(".tagfilter.bg-primary").length > 0) { //disabling other filters $(".tagfilter.bg-primary").addClass('bg-secondary'); $(".tagfilter.bg-primary").removeClass('bg-primary'); } $(sender).addClass('bg-primary'); $(sender).removeClass('bg-secondary'); } } function updateAggregateLabels() { //Sum var sumLabel = $("[data-aggregate-type='sum']"); if (sumLabel.length > 0) { var labelsToSum = $("[data-record-type='cost']").parent(":not('.override-hide')").children("[data-record-type='cost']").toArray(); var newSum = "0.00"; if (labelsToSum.length > 0) { newSum = labelsToSum.map(x => globalParseFloat(x.textContent)).reduce((a, b,) => a + b).toFixed(2); } sumLabel.text(`${sumLabel.text().split(':')[0]}: ${globalAppendCurrency(globalFloatToString(newSum))}`) } //Sum Distance var sumDistanceLabel = $("[data-aggregate-type='sum-distance']"); if (sumDistanceLabel.length > 0) { var distanceLabelsToSum = $("[data-record-type='distance']").parent(":not('.override-hide')").children("[data-record-type='distance']").toArray(); var newDistanceSum = 0; if (distanceLabelsToSum.length > 0) { newDistanceSum = distanceLabelsToSum.map(x => globalParseFloat(x.textContent)).reduce((a, b,) => a + b).toFixed(0); } sumDistanceLabel.text(`${sumDistanceLabel.text().split(':')[0]}: ${newDistanceSum}`) } //Count var newCount = $("[data-record-type='cost']").parent(":not('.override-hide')").length; var countLabel = $("[data-aggregate-type='count']"); countLabel.text(`${countLabel.text().split(':')[0]}: ${newCount}`); } function uploadVehicleFilesAsync(event) { let formData = new FormData(); var files = event.files; for (var x = 0; x < files.length; x++) { formData.append("file", files[x]); } sloader.show(); $.ajax({ url: "/Files/HandleMultipleFileUpload", data: formData, cache: false, processData: false, contentType: false, type: 'POST', success: function (response) { sloader.hide(); $(event).val(""); //clear out the filename from the uploader if (response.length > 0) { uploadedFiles.push.apply(uploadedFiles, response); $.post('/Vehicle/GetFilesPendingUpload', { uploadedFiles: uploadedFiles }, function (viewData) { $("#filesPendingUpload").html(viewData); }); } }, error: function () { sloader.hide(); errorToast("An error has occurred, please check the file size and try again later.") } }); } function uploadVehicleLinksAsync(event) { event.stopPropagation(); Swal.fire({ title: 'Add Link', html: ` `, confirmButtonText: 'Add Link', focusConfirm: false, preConfirm: () => { const newLinkName = $("#newLinkName").val(); const newLinkLocation = $("#newLinkLocation").val(); if (!newLinkName) { Swal.showValidationMessage(`Please enter a valid link name`); } if (!newLinkLocation) { Swal.showValidationMessage(`Please enter a valid link location`); } return { newLinkName, newLinkLocation } }, }).then(function (result) { if (result.isConfirmed) { uploadedFiles.push({ name: result.value.newLinkName, location: result.value.newLinkLocation, isPending: true }); $.post('/Vehicle/GetFilesPendingUpload', { uploadedFiles: uploadedFiles }, function (viewData) { $("#filesPendingUpload").html(viewData); }); } }); } function deleteFileFromUploadedFiles(fileLocation, event) { event.parentElement.parentElement.parentElement.remove(); uploadedFiles = uploadedFiles.filter(x => x.location != fileLocation); if ($("#documentsPendingUploadList > li").length == 0) { $("#documentsPendingUploadLabel").text(""); } if ($("#uploadedDocumentsList > li").length == 0) { $("#uploadedDocumentsLabel").text(""); } } function editFileName(fileLocation, event) { let currentFileName = $(event.parentElement.parentElement).find('a > .text-link').text(); Swal.fire({ title: 'Rename File or Link', html: ` `, confirmButtonText: 'Rename', focusConfirm: false, preConfirm: () => { const newFileName = $("#newFileName").val(); if (!newFileName) { Swal.showValidationMessage(`Please enter a valid name`) } return { newFileName } }, }).then(function (result) { if (result.isConfirmed) { var linkDisplayObject = $(event.parentElement.parentElement).find('a > .text-link'); linkDisplayObject.text(result.value.newFileName); var editFileIndex = uploadedFiles.findIndex(x => x.location == fileLocation); uploadedFiles[editFileIndex].name = result.value.newFileName; } }); } var scrollPosition = 0; var savedTag = ''; function saveScrollPosition() { scrollPosition = $(".vehicleDetailTabContainer").scrollTop(); let selectedTagElem = $(".tagfilter.bg-primary"); if (selectedTagElem.length > 0) { savedTag = selectedTagElem.text(); } } function restoreScrollPosition() { $(".vehicleDetailTabContainer").scrollTop(scrollPosition); scrollPosition = 0; if (savedTag != '') { let availableTagElem = $(".tagfilter").filter((index, elem) => $(elem).text() == savedTag); if (availableTagElem.length > 0) { availableTagElem.trigger('click'); } savedTag = ''; } } function toggleMarkDownOverlay(textAreaName) { var textArea = $(`#${textAreaName}`); if ($(".markdown-overlay").length > 0) { $(".markdown-overlay").remove(); return; } var text = textArea.val(); if (text == undefined) { return; } if (text.length > 0) { var formatted = markdown(text); //var overlay div var overlayDiv = `
`; textArea.parent().children(`label[for=${textAreaName}]`).append(overlayDiv); } } function setMarkDownStickerNotes() { var stickerContainers = $(".stickerNote"); if (stickerContainers.length > 0) { stickerContainers.map((index, elem) => { let originalStickerNote = $(elem).html().trim(); let markDownStickerNote = markdown(originalStickerNote); $(elem).html(markDownStickerNote); }); } } function showLinks(e) { var textAreaName = $(e.parentElement).attr("for"); toggleMarkDownOverlay(textAreaName); } function printTab() { setTimeout(function () { window.print(); }, 500); } function printContainer(htmlData) { $(".vehicleDetailTabContainer").addClass("hideOnPrint"); $(".stickerPrintContainer").addClass("showOnPrint"); $(".stickerPrintContainer").removeClass("hideOnPrint"); $(".stickerPrintContainer").html(htmlData); setTimeout(function () { window.print(); setTimeout(function () { $(".stickerPrintContainer").removeClass("showOnPrint"); $(".stickerPrintContainer").addClass("hideOnPrint"); $(".vehicleDetailTabContainer").removeClass("hideOnPrint"); $(".stickerPrintContainer").html(""); }, 1000); }, 500); } function printTabStickers(ids, source) { var vehicleId = GetVehicleId().vehicleId; $.post('/Vehicle/PrintRecordStickers', { vehicleId: vehicleId, recordIds: ids, importMode: source }, function (data) { if (isOperationResponse(data)) { return; } else if (data) { printContainer(data); } }) } function exportVehicleData(mode) { var vehicleId = GetVehicleId().vehicleId; $.get('/Vehicle/ExportFromVehicleToCsv', { vehicleId: vehicleId, mode: mode }, function (data) { if (isOperationResponse(data)) { return; } else if (data) { window.location.href = data; } }); } function showBulkImportModal(mode) { $.get(`/Vehicle/GetBulkImportModalPartialView?mode=${mode}`, function (data) { if (data) { $("#bulkImportModalContent").html(data); $("#bulkImportModal").modal('show'); } }) } function hideBulkImportModal() { $("#bulkImportModal").modal('hide'); } function getAndValidateExtraFields() { var hasError = false; var outputData = []; //get extra fields in modal that is currently open. var extraFieldsVisible = $(".modal.fade.show").find(".extra-field"); extraFieldsVisible.map((index, elem) => { var extraFieldName = $(elem).children("label").text(); var extraFieldInput = $(elem).find("input"); var extraFieldValue = extraFieldInput.val(); var extraFieldIsRequired = extraFieldInput.hasClass('extra-field-required'); if (extraFieldIsRequired && extraFieldValue.trim() == '') { hasError = true; extraFieldInput.addClass("is-invalid"); } else { extraFieldInput.removeClass("is-invalid"); } //only push fields with value in them if (extraFieldValue.trim() != '') { outputData.push({ name: extraFieldName, value: extraFieldValue, isRequired: extraFieldIsRequired }); } }); return { hasError: hasError, extraFields: outputData }; } function toggleSupplyUsageHistory() { var container = $("#supplyUsageHistoryModalContainer"); if (container.hasClass("d-none")) { container.removeClass("d-none"); } else { container.addClass("d-none"); } } function moveRecords(ids, source, dest) { if (ids.length == 0) { return; } $("#workAroundInput").show(); var friendlySource = ""; var friendlyDest = ""; var refreshDataCallBack; var recordVerbiage = ids.length > 1 ? `these ${ids.length} records` : "this record"; switch (source) { case "ServiceRecord": friendlySource = "Service Records"; refreshDataCallBack = getVehicleServiceRecords; break; case "RepairRecord": friendlySource = "Repairs"; refreshDataCallBack = getVehicleCollisionRecords; break; case "UpgradeRecord": friendlySource = "Upgrades"; refreshDataCallBack = getVehicleUpgradeRecords; break; } switch (dest) { case "ServiceRecord": friendlyDest = "Service Records"; break; case "RepairRecord": friendlyDest = "Repairs"; break; case "UpgradeRecord": friendlyDest = "Upgrades"; break; } Swal.fire({ title: "Confirm Move?", text: `Move ${recordVerbiage} from ${friendlySource} to ${friendlyDest}?`, showCancelButton: true, confirmButtonText: "Move", confirmButtonColor: "#dc3545" }).then((result) => { if (result.isConfirmed) { $.post('/Vehicle/MoveRecords', { recordIds: ids, source: source, destination: dest }, function (data) { if (data.success) { successToast(`${ids.length} Record(s) Moved`); var vehicleId = GetVehicleId().vehicleId; refreshDataCallBack(vehicleId); } else { errorToast(data.message); $("#workAroundInput").hide(); } }); } else { $("#workAroundInput").hide(); } }); } function deleteRecords(ids, source) { if (ids.length == 0) { return; } $("#workAroundInput").show(); var friendlySource = ""; var refreshDataCallBack; var recordVerbiage = ids.length > 1 ? `these ${ids.length} records` : "this record"; switch (source) { case "ServiceRecord": friendlySource = "Service Records"; refreshDataCallBack = getVehicleServiceRecords; break; case "RepairRecord": friendlySource = "Repairs"; refreshDataCallBack = getVehicleCollisionRecords; break; case "UpgradeRecord": friendlySource = "Upgrades"; refreshDataCallBack = getVehicleUpgradeRecords; break; case "TaxRecord": friendlySource = "Taxes"; refreshDataCallBack = getVehicleTaxRecords; break; case "SupplyRecord": friendlySource = "Supplies"; refreshDataCallBack = getVehicleSupplyRecords; break; case "NoteRecord": friendlySource = "Notes"; refreshDataCallBack = getVehicleNotes; break; case "OdometerRecord": friendlySource = "Odometer Records"; refreshDataCallBack = getVehicleOdometerRecords; break; case "ReminderRecord": friendlySource = "Reminders"; refreshDataCallBack = getVehicleReminders; break; case "GasRecord": friendlySource = "Fuel Records"; refreshDataCallBack = getVehicleGasRecords; break; case "InspectionRecord": friendlySource = "Inspection Records"; refreshDataCallBack = getVehicleInspectionRecords; } Swal.fire({ title: "Confirm Delete?", text: `Delete ${recordVerbiage} from ${friendlySource}?`, showCancelButton: true, confirmButtonText: "Delete", confirmButtonColor: "#dc3545" }).then((result) => { if (result.isConfirmed) { $.post('/Vehicle/DeleteRecords', { recordIds: ids, importMode: source }, function (data) { if (data.success) { successToast(`${ids.length} Record(s) Deleted`); var vehicleId = GetVehicleId().vehicleId; refreshDataCallBack(vehicleId); } else { errorToast(data.message); $("#workAroundInput").hide(); } }); } else { $("#workAroundInput").hide(); } }); } function duplicateRecords(ids, source) { if (ids.length == 0) { return; } $("#workAroundInput").show(); var friendlySource = ""; var refreshDataCallBack; var recordVerbiage = ids.length > 1 ? `these ${ids.length} records` : "this record"; switch (source) { case "ServiceRecord": friendlySource = "Service Records"; refreshDataCallBack = getVehicleServiceRecords; break; case "RepairRecord": friendlySource = "Repairs"; refreshDataCallBack = getVehicleCollisionRecords; break; case "UpgradeRecord": friendlySource = "Upgrades"; refreshDataCallBack = getVehicleUpgradeRecords; break; case "TaxRecord": friendlySource = "Taxes"; refreshDataCallBack = getVehicleTaxRecords; break; case "SupplyRecord": friendlySource = "Supplies"; refreshDataCallBack = getVehicleSupplyRecords; break; case "NoteRecord": friendlySource = "Notes"; refreshDataCallBack = getVehicleNotes; break; case "OdometerRecord": friendlySource = "Odometer Records"; refreshDataCallBack = getVehicleOdometerRecords; break; case "ReminderRecord": friendlySource = "Reminders"; refreshDataCallBack = getVehicleReminders; break; case "GasRecord": friendlySource = "Fuel Records"; refreshDataCallBack = getVehicleGasRecords; break; case "PlanRecord": friendlySource = "Plan"; refreshDataCallBack = getVehiclePlanRecords; break; case "InspectionRecord": friendlySource = "Inspection Record"; refreshDataCallBack = hideInspectionRecordTemplateModal; } Swal.fire({ title: "Confirm Duplicate?", text: `Duplicate ${recordVerbiage}?`, showCancelButton: true, confirmButtonText: "Duplicate", confirmButtonColor: "#dc3545" }).then((result) => { if (result.isConfirmed) { $.post('/Vehicle/DuplicateRecords', { recordIds: ids, importMode: source }, function (data) { if (data.success) { successToast(`${ids.length} Record(s) Duplicated`); var vehicleId = GetVehicleId().vehicleId; refreshDataCallBack(vehicleId); } else { errorToast(data.message); $("#workAroundInput").hide(); } }); } else { $("#workAroundInput").hide(); } }); } function duplicateRecordsToOtherVehicles(ids, source) { if (ids.length == 0) { return; } $("#workAroundInput").show(); var friendlySource = ""; var refreshDataCallBack; var recordVerbiage = ids.length > 1 ? `these ${ids.length} records` : "this record"; switch (source) { case "ServiceRecord": friendlySource = "Service Records"; refreshDataCallBack = getVehicleServiceRecords; break; case "RepairRecord": friendlySource = "Repairs"; refreshDataCallBack = getVehicleCollisionRecords; break; case "UpgradeRecord": friendlySource = "Upgrades"; refreshDataCallBack = getVehicleUpgradeRecords; break; case "TaxRecord": friendlySource = "Taxes"; refreshDataCallBack = getVehicleTaxRecords; break; case "SupplyRecord": friendlySource = "Supplies"; refreshDataCallBack = getVehicleSupplyRecords; break; case "NoteRecord": friendlySource = "Notes"; refreshDataCallBack = getVehicleNotes; break; case "OdometerRecord": friendlySource = "Odometer Records"; refreshDataCallBack = getVehicleOdometerRecords; break; case "ReminderRecord": friendlySource = "Reminders"; refreshDataCallBack = getVehicleReminders; break; case "GasRecord": friendlySource = "Fuel Records"; refreshDataCallBack = getVehicleGasRecords; break; case "PlanRecord": friendlySource = "Plan"; refreshDataCallBack = getVehiclePlanRecords; break; case "InspectionRecord": friendlySource = "Inspection Record"; refreshDataCallBack = hideInspectionRecordTemplateModal; } $.get(`/Home/GetVehicleSelector?vehicleId=${GetVehicleId().vehicleId}`, function (data) { if (data) { //prompt user to select a vehicle Swal.fire({ title: 'Duplicate to Vehicle(s)', html: data, confirmButtonText: 'Duplicate', focusConfirm: false, preConfirm: () => { //validate var selectedVehicleData = getAndValidateSelectedVehicle(); if (selectedVehicleData.hasError) { Swal.showValidationMessage(`You must select a vehicle`); } return { selectedVehicleData } }, }).then(function (result) { if (result.isConfirmed) { $.post('/Vehicle/DuplicateRecordsToOtherVehicles', { recordIds: ids, vehicleIds: result.value.selectedVehicleData.ids, importMode: source}, function (data) { if (data.success) { successToast(`${ids.length} Record(s) Duplicated`); } else { errorToast(data.message); } }); } }); } else { errorToast(genericErrorMessage()); } }) } function insertOdometer(ids, source) { if (ids.length == 0) { return; } $("#workAroundInput").show(); var friendlySource = ""; var refreshDataCallBack; var recordVerbiage = ids.length > 1 ? `these ${ids.length} records` : "this record"; switch (source) { case "ServiceRecord": friendlySource = "Service Records"; refreshDataCallBack = getVehicleServiceRecords; break; case "RepairRecord": friendlySource = "Repairs"; refreshDataCallBack = getVehicleCollisionRecords; break; case "UpgradeRecord": friendlySource = "Upgrades"; refreshDataCallBack = getVehicleUpgradeRecords; break; case "GasRecord": friendlySource = "Fuel Records"; refreshDataCallBack = getVehicleGasRecords; break; } Swal.fire({ title: "Create Odometer Records?", text: `Create Odometer Records based on ${recordVerbiage}?`, showCancelButton: true, confirmButtonText: "Create", confirmButtonColor: "#dc3545" }).then((result) => { if (result.isConfirmed) { $.post('/Vehicle/BulkCreateOdometerRecords', { recordIds: ids, importMode: source }, function (data) { if (data.success) { successToast(`${ids.length} Odometer Record(s) Created`); var vehicleId = GetVehicleId().vehicleId; refreshDataCallBack(vehicleId); } else { errorToast(data.message); $("#workAroundInput").hide(); } }); } else { $("#workAroundInput").hide(); } }); } var selectedRow = []; var isDragging = false; $(window).on('mouseup', function (e) { rangeMouseUp(e); }); $(window).on('mousedown', function (e) { rangeMouseDown(e); }); $(window).on('keydown', function (e) { var userOnInput = $(e.target).is("input") || $(e.target).is("textarea"); if (!userOnInput) { if ((e.ctrlKey || e.metaKey) && e.which == 65) { e.preventDefault(); e.stopPropagation(); selectAllRows(); selectAllVehicles(); } } }); function getCurrentTab() { return $(".tab-pane.active.show").attr('id'); } function selectAllRows() { clearSelectedRows(); $('.vehicleDetailTabContainer .table tbody tr:visible').addClass('table-active'); $('.vehicleDetailTabContainer .table tbody tr:visible').map((index, elem) => { addToSelectedRows($(elem).attr('data-rowId')); }); } function selectAllVehicles() { clearSelectedVehicles(); $('.garage-item:visible').addClass('garage-active'); $('.garage-item:visible').map((index, elem) => { addToSelectedVehicles($(elem).attr('data-rowId')); }); } function rangeMouseDown(e) { if (isRightClick(e)) { return; } var contextMenuAction = $(e.target).parents(".table-context-menu > li > .dropdown-item").length > 0 || $(e.target).is(".table-context-menu > li > .dropdown-item") || $(e.target).parents(".garage-context-menu > li > .dropdown-item").length > 0 || $(e.target).is(".garage-context-menu > li > .dropdown-item"); var selectMode = $("#chkSelectMode").length > 0 ? $("#chkSelectMode").is(":checked") : false; if (!(e.ctrlKey || e.metaKey || selectMode) && !contextMenuAction) { clearSelectedRows(); clearSelectedVehicles(); } isDragging = true; document.documentElement.onselectstart = function () { return false; }; } function isRightClick(e) { if (e.which) { return (e.which == 3); } else if (e.button) { return (e.button == 2); } return false; } function stopEvent() { if (isDragging) { isDragging = false; } event.stopPropagation(); } function rangeMouseUp(e) { if (isRightClick(e)) { return; } if ($(".table-context-menu").length > 0) { $(".table-context-menu").fadeOut("fast"); } if ($(".garage-context-menu").length > 0) { $(".garage-context-menu").fadeOut("fast"); } isDragging = false; document.documentElement.onselectstart = function () { return true; }; } function rangeMouseMove(e) { if (isDragging) { if (!$(e).hasClass('table-active')) { addToSelectedRows($(e).attr('data-rowId')); $(e).addClass('table-active'); } } } function addToSelectedRows(id) { if (selectedRow.findIndex(x => x == id) == -1) { selectedRow.push(id); } } function removeFromSelectedRows(id) { var rowIndex = selectedRow.findIndex(x => x == id) if (rowIndex != -1) { selectedRow.splice(rowIndex, 1); } } function clearSelectedRows() { selectedRow = []; $('.table tr').removeClass('table-active'); } function clearSelectedVehicles() { selectedVehicles = []; $('.garage-item').removeClass('garage-active'); } function getDeviceIsTouchOnly() { if (navigator.maxTouchPoints > 0 && matchMedia('(pointer: coarse)').matches && !matchMedia('(any-pointer: fine)').matches) { return true; } else { return false; } } function showTableContextMenu(e) { if (event != undefined) { event.preventDefault(); } if (getDeviceIsTouchOnly()) { return; } if (!$(e).hasClass('table-active')) { clearSelectedRows(); addToSelectedRows($(e).attr('data-rowId')); $(e).addClass('table-active'); } $(".table-context-menu").fadeIn("fast"); determineContextMenuItems(); $(".table-context-menu").css({ left: getMenuPosition(event.clientX, 'width', 'scrollLeft'), top: getMenuPosition(event.clientY, 'height', 'scrollTop') }); } function determineContextMenuItems() { var tableRows = $('.table tbody tr:visible'); var tableRowsActive = $('.table tr.table-active'); if (tableRowsActive.length == 1) { //only one row selected $(".context-menu-active-single").show(); $(".context-menu-active-multiple").hide(); } else if (tableRowsActive.length > 1) { //multiple rows selected $(".context-menu-active-single").hide(); $(".context-menu-active-multiple").show(); } else { //nothing was selected, bug case. $(".context-menu-active-single").hide(); $(".context-menu-active-multiple").hide(); } if (tableRows.length > 1) { $(".context-menu-multiple").show(); if (tableRows.length == tableRowsActive.length) { //all rows are selected, show deselect all button. $(".context-menu-deselect-all").show(); $(".context-menu-select-all").hide(); } else if (tableRows.length != tableRowsActive.length) { //not all rows are selected, show select all button. $(".context-menu-select-all").show(); $(".context-menu-deselect-all").hide(); } } else { $(".context-menu-multiple").hide(); } if (GetVehicleId().hasOdometerAdjustment) { $(".context-menu-odometer-adjustment").show(); } else { $(".context-menu-odometer-adjustment").hide(); } } function getMenuPosition(mouse, direction, scrollDir) { var win = $(window)[direction](), scroll = $(window)[scrollDir](), menu = $(".table-context-menu")[direction](), position = mouse + scroll; // opening menu would pass the side of the page if (mouse + menu > win && menu < mouse) position -= menu; return position; } function getGarageMenuPosition(mouse, direction, scrollDir) { var win = $(window)[direction](), scroll = $(window)[scrollDir](), menu = $(".garage-context-menu")[direction](), position = mouse + scroll; // opening menu would pass the side of the page if (mouse + menu > win && menu < mouse) position -= menu; return position; } function handleTableRowClick(e, callBack, rowId) { var selectMode = $("#chkSelectMode").length > 0 ? $("#chkSelectMode").is(":checked") : false; if (!(event.ctrlKey || event.metaKey || selectMode)) { callBack(rowId); } else if (!$(e).hasClass('table-active')) { addToSelectedRows($(e).attr('data-rowId')); $(e).addClass('table-active'); } else if ($(e).hasClass('table-active')) { removeFromSelectedRows($(e).attr('data-rowId')); $(e).removeClass('table-active'); } } function showTableContextMenuForMobile(e, xPosition, yPosition) { if (!$(e).hasClass('table-active')) { addToSelectedRows($(e).attr('data-rowId')); $(e).addClass('table-active'); shakeTableRow(e); } else { $(".table-context-menu").fadeIn("fast"); determineContextMenuItems(); $(".table-context-menu").css({ left: getMenuPosition(xPosition, 'width', 'scrollLeft'), top: getMenuPosition(yPosition, 'height', 'scrollTop') }); } } function shakeTableRow(e) { $(e).addClass('tablerow-shake'); setTimeout(function () { $(e).removeClass('tablerow-shake'); }, 1200) } var rowTouchTimer; var rowTouchDuration = 800; function detectRowLongTouch(sender) { var touchX = event.touches[0].clientX; var touchY = event.touches[0].clientY; if (!rowTouchTimer) { rowTouchTimer = setTimeout(function () { showTableContextMenuForMobile(sender, touchX, touchY); detectRowTouchEndPremature(sender); }, rowTouchDuration); } } function detectRowTouchEndPremature(sender) { if (rowTouchTimer) { clearTimeout(rowTouchTimer); rowTouchTimer = null; } } function handleSupplyAddCostKeyDown(event) { handleSwalEnter(event); interceptDecimalKeys(event); } function replenishSupplies() { Swal.fire({ title: 'Replenish Supplies', html: `