/* Minification failed. Returning unminified contents.
(1,9): run-time error CSS1031: Expected selector, found '='
(1,9): run-time error CSS1025: Expected comma or open brace, found '='
(2,15): run-time error CSS1031: Expected selector, found '='
(2,15): run-time error CSS1025: Expected comma or open brace, found '='
(4,1): run-time error CSS1019: Unexpected token, found '$'
(4,2): run-time error CSS1019: Unexpected token, found '('
(4,11): run-time error CSS1031: Expected selector, found ')'
(4,11): run-time error CSS1025: Expected comma or open brace, found ')'
(14,2): run-time error CSS1019: Unexpected token, found ')'
(16,10): run-time error CSS1031: Expected selector, found 'initPanelTitle('
(16,10): run-time error CSS1025: Expected comma or open brace, found 'initPanelTitle('
(57,10): run-time error CSS1031: Expected selector, found 'initFlaggedNotes('
(57,10): run-time error CSS1025: Expected comma or open brace, found 'initFlaggedNotes('
(102,10): run-time error CSS1031: Expected selector, found 'initPinnedState('
(102,10): run-time error CSS1025: Expected comma or open brace, found 'initPinnedState('
(116,10): run-time error CSS1031: Expected selector, found 'setPinned('
(116,10): run-time error CSS1025: Expected comma or open brace, found 'setPinned('
(119,10): run-time error CSS1031: Expected selector, found 'initTooltips('
(119,10): run-time error CSS1025: Expected comma or open brace, found 'initTooltips('
(124,10): run-time error CSS1031: Expected selector, found 'initScrollTop('
(124,10): run-time error CSS1025: Expected comma or open brace, found 'initScrollTop('
(128,10): run-time error CSS1031: Expected selector, found 'showScrollTop('
(128,10): run-time error CSS1025: Expected comma or open brace, found 'showScrollTop('
(144,10): run-time error CSS1031: Expected selector, found 'scrollToTop('
(144,10): run-time error CSS1025: Expected comma or open brace, found 'scrollToTop('
(150,10): run-time error CSS1031: Expected selector, found 'showDashboard('
(150,10): run-time error CSS1025: Expected comma or open brace, found 'showDashboard('
(158,10): run-time error CSS1031: Expected selector, found 'toggleDashboard('
(158,10): run-time error CSS1025: Expected comma or open brace, found 'toggleDashboard('
(165,10): run-time error CSS1031: Expected selector, found 'showDivFromCookieSetting('
(165,10): run-time error CSS1025: Expected comma or open brace, found 'showDivFromCookieSetting('
(183,10): run-time error CSS1031: Expected selector, found 'toggleDivFromCookieSetting('
(183,10): run-time error CSS1025: Expected comma or open brace, found 'toggleDivFromCookieSetting('
(196,10): run-time error CSS1031: Expected selector, found 'setCookie('
(196,10): run-time error CSS1025: Expected comma or open brace, found 'setCookie('
(204,10): run-time error CSS1031: Expected selector, found 'getCookie('
(204,10): run-time error CSS1025: Expected comma or open brace, found 'getCookie('
(223,10): run-time error CSS1031: Expected selector, found 'allowDrop('
(223,10): run-time error CSS1025: Expected comma or open brace, found 'allowDrop('
(227,10): run-time error CSS1031: Expected selector, found 'itemRedirect('
(227,10): run-time error CSS1025: Expected comma or open brace, found 'itemRedirect('
(233,10): run-time error CSS1031: Expected selector, found 'navigateToPage('
(233,10): run-time error CSS1025: Expected comma or open brace, found 'navigateToPage('
(237,10): run-time error CSS1031: Expected selector, found 'reloadPage('
(237,10): run-time error CSS1025: Expected comma or open brace, found 'reloadPage('
(246,10): run-time error CSS1031: Expected selector, found 'consolelog('
(246,10): run-time error CSS1025: Expected comma or open brace, found 'consolelog('
(251,10): run-time error CSS1031: Expected selector, found 'toInt('
(251,10): run-time error CSS1025: Expected comma or open brace, found 'toInt('
(254,10): run-time error CSS1031: Expected selector, found 'toBool('
(254,10): run-time error CSS1025: Expected comma or open brace, found 'toBool('
(257,10): run-time error CSS1031: Expected selector, found 'exportExcel('
(257,10): run-time error CSS1025: Expected comma or open brace, found 'exportExcel('
(266,10): run-time error CSS1031: Expected selector, found 'printPage('
(266,10): run-time error CSS1025: Expected comma or open brace, found 'printPage('
(345,10): run-time error CSS1031: Expected selector, found 'toggleDiv('
(345,10): run-time error CSS1025: Expected comma or open brace, found 'toggleDiv('
(349,10): run-time error CSS1031: Expected selector, found 'initPrintOption('
(349,10): run-time error CSS1025: Expected comma or open brace, found 'initPrintOption('
(357,10): run-time error CSS1031: Expected selector, found 'showAddress('
(357,10): run-time error CSS1025: Expected comma or open brace, found 'showAddress('
(364,10): run-time error CSS1031: Expected selector, found '_watchItem('
(364,10): run-time error CSS1025: Expected comma or open brace, found '_watchItem('
(389,10): run-time error CSS1031: Expected selector, found 'unWatchItem('
(389,10): run-time error CSS1025: Expected comma or open brace, found 'unWatchItem('
(404,10): run-time error CSS1031: Expected selector, found 'showItemOnMap('
(404,10): run-time error CSS1025: Expected comma or open brace, found 'showItemOnMap('
(411,10): run-time error CSS1031: Expected selector, found 'addSuffix('
(411,10): run-time error CSS1025: Expected comma or open brace, found 'addSuffix('
(415,10): run-time error CSS1031: Expected selector, found 'displayFrames('
(415,10): run-time error CSS1025: Expected comma or open brace, found 'displayFrames('
(460,10): run-time error CSS1031: Expected selector, found 'logout('
(460,10): run-time error CSS1025: Expected comma or open brace, found 'logout('
(464,10): run-time error CSS1031: Expected selector, found 'showHide('
(464,10): run-time error CSS1025: Expected comma or open brace, found 'showHide('
(471,10): run-time error CSS1031: Expected selector, found 'hasContent('
(471,10): run-time error CSS1025: Expected comma or open brace, found 'hasContent('
(484,10): run-time error CSS1031: Expected selector, found 'toProperId('
(484,10): run-time error CSS1025: Expected comma or open brace, found 'toProperId('
(512,10): run-time error CSS1031: Expected selector, found 'mapUrlParams('
(512,10): run-time error CSS1025: Expected comma or open brace, found 'mapUrlParams('
(527,10): run-time error CSS1031: Expected selector, found 'formatCurrency('
(527,10): run-time error CSS1025: Expected comma or open brace, found 'formatCurrency('
(531,10): run-time error CSS1031: Expected selector, found 'showHidden('
(531,10): run-time error CSS1025: Expected comma or open brace, found 'showHidden('
(535,10): run-time error CSS1031: Expected selector, found 'setClientDebugInfo('
(535,10): run-time error CSS1025: Expected comma or open brace, found 'setClientDebugInfo('
(539,10): run-time error CSS1031: Expected selector, found 'adjustBusinessDays('
(539,10): run-time error CSS1025: Expected comma or open brace, found 'adjustBusinessDays('
(566,10): run-time error CSS1031: Expected selector, found 'scrollToChild('
(566,10): run-time error CSS1025: Expected comma or open brace, found 'scrollToChild('
(577,10): run-time error CSS1031: Expected selector, found 'enableScrollLock('
(577,10): run-time error CSS1025: Expected comma or open brace, found 'enableScrollLock('
(610,10): run-time error CSS1031: Expected selector, found 'showMore('
(610,10): run-time error CSS1025: Expected comma or open brace, found 'showMore('
(618,10): run-time error CSS1031: Expected selector, found '_showLoading('
(618,10): run-time error CSS1025: Expected comma or open brace, found '_showLoading('
(622,10): run-time error CSS1031: Expected selector, found '_hideLoading('
(622,10): run-time error CSS1025: Expected comma or open brace, found '_hideLoading('
(637,10): run-time error CSS1031: Expected selector, found '_replacePageQs('
(637,10): run-time error CSS1025: Expected comma or open brace, found '_replacePageQs('
(652,10): run-time error CSS1031: Expected selector, found 'getId('
(652,10): run-time error CSS1025: Expected comma or open brace, found 'getId('
(658,10): run-time error CSS1031: Expected selector, found '_callFailed('
(658,10): run-time error CSS1025: Expected comma or open brace, found '_callFailed('
(670,10): run-time error CSS1031: Expected selector, found 'toggleHelp('
(670,10): run-time error CSS1025: Expected comma or open brace, found 'toggleHelp('
(679,9): run-time error CSS1031: Expected selector, found '='
(679,9): run-time error CSS1025: Expected comma or open brace, found '='
(680,19): run-time error CSS1031: Expected selector, found '='
(680,19): run-time error CSS1025: Expected comma or open brace, found '='
(681,23): run-time error CSS1031: Expected selector, found '='
(681,23): run-time error CSS1025: Expected comma or open brace, found '='
(682,26): run-time error CSS1031: Expected selector, found '='
(682,26): run-time error CSS1025: Expected comma or open brace, found '='
(683,24): run-time error CSS1031: Expected selector, found '='
(683,24): run-time error CSS1025: Expected comma or open brace, found '='
(684,22): run-time error CSS1031: Expected selector, found '='
(684,22): run-time error CSS1025: Expected comma or open brace, found '='
(685,25): run-time error CSS1031: Expected selector, found '='
(685,25): run-time error CSS1025: Expected comma or open brace, found '='
(686,24): run-time error CSS1031: Expected selector, found '='
(686,24): run-time error CSS1025: Expected comma or open brace, found '='
(687,17): run-time error CSS1031: Expected selector, found '='
(687,17): run-time error CSS1025: Expected comma or open brace, found '='
(688,24): run-time error CSS1031: Expected selector, found '='
(688,24): run-time error CSS1025: Expected comma or open brace, found '='
(689,19): run-time error CSS1031: Expected selector, found '='
(689,19): run-time error CSS1025: Expected comma or open brace, found '='
(690,26): run-time error CSS1031: Expected selector, found '='
(690,26): run-time error CSS1025: Expected comma or open brace, found '='
(691,20): run-time error CSS1031: Expected selector, found '='
(691,20): run-time error CSS1025: Expected comma or open brace, found '='
(694,10): run-time error CSS1031: Expected selector, found 'initTable('
(694,10): run-time error CSS1025: Expected comma or open brace, found 'initTable('
(828,10): run-time error CSS1031: Expected selector, found '_displaySummaryValue('
(828,10): run-time error CSS1025: Expected comma or open brace, found '_displaySummaryValue('
(836,10): run-time error CSS1031: Expected selector, found 'initTableSort('
(836,10): run-time error CSS1025: Expected comma or open brace, found 'initTableSort('
(849,10): run-time error CSS1031: Expected selector, found 'firstVisibleRow('
(849,10): run-time error CSS1025: Expected comma or open brace, found 'firstVisibleRow('
(869,10): run-time error CSS1031: Expected selector, found 'sortTable('
(869,10): run-time error CSS1025: Expected comma or open brace, found 'sortTable('
(904,10): run-time error CSS1031: Expected selector, found 'moveRowsToTheTop('
(904,10): run-time error CSS1025: Expected comma or open brace, found 'moveRowsToTheTop('
(915,10): run-time error CSS1031: Expected selector, found 'moveRowsToTheBottom('
(915,10): run-time error CSS1025: Expected comma or open brace, found 'moveRowsToTheBottom('
(926,10): run-time error CSS1031: Expected selector, found 'applyTableSort('
(926,10): run-time error CSS1025: Expected comma or open brace, found 'applyTableSort('
(940,10): run-time error CSS1031: Expected selector, found 'initTableRowClick('
(940,10): run-time error CSS1025: Expected comma or open brace, found 'initTableRowClick('
(963,10): run-time error CSS1031: Expected selector, found 'initInlineTableRowClick('
(963,10): run-time error CSS1025: Expected comma or open brace, found 'initInlineTableRowClick('
(977,10): run-time error CSS1031: Expected selector, found 'initTRC('
(977,10): run-time error CSS1025: Expected comma or open brace, found 'initTRC('
(1004,10): run-time error CSS1031: Expected selector, found 'initTableFilterSearch('
(1004,10): run-time error CSS1025: Expected comma or open brace, found 'initTableFilterSearch('
(1011,10): run-time error CSS1031: Expected selector, found 'rowContainsFilter('
(1011,10): run-time error CSS1025: Expected comma or open brace, found 'rowContainsFilter('
(1028,10): run-time error CSS1031: Expected selector, found 'filterTable('
(1028,10): run-time error CSS1025: Expected comma or open brace, found 'filterTable('
(1032,10): run-time error CSS1031: Expected selector, found 'countTableRows('
(1032,10): run-time error CSS1025: Expected comma or open brace, found 'countTableRows('
(1066,10): run-time error CSS1031: Expected selector, found 'setTableLegendFromCookie('
(1066,10): run-time error CSS1025: Expected comma or open brace, found 'setTableLegendFromCookie('
(1073,10): run-time error CSS1031: Expected selector, found 'resetLegend('
(1073,10): run-time error CSS1025: Expected comma or open brace, found 'resetLegend('
(1078,10): run-time error CSS1031: Expected selector, found 'toggleLegend('
(1078,10): run-time error CSS1025: Expected comma or open brace, found 'toggleLegend('
(1091,10): run-time error CSS1031: Expected selector, found 'sort('
(1091,10): run-time error CSS1025: Expected comma or open brace, found 'sort('
(1098,10): run-time error CSS1031: Expected selector, found 'onHrefRowClick('
(1098,10): run-time error CSS1025: Expected comma or open brace, found 'onHrefRowClick('
(1114,10): run-time error CSS1031: Expected selector, found 'tableSortComparer('
(1114,10): run-time error CSS1025: Expected comma or open brace, found 'tableSortComparer('
(1120,10): run-time error CSS1031: Expected selector, found 'getSortValue('
(1120,10): run-time error CSS1025: Expected comma or open brace, found 'getSortValue('
(1124,10): run-time error CSS1031: Expected selector, found 'highlightSelectedTableRowWithId('
(1124,10): run-time error CSS1025: Expected comma or open brace, found 'highlightSelectedTableRowWithId('
(1137,10): run-time error CSS1031: Expected selector, found 'highlightSelectedTableRowWithIdAndType('
(1137,10): run-time error CSS1025: Expected comma or open brace, found 'highlightSelectedTableRowWithIdAndType('
(1149,10): run-time error CSS1031: Expected selector, found 'rowData('
(1149,10): run-time error CSS1025: Expected comma or open brace, found 'rowData('
(1154,10): run-time error CSS1031: Expected selector, found 'initRowSelect('
(1154,10): run-time error CSS1025: Expected comma or open brace, found 'initRowSelect('
(1158,10): run-time error CSS1031: Expected selector, found 'rowSelectChanged('
(1158,10): run-time error CSS1025: Expected comma or open brace, found 'rowSelectChanged('
(1174,10): run-time error CSS1031: Expected selector, found 'rowsSelectChanged('
(1174,10): run-time error CSS1025: Expected comma or open brace, found 'rowsSelectChanged('
(1181,10): run-time error CSS1031: Expected selector, found 'getSelectedRows('
(1181,10): run-time error CSS1025: Expected comma or open brace, found 'getSelectedRows('
(1204,10): run-time error CSS1031: Expected selector, found 'toggleLegendLock('
(1204,10): run-time error CSS1025: Expected comma or open brace, found 'toggleLegendLock('
(1216,10): run-time error CSS1031: Expected selector, found 'displayLegendLock('
(1216,10): run-time error CSS1025: Expected comma or open brace, found 'displayLegendLock('
(1229,10): run-time error CSS1031: Expected selector, found 'initContextMenu('
(1229,10): run-time error CSS1025: Expected comma or open brace, found 'initContextMenu('
(1301,10): run-time error CSS1031: Expected selector, found 'setRowItemValues('
(1301,10): run-time error CSS1025: Expected comma or open brace, found 'setRowItemValues('
(1320,20): run-time error CSS1031: Expected selector, found ';'
(1320,20): run-time error CSS1025: Expected comma or open brace, found ';'
(1321,14): run-time error CSS1031: Expected selector, found '='
(1321,14): run-time error CSS1025: Expected comma or open brace, found '='
(1323,4): run-time error CSS1031: Expected selector, found '('
(1323,4): run-time error CSS1025: Expected comma or open brace, found '('
(1356,10): run-time error CSS1031: Expected selector, found 'toggleMenu('
(1356,10): run-time error CSS1025: Expected comma or open brace, found 'toggleMenu('
(1388,11): run-time error CSS1031: Expected selector, found 'showAlert('
(1388,11): run-time error CSS1025: Expected comma or open brace, found 'showAlert('
(1405,10): run-time error CSS1031: Expected selector, found 'showConfirm('
(1405,10): run-time error CSS1025: Expected comma or open brace, found 'showConfirm('
(1424,10): run-time error CSS1031: Expected selector, found 'modalShow('
(1424,10): run-time error CSS1025: Expected comma or open brace, found 'modalShow('
(1454,10): run-time error CSS1031: Expected selector, found 'modalHide('
(1454,10): run-time error CSS1025: Expected comma or open brace, found 'modalHide('
(1458,10): run-time error CSS1031: Expected selector, found 'resizeModal('
(1458,10): run-time error CSS1025: Expected comma or open brace, found 'resizeModal('
(1466,10): run-time error CSS1031: Expected selector, found 'getModalCss('
(1466,10): run-time error CSS1025: Expected comma or open brace, found 'getModalCss('
(1481,10): run-time error CSS1031: Expected selector, found 'emptyItemData('
(1481,10): run-time error CSS1025: Expected comma or open brace, found 'emptyItemData('
(1489,10): run-time error CSS1031: Expected selector, found 'getItemData('
(1489,10): run-time error CSS1025: Expected comma or open brace, found 'getItemData('
(1500,10): run-time error CSS1031: Expected selector, found 'resetModals('
(1500,10): run-time error CSS1025: Expected comma or open brace, found 'resetModals('
(1540,10): run-time error CSS1031: Expected selector, found 'showModal('
(1540,10): run-time error CSS1025: Expected comma or open brace, found 'showModal('
(1565,10): run-time error CSS1031: Expected selector, found 'showAddModal('
(1565,10): run-time error CSS1025: Expected comma or open brace, found 'showAddModal('
(1602,10): run-time error CSS1031: Expected selector, found 'showAddOtherModal('
(1602,10): run-time error CSS1025: Expected comma or open brace, found 'showAddOtherModal('
(1639,10): run-time error CSS1031: Expected selector, found 'showEditModal('
(1639,10): run-time error CSS1025: Expected comma or open brace, found 'showEditModal('
(1704,10): run-time error CSS1031: Expected selector, found 'showViewEditModal('
(1704,10): run-time error CSS1025: Expected comma or open brace, found 'showViewEditModal('
(1781,10): run-time error CSS1031: Expected selector, found 'pageViewEditModal_EditMode('
(1781,10): run-time error CSS1025: Expected comma or open brace, found 'pageViewEditModal_EditMode('
(1796,10): run-time error CSS1031: Expected selector, found 'pageViewEditModal_ViewMode('
(1796,10): run-time error CSS1025: Expected comma or open brace, found 'pageViewEditModal_ViewMode('
(1811,10): run-time error CSS1031: Expected selector, found 'setModalItem('
(1811,10): run-time error CSS1025: Expected comma or open brace, found 'setModalItem('
(1826,10): run-time error CSS1031: Expected selector, found 'getModalItem('
(1826,10): run-time error CSS1025: Expected comma or open brace, found 'getModalItem('
(1847,10): run-time error CSS1031: Expected selector, found 'itemQs('
(1847,10): run-time error CSS1025: Expected comma or open brace, found 'itemQs('
(1853,10): run-time error CSS1031: Expected selector, found 'getItemTitle('
(1853,10): run-time error CSS1025: Expected comma or open brace, found 'getItemTitle('
(1857,10): run-time error CSS1031: Expected selector, found 'getItemViewUrl('
(1857,10): run-time error CSS1025: Expected comma or open brace, found 'getItemViewUrl('
(1865,10): run-time error CSS1031: Expected selector, found '_enableAddModalSave('
(1865,10): run-time error CSS1025: Expected comma or open brace, found '_enableAddModalSave('
(1868,10): run-time error CSS1031: Expected selector, found '_enableEditModalDelete('
(1868,10): run-time error CSS1025: Expected comma or open brace, found '_enableEditModalDelete('
(1872,10): run-time error CSS1031: Expected selector, found 'setPageModalError('
(1872,10): run-time error CSS1025: Expected comma or open brace, found 'setPageModalError('
(1875,10): run-time error CSS1031: Expected selector, found 'setPageModalSuccess('
(1875,10): run-time error CSS1025: Expected comma or open brace, found 'setPageModalSuccess('
(1878,10): run-time error CSS1031: Expected selector, found 'setPageAddModalSystemError('
(1878,10): run-time error CSS1025: Expected comma or open brace, found 'setPageAddModalSystemError('
(1882,10): run-time error CSS1031: Expected selector, found 'setPageAddModalError('
(1882,10): run-time error CSS1025: Expected comma or open brace, found 'setPageAddModalError('
(1886,10): run-time error CSS1031: Expected selector, found 'setPageAddModalSuccess('
(1886,10): run-time error CSS1025: Expected comma or open brace, found 'setPageAddModalSuccess('
(1889,10): run-time error CSS1031: Expected selector, found 'setPageAddOtherModalSystemError('
(1889,10): run-time error CSS1025: Expected comma or open brace, found 'setPageAddOtherModalSystemError('
(1893,10): run-time error CSS1031: Expected selector, found 'setPageAddOtherModalError('
(1893,10): run-time error CSS1025: Expected comma or open brace, found 'setPageAddOtherModalError('
(1897,10): run-time error CSS1031: Expected selector, found 'setPageAddOtherModalSuccess('
(1897,10): run-time error CSS1025: Expected comma or open brace, found 'setPageAddOtherModalSuccess('
(1901,10): run-time error CSS1031: Expected selector, found 'setPageEditModalSystemError('
(1901,10): run-time error CSS1025: Expected comma or open brace, found 'setPageEditModalSystemError('
(1904,10): run-time error CSS1031: Expected selector, found 'setPageEditModalError('
(1904,10): run-time error CSS1025: Expected comma or open brace, found 'setPageEditModalError('
(1907,10): run-time error CSS1031: Expected selector, found 'setPageEditModalSuccess('
(1907,10): run-time error CSS1025: Expected comma or open brace, found 'setPageEditModalSuccess('
(1910,10): run-time error CSS1031: Expected selector, found 'setPageEditModalConfirm('
(1910,10): run-time error CSS1025: Expected comma or open brace, found 'setPageEditModalConfirm('
(1913,10): run-time error CSS1031: Expected selector, found 'setPageViewEditModalSystemError('
(1913,10): run-time error CSS1025: Expected comma or open brace, found 'setPageViewEditModalSystemError('
(1916,10): run-time error CSS1031: Expected selector, found 'setPageViewEditModalError('
(1916,10): run-time error CSS1025: Expected comma or open brace, found 'setPageViewEditModalError('
(1919,10): run-time error CSS1031: Expected selector, found 'setPageViewEditModalConfirm('
(1919,10): run-time error CSS1025: Expected comma or open brace, found 'setPageViewEditModalConfirm('
(1922,10): run-time error CSS1031: Expected selector, found 'setPageViewEditModalSuccess('
(1922,10): run-time error CSS1025: Expected comma or open brace, found 'setPageViewEditModalSuccess('
(1925,10): run-time error CSS1031: Expected selector, found 'setModalError('
(1925,10): run-time error CSS1025: Expected comma or open brace, found 'setModalError('
(1930,10): run-time error CSS1031: Expected selector, found 'setModalConfirm('
(1930,10): run-time error CSS1025: Expected comma or open brace, found 'setModalConfirm('
(1935,10): run-time error CSS1031: Expected selector, found 'setModalSuccess('
(1935,10): run-time error CSS1025: Expected comma or open brace, found 'setModalSuccess('
(1941,10): run-time error CSS1031: Expected selector, found 'setModalAccess('
(1941,10): run-time error CSS1025: Expected comma or open brace, found 'setModalAccess('
(1951,10): run-time error CSS1031: Expected selector, found 'dismissPageModal('
(1951,10): run-time error CSS1025: Expected comma or open brace, found 'dismissPageModal('
(1955,10): run-time error CSS1031: Expected selector, found 'dismissPageAddModal('
(1955,10): run-time error CSS1025: Expected comma or open brace, found 'dismissPageAddModal('
(1958,10): run-time error CSS1031: Expected selector, found 'dismissPageAddOtherModal('
(1958,10): run-time error CSS1025: Expected comma or open brace, found 'dismissPageAddOtherModal('
(1961,10): run-time error CSS1031: Expected selector, found 'dismissPageEditModal('
(1961,10): run-time error CSS1025: Expected comma or open brace, found 'dismissPageEditModal('
(1964,10): run-time error CSS1031: Expected selector, found 'dismissPageViewEditModal('
(1964,10): run-time error CSS1025: Expected comma or open brace, found 'dismissPageViewEditModal('
(1969,10): run-time error CSS1031: Expected selector, found 'isCommonContext('
(1969,10): run-time error CSS1025: Expected comma or open brace, found 'isCommonContext('
(1985,10): run-time error CSS1031: Expected selector, found '_loadCommonChildContent('
(1985,10): run-time error CSS1025: Expected comma or open brace, found '_loadCommonChildContent('
(2029,10): run-time error CSS1031: Expected selector, found '_applyFilters('
(2029,10): run-time error CSS1025: Expected comma or open brace, found '_applyFilters('
(2036,10): run-time error CSS1031: Expected selector, found '_clearFilters('
(2036,10): run-time error CSS1025: Expected comma or open brace, found '_clearFilters('
(2043,10): run-time error CSS1031: Expected selector, found '_showFilters('
(2043,10): run-time error CSS1025: Expected comma or open brace, found '_showFilters('
(2057,10): run-time error CSS1031: Expected selector, found '_saveFilters('
(2057,10): run-time error CSS1025: Expected comma or open brace, found '_saveFilters('
(2070,10): run-time error CSS1031: Expected selector, found '_loadNotes('
(2070,10): run-time error CSS1025: Expected comma or open brace, found '_loadNotes('
(2073,10): run-time error CSS1031: Expected selector, found '_showModalNotes('
(2073,10): run-time error CSS1025: Expected comma or open brace, found '_showModalNotes('
(2079,10): run-time error CSS1031: Expected selector, found '_showAddNote('
(2079,10): run-time error CSS1025: Expected comma or open brace, found '_showAddNote('
(2101,10): run-time error CSS1031: Expected selector, found '_showViewEditNote('
(2101,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditNote('
(2124,10): run-time error CSS1031: Expected selector, found '_loadAttachments('
(2124,10): run-time error CSS1025: Expected comma or open brace, found '_loadAttachments('
(2127,10): run-time error CSS1031: Expected selector, found '_showModalAttachments('
(2127,10): run-time error CSS1025: Expected comma or open brace, found '_showModalAttachments('
(2130,10): run-time error CSS1031: Expected selector, found '_showAddAttachment('
(2130,10): run-time error CSS1025: Expected comma or open brace, found '_showAddAttachment('
(2150,10): run-time error CSS1031: Expected selector, found '_showAddLogo('
(2150,10): run-time error CSS1025: Expected comma or open brace, found '_showAddLogo('
(2166,10): run-time error CSS1031: Expected selector, found 'viewAttachment('
(2166,10): run-time error CSS1025: Expected comma or open brace, found 'viewAttachment('
(2177,10): run-time error CSS1031: Expected selector, found 'deleteAttachment('
(2177,10): run-time error CSS1025: Expected comma or open brace, found 'deleteAttachment('
(2192,10): run-time error CSS1031: Expected selector, found '_loadAppointments('
(2192,10): run-time error CSS1025: Expected comma or open brace, found '_loadAppointments('
(2195,10): run-time error CSS1031: Expected selector, found '_showModalAppointments('
(2195,10): run-time error CSS1025: Expected comma or open brace, found '_showModalAppointments('
(2198,10): run-time error CSS1031: Expected selector, found '_showAddAppointment('
(2198,10): run-time error CSS1025: Expected comma or open brace, found '_showAddAppointment('
(2218,10): run-time error CSS1031: Expected selector, found '_showViewEditAppointment('
(2218,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditAppointment('
(2236,10): run-time error CSS1031: Expected selector, found '_showViewEditInstallation('
(2236,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditInstallation('
(2253,10): run-time error CSS1031: Expected selector, found '_showViewEditSurvey('
(2253,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditSurvey('
(2273,10): run-time error CSS1031: Expected selector, found '_loadItemMessages('
(2273,10): run-time error CSS1025: Expected comma or open brace, found '_loadItemMessages('
(2276,10): run-time error CSS1031: Expected selector, found '_showAddItemMessage('
(2276,10): run-time error CSS1025: Expected comma or open brace, found '_showAddItemMessage('
(2295,10): run-time error CSS1031: Expected selector, found '_showViewItemMessage('
(2295,10): run-time error CSS1025: Expected comma or open brace, found '_showViewItemMessage('
(2303,10): run-time error CSS1031: Expected selector, found '_showEditItemMessage('
(2303,10): run-time error CSS1025: Expected comma or open brace, found '_showEditItemMessage('
(2323,10): run-time error CSS1031: Expected selector, found '_loadTasks('
(2323,10): run-time error CSS1025: Expected comma or open brace, found '_loadTasks('
(2326,10): run-time error CSS1031: Expected selector, found '_showAddTask('
(2326,10): run-time error CSS1025: Expected comma or open brace, found '_showAddTask('
(2345,10): run-time error CSS1031: Expected selector, found '_showEditTask('
(2345,10): run-time error CSS1025: Expected comma or open brace, found '_showEditTask('
(2365,10): run-time error CSS1031: Expected selector, found '_showViewEditTask('
(2365,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditTask('
(2384,10): run-time error CSS1031: Expected selector, found '_nudgeTask('
(2384,10): run-time error CSS1025: Expected comma or open brace, found '_nudgeTask('
(2389,10): run-time error CSS1031: Expected selector, found '_showTaskModalNotes('
(2389,10): run-time error CSS1025: Expected comma or open brace, found '_showTaskModalNotes('
(2395,10): run-time error CSS1031: Expected selector, found '_showAddReferenceData('
(2395,10): run-time error CSS1025: Expected comma or open brace, found '_showAddReferenceData('
(2410,10): run-time error CSS1031: Expected selector, found '_showChangeStatus('
(2410,10): run-time error CSS1025: Expected comma or open brace, found '_showChangeStatus('
(2433,10): run-time error CSS1031: Expected selector, found '_loadPayments('
(2433,10): run-time error CSS1025: Expected comma or open brace, found '_loadPayments('
(2436,10): run-time error CSS1031: Expected selector, found '_showAddOmitPayment('
(2436,10): run-time error CSS1025: Expected comma or open brace, found '_showAddOmitPayment('
(2456,10): run-time error CSS1031: Expected selector, found '_showAddPayment('
(2456,10): run-time error CSS1025: Expected comma or open brace, found '_showAddPayment('
(2476,10): run-time error CSS1031: Expected selector, found '_showAddBalanceCorrection('
(2476,10): run-time error CSS1025: Expected comma or open brace, found '_showAddBalanceCorrection('
(2496,10): run-time error CSS1031: Expected selector, found '_showViewEditPayment('
(2496,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditPayment('
(2525,10): run-time error CSS1031: Expected selector, found '_showAddInvoice('
(2525,10): run-time error CSS1025: Expected comma or open brace, found '_showAddInvoice('
(2545,10): run-time error CSS1031: Expected selector, found '_showEditInvoice('
(2545,10): run-time error CSS1025: Expected comma or open brace, found '_showEditInvoice('
(2569,10): run-time error CSS1031: Expected selector, found '_loadProducts('
(2569,10): run-time error CSS1025: Expected comma or open brace, found '_loadProducts('
(2572,10): run-time error CSS1031: Expected selector, found '_showAddProduct('
(2572,10): run-time error CSS1025: Expected comma or open brace, found '_showAddProduct('
(2591,10): run-time error CSS1031: Expected selector, found '_showViewEditProduct('
(2591,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditProduct('
(2607,10): run-time error CSS1031: Expected selector, found '_loadPartOrders('
(2607,10): run-time error CSS1025: Expected comma or open brace, found '_loadPartOrders('
(2610,10): run-time error CSS1031: Expected selector, found '_showModalPartOrders('
(2610,10): run-time error CSS1025: Expected comma or open brace, found '_showModalPartOrders('
(2613,10): run-time error CSS1031: Expected selector, found '_showAddPartOrder('
(2613,10): run-time error CSS1025: Expected comma or open brace, found '_showAddPartOrder('
(2631,10): run-time error CSS1031: Expected selector, found '_showViewEditPartOrder('
(2631,10): run-time error CSS1025: Expected comma or open brace, found '_showViewEditPartOrder('
(2646,10): run-time error CSS1031: Expected selector, found '_loadItemLinks('
(2646,10): run-time error CSS1025: Expected comma or open brace, found '_loadItemLinks('
(2649,10): run-time error CSS1031: Expected selector, found '_showShareItem('
(2649,10): run-time error CSS1025: Expected comma or open brace, found '_showShareItem('
(2670,10): run-time error CSS1031: Expected selector, found '_loadAppContent('
(2670,10): run-time error CSS1025: Expected comma or open brace, found '_loadAppContent('
(2693,10): run-time error CSS1031: Expected selector, found '_showAppModal('
(2693,10): run-time error CSS1025: Expected comma or open brace, found '_showAppModal('
(2711,10): run-time error CSS1031: Expected selector, found '_onSuccessAdd('
(2711,10): run-time error CSS1025: Expected comma or open brace, found '_onSuccessAdd('
(2753,10): run-time error CSS1031: Expected selector, found '_onSuccessAddOther('
(2753,10): run-time error CSS1025: Expected comma or open brace, found '_onSuccessAddOther('
(2795,10): run-time error CSS1031: Expected selector, found '_onSuccessEdit('
(2795,10): run-time error CSS1025: Expected comma or open brace, found '_onSuccessEdit('
(2827,10): run-time error CSS1031: Expected selector, found '_onSuccessViewEdit('
(2827,10): run-time error CSS1025: Expected comma or open brace, found '_onSuccessViewEdit('
(2847,10): run-time error CSS1031: Expected selector, found '_onErrorAdd('
(2847,10): run-time error CSS1025: Expected comma or open brace, found '_onErrorAdd('
(2862,10): run-time error CSS1031: Expected selector, found '_onErrorAddOther('
(2862,10): run-time error CSS1025: Expected comma or open brace, found '_onErrorAddOther('
(2877,10): run-time error CSS1031: Expected selector, found '_onErrorEdit('
(2877,10): run-time error CSS1025: Expected comma or open brace, found '_onErrorEdit('
(2891,10): run-time error CSS1031: Expected selector, found '_onErrorViewEdit('
(2891,10): run-time error CSS1025: Expected comma or open brace, found '_onErrorViewEdit('
(2906,10): run-time error CSS1031: Expected selector, found 'getErrorMsg('
(2906,10): run-time error CSS1025: Expected comma or open brace, found 'getErrorMsg('
(2918,10): run-time error CSS1031: Expected selector, found 'displayAddModalMessage('
(2918,10): run-time error CSS1025: Expected comma or open brace, found 'displayAddModalMessage('
(2921,10): run-time error CSS1031: Expected selector, found 'displayAddOtherModalMessage('
(2921,10): run-time error CSS1025: Expected comma or open brace, found 'displayAddOtherModalMessage('
(2924,10): run-time error CSS1031: Expected selector, found 'displayEditModalMessage('
(2924,10): run-time error CSS1025: Expected comma or open brace, found 'displayEditModalMessage('
(2927,10): run-time error CSS1031: Expected selector, found 'displayVewEditModalMessage('
(2927,10): run-time error CSS1025: Expected comma or open brace, found 'displayVewEditModalMessage('
(2930,10): run-time error CSS1031: Expected selector, found 'displayModalMessage('
(2930,10): run-time error CSS1025: Expected comma or open brace, found 'displayModalMessage('
(2943,10): run-time error CSS1031: Expected selector, found '_formSave('
(2943,10): run-time error CSS1025: Expected comma or open brace, found '_formSave('
(2953,10): run-time error CSS1031: Expected selector, found 'initTextSelect('
(2953,10): run-time error CSS1025: Expected comma or open brace, found 'initTextSelect('
(2958,10): run-time error CSS1031: Expected selector, found 'initDatePickers('
(2958,10): run-time error CSS1025: Expected comma or open brace, found 'initDatePickers('
(2964,10): run-time error CSS1031: Expected selector, found 'applyMultiselects('
(2964,10): run-time error CSS1025: Expected comma or open brace, found 'applyMultiselects('
(2972,10): run-time error CSS1031: Expected selector, found 'applyNumericMinValue('
(2972,10): run-time error CSS1025: Expected comma or open brace, found 'applyNumericMinValue('
(2981,10): run-time error CSS1031: Expected selector, found 'applyMultiselect('
(2981,10): run-time error CSS1025: Expected comma or open brace, found 'applyMultiselect('
(2993,10): run-time error CSS1031: Expected selector, found 'setMultiselectData('
(2993,10): run-time error CSS1025: Expected comma or open brace, found 'setMultiselectData('
(2998,10): run-time error CSS1031: Expected selector, found 'onConfirmOk('
(2998,10): run-time error CSS1025: Expected comma or open brace, found 'onConfirmOk('
(3005,10): run-time error CSS1031: Expected selector, found 'enableInput('
(3005,10): run-time error CSS1025: Expected comma or open brace, found 'enableInput('
(3010,10): run-time error CSS1031: Expected selector, found 'disableInput('
(3010,10): run-time error CSS1025: Expected comma or open brace, found 'disableInput('
(3014,10): run-time error CSS1031: Expected selector, found 'enableDisableInput('
(3014,10): run-time error CSS1025: Expected comma or open brace, found 'enableDisableInput('
(3021,10): run-time error CSS1031: Expected selector, found 'toggleArchivedWarning('
(3021,10): run-time error CSS1025: Expected comma or open brace, found 'toggleArchivedWarning('
(3026,10): run-time error CSS1031: Expected selector, found 'isInputChecked('
(3026,10): run-time error CSS1025: Expected comma or open brace, found 'isInputChecked('
(3030,10): run-time error CSS1031: Expected selector, found 'setInputChecked('
(3030,10): run-time error CSS1025: Expected comma or open brace, found 'setInputChecked('
(3035,10): run-time error CSS1031: Expected selector, found 'setInputDateType('
(3035,10): run-time error CSS1025: Expected comma or open brace, found 'setInputDateType('
(3042,10): run-time error CSS1031: Expected selector, found 'convertDateTime('
(3042,10): run-time error CSS1025: Expected comma or open brace, found 'convertDateTime('
(3056,10): run-time error CSS1031: Expected selector, found 'populateFramesDropdown('
(3056,10): run-time error CSS1025: Expected comma or open brace, found 'populateFramesDropdown('
(3070,10): run-time error CSS1031: Expected selector, found 'restrictFutureDates('
(3070,10): run-time error CSS1025: Expected comma or open brace, found 'restrictFutureDates('
(3078,10): run-time error CSS1031: Expected selector, found 'selectFirstOptionIfValueMissing('
(3078,10): run-time error CSS1025: Expected comma or open brace, found 'selectFirstOptionIfValueMissing('
(3088,10): run-time error CSS1031: Expected selector, found 'setDropdownValue('
(3088,10): run-time error CSS1025: Expected comma or open brace, found 'setDropdownValue('
(3103,16): run-time error CSS1031: Expected selector, found '='
(3103,16): run-time error CSS1025: Expected comma or open brace, found '='
(3105,10): run-time error CSS1031: Expected selector, found 'ajaxPostJsonAddData('
(3105,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostJsonAddData('
(3116,10): run-time error CSS1031: Expected selector, found 'ajaxPostJsonAddOtherData('
(3116,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostJsonAddOtherData('
(3127,10): run-time error CSS1031: Expected selector, found 'ajaxPostJsonEditData('
(3127,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostJsonEditData('
(3138,10): run-time error CSS1031: Expected selector, found 'ajaxPostJsonViewEditData('
(3138,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostJsonViewEditData('
(3149,10): run-time error CSS1031: Expected selector, found 'ajaxPostJsonData('
(3149,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostJsonData('
(3180,10): run-time error CSS1031: Expected selector, found 'ajaxPostFormAddData('
(3180,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostFormAddData('
(3184,10): run-time error CSS1031: Expected selector, found 'ajaxPostFormData('
(3184,10): run-time error CSS1025: Expected comma or open brace, found 'ajaxPostFormData('
(3212,10): run-time error CSS1031: Expected selector, found 'loadUrl('
(3212,10): run-time error CSS1025: Expected comma or open brace, found 'loadUrl('
(3220,10): run-time error CSS1031: Expected selector, found 'loadDiv('
(3220,10): run-time error CSS1025: Expected comma or open brace, found 'loadDiv('
(3234,10): run-time error CSS1031: Expected selector, found 'loadPostUrl('
(3234,10): run-time error CSS1025: Expected comma or open brace, found 'loadPostUrl('
(3257,10): run-time error CSS1031: Expected selector, found 'loadFormPostUrl('
(3257,10): run-time error CSS1025: Expected comma or open brace, found 'loadFormPostUrl('
(3281,10): run-time error CSS1031: Expected selector, found 'getData('
(3281,10): run-time error CSS1025: Expected comma or open brace, found 'getData('
(3333,10): run-time error CSS1031: Expected selector, found 'initDropdown('
(3333,10): run-time error CSS1025: Expected comma or open brace, found 'initDropdown('
(3342,10): run-time error CSS1031: Expected selector, found 'populateDropdown('
(3342,10): run-time error CSS1025: Expected comma or open brace, found 'populateDropdown('
(3377,10): run-time error CSS1031: Expected selector, found 'displayErrors('
(3377,10): run-time error CSS1025: Expected comma or open brace, found 'displayErrors('
(3402,32): run-time error CSS1031: Expected selector, found '='
(3402,32): run-time error CSS1025: Expected comma or open brace, found '='
(3403,23): run-time error CSS1031: Expected selector, found '='
(3403,23): run-time error CSS1025: Expected comma or open brace, found '='
(3404,22): run-time error CSS1031: Expected selector, found '='
(3404,22): run-time error CSS1025: Expected comma or open brace, found '='
(3406,10): run-time error CSS1031: Expected selector, found 'validateAddModalFrm('
(3406,10): run-time error CSS1025: Expected comma or open brace, found 'validateAddModalFrm('
(3410,10): run-time error CSS1031: Expected selector, found 'validateAddOtherModalFrm('
(3410,10): run-time error CSS1025: Expected comma or open brace, found 'validateAddOtherModalFrm('
(3414,10): run-time error CSS1031: Expected selector, found 'validateEditModalFrm('
(3414,10): run-time error CSS1025: Expected comma or open brace, found 'validateEditModalFrm('
(3418,10): run-time error CSS1031: Expected selector, found 'validateViewEditModalFrm('
(3418,10): run-time error CSS1025: Expected comma or open brace, found 'validateViewEditModalFrm('
(3422,10): run-time error CSS1031: Expected selector, found 'validateModalFrm('
(3422,10): run-time error CSS1025: Expected comma or open brace, found 'validateModalFrm('
(3430,10): run-time error CSS1031: Expected selector, found 'validateAddFrm('
(3430,10): run-time error CSS1025: Expected comma or open brace, found 'validateAddFrm('
(3433,10): run-time error CSS1031: Expected selector, found 'validateEditFrm('
(3433,10): run-time error CSS1025: Expected comma or open brace, found 'validateEditFrm('
(3436,10): run-time error CSS1031: Expected selector, found 'validateViewEditFrm('
(3436,10): run-time error CSS1025: Expected comma or open brace, found 'validateViewEditFrm('
(3439,10): run-time error CSS1031: Expected selector, found 'validateFrm('
(3439,10): run-time error CSS1025: Expected comma or open brace, found 'validateFrm('
(3494,10): run-time error CSS1031: Expected selector, found 'validateAddFld('
(3494,10): run-time error CSS1025: Expected comma or open brace, found 'validateAddFld('
(3497,10): run-time error CSS1031: Expected selector, found 'validateEditFld('
(3497,10): run-time error CSS1025: Expected comma or open brace, found 'validateEditFld('
(3500,10): run-time error CSS1031: Expected selector, found 'validateViewEditFld('
(3500,10): run-time error CSS1025: Expected comma or open brace, found 'validateViewEditFld('
(3503,10): run-time error CSS1031: Expected selector, found 'validateFld('
(3503,10): run-time error CSS1025: Expected comma or open brace, found 'validateFld('
(3589,10): run-time error CSS1031: Expected selector, found 'clearInvalid('
(3589,10): run-time error CSS1025: Expected comma or open brace, found 'clearInvalid('
(3609,10): run-time error CSS1031: Expected selector, found 'setInvalid('
(3609,10): run-time error CSS1025: Expected comma or open brace, found 'setInvalid('
(3646,10): run-time error CSS1031: Expected selector, found 'setRequired('
(3646,10): run-time error CSS1025: Expected comma or open brace, found 'setRequired('
(3652,10): run-time error CSS1031: Expected selector, found 'validationLog('
(3652,10): run-time error CSS1025: Expected comma or open brace, found 'validationLog('
(3657,10): run-time error CSS1031: Expected selector, found 'addValidationError('
(3657,10): run-time error CSS1025: Expected comma or open brace, found 'addValidationError('
(3668,10): run-time error CSS1031: Expected selector, found 'isRequired('
(3668,10): run-time error CSS1025: Expected comma or open brace, found 'isRequired('
(3676,10): run-time error CSS1031: Expected selector, found 'validateHiddenFields('
(3676,10): run-time error CSS1025: Expected comma or open brace, found 'validateHiddenFields('
 */
let gId = 0;
let gProperId = '';

$(document).ready(function () {
    initTableSort();
    initTableRowClick();
    initTableFilterSearch();
    initTooltips();

    initDatePickers();
    initPrintOption();
    initTextSelect();
    initPanelTitle();
});

function initPanelTitle() {
    $(document).on("click", ".frame-panel-title-text", function () {
        const title = $(this);
        const target = title.data("target");
        const key = "panel-pin-" + target;

        if (localStorage.getItem(key) === "true") { return; }

        // Only pinnable titles show the pin
        const panel = $("#" + target);
        //const pin = title.hasClass("pinnable")
        //    ? title.find(".pin-icon")
        //    : null;

        const pin = title.find(".pin-icon");

        panel.toggle();

        if (pin) {
            if (panel.is(":visible")) {
                pin.hide();
            } else {
                pin.show();
            }
        }
    });
    $(document).on("click", ".pin-icon", function (e) {
        e.stopPropagation(); // prevent panel from reopening

        const target = $(this).data("pin");
        const pinned = localStorage.getItem("panel-pin-" + target) === "true";

        if (pinned) {
            localStorage.removeItem("panel-pin-" + target);
            $(this).removeClass("pinned");
        } else {
            setPinned(target);
            $(this).addClass("pinned");
        }
    });
}
function initFlaggedNotes(panelName) {
    function updateUI(cId, eId) {
        const collapsed = document.getElementById(cId);
        const expanded = document.getElementById(eId);

        if (!collapsed || !expanded) return;

        // Load saved state
        const isCollapsed = localStorage.getItem(cId) === "true";

        collapsed.style.display = isCollapsed ? "" : "none";
        expanded.style.display = isCollapsed ? "none" : "";
    }

    const collapsedPanelId = panelName + 'notesCollapsed';
    const expandedPanelId = panelName + 'notesExpanded';
    updateUI(collapsedPanelId, expandedPanelId);

    const collapsed = document.getElementById(collapsedPanelId);
    if (!collapsed) return;
    collapsed.addEventListener("click", function (e) {
        e.stopPropagation();
        consolelog('collapsed clicked');
        localStorage.setItem(collapsedPanelId, false);
        updateUI(collapsedPanelId, expandedPanelId);
    });

    const expanded = document.getElementById(expandedPanelId);
    if (!expanded) return;
    expanded.addEventListener("click", function (e) {
        e.stopPropagation();
        consolelog('expanded clicked');
        localStorage.setItem(collapsedPanelId, true);
        updateUI(collapsedPanelId, expandedPanelId);
    });

    const container = document.getElementById(panelName + 'flaggedNotesPanel');
    if (!container) return;
    container.addEventListener("click", function () {
        consolelog('panel clicked');
        let c = localStorage.getItem(collapsedPanelId) === "true";
        localStorage.setItem(collapsedPanelId, !c);
        updateUI(collapsedPanelId, expandedPanelId);
    });
}
function initPinnedState() {
    $(".frame-panel-title-text").each(function () {
        const target = $(this).data("target");
        const panel = $("#" + target);
        const pin = $(this).find(".pin-icon");

        consolelog('initPinnedState', target);

        if (localStorage.getItem("panel-pin-" + target) === "true") {
            panel.hide();
            pin.show().addClass("pinned");
        }
    });
}
function setPinned(target) {
    localStorage.setItem("panel-pin-" + target, "true");
}
function initTooltips() {
    consolelog('initTooltips');
    $('[data-toggle="tooltip"]').tooltip();
}

function initScrollTop(divId, reposition) {
    consolelog('initScrollTop: ' + divId);
    $("#" + divId).on("scroll", function () { showScrollTop(divId, reposition) });
}
function showScrollTop(divId, reposition) {
    if ($("#btnScrollTop") && $('#' + divId)) {
        if ($('#'+divId).scrollTop() > 20) {
            $("#btnScrollTop").data('target', divId);
            if (reposition) {
                var calcLeft = $('#' + divId).position().left + $('#' + divId).outerWidth() - 100;
                var calcBottom = $('#' + divId).position().top + $('#' + divId).outerHeight() - 100;
                $("#btnScrollTop").css('left', calcLeft).css('top', calcBottom);
            }

            $("#btnScrollTop").show();
        } else {
            $("#btnScrollTop").hide();
        }
    }
}
function scrollToTop() {
    var myButton = $("#btnScrollTop");
    var target = $(myButton).data('target');
    $("#" + target).scrollTop(0);
}

function showDashboard(key, dashboardUrl) {
    consolelog('showDashboard', dashboardUrl);
    if (hasContent(dashboardUrl)) {
        showDivFromCookieSetting('dashboard', key, dashboardUrl);
    } else {
        $('#dashboard').hide();
    }
}
function toggleDashboard(key, dashboardUrl) {
    consolelog('toggleDashboard: ' + dashboardUrl);
    toggleDivFromCookieSetting('dashboard', key, dashboardUrl);
    //$('#subContentScroll').removeClass('dashboard');
    //if ($('#dashboard').css('display') !== 'none') $('#subContentScroll').addClass('dashboard')
}

function showDivFromCookieSetting(divId, key, loadUrl) {
    consolelog('showDivFromCookieSetting', [divId, loadUrl]);
    if (!hasContent(divId) || !hasContent(loadUrl)) return;
    let properDivId = toProperId(divId, false).value;

    var show = getCookie(divId+key);
    if (!show || show == '') {
        consolelog('showDivFromCookieSetting: hide');
        $(properDivId).hide();
    }
    else {
        //debugger;
        consolelog('showDivFromCookieSetting: show');
        $(properDivId).load(loadUrl);

        $(properDivId).show();
    }
}
function toggleDivFromCookieSetting(divId, key, loadUrl) {
    consolelog('toggleDivFromCookieSetting: ' + divId + ' with url: ' + loadUrl);
    if (!divId || divId == '' || !loadUrl || loadUrl == '') return;

    var show = getCookie(divId+key);
    if (!show || show == '')
        setCookie(divId+key, 'true', 100);
    else
        setCookie(divId+key, '', 1);

    showDivFromCookieSetting(divId, key, loadUrl);
}

function setCookie(cname, cvalue, exdays) {
    //consolelog('setCookie', [cname, cvalue, exdays]);
    if (cvalue) cvalue = cvalue.replaceAll(';', '|');
    const d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    let expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
function getCookie(cname, defaultVal) {
    let name = cname + "=";
    let ca = document.cookie.split(';');
    var cval;
    for (let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            cval = c.substring(name.length, c.length);
        }
    }

    if (!cval) cval = defaultVal;
    if (cval) cval = cval.replaceAll('|', ';');
    return cval;
}

function allowDrop(ev) {
    ev.preventDefault();
}

function itemRedirect(itemTypeId, itemId) {
    if (toInt(itemTypeId) <= 0 || toInt(itemId) <= 0) return _callFailed('itemRedirect');
    var url = '/App/ItemRedirect/?itemType=' + itemTypeId + '&itemId=' + itemId;
    navigateToPage(url);
}

function navigateToPage(url) {
    if (!hasContent(url)) return _callFailed('navigateToPage', true);
    window.location.href = url;
}
function reloadPage(pathOnly) {
    consolelog('reloadPage', pathOnly);
    if (pathOnly) {
        window.location.href = location.origin + location.pathname;
    } else {
        window.location.reload();
    }
}

function consolelog(text, value, isError) {
    if (typeof _consolelog === 'function') {
        _consolelog(text, value, isError);
    }
}
function toInt(text) {
    return parseInt(text||0, 10);
}
function toBool(value) {
    return value === true || value === 1 || String(value).toLowerCase() === 'true'
}
function exportExcel(url) {
    const currentUrl = window.location.href;
    document.getElementById('downloadFrame').src = url;
}
//function printPage(content = 'subContentScroll') {
//    var myFrame = $("#printFrame").contents().find('body');
//    myFrame.html($('#' + content).html());
//    document.getElementById("printFrame").contentWindow.print();
//}
function printPage(content = 'subContentScroll') {
    const iframe = document.getElementById("printFrame");
    const doc = iframe.contentDocument || iframe.contentWindow.document;

    const styles = [...document.querySelectorAll('link[rel="stylesheet"], style')]
        .map(node => node.outerHTML)
        .join('\n');

    doc.open();
    doc.write(`
        <html>
        <head>
            ${styles}
            <style>
@media print {

    /* Force landscape */
    @page {
        size: landscape;
        margin: 10mm;
    }

    /* Hide elements marked as no-print */
    .no-print {
        display: none !important;
    }

    /* Prevent Bootstrap tables from collapsing */
    table {
        border-collapse: collapse !important;
        width: 100% !important;
    }

    /* Ensure all table cells keep their spacing */
    th, td {
        padding: 6px 12px !important;
    }

    /* Force background colors to print */
    table tr,
    table td,
    table th {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    /* Restore Bootstrap header colors */
    .table thead th,
    th[class*="bg-"],
    .table-dark th {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    /* Restore Bootstrap striped rows */
    .table-striped tbody tr:nth-of-type(odd) {
        background-color: #f2f2f2 !important;
    }

    /* Optional: darker stripe if you prefer */
    /* .table-striped tbody tr:nth-of-type(odd) {
        background-color: #e9ecef !important;
    } */
}

            </style>

        </head>
        <body>
            ${document.getElementById(content).innerHTML}
        </body>
        </html>
    `);
    doc.close();

    iframe.contentWindow.print();
}


function toggleDiv(id) {
    $('#' + id).toggle();
}

function initPrintOption() {
    $('.print-option').hover(function () {
        $('#subContentScroll').css("box-shadow", "0 0 2px #f00 inset;");
    }, function () {
        $('#subContentScroll').css("box-shadow", "none");
    });
}

function showAddress(rowItemAddress) {
    var address = rowItemAddress;
    if (!rowItemAddress) address = rowMenuItemAddress;

    if (!address) return;
    window.open('https://www.google.com/maps/search/' + address);
}
function _watchItem(css, itemId, itemTypeId, itemReference) {
    consolelog('_watchItem', [itemId, itemTypeId, itemReference]);
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return;

    var postUrl = '/App/Watch';

    var data = {
        WatchItemId: itemId,
        WatchItemTypeId: itemTypeId,
        WatchItemReference: itemReference
    };

    var successCallback = function () {
        showAlert('Watch', 'You are now watching this record', css);
    };

    var errorCallback = function () {
        showAlert('Watch - Error', 'Unable to watch this record', css);
    };

    ajaxPostJsonData(postUrl, data, successCallback, errorCallback);
}
function unWatchItem(itemTypeId, itemId, css) {
    consolelog('unWatchItem', [itemTypeId, itemId]);

    var confirmCallback = function () {
        var postUrl = '/App/UnWatch';
        var data = {
            WatchItemTypeId: itemTypeId,
            WatchItemId: itemId,
        };
        ajaxPostJsonData(postUrl, data, unWatchSuccess);
    };

    showConfirm('Watch', 'Are you sure you no longer want to watch this record?', 'watch', confirmCallback, css);
}

function showItemOnMap(itemAddress) {
    if (!hasContent(itemAddress)) itemAddress = rowMenuItemAddress;
    if (!hasContent(itemAddress)) return _callFailed('showItemOnMap');
    consolelog('showItemOnMap', [rowMenuItemAddress]);

    showAddress(itemAddress);
}
function addSuffix(name, suffix) {
    if (!suffix || suffix == '') return name;
    return name + ' (' + suffix + ')';
}
function displayFrames(toDisplay) {
    consolelog('displayFrames: ' + toDisplay);

    setCookie('displayFrames', toDisplay);

    $('.frameDisplay').hide();
    $('.frameTypeDisplay').removeClass('activeFrameType');
    if (!toDisplay || toDisplay == '') toDisplay = 't';

    if (toDisplay == 't') {
        $('.frameDisplay.framesCount').show();
        $('.frameTypeDisplay.frameTypeQ').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frame Quantities');
    }
    if (toDisplay == 'c') {
        $('.frameDisplay.framesCalledOff').show();
        $('.frameTypeDisplay.frameTypeC').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frames Called Off');
    }
    if (toDisplay == 'o') {
        $('.frameDisplay.framesOrdered').show();
        $('.frameTypeDisplay.frameTypeO').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frames Ordered');
    }
    if (toDisplay == 'd') {
        $('.frameDisplay.framesDelivered').show();
        $('.frameTypeDisplay.frameTypeD').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frames Delivered');
    }
    if (toDisplay == 'i') {
        $('.frameDisplay.framesInstalled').show();
        $('.frameTypeDisplay.frameTypeI').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frames Installed');
    }
    if (toDisplay == 'w') {
        $('.frameDisplay.framesWip').show();
        $('.frameTypeDisplay.frameTypeW').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frames WIP');
    }
    if (toDisplay == 'r') {
        $('.frameDisplay.framesRem').show();
        $('.frameTypeDisplay.frameTypeR').addClass('activeFrameType');
        $('#legendText').html('Displaying - Frames Remaining');
    }
}
function logout() {
    showConfirm('Logout', 'Are you sure you want to logout?', 'logout');
}

function showHide(id, show) {
    //consolelog('showHide: ' + id + ', ' + show);
    if (!toProperId(id).exists) return _callFailed('showHide:' + id);

    show ? $(gProperId).show() : $(gProperId).hide();
}

function hasContent(value, match, caseSensitive = false) {
    const has = typeof value === 'string' && value.trim().length > 0;
    if (!has) return false;

    if (typeof match === 'string') {
        if (caseSensitive) return value === match;
        return value.toLowerCase() === match.toLowerCase();
    }

    return true;
}


function toProperId(id, setGlobal, debug) {
    if (debug) consolelog('toProperId...', [id, setGlobal, debug]);
    if (setGlobal ==  null || setGlobal == 'undefined') setGlobal = true;
    if (setGlobal) gProperId = '';

    if (typeof id !== 'string') {
        if (debug) consolelog('id is not a string');
        return { fails: true, value: '' };
    }

    const trimmed = id.trim();
    if (!trimmed) {
        if (debug) consolelog('id (trimmed) is empty');
        return { fails: true, exists: false, value: '' };
    }

    const formatted = trimmed.startsWith('#') ? trimmed : `#${trimmed}`;
    if (setGlobal) gProperId = formatted;

    const element = document.querySelector(formatted);
    const exists = element !== null && element !== undefined;

    const result = { fails: false, exists: exists, value: formatted };
    if (debug) consolelog('result', result);

    return result;
}

function mapUrlParams(url, params) {
    if (!hasContent(url)) return;
    if (!params || params.length == 0) return url;
    //consolelog('mapUrlParams', [url + '...']);

    let localUrl = url;
    for (var i = 1; i <= params.length; i++) {
        var token = '{' + i + '}';
        localUrl = localUrl.replace(token, params[i-1]);
    }
    localUrl = localUrl.replaceAll(/&amp;/g, '&');

    return localUrl;
}

function formatCurrency(value) {
    return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, "$1,");
}

function showHidden() {
    $("input:hidden").attr("type", "text");
}

function setClientDebugInfo(msg) {
    $('#clientDebugInfo').html(msg);
}

function adjustBusinessDays(inputDate, targetDays, backwards, isoWeekday) {
    let date = moment(inputDate); // Start from the input date
    let adjustedDays = 0;

    // Determine the step (1 for forward, -1 for backward)
    const step = backwards === true ? -1 : 1;

    // Add or remove business days
    while (adjustedDays < targetDays) {
        date = date.add(step, 'days'); // Increment or decrement the date by 1 day

        // Check if it's a weekday (Monday-Friday)
        if (date.isoWeekday() < 6) {
            adjustedDays++; // Count only weekdays
        }
    }

    // Adjust the resulting date to the next or previous specified ISO weekday
    if (isoWeekday > 0 && isoWeekday < 8) {
        while (date.isoWeekday() !== isoWeekday) {
            date = date.add(step, 'days'); // Move to the next or previous specified ISO weekday
        }
    }

    return date.format('YYYY-MM-DD'); // Return the final date in the desired format
}

function scrollToChild(childName) {
    const childId = '#content_' + childName;
    if (!toProperId(childId, false).exists) return;

    consolelog('scrollToChild', childId);

    var scrollTo = $(childId);
    if (scrollTo.length)
        scrollTo[0].scrollIntoView({ behavior: 'smooth', block: 'start' });
}

function enableScrollLock(scrollableDivId, containerDivId) {
    var container = document.getElementById(containerDivId);
    var div = document.getElementById(scrollableDivId);

    container.addEventListener("wheel", function (event) {
        var containerRect = container.getBoundingClientRect();
        var divRect = div.getBoundingClientRect();

        // Allow normal page scrolling when the container is still in view
        if (containerRect.top >= 0) {
            document.body.style.overflow = "auto";
            div.style.overflowY = "hidden";
            return;
        }

        // Lock scrolling inside the div once it reaches the top of its container
        if (divRect.top <= containerRect.top) {
            if (div.scrollTop === 0 && event.deltaY < 0) {
                // At the top of the div, allow page scrolling
                document.body.style.overflow = "auto";
            } else if (div.scrollTop + div.clientHeight >= div.scrollHeight && event.deltaY > 0) {
                // At the bottom of the div, allow page scrolling
                document.body.style.overflow = "auto";
            } else {
                // Inside the div, lock scrolling here
                document.body.style.overflow = "hidden";
                div.style.overflowY = "scroll";
            }
        }
    });
}


function showMore() {
    var more = $('.moreDiv:visible').length > 0;
    more ? $('.lessDiv').show() : $('.lessDiv').hide();
    more ? $('.moreDiv').hide() : $('.moreDiv').show();
}



function _showLoading() {
    $('#loadingOverlay').show();
}

function _hideLoading() {
    $('#loadingOverlay').hide();
}

//function _getPageId(qsName) {
//    const params = new URLSearchParams(window.location.search);
//    const id = params.get(qsName) || $('#parentId').val();
//    return toInt(id);
//}
//function _replacePageId(url, qsName, id) {
//    //debugger;
//    const newUrl = mapUrlParams(url, [id]);
//    window.history.replaceState({ [qsName]: id }, '', newUrl);
//}

function _replacePageQs(url, keys, values) {
    let newUrl = mapUrlParams(url, values);
    newUrl = new URL(newUrl, window.location.origin);

    keys.forEach((key, i) => {
        newUrl.searchParams.set(key, values[i]);
    });

    const state = Object.fromEntries(keys.map((k, i) => [k, values[i]]));

    window.history.replaceState(state, '', newUrl.toString());
}



function getId(primaryId, secondaryId) {
    gId = toInt(primaryId);
    if (gId <= 0) gId = toInt(secondaryId);
    return toInt(gId);
}

function _callFailed(name, ignore) {
    consolelog('Call Failed', name);
    //debugger;

    if (!toBool(ignore)) {
        document.body.classList.add("warning-border");
        consolelog('*******************************');
    }

    return false;
}

function toggleHelp(pageLevel) {
    if (pageLevel) {
        $('.formHelp').toggle();
        $('.pageHelp').toggle();
    }

    $('.modal .formHelp').toggle();
    $('.modal .pageHelp').toggle();
}
let tab = '';
let rowMenuItemId = 0;
let rowMenuItemTypeId = 0;
let rowMenuItemReference = '';
let rowMenuItemAddress = '';
let rowMenuItemEmail = '';
let rowMenuItemStatusId = 0;
let rowMenuDeleteState = 0;
let rowMenuHref = '';
let rowMenuSelectedRow = null;
let rowMenuSiteId = 0;
let rowMenuCallOffTypeId = 0;
let rowMenuVisitId = 0;

/*Table Functions */
function initTable(name, showMore) {
    ////debugger;
    consolelog('initTable', [name, showMore]);
    var tableName = name + 'Table';
    var table = $("#" + tableName);
    if (!table) {
        //consolelog('Can not find table:' + tableName)
        return;
    }
    initContextMenu(tableName);

    let filterTextbox = $('#' + name + 'Filter');
    filterTextbox.removeClass('filter-higlight');
    var filter = filterTextbox.val();
    if (hasContent(filter)) filterTextbox.addClass('filter-higlight');

    var rows = $('#' + tableName + ' > tbody tr');
    var found = false;

    $("#" + tableName).hide();

    //filter rows
    for (i = 0; i < rows.length; i++) {
        found = rowContainsFilter(rows[i], filter);
        rows[i].style.display = found ? "table-row" : "none";
        rows[i].style.visibility = found ? "visible" : "hidden";
    }

    //display legend
    var legendNames = $('#legendNames' + name).val();
    var legendDisplay = $('#legendDisplay' + name);
    var smallLegend = $('#hiddenLegendSmall' + name).val() == 'true';
    ////debugger;

    if (legendNames) {
        setTableLegendFromCookie(name);

        var hiddenLegendNames = $('#hiddenLegendNames' + name).val();
        var arrLegendNames = legendNames.split(";");
        var hasHiddenRows = false;
        var legendHtml = '';
        for (var x = 0; x < arrLegendNames.length; x++) {
            var legendCss = arrLegendNames[x].split(":")[0];
            if (legendCss && legendCss.length > 0) {
                var legendName = arrLegendNames[x].split(":")[1];
                var legendHidden = hiddenLegendNames && hiddenLegendNames.length > 0 && hiddenLegendNames.split(";").includes(legendCss);

                if (legendHidden) {
                    $("#" + name + "Table > tbody tr[data-legend-css='" + legendCss + "']").hide();
                    $("#" + name + "Table > tbody tr." + legendCss).hide();
                } 

                var rowCount = 0;
                if (legendHidden) {
                    hasHiddenRows = true;
                    //consolelog(legendCss + ' is hidden');
                    rows = $('#' + name + 'Table > tbody tr.' + legendCss);
                    if (rows.length == 0) {
                        rows = $('#' + name + 'Table > tbody tr[data-legend-css="' + legendCss + '"]');
                    }

                    for (i = 0; i < rows.length; i++) {
                        if (rowContainsFilter(rows[i], filter)) rowCount += 1;
                    }
                } else {
                    //consolelog(legendCss + ' is not hidden');
                    //rowCount = $("#" + name + "Table > tbody tr." + legendCss + ':visible').length;
                    var rowCount = $("#" + name + "Table > tbody tr." + legendCss).filter(function () {
                        return $(this).css('display') !== 'none';
                    }).length;

                    if (rowCount == 0) {
                        var rowCount = $('#' + name + 'Table > tbody tr[data-legend-css="' + legendCss + '"]').filter(function () {
                            return $(this).css('display') !== 'none';
                        }).length;
                    }
                }

                //consolelog('Counting rows for ' + legendCss + ' - ' + rowCount + ' found');

                if (rowCount > 0) {
                    var legendText = legendName + " (" + rowCount + ")";
                    if (smallLegend) legendText = rowCount;
                    if (legendHidden) legendText += "*";
                    legendHtml += "<span class='badge " + legendCss + "' data-legend-context='"  + name + "' data-legend='" + legendCss + "' title='Click to show/hide " + legendName + " rows'>" + legendText + "</span>"
                }
            }
        }

        if (hasHiddenRows) {
            legendHtml += smallLegend ? '<span class="text-info"> *hidden</span>' : '<span class="text-info"> *you have some rows hidden</span>';
        }

        legendDisplay.html(legendHtml);
        $("span.badge[data-legend-context=" + name + "]").off('click').click(function () {
            toggleLegend($(this).data('legend'), name);
        });
    } else {
        consolelog('no legend names');
        legendDisplay.html("");
    }

    countTableRows(name);

    //var visibleRowCount = $("#" + tableName + " > tbody tr:visible").length;
    var visibleRowCount = $("#" + tableName + " > tbody tr").filter(function () {
        return $(this).css('display') !== 'none';
    }).length;

    //consolelog('Visible Rows', [visibleRowCount]);
    //visibleRowCount > 0 ? $("#" + tableName).show() : $("#" + tableName).hide();
    visibleRowCount > 0 ? $("#" + tableName + " > tbody").show() : $("#" + tableName + " > tbody").hide();
    $("#" + tableName).show();
    //consolelog('Deciding to show message', ["#noRowsFound" + name]);
    //visibleRowCount > 0 ? $("#noRowsFound" + name).css('display', 'none') : $("#noRowsFound" + name).css('display', 'block');

    applyTableSort(name);

    if (showMore && showMore == 'True') $('[id^=more]').trigger('click');

    //If the summary row is displayed, calculate the summary value
    if (toProperId('#tableSummary').exists) {
        var sumValue = 0;
        $("#" + tableName + " > tbody tr").filter(function () {
            if ($(this).css('display') !== 'none') {
                var value = $(this).attr('data-summary-value');
                if (value) sumValue += Number(value);
            }
        });

        _displaySummaryValue(sumValue);
    }
}

function _displaySummaryValue(sumValue) {
    if (typeof displaySummaryValue == 'function') {
        displaySummaryValue(sumValue);
    } else {
        $('#tableSummary').html('<b>Total</b>: £' + formatCurrency(sumValue));
    }
}

function initTableSort(tableName) {
    $('th[data-sortable]').off('click').click(function () {
        //consolelog('Sort Click', [this]);
        sortTable(this);
    });

    if (tableName) {
        var tableId = tableName + 'Table';
        moveRowsToTheTop(tableId);
        moveRowsToTheBottom(tableId);
    }
}

function firstVisibleRow(name) {
    const row = $(`#${name}Table > tbody tr:visible`).first();

    if (row.length === 0) {
        return {id: 0, itemId: 0, altItemId: 0, itemTypeId: 0, itemRef: ''};
    }

    const get = attr => toInt(row.data(attr));

    return {
        id: get('id'),
        itemId: get('itemid'),
        altItemId: get('altitemid'),
        itemTypeId: get('itemtypeid'),
        itemRef: row.data('itemref')
    };
}



function sortTable(thElem, sortOrder, tId) {
    var table = $(thElem).parents('table').eq(0)
    var tableId = table.attr('id');
    //consolelog('sortTable', [thElem, sortOrder, tId]);

    if (table.length > 0) {
        var colIndex = $(thElem).index();

        var rows = table.find('tr:gt(0)').toArray().sort(tableSortComparer($(thElem).index()))
        thElem.asc = !thElem.asc
        $(table).find('th > i').removeClass('fa-sort').removeClass('fa-sort-asc').removeClass('fa-sort-desc');
        $(table).find('th > i').addClass('fa-sort')
        $(thElem).children('i').eq(0).removeClass('fa-sort');

        if (sortOrder == 'desc' || !thElem.asc) {
            sortOrder = 'desc';
            $(thElem).children('i').eq(0).addClass('fa-sort-desc');
            rows = rows.reverse();
        } else {
            sortOrder = 'asc';
            $(thElem).children('i').eq(0).addClass('fa-sort-asc');
        }

        for (var i = 0; i < rows.length; i++) { table.append(rows[i]) }
    } else {
        tableId = tId + 'Table';
    }

    moveRowsToTheTop(tableId);

    moveRowsToTheBottom(tableId);

    setCookie(tableId + 'sort', JSON.stringify({tableId: tableId, colIndex: colIndex, sortOrder: sortOrder}), 100);
}

function moveRowsToTheTop(tableId) {
    var selector = '#' + tableId + " tr.rowTop";
    var rowsToMove = $(selector);

    //consolelog('moveRowsToTheTop', [selector, rowsToMove.length]);

    rowsToMove.each(function () {
        $(this).prependTo($(this).closest("tbody"));
    });
}

function moveRowsToTheBottom(tableId) {
    var selector = '#' + tableId + " tr.rowBottom";
    var rowsToMove = $(selector);

    //consolelog('moveRowsToTheBottom', [selector, rowsToMove.length]);

    rowsToMove.each(function () {
        $(this).appendTo($(this).closest("tbody"));
    });
}

function applyTableSort(tableId) {
    //consolelog('Getting table sort cookie', tableId);
    let data = $('#' + tableId + 'Table').data('apply-sort');
    if (data == false) return;

    let cookieValue = getCookie(tableId + 'Tablesort', '');
    if (!hasContent(cookieValue)) return;

    let sortValues = JSON.parse(cookieValue);
    //consolelog('applyTableSort', [sortValues.tableId, sortValues.colIndex, sortValues.sortOrder]);

    sortTable($('#' + sortValues.tableId).find("th").eq(sortValues.colIndex), sortValues.sortOrder, tableId);
}

function initTableRowClick(tableName, rowSelector, override, clickFunc) {
    let _rowClickEnabled = window.contorConfig?.RowClickEnabled ?? true;
    if (!_rowClickEnabled && !override) return;

    var element = hasContent(tableName)
        ? $('#' + tableName + 'Table') : $(document.body);

    var selector = hasContent(rowSelector)
        ? "tr[" + rowSelector + "]" : "tr[data-href]";

    consolelog('initTableRowClick', [tableName, selector]);
    $(element).off("click").on("click", selector, function (evt) {
        if ($(evt.target).closest("a, i, input").length) return;
        //debugger;
        if (typeof clickFunc === 'function') {
            clickFunc();

        }else if (typeof onHrefRowClick === 'function') {
            var href = this.dataset.href;
            onHrefRowClick(href);
        }
    });
}
function initInlineTableRowClick(tableName, callback, override) {
    let _rowClickEnabled = window.contorConfig?.RowClickEnabled ?? true;
    if (!_rowClickEnabled && !override) return;

    $('#' + tableName + 'Table').on("click", "tr[data-itemid]", function (evt) {

        if ($(evt.target).closest("a, i, input").length) return;

        const id = $(this).data("itemid");
        const ref = $(this).data("itemref");

        callback(id, ref);
    });
}
function initTRC(tableName, override, clickFunction) {
    let _rowClickEnabled = window.contorConfig?.RowClickEnabled ?? true;
    if (!_rowClickEnabled && !override) return;

    var element = hasContent(tableName)
        ? $('#' + tableName + 'Table') : $(document.body);

    //Only rows with data-itemid attribute
    var selector = "tr[data-itemid]";

    $(element).off("click.trc").on("click.trc", selector, function (evt) {
        // Ignore clicks on links, icons, and inputs
        if ($(evt.target).closest("a, button, input, select, textarea, i, svg").length) return;

        var href = this.dataset.href;
        if (hasContent(href) && typeof onHrefRowClick === 'function') {
            onHrefRowClick(href);
            return;
        }

        clickFunction(this);
    });
}




function initTableFilterSearch() {
    consolelog('initTableFilterSearch');
    $('.tableFilterInput').on('search', function () {
        var name = $(this).data('filter-name');
        filterTable(name);
    });
}
function rowContainsFilter(row, filter) {
    if (!row) return false;
    if (!filter || filter.length == 0) return true;

    cells = row.getElementsByTagName("td");
    for (j = 0; j < cells.length; j++) {
        td = cells[j];
        if (td) {
            txtValue = td.innerText; //td.textContent ||
            if (txtValue.toUpperCase().indexOf(filter.toUpperCase()) > -1) {
                //consolelog("found: " + txtValue);
                return true;
            }
        }
    }
    return false;
}
function filterTable(name) {
    consolelog('filterTable', name);
    initTable(name);
}
function countTableRows(name) {
    consolelog('countTableRows', name);
    if (!name || name.length == 0) return;
    if (!toProperId('legendCount' + name).exists) return _callFailed('countTableRows: legendCount' +name + ' is missing', true)

    var smallLegend = $('#hiddenLegendSmall' + name).val() == 'true';
    var rowCount = $("#" + name + "Table > tbody tr").length;

    if (rowCount > 0) {
        //var visibleRowCount = $("#" + name + "Table > tbody tr:visible").length;
        var visibleRowCount = $("#" + name + "Table > tbody tr").filter(function () {
            return $(this).css('display') !== 'none';
        }).length;

        var rowCountText = smallLegend ? rowCount : rowCount + ' Records Displayed';
        $('#legendCount' + name).removeClass('text-danger').removeClass('text-info').attr('title', '');
        $('#legendCount' + name).off('click');

        if (rowCount >= 1000) $('#legendCount' + name).addClass('text-danger');

        if (rowCount > visibleRowCount) {
            rowCountText = smallLegend ? visibleRowCount + '/' + rowCount : visibleRowCount + '/' + rowCount + ' Records Displayed';
            if (rowCount < 1000) $('#legendCount' + name).addClass('text-info')
            $('#legendCount' + name).attr('title', 'Click to show ALL rows');
            $('#legendCount' + name).on('click', function () {
                resetLegend(name);
            });
        }

        $('#legendCount' + name).text(rowCountText);
    } else {
        $('#legendCount' + name).text("No Records Found");
    }
}
function setTableLegendFromCookie(name) {
    consolelog('setTableLegendFromCookie', name);

    var cname = 'hiddenLegendNames' + name;
    var cvalue = getCookie(cname, '?');
    if (cvalue.length > 0 && cvalue !== '?') $('#' + cname).val(cvalue);
}
function resetLegend(tableName) {
    $('#hiddenLegendNames' + tableName).val('');
    setCookie('hiddenLegendNames' + tableName, '', 100)
    initTable(tableName);
}
function toggleLegend(cssName, tableName) {
    consolelog('toggleLegend', [cssName, tableName]);
    var hiddenNames = $('#hiddenLegendNames' + tableName).val();
    if (hiddenNames && hiddenNames.length > 0 && hiddenNames.split(";").includes(cssName)) {
        hiddenNames = hiddenNames.replace(cssName + ';', '');
    } else {
        hiddenNames = hiddenNames ? hiddenNames + cssName + ";" : cssName + ";";
    }

    $('#hiddenLegendNames' + tableName).val(hiddenNames);
    setCookie('hiddenLegendNames' + tableName, hiddenNames, 100)
    initTable(tableName);
}
function sort(table, column, direction) {
    var filter = '';
    if (table && table.length > 0) {
        filter = $('#' + table + 'Filter').val();
    }
    window.location.href = window.location.pathname + "?" + $.param({ 'sort': column, 'dir': direction, 'flt': filter });
}
function onHrefRowClick(url) {
    if (!hasContent(url)) return _callFailed('onHrefRowClick', true);

    if (url.indexOf('script:', 0) == 0) {
        consolelog('onHrefRowClick', ['script', url]);
        window[url.replace('script:', '')]();
    }
    else if (window.location.href == url || window.location.href.endsWith(url)) {
        consolelog('onHrefRowClick', ['reload', url]);
        location.reload();
    }
    else {
        consolelog('onHrefRowClick', ['redirect', url]);
        window.location.href = url;
    }
}
function tableSortComparer(index) {
    return function (a, b) {
        var valA = getSortValue(a, index), valB = getSortValue(b, index);
        return $.isNumeric(valA) && $.isNumeric(valB) ? valA - valB : valA.toString().localeCompare(valB);
    }
}
function getSortValue(row, index) {
    var sortValue = $(row).children('td').eq(index).data('sort-value');
    return sortValue;
}
function highlightSelectedTableRowWithId(name, itemId, moveToRow) {
    consolelog('highlightSelectedTableRowWithId', [name, itemId, moveToRow]);
    //debugger;
    $("#" + name + "Table > tbody tr").removeClass('sel');
    var headerClass = $('#headerClass').val();
    var selectedClass = 'sel ' + headerClass ?? "" ;
    var row = $('tr[data-itemid="' + itemId + '"]');

    if (row) {
        row.removeClass().addClass(selectedClass);
        if (moveToRow && row.length > 0) row[0].scrollIntoView();
    }
}
function highlightSelectedTableRowWithIdAndType(name, itemId, itemTypeId, moveToRow) {
    //debugger;
    consolelog('highlightSelectedTableRowWithIdAndType', [name, itemId, itemTypeId, moveToRow]);
    $("#" + name + "Table > tbody tr").removeClass('sel');
    var headerClass = $('#headerClass').val();
    var selectedClass = 'sel ' + headerClass ?? "";
    var row = $('tr[data-itemid="' + itemId + '"][data-itemtypeid="' + itemTypeId + '"]');
    if (row) {
        row.removeClass().addClass(selectedClass);
        if (moveToRow && row.length > 0) row[0].scrollIntoView();
    }
}
function rowData(name, rowIndex, dataName) {
    var rows = $('#' + name + 'Table > tbody tr');
    var data = $(rows[rowIndex]).data(dataName);
    return data ? data : 0;
}
function initRowSelect(ctx) {
    //consolelog('initRowSelect: ' + context);
    $('[data-role="rowSelect"][data-context= "' + ctx + '"]').prop('checked', false).change();
}
function rowSelectChanged(chkbox) {
    var rowId = $(chkbox).data('itemid');
    var context = $(chkbox).data('context');
    var isChecked = $(chkbox).is(':checked');
    //consolelog('rowSelectChanged: ' + rowId + ', ' + context + ', ' + isChecked);

    var row = $('#row' + rowId + '[data-context="' + context + '"]');
    $(row).removeClass("table-dark");

    $('[data-role="rowInput"][data-itemid="' + rowId + '"][data-context= "' + context + '"]').prop('disabled', !isChecked);
    $('[data-role="all"][data-itemid="' + rowId + '"][data-context= "' + context + '"]').prop('disabled', !isChecked);

    if (isChecked) $(row).addClass('table-dark');

    if (typeof rowSelected !== 'undefined' && typeof rowSelected === 'function') rowSelected(rowId, context, isChecked);
}
function rowsSelectChanged(chkbox) {
    var context = $(chkbox).data('context');
    var isChecked = $(chkbox).is(':checked');
    //consolelog('rowsSelectChanged: ' + context + ', ' + isChecked);

    $('[data-role="rowSelect"][data-context= "' + context + '"]').prop('checked', isChecked).change();
}
function getSelectedRows(ctx, title, css) {
    var selectedValues = [];
    var selector = "tr[data-context='" + ctx + "']";
    var matchedRows = $(selector);
    matchedRows.each(function () {
        var row = $(this);
        var isSelected = row.find('input[data-role="rowSelect"]').is(':checked');
        //debugger;
        if (isSelected) {
            var itemid = row.find('input[data-role="rowSelect"]').data('itemid');
            selectedValues.push({
                Id: itemid,
            });
        }
    });

    if (selectedValues.length === 0 && hasContent(title)) {
        showAlert(title, 'Please select at least one row.', css);
    }

    return selectedValues;
}

function toggleLegendLock(name) {
    var cookieName = 'legendLock' + name;
    consolelog('toggleLegendLock', cookieName);

    if (getCookie(cookieName, '0') == '1') {
        setCookie(cookieName, '0', 0);
    } else {
        setCookie(cookieName, '1', 100);
    }

    displayLegendLock(name);
}
function displayLegendLock(name) {
    consolelog('displayLegendLock', name);

    var cookieName = 'legendLock' + name;
    var addClassName = 'on';
    var removeClassName = 'off';
    if (getCookie(cookieName, '0') == '1') {
        addClassName = 'on';
        removeClassName = 'off';
    }

    $('#legendLockIcon' + name).addClass('fa-toggle-' + addClassName).removeClass('fa-toggle-' + removeClassName);
}
function initContextMenu(tableName) {
    consolelog("initContextMenu");
    $('#' + tableName + ' > tbody tr').each(function () {
        if (!hasContent($(this).data('menuname'))) {
            $(this).css('cursor', 'pointer');
        }
    });

    $('#' + tableName + ' > tbody tr').on('contextmenu', function (e) {
        e.preventDefault(); // Prevent the default context menu

        let ctxMenuName = $(this).data('menuname');
        if (!hasContent(ctxMenuName)) {
            return _callFailed('initContextMenu: row does not contain a data-menuname property', true);
        }

        const altMenuIndex = toInt($(this).data('altmenu-index'));
        if (altMenuIndex > 0) ctxMenuName += '-alt' + altMenuIndex;

        const properId = toProperId('#rowmenu' + ctxMenuName);
        if (!properId.exists) return _callFailed('initContextMenu: context menu ' + properId.value + ' not found', true);

        setRowItemValues(this);

        const ctxMenu = $('#rowmenu' + ctxMenuName);

        ctxMenu.css({
            position: 'absolute',
            top: e.clientY + 'px',
            left: e.clientX + 'px',
            display: 'block'
        });

        // Check if the menu goes off the page vertically
        const menuHeight = ctxMenu.outerHeight();
        const windowHeight = $(window).height();
        if (e.clientY + menuHeight > windowHeight) {
            ctxMenu.css('top', e.clientY - menuHeight + 'px');
        }

        // Check if the menu goes off the page horizontally
        const menuWidth = ctxMenu.outerWidth();
        const windowWidth = $(window).width();
        if (e.clientX + menuWidth > windowWidth) {
            ctxMenu.css('left', e.clientX - menuWidth + 'px');
        }

        ctxMenu.mouseleave(function () {
            setTimeout(function () {
                ctxMenu.css("display", "none");
            }, 200);
        });

        return false; //blocks default Webbrowser right click menu
    });

    $("body").on("click", function () {
        if ($("#ctxmenu").css('display') == 'block') {
            $(" #ctxmenu ").hide();
        }
    });

    $("#ctxmenu a").on("click", function () {
        $(this).parent().hide();
    });

    $("table").on("click", ".row-menu [data-toggle='dropdown']", function (e) {
        const row = $(this).closest("tr")[0];
        setRowItemValues(row);
    });
}

function setRowItemValues(row) {
    if (!row) return;
    //debugger;
    rowMenuSelectedRow = row;
    rowMenuItemTypeId = toInt($(row).data('itemtypeid'));
    rowMenuItemId = toInt($(row).data('itemid'));
    rowMenuItemReference = $(row).data('itemref');
    rowMenuItemAddress = $(row).data('itemaddress');
    rowMenuItemEmail = $(row).data('itememail');
    rowMenuItemStatusId = toInt($(row).data('itemstatusid'));
    rowMenuHref = $(row).data('href');
    rowMenuDeleteState = toInt($(row).data('deletestate'));

    rowMenuSiteId = toInt($(row).data('siteid'));
    rowMenuCallOffTypeId = toInt($(row).data('callofftypeid'));
    rowMenuVisitId = toInt($(row).data('visitid'));
}

/* Loop through all dropdown buttons to toggle between hiding and showing its dropdown content - This allows the user to have multiple dropdowns without any conflict */
var dropdownList, i;
dropdownList = document.getElementsByClassName("dropdown-btn");

if (dropdownList) {
    var showSM = getCookie('showSideMenu');
    for (let i = 0; i < dropdownList.length; i++) {
        var ddl = dropdownList[i];
        var ddm = ddl.nextElementSibling;

        if (showSM == 'true') {
            ddl.classList.add("active");
            ddm.style.display = "block";
        }

        ddl.addEventListener("click", function () {
            consolelog('Menu Click');
            var dropdownMenu = this.nextElementSibling;
            if (dropdownMenu.style.display === "block") {
                this.classList.remove("active");
                dropdownMenu.style.display = "none";
            } else {
                this.classList.add("active");
                dropdownMenu.style.display = "block";

                // Scroll the last link into view
                const lastLink = dropdownMenu.querySelector("a:last-child");
                if (lastLink) {
                    lastLink.scrollIntoView({ behavior: "smooth", block: "end" });
                }

                navigateToPage($(this).data('href'));
            }
        });
    }
}

function toggleMenu() {
    var dropdownList, i;
    dropdownList = document.getElementsByClassName("dropdown-btn");

    if (dropdownList) {
        var open = false, closed = false;
        for (i = 0; i < dropdownList.length; i++) {
            var dropdownContent = dropdownList[i].nextElementSibling;
            if (dropdownContent.style.display === "block") {
                open = true;
            } else {
                closed = true;
            }
        }
        for (i = 0; i < dropdownList.length; i++) {
            dropdownContent = dropdownList[i].nextElementSibling;
            if ((open && closed) || closed) {
                dropdownList[i].classList.add("active");
                dropdownContent.style.display = "block";
            } else {
                dropdownList[i].classList.remove("active");
                dropdownContent.style.display = "none";
            }
        }

        if ((open && closed) || closed) {
            setCookie('showSideMenu', 'true', 100);
        } else {
            setCookie('showSideMenu', 'false', 100);
        }
    }
}
 function showAlert(title, message, css) {
    if (!hasContent(message)) return _callFailed('showAlert');
    if (!hasContent(title)) title = 'Contor';
    if (!hasContent(css)) css = 'ctr';

    consolelog('showAlert', [title, message, css]);

    $('#pageAlertTitle').html(title);
    $('#pageAlertContent').html(message);

    var options = {
        "backdrop": "static",
        "keyboard": false
    }

    modalShow('#pageAlert', true, css);
}
function showConfirm(title, message, context, onConfirmCallback, css) {
    if (!title || title.length == 0) title = 'Contor';
    if (!message || message.length == 0) return;
    consolelog('showConfirm', [title, message, context]);

    $('#pageConfirmOkButton').off('click');
    $('#pageConfirmTitle').html(title);
    $('#pageConfirmContent').html(message);
    $('#pageConfirmContext').val(context);

    var onConfirm = function () {
        onConfirmCallback ? onConfirmCallback(context) : onConfirmOk(context);
    };

    $('#pageConfirmOkButton').click(onConfirm);

    modalShow('#pageConfirm', true, css);
}

function modalShow(id, onTop, css) {
    if (!css || css == '') css = 'ctr';
    consolelog('modalShow', [id, onTop, css]);

    if (onTop) $(id).css('zIndex', 99999);
    $(id).modal({
        backdrop: 'static',
        keyboard: false
    });

    $(id).draggable({
        handle: ".modal-header",
    });

    $(".modal-backdrop.in").css({ opacity: 0.1 });
    $(".modal-header").css({
        cursor: 'move',
    });

    $('.modal-header').addClass(css);

    var type = window.contorConfig.DebugMode ? "text" : "hidden";
    $(id + 'ItemTypeId').attr({title: 'Item Type Id', type: type});
    $(id + 'ItemId').attr({ title: 'Item Id', type: type });
    $(id + 'ItemReference').attr({ title: 'Item Reference', type: type });
    $(id + 'ParentItemTypeId').attr({ title: 'Parent Item Type Id', type: type });
    $(id + 'ParentItemId').attr({ title: 'Parent Item Id', type: type });
    $(id + 'ParentItemReference').attr({ title: 'Parent Item Reference', type: type });
}

function modalHide(id) {
    $(id).modal("hide")
}

function resizeModal(id, size) {
    if (!hasContent(size)) size = 'xl';

    if (size && (size == 'sm' || size == 'md' || size == 'lg' || size == 'xl')) {
        $('#'+id).removeClass('modal-sm').removeClass('modal-md').removeClass('modal-lg').removeClass('modal-xl');
        $('#'+id).addClass('modal-' + size);
    }
}
function getModalCss(type) {
    if (!type) return 'ctr';
    type = toInt(type);

    if (type == 1 || type == 101) return 'enq';
    if (type == 2 || type == 4) return 'obk';
    if (type == 3 || (type > 30 && type < 40)) return 'nwb';
    if (type == 5 || type == 51) return 'svc';
    if (type == 6 || (type > 60 && type < 70)) return 'prd';
    if (type == 7) return 'rep';
    if (type == 8 || (type > 80 && type < 90)) return 'adm';
    if (type == 10) return 'sys';

    return 'ctr';
}
function emptyItemData(type) {
    return {
        id: 0,
        type: toInt(type),
        ref: '',
        validate: false
    };
}
function getItemData(type, id, ref, validate) {
    if (validate == null) validate = true;

    return {
        id: toInt(id),
        type: toInt(type),
        ref: ref,
        validate: toBool(validate)
    };
}

function resetModals(modalType) { //NEED TO THINK THIS THROUGH
    if (hasContent(modalType, 'Add')) {
        $('#pageAddModalContent').html('');
        $('#pageAddModalHeader').removeClass().addClass('modal-header').addClass('ui-gragable-handle');
        $('#pageAddModalMessage').html('').hide();
        $('#pageAddModalAlert').html('').hide();
        $('#pageAddModalSaveButton').off('click');
        $('#pageAddModalSaveButton').prop('disabled', false);
    } else if (hasContent(modalType, 'AddOther')) {
        $('#pageAddOtherModalContent').html('');
        $('#pageAddOtherModalHeader').removeClass().addClass('modal-header').addClass('ui-gragable-handle');
        $('#pageAddOtherModalMessage').html('').hide();
        $('#pageAddOtherModalAlert').html('').hide();
        $('#pageAddOtherModalSaveButton').off('click');
        $('#pageAddOtherModalSaveButton').prop('disabled', false);
    } else if (hasContent(modalType, 'Edit')) {
        $('#pageEditModalContent').html('');
        $('#pageEditModalHeader').removeClass().addClass('modal-header').addClass('ui-gragable-handle');
        $('#pageEditModalMessage').html('').hide();
        $('#pageEditModalAlert').html('').hide();
        $('#pageEditModalSaveButton').off('click');
        $('#pageEditModalSaveButton').prop('disabled', false);
        $('#pageEditModalSaveButton').show();
        $('#pageEditModalConfirmButton').off('click').hide();
        $('#pageEditModalDeleteButton').off('click').hide();
        $('#pageEditModalDeleteButton').prop('disabled', false);
    } else if (hasContent(modalType, 'ViewEdit')) {
        $('#pageViewEditModalContent').html('');
        $('#pageViewEditModalHeader').removeClass().addClass('modal-header').addClass('ui-gragable-handle');
        $('#pageViewEditModalMessage').html('').hide();
        $('#pageViewEditModalAlert').html('').hide();
        $('#pageViewEditModalSaveButton').off('click').hide();
        $('#pageViewEditModalSaveButton').prop('disabled', false);
        $('#pageViewEditModalEditButton').off('click').hide();
        $('#pageViewEditModalCancelButton').off('click').hide();
        $('#pageViewEditModalConfirmButton').off('click').hide();
        $('#pageViewEditModalDeleteButton').off('click').hide();
        $('#pageViewEditModalDeleteButton').prop('disabled', false);
    }
}
function showModal(title, url, itemData, viewUrl, viewName, css, size, onShowModal) {
    consolelog('showModal', [title, url, viewUrl, css]);
    if (!itemData || (itemData.validate && (toInt(itemData.id) == 0 || toInt(itemData.type) == 0))) return _callFailed('showModal: itemData');
    if (!hasContent(url)) return _callFailed('showModal: url');

    $('#pageModalViewButton').hide();
    $('#pageModalMessage').hide();

    $('#pageModalTitle').html(title);
    loadUrl('#pageModalContent', url, function () {
        $('.modal .formHelp').hide();
        $('.modal .pageHelp').hide();
        if (typeof onShowModal == 'function') onShowModal();
    });

    if (viewUrl && viewUrl.length > 0) {
        $('#pageModalViewButton').prop('href', viewUrl);
        $('#pageModalViewButton').html(viewName && viewName.length > 0 ? 'View ' + viewName : 'View');
        $('#pageModalViewButton').show();
    }

    resizeModal('pageModalDialog', size);

    modalShow('#pageModal', false, css);
}
function showAddModal(title, url, itemData, saveCallback, css, size, saveButtonText, onShowCallback, validate) {
    consolelog('showAddModal', [title, url, css]);

    if (!itemData || (itemData.validate && (toInt(itemData.id) == 0 || toInt(itemData.type) == 0))) return _callFailed('showAddModal: itemData');
    if (!hasContent(url)) return _callFailed('showAddModal: url');

    if (!hasContent(css)) css = getModalCss(itemData.type);
    if (!hasContent(saveButtonText)) saveButtonText = 'Save';

    resetModals('Add');

    $('#pageAddModalHeader').addClass(css);
    $('#pageAddModalSaveButton').html(saveButtonText);
    $('#pageAddModalTitle').html(title);

    if (saveCallback) {
        $('#pageAddModalSaveButton').click(saveCallback);
    }
    else {
        setPageAddModalSystemError('Save button not bound');
        $('#pageAddModalSaveButton').prop('disabled', true);
    }

    if (validate == null) validate = true;

    setModalItem('Add', itemData.type, itemData.id, itemData.reference, css);
    loadUrl('#pageAddModalContent', url, function () {
        if (validate) validateModalFrm('pageAddModalContent');
        $('.modal .formHelp').hide();
        $('.modal .pageHelp').hide();
        if (typeof onShowCallback == 'function') onShowCallback();
    });

    resizeModal('pageAddModalDialog', size);

    modalShow('#pageAddModal', null, css);
}
function showAddOtherModal(title, url, itemData, saveCallback, css, size, saveButtonText, onShowCallback, validate) {
    consolelog('showAddOtherModal', [title, url, css]);

    if (!itemData || (itemData.validate && (toInt(itemData.id) == 0 || toInt(itemData.type) == 0))) return _callFailed('showAddOtherModal: itemData');
    if (!hasContent(url)) return _callFailed('showAddOtherModal');

    if (!hasContent(css)) css = getModalCss(itemData.type);
    if (!hasContent(saveButtonText)) saveButtonText = 'Save';

    resetModals('AddOther');

    $('#pageAddOtherModalHeader').addClass(css);
    $('#pageAddOtherModalSaveButton').html(saveButtonText);
    $('#pageAddOtherModalTitle').html(title);

    if (saveCallback) {
        $('#pageAddOtherModalSaveButton').click(saveCallback);
    }
    else {
        setPageAddModalSystemError('Save button not bound');
        $('#pageAddOtherModalSaveButton').prop('disabled', true);
    }

    if (validate == null) validate = true;
    //debugger;
    setModalItem('AddOther', itemData.type, itemData.id, itemData.reference, css);
    loadUrl('#pageAddOtherModalContent', url, function () {
        if (validate) validateModalFrm('pageAddOtherModalContent');
        $('.modal .formHelp').hide();
        $('.modal .pageHelp').hide();
        if (typeof onShowCallback == 'function') onShowCallback();
    });

    resizeModal('pageAddOtherModalDialog', size);

    modalShow('#pageAddOtherModal', null, css);
}
function showEditModal(title, viewUrl, itemData, saveCallback, deleteCallback, recoverCallback, deleteState, delteConfirm, css, size, saveButtonText, deleteButtonText) {
    consolelog('showEditModal', [title, viewUrl, css]);

    if (!itemData || (itemData.validate && (toInt(itemData.id) == 0 || toInt(itemData.type) == 0))) return _callFailed('showEditModal: itemData');
    if (!hasContent(viewUrl)) return _callFailed('showEditModal: url');

    if (!hasContent(css)) css = getModalCss(itemData.type);
    if (!hasContent(saveButtonText)) saveButtonText = 'Save';
    if (!hasContent(deleteButtonText)) deleteButtonText = 'Delete';

    resetModals('Edit');

    $('#pageEditModalHeader').addClass(css);
    $('#pageEditModalTitle').html(title);

    const canDelete = toInt(deleteState) > 0;
    const isDeleted = toInt(deleteState) == 2;

    if (isDeleted) {
        $('#pageEditModalTitle').html(title + ' <span class="text-danger">[DELETED]</span>');
    }

    var showDelete = canDelete && deleteCallback != null;
    var showRecover = isDeleted && recoverCallback != null;

    if (saveCallback) {
        $('#pageEditModalSaveButton').html(saveButtonText)
        $('#pageEditModalSaveButton').click(saveCallback);
    }
    else {
        setPageEditModalSystemError('Save button not bound');
        $('#pageEditModalSaveButton').prop('disabled', true);
    }

    if (showRecover) {
        $('#pageEditModalDeleteButton').click(recoverCallback);
        $('#pageEditModalDeleteButton').html('Recover').removeClass('btn-danger').addClass('btn-primary');

    } else if (showDelete) {
        $('#pageEditModalDeleteButton').html(deleteButtonText).removeClass('btn-primary').addClass('btn-danger');
        if (delteConfirm) {
            $('#pageEditModalConfirmButton').click(deleteCallback);
            $('#pageEditModalDeleteButton').click(function () {
                setPageEditModalConfirm('Are you sure you want to delete this data?');
                $('#pageEditModalDeleteButton').hide();
                $('#pageEditModalSaveButton').hide();
                $('#pageEditModalConfirmButton').show();
            });
        } else {
            $('#pageEditModalDeleteButton').click(deleteCallback);
        }
    }

    setModalItem('Edit', itemData.type, itemData.id, itemData.reference, css);
    loadUrl('#pageEditModalContent', viewUrl, function () {
        validateModalFrm('pageEditModalContent');
        $('.modal .formHelp').hide();
        $('.modal .pageHelp').hide();
    });

    resizeModal('pageEditModalDialog', size);

    modalShow('#pageEditModal', null, css);
}

function showViewEditModal(title, viewUrl, editUrl, itemData, saveCallback, deleteCallback, recoverCallback, deleteState, deleteConfirm, css, size, saveButtonText, deleteButtonText) {
    consolelog('showViewEditModal: ' + title + '; ' + viewUrl + ', ' + editUrl + '; ' + css);

    if (!itemData || (itemData.validate && (toInt(itemData.id) == 0 || toInt(itemData.type) == 0))) return _callFailed('showViewEditModal: itemData');
    if (!hasContent(viewUrl) || !hasContent(editUrl)) return _callFailed('showViewEditModal: url');

    if (!hasContent(css)) css = getModalCss(itemData.type);

    resetModals('ViewEdit');

    $('#pageViewEditModalHeader').addClass(css);
    $('#pageViewEditModalTitle').html(title);

    const canDelete = toInt(deleteState) > 0;
    const isDeleted = toInt(deleteState) == 2;

    if (isDeleted) {
        $('#pageViewEditModalTitle').html(title + ' <span class="text-danger">[DELETED]</span>');
    }

    var showDelete = canDelete && deleteCallback != null;
    var showRecover = isDeleted && recoverCallback != null;

    var view = function () {
        pageViewEditModal_ViewMode(showRecover);
        loadUrl('#pageViewEditModalContent', viewUrl, function () {
            $('.modal .formHelp').hide();
            $('.modal .pageHelp').hide();
        });
    };
    var edit = function () {
        pageViewEditModal_EditMode(viewUrl, showDelete);
        loadUrl('#pageViewEditModalContent', editUrl, function () {
            validateModalFrm('pageViewEditModalContent');
            $('.modal .formHelp').hide();
            $('.modal .pageHelp').hide();
        });
    };

    if (saveCallback) {
        $('#pageViewEditModalSaveButton').click(saveCallback);
    }
    else {
        setPageViewEditModalSystemError('Save button not bound');
        $('#pageEditModalSaveButton').prop('disabled', true);
    }

    if (showRecover) {
        $('#pageViewEditModalDeleteButton').click(recoverCallback);
        $('#pageViewEditModalDeleteButton').html('Recover').removeClass('btn-danger').addClass('btn-primary');

    } else if (showDelete) {
        $('#pageViewEditModalDeleteButton').html('Delete').removeClass('btn-primary').addClass('btn-danger');
        if (deleteConfirm) {
            $('#pageViewEditModalConfirmButton').click(deleteCallback);
            $('#pageViewEditModalDeleteButton').click(function () {
                setPageViewEditModalConfirm('Are you sure you want to delete this data?');
                $('#pageViewEditModalDeleteButton').hide();
                $('#pageViewEditModalSaveButton').hide();
                $('#pageViewEditModalConfirmButton').show();
            });
        } else {
            $('#pageViewEditModalDeleteButton').click(deleteCallback);
        }
    }

    $('#pageViewEditModalCancelButton').click(view);
    $('#pageViewEditModalEditButton').click(edit);

    if (viewUrl && viewUrl.length > 0) view(); else edit();

    setModalItem('ViewEdit', itemData.type, itemData.id, itemData.reference, css);

    resizeModal('pageViewEditModalDialog', size);

    modalShow('#pageViewEditModal', null, css);
}
function pageViewEditModal_EditMode(viewUrl, showDelete) {
    if (viewUrl && viewUrl.length > 0) {
        $('#pageViewEditModalCloseButton').hide();
        $('#pageViewEditModalCancelButton').show();
    } else {
        $('#pageViewEditModalCloseButton').show();
        $('#pageViewEditModalCancelButton').hide();
    }

    $('#pageViewEditModalEditButton').hide();
    $('#pageViewEditModalSaveButton').show();
    $('#pageViewEditModalMessage').hide();

    if (showDelete) $('#pageViewEditModalDeleteButton').show();
}
function pageViewEditModal_ViewMode(showRecover) {
    $('#pageViewEditModalSaveButton').hide();
    $('#pageViewEditModalCancelButton').hide();
    $('#pageViewEditModalConfirmButton').hide();
    $('#pageViewEditModalDeleteButton').hide();
    $('#pageViewEditModalEditButton').show();
    $('#pageViewEditModalCloseButton').show();
    $('#pageViewEditModalMessage').hide();

    if (showRecover) {
        $('#pageViewEditModalDeleteButton').show();
        $('#pageViewEditModalEditButton').hide();
    }
}

function setModalItem(modalType, itemTypeId, itemId, itemReference, itemCss) {
    if (!hasContent(itemCss)) itemCss = 'ctr';
    //debugger;
    let ctrlId = '#page' + modalType + 'ModalItemTypeId'
    if (toInt($(ctrlId).val()) == 0 && toInt(itemTypeId) > 0)
        $(ctrlId).val(itemTypeId); 

    ctrlId = '#page' + modalType + 'ModalItemId';
    if (toInt($(ctrlId).val()) == 0 && toInt(itemId) > 0)
        $(ctrlId).val(itemId);

    ctrlId = '#page' + modalType + 'ModalItemReference';
    if (!hasContent($(ctrlId).val()) && hasContent(itemReference))
        $(ctrlId).val(itemReference);
}
function getModalItem(modalType, data) {
    let id = toInt($('#page' + modalType + 'ModalItemTypeId').val());
    if (id > 0) data.ItemTypeId = id;

    id = toInt($('#page' + modalType + 'ModalItemId').val());
    if (id > 0) data.ItemId = id;

    let ref = $('#page' + modalType + 'ModalItemReference').val();
    if (hasContent(ref)) data.ItemReference = ref;

    id = toInt($('#page' + modalType + 'ModalParentItemTypeId').val());
    if (id > 0) data.ParentItemTypeId = id;

    id = toInt($('#page' + modalType + 'ModalParentItemId').val());
    if (id > 0) data.ParentItemId = id;

    ref = $('#page' + modalType + 'ModalParentItemReference').val();
    if (hasContent(ref)) data.ParentItemReference = ref;

    return data;
}
function itemQs(itemData, displayMode) {
    if (!itemData) return '';
    var qs = `?itemType=${itemData.type}&itemId=${itemData.id}&itemRef=${itemData.ref}`;
    if (displayMode) qs += `&displayMode=${toInt(displayMode)}`;
    return qs;
}
function getItemTitle(itemData, name) {
    const title = name + (hasContent(itemData.ItemReference) ? ' for ' + itemData.ItemReference : '');
    return title;
}
function getItemViewUrl(itemData, itemUrl) {
    if (!hasContent(itemUrl)) {
        itemUrl = '/App/ItemRedirect/?itemType=' + itemData.ItemTypeId + '&itemId=' + itemData.ItemId;
    }

    return itemUrl;
}

function _enableAddModalSave(enable) {
    $('#pageAddModalSaveButton').prop('disabled', !enable);
}
function _enableEditModalDelete(enable) {
    $('#pageEditModalDeleteButton').prop('disabled', !enable);
}

function setPageModalError(msg) {
    setModalError('pageModalMessage', msg);
};
function setPageModalSuccess(msg) {
    setModalSuccess('pageModalMessage', msg);
};
function setPageAddModalSystemError(msg) {
    consolelog('setPageAddModalError: ' + msg);
    setModalError('pageAddModalMessage', '**SYSTEM**' + msg);
};
function setPageAddModalError(msg) {
    consolelog('setPageAddModalError: ' + msg);
    setModalError('pageAddModalMessage', msg);
};
function setPageAddModalSuccess(msg) {
    setModalSuccess('pageAddModalMessage', msg);
};
function setPageAddOtherModalSystemError(msg) {
    consolelog('setPageAddOtherModalError: ' + msg);
    setModalError('pageAddOtherModalMessage', '**SYSTEM**' + msg);
};
function setPageAddOtherModalError(msg) {
    consolelog('setPageAddOtherModalError: ' + msg);
    setModalError('pageAddOtherModalMessage', msg);
};
function setPageAddOtherModalSuccess(msg) {
    setModalSuccess('pageAddOtherModalMessage', msg);
};

function setPageEditModalSystemError(msg) {
    setModalError('pageEditModalMessage', '**SYSTEM**'+msg);
};
function setPageEditModalError(msg) {
    setModalError('pageEditModalMessage', msg);
};
function setPageEditModalSuccess(msg) {
    setModalSuccess('pageEditModalMessage', msg);
};
function setPageEditModalConfirm(msg) {
    setModalConfirm('pageEditModalMessage', msg);
};
function setPageViewEditModalSystemError(msg) {
    setModalError('pageViewEditModalMessage', '**SYSTEM**'+msg);
};
function setPageViewEditModalError(msg) {
    setModalError('pageViewEditModalMessage', msg);
};
function setPageViewEditModalConfirm(msg) {
    setModalConfirm('pageViewEditModalMessage', msg);
};
function setPageViewEditModalSuccess(msg) {
    setModalSuccess('pageViewEditModalMessage', msg);
};
function setModalError(id, msg) {
    consolelog('setModalError: ' + id + '; ' + msg);
    $('#' + id).removeClass('text-success').removeClass('text-info').addClass('text-danger').html(msg);
    $('#' + id).show();
};
function setModalConfirm(id, msg) {
    consolelog('setModalConfirm', [id,msg]);
    $('#' + id).removeClass('text-success').removeClass('text-danger').html(msg).addClass('text-info').html(msg);
    $('#' + id).show();
};
function setModalSuccess(id, msg) {
    consolelog('setModalSuccess: ' + id + '; ' + msg);
    $('#' + id).removeClass('text-danger').removeClass('text-info').addClass('text-success').html(msg);
    $('#' + id).show();
};

function setModalAccess(canAdd, canEdit) {
    $('#pageAddModalSaveButton').prop('disabled', !canAdd);
    $('#pageAddOtherModalSaveButton').prop('disabled', !canAdd);
    $('#pageEditModalSaveButton').prop('disabled', !canEdit);
    $('#pageEditModalDeleteButton').prop('disabled', !canEdit);
    $('#pageViewEditModalEditButton').prop('disabled', !canEdit);
    $('#pageViewEditModalSaveButton').prop('disabled', !canEdit);
    $('#pageViewEditModalDeleteButton').prop('disabled', !canEdit);
}

function dismissPageModal() {
    consolelog('dismissPageModal');
    modalHide('#pageModal');
};
function dismissPageAddModal() {
    modalHide('#pageAddModal');
};
function dismissPageAddOtherModal() {
    modalHide('#pageAddOtherModal');
};
function dismissPageEditModal() {
    modalHide('#pageEditModal');
};
function dismissPageViewEditModal() {
    modalHide('#pageViewEditModal');
};

//COMMON
function isCommonContext(ctx) {
    if (!hasContent(ctx)) return false;
    ctx = ctx.toLowerCase();

    if (ctx == 'notes') return true;
    if (ctx == 'tasks') return true;
    if (ctx == 'payments') return true;
    if (ctx == 'appointments') return true;
    if (ctx == 'attachments') return true;
    if (ctx == 'products') return true;
    if (ctx == 'partorders') return true;
    if (ctx == 'itemlinks') return true;
    if (ctx == 'itemmessages') return true;

    return false;
}
function _loadCommonChildContent(childName, itemTypeId, itemId, itemReference, displayMode) {
    if (!hasContent(childName) || toInt(itemTypeId) <= 0 || toInt(itemId) <= 0)
        return _callFailed('_loadCommonChildContent: invalid parameters');

    if (!isCommonContext(childName))
        return _callFailed('_loadCommonChildContent: not common context', true);

    const contentId = 'content_' + childName;
    if (!toProperId(contentId, false).exists)
        return _callFailed('_loadCommonChildContent: contentId');

    displayMode = toInt(displayMode);
    if (displayMode < 1) displayMode = 1;   //FYI: inline
    if (displayMode > 2) displayMode = 2;   //FYI: modal

    let loaded = true;

    if (childName == 'notes')
        _loadNotes(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'tasks')
        _loadTasks(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'payments')
        _loadPayments(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'appointments')
        _loadAppointments(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'attachments')
        _loadAttachments(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'products')
        _loadProducts(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'partorders')
        _loadPartOrders(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'itemmessages')
        _loadItemMessages(contentId, itemTypeId, itemId, itemReference, displayMode);
    else if (childName == 'itemlinks')
        _loadItemLinks(contentId, itemTypeId, itemId, itemReference, displayMode);
    else
        loaded = false;

    consolelog('_loadCommonChildContent', [contentId, loaded]);

    return loaded;
}

//FILTERS
function _applyFilters(filterType, filtersId) {
    if (toInt(filterType) <= 0 || toInt(filtersId) <= 0)
        return _callFailed('_applyFilters: invalid parameters');

    consolelog('_applyFilters', [filterType, filtersId]);
    ajaxPostJsonData('/App/FiltersApply', { filterType: filterType, filterId: filtersId }, reloadPage);
}
function _clearFilters(filterType) {
    if (toInt(filterType) <= 0)
        return _callFailed('_clearFilters: invalid parameters');

    consolelog('_clearFilters', filterType);
    ajaxPostJsonData('/App/FiltersClear', { filterType: filterType }, reloadPage);
}
function _showFilters(title, showUrl, saveUrl, css) {
    if (!hasContent(showUrl) || !hasContent(saveUrl))
        return _callFailed('_showFilters: invalid parameters');
    if (!hasContent(title))
        title = 'Filters';

    consolelog('_showFilters', [title, showUrl, saveUrl, css]);

    var saveCallback = function () {
        _saveFilters(saveUrl);
    };

    showAddModal(title, showUrl, emptyItemData(), saveCallback, css);
}
function _saveFilters(url) {
    if (!hasContent(url))
        return _callFailed('_saveFilters: invalid parameters');;
    if (typeof getFiltersData !== 'function')
        return _callFailed('_saveFilters: invalid function call');;

    consolelog('_saveFilters', url);

    const filterData = getFiltersData();
    ajaxPostJsonData(url, filterData, reloadPage);
}

//NOTES
function _loadNotes(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('Notes', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showModalNotes(itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl) {
    if (hasContent(itemName) && !hasContent(itemViewUrl)) {
        itemViewUrl = '/App/ItemRedirect?itemType=' + itemTypeId  + '&itemId=' + itemId;
    }
    _showAppModal('Notes', itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl); 
}
function _showAddNote(titleSuffix, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showAddNote');

    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    const itemData = getItemData(itemTypeId, itemId, itemReference);

    consolelog('_showAddNote', itemData);

    const getUrl = '/App/NoteAdd/' + itemQs(itemData);
    const postUrl = '/App/NoteAdd';

    const saveCallback = function () {
        if (!validateAddModalFrm()) return;

        const data = getNoteAddData();
        ajaxPostJsonAddData(postUrl, data, 'notes');
    };

    showAddModal(addSuffix("Add Note", titleSuffix), getUrl, itemData, saveCallback, itemCss, 'lg');
}
function _showViewEditNote(titleSuffix, itemId, itemCss) {
    if (toInt(itemId) <= 0) return callFailed('_showViewEditNote');

    consolelog('_showViewEditNote', itemId);

    const viewUrl = '/App/Note?noteId=' + itemId;
    const getUrl = '/App/NoteEdit?noteId=' + itemId;
    const postUrl = '/App/NoteEdit';

    const saveCallback = function () {
        if (!validateViewEditModalFrm()) return;

        const data = getNoteEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'notes');
    };

    const deleteCallback = null;
    const recoverCallback = null;

    showViewEditModal(addSuffix("View Note", titleSuffix), viewUrl, getUrl, emptyItemData(), saveCallback, deleteCallback, recoverCallback, 0, true, itemCss);
}

//ATTACHMENTS
function _loadAttachments(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('Attachments', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showModalAttachments(itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl) {
    _showAppModal('Attachments', itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl);
}
function _showAddAttachment(titleSuffix, itemTypeId, itemId, itemReference, itemCss) {
    //debugger;
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return;

    const getUrl = '/App/AttachmentAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    const postUrl = '/App/AttachmentAdd';
    consolelog('showAddAttachment', [getUrl, titleSuffix]);

    const saveCallback = function () {
        if (validateAddModalFrm()) {
            const data = getAttachmentAddData();
            ajaxPostFormAddData(postUrl, data, 'attachments');
        }
    };

    showAddModal(addSuffix("Add Attachment", titleSuffix), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'lg');
}
function _showAddLogo(itemTypeId, itemId) {
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return;

    const getUrl = '/App/AttachmentAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=';
    const postUrl = '/App/LogoAdd';
    consolelog('showAddAttachment', [getUrl]);

    const saveCallback = function () {
        if (validateAddModalFrm()) {
            const data = getAttachmentAddData();
            ajaxPostFormAddData(postUrl, data, 'attachments');
        }
    };

    showAddModal("Select an Image", getUrl, getItemData(itemTypeId, itemId), saveCallback, '', 'md');
}
function viewAttachment(attachmentId) {
    if (toInt(attachmentId) <= 0) attachmentId = rowMenuItemId;
    if (toInt(attachmentId) <= 0) return _callFailed('viewItemAttachment');

    var url = '/App/Attachment/?attachmentId=' + attachmentId;
    const win = window.open(url, '_blank');

    if (!win) {
        alert("Your browser blocked the attachment popup. Please allow popups for this site.");
    }
}
function deleteAttachment(itemId, itemTypeId) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('deleteAttachment');

    var onConfirmDelete = function () {
        var data = { itemId: itemId, itemTypeId: itemTypeId };
        ajaxPostJsonData('/App/AttachmentDelete/', data, onSuccessEdit, null, 'attachment');
    };

    showConfirm('Delete Attachment', 'Are you sure you want to delete this attachment?', 'attachment', onConfirmDelete);
}


//APPOINTMENTS
function _loadAppointments(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('Appointments', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showModalAppointments(itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl) {
    _showAppModal('Appointments', itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl);
}
function _showAddAppointment(titleSuffix, appointmentTypeId, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    const itemData = getItemData(itemTypeId, itemId, itemReference, false);

    consolelog('_showAddAppointment', itemData);

    const getUrl = '/App/AppointmentAdd/' + itemQs(itemData) + '&appointmentType=' + appointmentTypeId;
    const postUrl = '/App/AppointmentAdd';

    const saveCallback = function () {
        if (!validateAddModalFrm()) return;

        const data = getAppointmentAddData();
        ajaxPostJsonAddData(postUrl, data, 'appointments');
    };

    showAddModal(addSuffix("Add Appointment", titleSuffix), getUrl, itemData, saveCallback, itemCss, 'lg');
}
function _showViewEditAppointment(titleSuffix, itemId, deleteState, confirm, itemCss) {
    //debugger;
    if (toInt(itemId) <= 0) return;

    const viewUrl = '/App/Appointment?appointmentId=' + itemId;
    const getUrl = '/App/AppointmentEdit?appointmentId=' + itemId;
    const postUrl = '/App/AppointmentEdit';
    consolelog('_showViewEditAppointment', [getUrl, titleSuffix]);

    const saveCallback = function () {
        if (!validateViewEditModalFrm()) return;

        const data = getAppointmentEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'appointments');
    };

    showViewEditModal(addSuffix("View Appointment", titleSuffix), viewUrl, getUrl, emptyItemData(), saveCallback, null, null, deleteState, confirm, itemCss, 'lg');
}
function _showViewEditInstallation(titleSuffix, itemId, deleteState, confirm, itemCss) {
    if (toInt(itemId) <= 0) return;

    const viewUrl = '/App/Installation?installationId=' + itemId;
    const getUrl = '/App/InstallationEdit?installationId=' + itemId;
    const postUrl = '/App/InstallationEdit';
    consolelog('_showViewEditInstallation', [getUrl, titleSuffix]);

    const saveCallback = function () {
        if (!validateViewEditModalFrm()) return;

        const data = getInstallationEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'appointments');
    };

    showViewEditModal(addSuffix("View Installation", titleSuffix), viewUrl, getUrl, emptyItemData(), saveCallback, null, null, deleteState, confirm, itemCss, 'lg');
}
function _showViewEditSurvey(titleSuffix, itemId, deleteState, confirm, itemCss) {
    if (toInt(itemId) <= 0) return;

    const viewUrl = '/App/Survey?surveyId=' + itemId;
    const getUrl = '/App/SurveyEdit?surveyId=' + itemId;
    const postUrl = '/App/SurveyEdit';
    consolelog('_showViewEditSurvey', [getUrl, titleSuffix]);

    const saveCallback = function () {
        if (!validateViewEditModalFrm()) return;

        const data = getSurveyEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'appointments');
    };

    showViewEditModal(addSuffix("View Survey", titleSuffix), viewUrl, getUrl, emptyItemData(), saveCallback, null, null, deleteState, confirm, itemCss, 'lg');
}


//ITEM MESSAGE
function _loadItemMessages(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('ItemMessages', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showAddItemMessage(titleSuffix, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return callFailed('_showAddItemMessage');

    var getUrl = '/App/ItemMessageAdd?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/ItemMessageAdd';
    consolelog('showAddItemMessage'[getUrl, titleSuffix]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getItemMessageAddData();
            ajaxPostJsonAddData(postUrl, data, 'itemmessages');
        }
    };

    showAddModal(addSuffix("Add Message", titleSuffix), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, '');
}
function _showViewItemMessage(titleSuffix, itemId, itemCss) {
    if (toInt(itemId) <= 0) return _callFailed('_showViewItemMessage');

    const url = '/App/ItemMessage?itemMessageId=' + itemId;
    consolelog('_showViewItemMessage', [url, titleSuffix]);

    showModal(addSuffix("View Message", titleSuffix), url, emptyItemData(), null, null, itemCss, 'lg');
}
function _showEditItemMessage(titleSuffix, itemId, itemCss) {
    if (toInt(itemId) <= 0) return _callFailed('_showEditItemMessage');

    const getUrl = '/App/ItemMessageEdit?itemMessageId=' + itemId;
    const postUrl = '/App/ItemMessageEdit';
    consolelog('_showEditItemMessage', [getUrl, titleSuffix]);

    const saveCallback = function () {
        if (!validateEditModalFrm()) return;

        const data = getItemMessageEditData();
        ajaxPostJsonEditData(postUrl, data, 'itemmessages');
    };
    const deleteCallback = null;
    const recoverCallback = null;

    showEditModal(addSuffix("Edit Message", titleSuffix), getUrl, emptyItemData(), saveCallback, deleteCallback, recoverCallback, 0, true, itemCss, 'lg', 'Send');
}

//TASKS
function _loadTasks(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('Tasks', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showAddTask(titleSuffix, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return;

    var getUrl = '/App/TaskAdd?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/TaskAdd';
    consolelog('showAddTask'[getUrl, titleSuffix]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getTaskAddData();
            ajaxPostJsonAddData(postUrl, data, 'tasks');
        }
    };

    showAddModal(addSuffix("Add Task", titleSuffix), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'lg');
}
function _showEditTask(titleSuffix, itemId, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemId) <= 0) return _callFailed('_showEditTask');

    let getUrl = '/App/TaskEdit?taskId=' + itemId;
    let postUrl = '/App/TaskEdit';
    consolelog('_showEditTask'[getUrl, titleSuffix]);

    var saveCallback = function () {
        if (validateEditModalFrm()) {
            var data = getTaskEditData();
            ajaxPostJsonAddData(postUrl, data, 'tasks');
        }
    };

    const deleteCallback = null;
    const recoverCallback = null;

    showEditModal(addSuffix("Edit Task", titleSuffix), getUrl, emptyItemData(), saveCallback, deleteCallback, recoverCallback, 0, true, itemCss, 'lg');
}
function _showViewEditTask(titleSuffix, itemId, deleteState, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemId) <= 0) return _callFailed('_showViewEditTask');
    //debugger;
    consolelog('_showViewEditTask', [itemId, titleSuffix]);

    let viewUrl = '/App/Task?taskId=' + itemId;
    let editUrl = '/App/TaskEdit?taskId=' + itemId;
    let postUrl = '/App/TaskEdit';

    let saveCallback = function () {
        if (!validateFrm(null, 'pageViewEditModalContent')) return;

        var data = getTaskEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'tasks');
    };

    showViewEditModal(addSuffix("View/Edit Task", titleSuffix), viewUrl, editUrl, emptyItemData(), saveCallback, null, null, deleteState, true, itemCss, 'lg');
}
function _nudgeTask(taskId, onSuccess) {
    const data = { TaskId: taskId };
    const url = '/App/TaskNudge';
    ajaxPostJsonData(url, data, onSuccess, null, '', '');
}
function _showTaskModalNotes(itemTypeId, itemId, itemReference) {
    var itemViewUrl = '/App/ItemRedirect?itemType=' + itemTypeId + '&itemId=' + itemId;
    _showAppModal('Notes', itemTypeId, itemId, itemReference, '', 'Task', itemViewUrl);
}

//GENERAL
function _showAddReferenceData(titleSuffix, itemType, itemCss, tag) {
    var getUrl = '/App/ReferenceDataAdd?itemType=' + itemType;
    if (tag && tag != '') getUrl += '&tag=' + tag;
    var postUrl = '/App/ReferenceDataAdd';
    consolelog('_showAddReferenceData'[getUrl, titleSuffix]);

    var saveCallback = function () {
        if (validateAddOtherModalFrm()) {
            var data = getReferenceDataAddData();
            ajaxPostJsonAddOtherData(postUrl, data, 'refdata');
        }
    };

    showAddOtherModal(addSuffix("Add", titleSuffix), getUrl, getItemData(itemType, 0, '', false), saveCallback, itemCss, 'md');
}
function _showChangeStatus(titleSuffix, currentStatus, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = toInt(rowMenuItemId);
    if (toInt(itemTypeId) <= 0) itemTypeId = toInt(rowMenuItemTypeId);
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(currentStatus) <= 0) currentStatus = toInt(rowMenuItemStatusId);

    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) callFailed('_showChangeStatus');

    const getUrl = '/App/ChangeStatus?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference + '&statusId=' + currentStatus;
    const postUrl = '/App/ChangeStatus';
    consolelog('_showChangeStatus', [getUrl, titleSuffix]);

    const saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getChangeStatusData();
            ajaxPostJsonAddData(postUrl, data, 'changestatus');
        }
    };

    showAddModal(addSuffix("Change Status", titleSuffix), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'md');
}

//PAYMENTS
function _loadPayments(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('Payments', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showAddOmitPayment(itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showAddOmitPayment');

    var getUrl = '/App/PaymentAddOmitAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/PaymentAdd';

    consolelog('_showAddOmitPayment', [getUrl, itemReference]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getPaymentAddOmitAddData();
            ajaxPostJsonAddData(postUrl, data, 'payments');
        }
    };

    showAddModal(addSuffix("Add - Add/Omit Transaction", itemReference), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'lg');
}
function _showAddPayment(itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showAddPayment');

    var getUrl = '/App/PaymentAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/PaymentAdd';

    consolelog('_showAddPayment', [getUrl, itemReference]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getPaymentAddData();
            ajaxPostJsonAddData(postUrl, data, 'payments');
        }
    };

    showAddModal(addSuffix("Add - Payment Transaction", itemReference), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'lg');
}
function _showAddBalanceCorrection(itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showAddBalanceCorrection');

    var getUrl = '/App/PaymentBalanceCorrectionAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/PaymentAdd';

    consolelog('_showAddBalanceCorrection', [getUrl, itemReference]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getPaymentBalanceCorrectionAddData();
            ajaxPostJsonAddData(postUrl, data, 'payments');
        }
    };

    showAddModal(addSuffix("Add - Balance Correction", itemReference), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'md');
}
function _showViewEditPayment(paymentId, itemTypeId, itemReference, itemCss, deleteState) {
    if (toInt(paymentId) <= 0) paymentId = rowMenuItemId;
    if (toInt(paymentId) <= 0) return;

    consolelog('_showViewEditPayment', [paymentId, itemReference]);

    let viewUrl = '/App/Payment?paymentId=' + paymentId;
    let editUrl = '/App/PaymentEdit?itemType=' + itemTypeId + '&paymentId=' + paymentId;
    let postUrl = '/App/PaymentEdit';

    let saveCallback = function () {
        if (!validateFrm(null, 'pageViewEditModalContent')) return;

        var data = getPaymentEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'payments');
    };

    const deleteCallback = function () {
        let deleteUrl = '/App/PaymentDelete';
        let data = { paymentId: paymentId, itemTypeId: itemTypeId };
        ajaxPostJsonViewEditData(deleteUrl, data, 'payments');
    };

    const recoverCallback = deleteCallback;

    showViewEditModal(addSuffix("View Payment", itemReference), viewUrl, editUrl, getItemData(itemTypeId, paymentId, itemReference), saveCallback, deleteCallback, recoverCallback, deleteState, true, itemCss, 'lg');
}

//INVOICE
function _showAddInvoice(itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showAddInvoice');

    var getUrl = '/App/InvoiceAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/InvoiceAdd';

    consolelog('_showAddInvoice', [getUrl, itemReference]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getInvoiceAddData();
            ajaxPostJsonAddData(postUrl, data, 'invoice');
        }
    };

    showAddModal(addSuffix("Add Invoice", itemReference), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'lg');
}
function _showEditInvoice(invoiceId, itemTypeId, itemReference, itemCss) {
    if (toInt(invoiceId) <= 0) invoiceId = rowMenuItemId;
    if (toInt(invoiceId) <= 0) return _callFailed('_showViewEditInvoice');

    consolelog('_showViewEditInvoice', [invoiceId, itemReference]);

    let viewUrl = '/App/Invoice?invoiceId=' + invoiceId;
    let editUrl = '/App/InvoiceEdit?itemType=' + itemTypeId + '&invoiceId=' + invoiceId;
    let postUrl = '/App/InvoiceEdit';

    const saveCallback = function () {
        if (!validateFrm(null, 'pageViewEditModalContent')) return;

        var data = getInvoiceEditData();
        ajaxPostJsonEditData(postUrl, data, 'invoice');
    };

    const deleteCallback = null;
    const recoverCallback = null;

    showEditModal(addSuffix("Edit Invoice", itemReference), editUrl, getItemData(itemTypeId, invoiceId, itemReference), saveCallback, deleteCallback, recoverCallback, 0, true, itemCss, 'lg');
}

//PRODUCTS
function _loadProducts(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('Products', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showAddProduct(titleSuffix, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showAddProduct');

    var getUrl = '/App/ProductAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/ProductAdd';
    consolelog('showAddProduct', [getUrl, titleSuffix]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getProductAddData();
            ajaxPostJsonAddData(postUrl, data, 'products');
        }
    };

    showAddModal(addSuffix("Add Product", titleSuffix), getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss);
}
function _showViewEditProduct(titleSuffix, id) {
    var viewUrl = '/App/Product?productId=' + id;
    var getUrl = '/App/ProductEdit?productId=' + id;
    var postUrl = '/App/ProductEdit';
    consolelog('showViewEditProduct', [getUrl, titleSuffix]);

    var saveCallback = function () {
        if (!validateFrm(null, 'pageViewEditModalContent')) return;

        var data = getProductEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'products');
    };
    showViewEditModal(addSuffix("View Product", titleSuffix), viewUrl, getUrl, emptyItemData(), saveCallback);
}

//PART ORDERS
function _loadPartOrders(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('PartOrders', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showModalPartOrders(itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl) {
    _showAppModal('Part Orders', itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl);
}
function _showAddPartOrder(titleSuffix, itemTypeId, itemId, itemReference, itemCss) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;

    var getUrl = '/PartOrders/PartOrderAdd/?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/PartOrders/PartOrderAdd';
    consolelog('_showAddPartOrder', [getUrl, titleSuffix]);

    var saveCallback = function () {
        if (validateAddModalFrm()) {
            var data = getPartOrderAddData();
            ajaxPostJsonAddData(postUrl, data, 'partorders');
        }
    };

    showAddModal(addSuffix("Add Part Order", titleSuffix), getUrl, getItemData(itemTypeId, itemId, itemReference, false), saveCallback, itemCss);
}
function _showViewEditPartOrder(titleSuffix, id) {
    var viewUrl = '/PartOrders/PartOrder?partOrderId=' + id;
    var getUrl = '/PartOrders/PartOrderEdit?partOrderId=' + id;
    var postUrl = '/PartOrders/PartOrderEdit';
    consolelog('showViewEditPartOrder', [getUrl, titleSuffix]);

    var saveCallback = function () {
        if (!validateFrm(null, 'pageViewEditModalContent')) return;

        var data = getPartOrderEditData();
        ajaxPostJsonViewEditData(postUrl, data, 'partorders');
    };
    showViewEditModal(addSuffix("View Part Order", titleSuffix), viewUrl, getUrl, emptyItemData(), saveCallback);
}

function _loadItemLinks(contentId, itemTypeId, itemId, itemReference, displayMode) {
    _loadAppContent('ItemLinks', contentId, itemTypeId, itemId, itemReference, displayMode);
}
function _showShareItem(itemCss, itemId, itemTypeId, itemReference) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showShareItem');

    var getUrl = '/App/Share?itemType=' + itemTypeId + '&itemId=' + itemId + '&itemRef=' + itemReference;
    var postUrl = '/App/Share';
    consolelog('showShare', [getUrl]);

    var saveCallback = function () {
        if (!validateFrm(null, 'pageAddModalContent')) return;

        var data = getShareData();
        ajaxPostJsonAddData(postUrl, data, 'share');
    };

    showAddModal("Share Link", getUrl, getItemData(itemTypeId, itemId, itemReference), saveCallback, itemCss, 'md');
}

//GENERIC FUNCTIONS - WILL CALL LOCAL FUNCTIONS IF CTX NOT HANDLED
function _loadAppContent(actionName, contentId, itemTypeId, itemId, itemReference, displayMode) {
    const methodName = '_loadAppContent';
    const properId = toProperId(contentId);

    if (!properId.exists)
        return _callFailed(methodName + ': ContentId ' + contentId + ' not found');

    if (!hasContent(actionName))
        return _callFailed(methodName + ': actionName not specified');

    if (toInt(itemId) <= 0) itemId = toInt(rowMenuItemId);
    if (toInt(itemTypeId) <= 0) itemTypeId = toInt(rowMenuItemTypeId);
    if (itemId <= 0 || itemTypeId <= 0)
        return _callFailed(methodName + ': itemId or itemTypeId not specified');;

    contentId = properId.value;

    let url = mapUrlParams('/App/{1}/?itemType={2}&itemId={3}', [actionName, itemTypeId, itemId]);
    if (hasContent(itemReference)) url += '&itemRef=' + itemReference;
    if (toInt(displayMode) > 0) url += '&displayMode=' + displayMode;

    loadUrl(contentId, url, initPinnedState);
}
function _showAppModal(modalFor, itemTypeId, itemId, itemReference, itemCss, itemName, itemViewUrl) {
    if (toInt(itemId) <= 0) itemId = rowMenuItemId;
    if (toInt(itemTypeId) <= 0) itemTypeId = rowMenuItemTypeId;
    if (toInt(itemId) <= 0 || toInt(itemTypeId) <= 0) return _callFailed('_showModal' + modalFor.replace(' ', ''));
    //debugger;

    if (!hasContent(itemReference)) itemReference = rowMenuItemReference;
    const itemData = getItemData(itemTypeId, itemId, itemReference);

    consolelog('_showModal' + modalFor.replace(' ', ''), itemData);

    const getUrl = '/App/' + modalFor.replace(' ', '')  + '/' + itemQs(itemData, 2);
    const title = getItemTitle(itemData, modalFor);
    const viewUrl = getItemViewUrl(itemData, itemViewUrl);

    showModal(addSuffix(title, itemReference), getUrl, itemData, viewUrl, itemName, itemCss);
}

function _onSuccessAdd(data, ctx) {
    consolelog('_onSuccessAdd', [ctx]);
    dismissPageAddModal();

    if (!ctx) ctx = '';

    //SHARE SUCCESS
    if (ctx.toLowerCase() == 'share') {
        showAlert('Share', 'Record shared successfuly');

    //REF DATA SUCCESS
    } else if (ctx.toLowerCase() == 'refdata') {
        if (data && toInt(data.Id) > 0 && data.Tag && data.Tag != '' && toInt(data.ItemType) > 0) {
            var url = '/App/ReferenceDataList?itemType=' + data.ItemType;
            populateDropdown(url, data.Tag, data.Id);
        }

    //ADDRESS DETAIL SUCCESS
    } else if (ctx.toLowerCase() == 'addressdetail') {
        consolelog('_onSuccessAdd', ['Calling _onSuccessAddAddressDetail']);
        _onSuccessAddAddressDetail(data, ctx);

    //GENERAl ITEM ADDED
    } else if (isCommonContext(ctx)) {
        if (typeof loadHeader === 'function') {
            loadHeader(ctx);
        }
        else {
            reloadPage();
        }
    }
    //ON SUCCESS ADD AVAILABLE
    else if (typeof onSuccessAdd === 'function') {
        consolelog('_onSuccessAdd', ['Calling onSuccessAdd']);
        onSuccessAdd(data, ctx);
    }

    //CATCH ALL
    else {
        reloadPage();
    }
}
function _onSuccessAddOther(data, ctx) {
    consolelog('_onSuccessAddOther', [ctx]);
    dismissPageAddOtherModal();

    if (!ctx) ctx = '';

    //SHARE SUCCESS
    if (ctx.toLowerCase() == 'share') {
        showAlert('Share', 'Record shared successfuly');

        //REF DATA SUCCESS
    } else if (ctx.toLowerCase() == 'refdata') {
        if (data && toInt(data.Id) > 0 && data.Tag && data.Tag != '' && toInt(data.ItemType) > 0) {
            var url = '/App/ReferenceDataList?itemType=' + data.ItemType;
            populateDropdown(url, data.Tag, data.Id);
        }

        //ADDRESS DETAIL SUCCESS
    } else if (ctx.toLowerCase() == 'addressdetail') {
        consolelog('_onSuccessAdd', ['Calling _onSuccessAddAddressDetail']);
        _onSuccessAddAddressDetail(data, ctx);

        //GENERAl ITEM ADDED
    } else if (isCommonContext(ctx)) {
        if (typeof loadHeader === 'function') {
            loadHeader(ctx);
        }
        else {
            reloadPage();
        }
    }
    //ON SUCCESS ADD AVAILABLE
    else if (typeof onSuccessAdd === 'function') {
        consolelog('_onSuccessAdd', ['Calling onSuccessAdd']);
        onSuccessAdd(data, ctx);
    }

    //CATCH ALL
    else {
        reloadPage();
    }
}
function _onSuccessEdit(data, ctx) {
    consolelog('_onSuccessEdit', [ctx]);
    dismissPageEditModal();

    if (!ctx) ctx = '';

    if (ctx.toLowerCase() == '?') {
        //Do nothing ??

    } else if (ctx.toLowerCase() == 'addressdetail') {
        //ADDRESS DETAIL SUCCESS
        consolelog('_onSuccessEdit', ['Calling _onSuccessEditAddressDetail']);
        _onSuccessEditAddressDetail(data, ctx);

    } else if (typeof onSuccessEdit === 'function') {
        consolelog('_onSuccessEdit', ['Calling onSuccessEdit', ctx]);
        onSuccessEdit(data, ctx);
    } else {
        if (isCommonContext(ctx)) {
            if (typeof loadParent === 'function')
                loadParent();
            else
                reloadPage();
        }
        else {
            if (typeof loadChild === 'function')
                loadChild();
            else
                reloadPage();   
        }
    }
}
function _onSuccessViewEdit(data, ctx) {
    consolelog('_onSuccessViewEdit', [ctx]);
    dismissPageViewEditModal();

    if (!ctx) ctx = '';

    if (ctx.toLowerCase() == '?') {
    } else if (typeof onSuccessViewEdit === 'function') {
        onSuccessViewEdit(data, ctx);
    } else {
        if (isCommonContext(ctx)) {
            if (typeof loadParent === 'function')
                loadParent();
            else
                reloadPage();
        }
        else
            if (typeof loadChild === 'function') loadChild();
    }
}
function _onErrorAdd(errData, ctx) {
    consolelog('_onErrorAdd', [ctx]);

    if (!ctx) ctx = '';

    let errMsg = getErrorMsg(errData);

    if (ctx.toLowerCase() == 'share') {
        showAlert('Share', errMsg);
    } else if (typeof onErrorAdd === 'function') {
        onErrorAdd(errMsg, ctx);
    } else {
        setPageAddModalError(errMsg);
    }
}
function _onErrorAddOther(errData, ctx) {
    consolelog('_onErrorAddOther', [ctx]);

    if (!ctx) ctx = '';

    let errMsg = getErrorMsg(errData);

    if (ctx.toLowerCase() == 'share') {
        showAlert('Share', errMsg);
    } else if (typeof onErrorAdd === 'function') {
        onErrorAdd(errMsg, ctx);
    } else {
        setPageAddOtherModalError(errMsg);
    }
}
function _onErrorEdit(errData, ctx) {
    consolelog('_onErrorEdit', [ctx]);

    if (!ctx) ctx = '';
    ////debugger;
    let errMsg = getErrorMsg(errData);

    if (ctx.toLowerCase() == '?') {
    } else if (typeof onErrorEdit === 'function') {
        onErrorEdit(errMsg, ctx);
    } else {
        setPageEditModalError(errMsg);
    }
}
function _onErrorViewEdit(errData, ctx) {
    consolelog('_onErrorViewEdit', [ctx]);

    if (!ctx) ctx = '';

    let errMsg = getErrorMsg(errData);

    if (ctx.toLowerCase() == '?') {
    } else if (typeof onErrorViewEdit === 'function') {
        onErrorViewEdit(errMsg, ctx);
    } else {
        setPageViewEditModalError(errMsg);
    }
}

function getErrorMsg(errData) {
    let errMsg = 'Unexpexted Error';
    if (errData != null && typeof errData === 'object') {
        errMsg += ': ' + errData.ResponseText;
        //errMsg += ': ' + errData.ErrorDetail;
    } else if (hasContent(errData)) {
        errMsg += ': ' + errData;
    }

    return errMsg;
}

function displayAddModalMessage(msg, level) {
    displayModalMessage(msg, 'pageAddModalAlert', level);
}
function displayAddOtherModalMessage(msg, level) {
    displayModalMessage(msg, 'pageAddOtherModalAlert', level);
}
function displayEditModalMessage(msg, level) {
    displayModalMessage(msg, 'pageEditModalAlert', level);
}
function displayVewEditModalMessage(msg, level) {
    displayModalMessage(msg, 'pageVewEditModalAlert', level);
}
function displayModalMessage(msg, alertId, level) {
    if (!msg || msg.length == 0 || !alertId || alertId.length == 0) return;

    alertId = alertId.startsWith('#') ? alertId : '#' + alertId;
    level = level < 1 ? 1 : level > 3 ? 3 : level;

    var cssClass = level == 1 ? 'alert-info' : level == 2 ? 'alert-warning' : 'alert-danger';
    $(alertId).removeClass('alert-danger').removeClass('alert-warning')
        .removeClass('alert-info').addClass(cssClass);

    $(alertId).append(msg + '<br/>').show();
}

function _formSave(formId, validationErrorsContainerId, formSaveContext) {
    consolelog('_formSave', [formId, validationErrorsContainerId, formSaveContext]);

    if (validateFrm(null, formId, validationErrorsContainerId)) {
        if (typeof formSave === 'function') formSave(formSaveContext);
    } else {
        consolelog('_formSave', ['invalid']);
    }
}

function initTextSelect() {
    $("input").on("click", function () {
        $(this).select();
    });
}
function initDatePickers() {
    $('input[type="date"]').on("click", function (evt) {
        this.showPicker();
    });
}

function applyMultiselects() {
    consolelog('applyMultiselects');
    $('.select2').each(function () {
        $(this).select2({
            width: '100%'
        });
    })
}
function applyNumericMinValue(id) {
    const properId = toProperId(id);
    if (!properId.exists) return _callFailed('applyNumericMinValue');
    $(properId.value).on('input', function () {
        const min = parseFloat($(this).attr('min')) || 0;
        const current = parseFloat($(this).val());
        if (current < min) $(this).val(min);
    });
}
function applyMultiselect(id, selectedData) {
    consolelog('applyMultiselect: ' + id + ', ' + selectedData);
    var listId = '#' + id;

    $(listId).select2({
        width: '100%'
    });

    if (selectedData) {
        setMultiselectData(listId, selectedData.split(','));
    }
}
function setMultiselectData(id, data) {
    $(id).val(data);
    $(id).trigger('change');
}

function onConfirmOk(context) {
    consolelog('onConfirmOk: ' + context);
    if (context == 'logout') {
        navigateToPage('/App/Logout');
    }
}

function enableInput(inputId, value, hdnField) {
    $('#' + inputId).prop('disabled', false);
    if (value) $('#' + inputId).val(value);
    if (hdnField) $('#' + hdnField).val(1);
}
function disableInput(inputId, value) {
    $('#'+inputId).prop('disabled', true);
    if (value) $('#' + inputId).val(value);
}
function enableDisableInput(inputId, enabled, value) {
    const id = toProperId(inputId, false).value;
    consolelog('enableDisableInput', [id, enabled]);
    $(id).prop('disabled', !enabled);
    if (value) $(id).val(value);
}

function toggleArchivedWarning(controlId) {
    $('#incArchivedWarning').hide()
    if (isInputChecked(controlId)) $('#incArchivedWarning').show();
}

function isInputChecked(inputId) {
    const id = toProperId(inputId, false).value;
    return $(id).is(':checked');
}
function setInputChecked(inputId, checked) {
    const id = toProperId(inputId, false).value;
    return $(id).prop('checked', checked);
}

function setInputDateType(inputId, isDateOnly) {
    var inputType = isDateOnly ? 'date' : 'datetime-local';
    var currentVal = $('#' + inputId).val();
    $('#' + inputId).attr("type", inputType);

    $('#' + inputId).val(convertDateTime(isDateOnly, currentVal));
}
function convertDateTime(isDateOnly, value) {
    if (isDateOnly && value.indexOf('T') > 0) {
        consolelog('convertDateTime: removing time from ' + value);
        return value.substring(0, value.indexOf('T'));
    }

    if (!isDateOnly && value.indexOf('T') <= 0) {
        consolelog('convertDateTime: adding time to ' + value);
        return value+'T00:01';
    }

    return value;
}

function populateFramesDropdown(value, id) {
    if (!id || id.length == 0) return;
    var dropdown = $('#' + id);
    if (dropdown == undefined) return;

    dropdown.html('');

    for(var i = 0; i <= value; i++) {
        dropdown.append($("<option />").val(i).text(i));
    };

    dropdown.prop('disabled', value == 0);
}

function restrictFutureDates(selector) {
    const properId = toProperId(selector, false);
    if (!properId.exists) return;

    var today = new Date().toISOString().split('T')[0]; // Get today's date in YYYY-MM-DD format
    $(properId.value).attr('max', today);
}

function selectFirstOptionIfValueMissing(id, value) {
    let $select = $("#" + id);
    if ($select.find("option[value='" + value + "']").length == 0) {
        $select.val($select.find("option:first").val());
        return true;
    }

    return false;
}

function setDropdownValue(dropdownId, value) {
    var dropdown = $('#' + dropdownId);

    // Check if the value exists
    if (dropdown.find("option[value='" + value + "']").length === 0) {
        // If it doesn't exist, select the first option
        dropdown.val(dropdown.find("option:first").val());
    } else {
        // If it exists, set the value
        dropdown.val(value);
    }
}



let loadingGif = '<img style="margin:20px;" src="/images/loading.gif" width="40px" height="40px" /> Loading...';

function ajaxPostJsonAddData(url, jsonData, ctx, successCallback, errorCallback, alertElement) {
    consolelog('ajaxPostJsonAddData', [url, ctx]);
    var localSuccessCallback = _onSuccessAdd;
    var localErrorCallback = _onErrorAdd;
    if (!alertElement) alertElement = 'pageAddModalAlert';
    if (successCallback) localSuccessCallback = successCallback;
    if (errorCallback) localErrorCallback = errorCallback;

    jsonData = getModalItem('Add', jsonData);
    return ajaxPostJsonData(url, jsonData, localSuccessCallback, localErrorCallback, ctx, 'pageAddModalAlert');
}
function ajaxPostJsonAddOtherData(url, jsonData, ctx, successCallback, errorCallback, alertElement) {
    consolelog('ajaxPostJsonAddOtherData', [url, ctx]);
    var localSuccessCallback = _onSuccessAddOther;
    var localErrorCallback = _onErrorAddOther;
    if (!alertElement) alertElement = 'pageAddOtherModalAlert';
    if (successCallback) localSuccessCallback = successCallback;
    if (errorCallback) localErrorCallback = errorCallback;

    jsonData = getModalItem('AddOther', jsonData);
    return ajaxPostJsonData(url, jsonData, localSuccessCallback, localErrorCallback, ctx, 'pageAddOtherModalAlert');
}
function ajaxPostJsonEditData(url, jsonData, ctx, successCallback, errorCallback, alertElement) {
    consolelog('ajaxPostJsonEditData', [url, ctx]);
    var localSuccessCallback = _onSuccessEdit;
    var localErrorCallback = _onErrorEdit;
    if (!alertElement) alertElement = 'pageEditModalAlert';
    if (successCallback) localSuccessCallback = successCallback;
    if (errorCallback) localErrorCallback = errorCallback;

    jsonData = getModalItem('Edit', jsonData);
    ajaxPostJsonData(url, jsonData, localSuccessCallback, localErrorCallback, ctx, alertElement);
}
function ajaxPostJsonViewEditData(url, jsonData, ctx, successCallback, errorCallback, alertElement) {
    consolelog('ajaxPostJsonViewEditData', [url, ctx]);
    var localSuccessCallback = _onSuccessViewEdit;
    var localErrorCallback = _onErrorViewEdit;
    if (!alertElement) alertElement = 'pageViewEditModalAlert';
    if (successCallback) localSuccessCallback = successCallback;
    if (errorCallback) localErrorCallback = errorCallback;

    jsonData = getModalItem('ViewEdit', jsonData);
    ajaxPostJsonData(url, jsonData, localSuccessCallback, localErrorCallback, ctx, 'pageViewEditModalAlert');
}
function ajaxPostJsonData(url, jsonData, onSuccessCallback, onErrorCallback, ctx, alertElement) {
    //debugger;
    consolelog('ajaxPostJsonData', [url, ctx]);
    var stringifyJson = JSON.stringify(jsonData);

    $.ajax({
        url: encodeURI(url),
        type: 'POST',
        data: stringifyJson,
        contentType: 'application/json; charset=utf-8',
        success: function (data) {
            if (data.Success) {
                consolelog('ajaxPostJsonData - success');
                if (hasContent(data.RedirectUrl)) {
                    navigateToPage(data.RedirectUrl);
                } else if (typeof onSuccessCallback === 'function') {
                    onSuccessCallback(data, ctx);
                }
            } else {
                consolelog('ajaxPostJsonData - failed', data.ResponseText);
                displayErrors(data, alertElement);
                if (typeof onErrorCallback === 'function')
                    onErrorCallback(data, ctx);
            }
        },
        error: function (xhr, status, error) {
            showAlert('Contor Error', xhr.responseText);
        }
    });
}

function ajaxPostFormAddData(url, fileData, ctx) {
    consolelog('ajaxPostFormData', [url, ctx]);
    ajaxPostFormData(url, fileData, _onSuccessAdd, _onErrorAdd, ctx, 'pageAddModalAlert');
}
function ajaxPostFormData(url, fileData, onSuccessCallback, onErrorCallback, ctx, alertElement) {
    consolelog('ajaxPostFormData', [url, ctx]);
    $.ajax({
        url: encodeURI(url),
        type: 'POST',
        contentType: false,
        processData: false,
        data: fileData,
        success: function (data) {
            if (data.Success) {
                if (hasContent(data.RedirectUrl)) {
                    navigateToPage(data.RedirectUrl);
                } else if (typeof onSuccessCallback === 'function') {
                    onSuccessCallback(data, ctx);
                }
            } else {
                consolelog(data.ResponseText);
                displayErrors(data, alertElement);
                if (typeof onErrorCallback === 'function')
                    onErrorCallback(data);
            }
        },
        error: function (xhr, status, error) {
            showAlert('Contor Error', xhr.responseText);
        }
    });
}

function loadUrl(divId, url, callBack) {
    const properId = toProperId(divId, false);
    if (!properId.exists) return _callFailed('loadUrl: divId ' + divId);
    if (!hasContent(url)) return _callFailed('loadUrl: url');

    url = url.replace('&amp;', '&');
    loadDiv($(properId.value), url, callBack);
}
function loadDiv(div, url, callBack) {
    if (!div || div.length == 0) return _callFailed('loadDiv');

    div.html(loadingGif);

    div.load(encodeURI(url), function (response, status, xhr) {
        if (status == "error") {
            throw new Error(`Failed to load content: ${url} ${xhr.status} ${xhr.statusText}`);
        } else {
            $('.formHelp').hide();
            if (typeof callBack === 'function') callBack();
        }
    });
}
function loadPostUrl(divId, url, requestData) {
    if (!divId || divId.length == 0) return;

    url = url.replace('&amp;', '&');
    consolelog('loadPostUrl', [url]);

    divId = divId.startsWith('#') ? divId : '#' + divId;
    $(divId).html(loadingGif);
    var jsonData = JSON.stringify(requestData);
    $.ajax({
        url: encodeURI(url),
        type: 'POST',
        async: false,
        data: jsonData,
        contentType: 'application/json; charset=utf-8',
        success: function (data) {
            $(divId).html(data);
        },
        error: function (err) {
            showAlert('', err.statusText);
        }
    });
}
function loadFormPostUrl(divId, url, requestData) {
    if (!divId || divId.length == 0) return;

    url = url.replace('&amp;', '&');
    consolelog('loadPostUrl', [url]);

    divId = divId.startsWith('#') ? divId : '#' + divId;
    $(divId).html(loadingGif);

    $.ajax({
        url: encodeURI(url),
        type: 'POST',
        data: requestData,
        contentType: false,
        processData: false,
        success: function (data) {
            $(divId).html(data);
        },
        error: function (xhr, status, error) {
            showAlert('Contor Error', xhr.responseText);
        }
    });
}

function getData(url, requestData, forcePost) {
    consolelog('getData: ' + url);
    let returnData;
    _showLoading();

    if (requestData || forcePost) {
        var jsonData = JSON.stringify(requestData);
        $.ajax({
            url: encodeURI(url),
            type: 'POST',
            async: false,
            data: jsonData,
            contentType: 'application/json; charset=utf-8',
            success: function (data) {
                _hideLoading();
                if (data.Success) {
                    returnData = data.Data;
                } else {
                    consolelog(data.ResponseText);
                }
            },
            error: function (err) {
                _hideLoading();
                alert(err.statusText);
            }
        });
    } else {
        $.ajax({
            url: encodeURI(url),
            type: 'GET',
            async: false,
            contentType: 'application/json; charset=utf-8',
            success: function (data) {
                _hideLoading();

                if (data.Success) {
                    returnData = data.Data;
                } else {
                    consolelog(data.ResponseText);
                }
            },
            error: function (err) {
                _hideLoading();
                alert(err.statusText);
            }
        });
    }

    consolelog(returnData);
    return returnData;
}

function initDropdown(dropdownId) {
    const properId = toProperId(dropdownId, false);
    if (!properId.exists) return;

    const required = isRequired(properId.value);
    const value = required ? '' : '';
    const text = required ? '-Required-' : '-Optional-';
    $(properId.value).html('').append($("<option />").val(value).text(text));
}
function populateDropdown(url, dropdownId, selectedValue) {
    if (!dropdownId || dropdownId.length == 0) return;

    url = url.replace('&amp;', '&');
    consolelog('populateDropdown', [url, dropdownId, selectedValue]);

    dropdownId = dropdownId.startsWith('#') ? dropdownId : '#' + dropdownId;
    var options = $(dropdownId);

    $.ajax({
        url: encodeURI(url),
        type: 'GET',
        async: false,
        success: function (data) {
            if (data.Success) {
                options.html('');
                $.each(data.Data, function (index, item) {
                    options.append($("<option />").val(item.Value).text(item.Text));
                });

                if (toInt(selectedValue) > 0 || hasContent(selectedValue))
                    $(dropdownId).val(selectedValue);
                else
                    options.prop('selectedIndex', 0);

            } else {
                showAlert('', data.ResponseText);
            }
        },
        error: function (err) {
            showAlert('', err.statusText);
        }
    });
}

function displayErrors(data, alertElement) {
    consolelog('displayErrors');

    if (!data || !alertElement) return;
    $('#' + alertElement).hide();
    $('#modalOverrideValidation').hide();
    $('#overrideValidation').prop('checked', false);

    var displayMessage = '';
    if (data.Severity == 1) {
        for (var i = 0; i < data.ValidationErrors.length; i++) {
            displayMessage += data.ValidationErrors[i].Message + '<br/>';
        }
        $('#' + alertElement).removeClass('alert-danger').addClass('alert-warning');
        if (data.ValidationErrors.length > 0) $('#modalOverrideValidation').show();
    } else {
        if (data.ErrorDetail && data.ErrorDetail.length > 0) {
            consolelog('Validation Error Detail', data.ErrorDetail);
        }
    }
    if (displayMessage && displayMessage.length > 0)
        $('#' + alertElement).html(displayMessage).show();
}


let clientSideValidationEnable = window.contorConfig?.ClientSideValidationEnable ?? true;
let validationLogging = window.contorConfig?.ValidationLogging ?? false;
let showDebugOnError = window.contorConfig?.ShowDebugOnError ?? false;

function validateAddModalFrm(errContainer) {
	var success = validateModalFrm('pageAddModalContent', _onErrorAdd, errContainer);
	return success;
}
function validateAddOtherModalFrm(errContainer) {
	var success = validateModalFrm('pageAddOtherModalContent', _onErrorAddOther, errContainer);
	return success;
}
function validateEditModalFrm(errContainer) {
	var success = validateModalFrm('pageEditModalContent', _onErrorEdit, errContainer);
	return success;
}
function validateViewEditModalFrm(errContainer) {
	var success = validateModalFrm('pageViewEditModalContent', _onErrorViewEdit, errContainer);
	return success;
}
function validateModalFrm(frmId, fnError, errContainer) {
	var success = validateFrm(null, frmId, errContainer, true, true, false);
	if (!success && fnError)
		fnError('Validation Failed. Please enter all details as indicated.')

	return success;
}

function validateAddFrm(frmId, highlightErrors) {
	return validateFrm(null, frmId, 'pageAddModalMessage', highlightErrors);
}
function validateEditFrm(frmId, highlightErrors) {
	return validateFrm(null, frmId, 'pageEditModalMessage', highlightErrors);
}
function validateViewEditFrm(frmId, highlightErrors) {
	return validateFrm(null, frmId, 'pageViewEditModalMessage', highlightErrors);
}
function validateFrm(evt, frmId, errContainer, highlightErrors, clearServerSide, callValidatePage, silent) {
	//Make sure the frm exists
	if (!toProperId(frmId).exists) return false;
	frmId = gProperId;

	$(frmId + ' .text-val-danger').removeClass('text-val-danger');
	$(frmId + ' .text-val-warning').removeClass('text-val-warning');

	consolelog('Validating...', [frmId]);

    //Check if client side validation is enabled
	if (!clientSideValidationEnable) {
		consolelog('Validation Complete...', [frmId, 'Client Side Validation Disabled']);
		return true;
	} 

	//Default to valid
	let valid = true;

	if (clearServerSide && clearServerSide == true)
		$('#serverSideValidationErrorsId').html('');

	if (toProperId(errContainer).exists)
		$(gProperId).html('');

    //Validate all inputs, selects, and textareas in the form
	$(frmId).find('input, select, textarea').each(function () {
		valid = validateFld(this, errContainer, highlightErrors) && valid;
	});

	//Call additional validation that might exist on the page
	if (callValidatePage == null) callValidatePage = true;
	if (callValidatePage && typeof pageValidation === 'function') {
		valid = pageValidation(errContainer == null || toBool(silent)) && valid;
	}

	if (!valid) {
		if (evt) evt.preventDefault();
		consolelog('Validation Complete ', [frmId, '**invalid**']);
		return false;
	}

    //REMOVE DISABLED ATTRIBUTES FROM INPUTS SO THEY WILL BE SUBMITTED
	if (evt) {
		$(frmId).find('input, select, textarea').each(function () {
			if ($(this).prop('disabled')) {
				$(this).prop('disabled', false); // Remove disabled attribute
			}
		});
	}

	consolelog('Validation Complete ', [frmId, '!!valid!!']);
	return valid;
}

function validateAddFld(fld, highlightErrors) {
	return validateFld(fld, 'pageAddModalMessage', highlightErrors);
}
function validateEditFld(fld, highlightErrors) {
	return validateFld(fld, 'pageEditModalMessage', highlightErrors);
}
function validateViewEditFld(fld, highlightErrors) {
	return validateFld(fld, 'pageViewEditModalMessage', highlightErrors);
}
function validateFld(fld, errContainer, highlightErrors) {
	if (!fld) return true;

	if (!toProperId(fld.id).exists) {
		validationLog('.Does not exist: ' + gProperId);
		return true;
	}

	const fldId = gProperId;

	if (fldId.startsWith("multiselect_")) {
		validationLog('.Ignoring multiselect: ' + fldId);
		return true;
	}

	validationLog('.Validating Field: ' + fldId);

	const altId = toProperId($(fldId).attr('data-validation-alt-id')).value;

	clearInvalid(fldId, altId);

	let ignoreSuccess = false;
	let toProperIgnoreId = toProperId($(fldId).attr('data-validation-ignoreif'));

	let ignoreIfId = '';
	if (toProperIgnoreId.exists) {
		ignoreIfId = toProperIgnoreId.value;
		const ignoreIfValue = $(ignoreIfId).val();
		const ignoreIfType = $(ignoreIfId).attr('type');
		if (ignoreIfType == 'number' && ignoreIfValue && ignoreIfValue > 0) ignoreSuccess = true;
		if (ignoreIfType == 'text' && ignoreIfValue && ignoreIfValue.length > 0) ignoreSuccess = true;

		if (ignoreSuccess) {
			validationLog('...ignoring because ' + ignoreIfId + ' is set')
			return true;
		}
	}
	
	const ignoreHidden = $(fldId).attr('data-ignore-hidden');
	if (ignoreHidden == 'true' && $(fldId).is(":hidden")) {
		validationLog('...ignoring because ' + fldId + ' is hidden')
		return true;
	}

	const value = $(fldId).val();
	const required = isRequired(fldId);
	validationLog('...required: ' + required + ', value: ' + value);

	if (required && (!value || value == '' || value == '0')) {
		setInvalid(fldId, 'Required', altId, errContainer, highlightErrors, ignoreIfId);
		return false;
	}

	const minLength = $(fldId).attr('data-validation-min-length');
	if (minLength && !isNaN(minLength) && value.length < minLength) {
		setInvalid(fldId, 'This field requires at least ' + minLength + ' characters', altId, errContainer, highlightErrors);
		return false;
	}

	const maxLength = $(fldId).attr('data-validation-max-length');
	if (maxLength && !isNaN(maxLength) && value.length > maxLength) {
		setInvalid(fldId, 'This field is limited to ' + maxLength + ' characters', altId, errContainer, highlightErrors);
		return false;
	}

	const match = $(fld).attr('data-validation-match');
    const matchProperId = toProperId(match);
	if (matchProperId.exists) {
        const matchId = matchProperId.value;
		const matchValue = $(matchId).val();
		if (matchValue != value) {
			const matchName = $(matchId).attr('data-validation-name')
			setInvalid(fldId, 'This field does not match the field ' + matchName, altId, errContainer, highlightErrors);
			return false;
		}
	}

	const minValue = $(fldId).attr('data-validation-min-value');
	if (minValue && !isNaN(minValue) && (isNaN(value) || value < minValue)) {
		setInvalid(fldId, 'This field must have a value of at least ' + minValue, altId, errContainer, highlightErrors);
		return false;
	}

	return true;
}

function clearInvalid(fldId, altId) {
	if (!hasContent(fldId) && !hasContent(altId)) return;

	const id = hasContent(altId) ? altId : fldId;

	validationLog('...clearing invalid: ' + id);

	if (!toProperId(id).exists) return;
	const cleanId = id.replace('#', '');

	$("label[for='" + cleanId + "']").removeClass('text-val-danger').removeClass('text-val-warning');
	$("h2[for='" + cleanId + "']").removeClass('text-val-danger').removeClass('text-val-warning');
	$("span[for='" + cleanId + "']").removeClass('text-val-danger').removeClass('text-val-warning');

	$(id).removeClass('invalid').removeClass('invalidwarning');
	if (toProperId(id + 'Invalid').exists) {
		$(id + 'Invalid').html('').removeClass('invalid').removeClass('invalidwarning');
	}
}

function setInvalid(fldId, msg, altId, errContainer, highlightErrors, ignoreIfId) {
	const properFldId = toProperId(fldId, false);
	const properAltFldId = toProperId(altId, false);
	let actualFldId = '';
	let colourClass = 'text-val-danger';
	let invalidCss = 'invalid';
	let name = '';

	if (properFldId.exists || properAltFldId.exists) {
		actualFldId = properFldId.value;
		if (properAltFldId.exists) actualFldId = properAltFldId.value;
		validationLog('...setting fld invalid: ' + actualFldId);

		const cleanId = actualFldId.replace('#', '');
		const ignoring = toProperId(ignoreIfId).exists;
		colourClass = ignoring ? 'text-val-warning' : colourClass;
		invalidCss = ignoring ? 'invalidwarning' : invalidCss;
		name = $(actualFldId).attr('data-validation-name');

		$("label[for='" + cleanId + "']").addClass(colourClass);
		$("h2[for='" + cleanId + "']").addClass(colourClass);
		$("span[for='" + cleanId + "']").addClass(colourClass);

		if (highlightErrors) $(actualFldId).addClass(invalidCss);
		if (toProperId(actualFldId + 'Invalid').exists) $(gProperId).html(msg).addClass(invalidCss);
	}

	const toProperErrContainer = toProperId(errContainer, false);
	if (!toProperErrContainer.exists) return; 

	let html = '<div style="cursor:pointer;" class="' + colourClass + '"';
	if (hasContent(actualFldId)) html += ' onclick="$(\'' + actualFldId + '\').focus();"'
	html += hasContent(name) ? '><b>' + name + '</b>: ' + msg + '</div>' : '><b>Error</b>: ' + msg + '</div>';

	$(toProperErrContainer.value).append(html);
}

function setRequired(fldId, required) {
	if (!toProperId(fldId).exists) return;
	consolelog('setRequired', [gProperId, required]);
	$(gProperId).attr('data-validation-required', required);
}

function validationLog(msg, values) {
	if (validationLogging == true)
		consolelog(msg, values);
}

function addValidationError(fldId, name, msg, errContainer, labelId, silent) {
	if (!hasContent(errContainer)) errContainer = 'clientSideValidationErrors';
	//setInvalid(fld, msg, null, errContainer);

	if (hasContent(labelId)) $(toProperId(labelId).value).addClass('text-val-danger');
	let html = '<div style="cursor:pointer;" class="text-val-danger"';
	if (hasContent(fldId)) html += ' onclick="$(\'' + fldId + '\').focus();"'
	html += hasContent(name) ? '><b>' + name + '</b>: ' + msg + '</div>' : '><b>Error</b>: ' + msg + '</div>';
	if (!silent) $(toProperId(errContainer).value).append(html);
}

function isRequired(id) {
	const properId = toProperId(id, false);
	if (!properId.exists) return;

	const attr = $(properId.value).attr('data-validation-required');
	return attr && attr == 'true';
}

function validateHiddenFields(fields) {
    var isValid = true;
	if (fields === null) return isValid;

	if (Array.isArray(fields)) {
		fields.forEach(function (field) {
			isValid = isValid && $(toProperId(field).value).val() !== '';
		});
	} else {
		isValid = $(toProperId(field).value).val() !== '';
	}

	if (!isValid) document.body.classList.add("warning-border");

	return isValid;
}
