TimeTrex Community Edition v16.2.0

This commit is contained in:
2022-12-13 07:10:06 +01:00
commit 472f000c1b
6810 changed files with 2636142 additions and 0 deletions
.htaccess3rd_party_credits.txtINSTALL.txtLICENSEREADME.txtUPGRADE.txt
api
classes
.htaccess
ChequeForms
GovernmentForms
GovernmentForms.class.phpGovernmentForms_Base.class.php
country
ca
grid.class.php
us
examples
adodb
adodb-active-record.inc.phpadodb-active-recordx.inc.phpadodb-csvlib.inc.phpadodb-datadict.inc.phpadodb-error.inc.phpadodb-errorhandler.inc.phpadodb-errorpear.inc.phpadodb-exceptions.inc.phpadodb-iterator.inc.phpadodb-lib.inc.phpadodb-loadbalancer.inc.phpadodb-memcache.lib.inc.phpadodb-pager.inc.phpadodb-pear.inc.phpadodb-perf.inc.phpadodb-php4.inc.phpadodb-time.inc.phpadodb-xmlschema.inc.phpadodb-xmlschema03.inc.phpadodb.inc.php
contrib
datadict
drivers
lang
license.txt
pear
perf
pivottable.inc.phprsfilter.inc.phpserver.phptips_portable_sql.htmtoexport.inc.phptohtml.inc.phptute.htmxmlschema.dtdxmlschema03.dtd
xsl
bitmask
bounce_handler
misc
modules
accrual
api
accrual
client
company
core
department
hierarchy
holiday
import
kpi
message
notification
pay_stub
pay_stub_amendment
payperiod
policy
punch
qualification
report
request
roe
schedule
system_job_queue
ui_kit
unauthenticated
users
company
core
Authentication.class.phpAuthenticationTrustedDeviceFactory.class.phpAuthenticationTrustedDeviceListFactory.class.phpAuthorizationFactory.class.phpAuthorizationListFactory.class.phpBackgroundProcess.class.phpBreadCrumb.class.phpCalculatePayStub.class.phpCalculatePolicy.class.phpCurrencyFactory.class.phpCurrencyListFactory.class.phpCurrencyRateFactory.class.phpCurrencyRateListFactory.class.phpCustomFieldFactory.class.phpCustomFieldListFactory.class.phpDebug.class.phpDependencyTree.class.phpEnvironment.class.phpException.class.phpExceptionFactory.class.phpExceptionListFactory.class.phpFactory.class.phpFactoryListIterator.class.phpFastTree.class.phpFormVariables.class.phpGeneralLedgerExport.class.phpGroup.class.phpIdempotentRequestFactory.class.phpIdempotentRequestListFactory.class.phpLockFile.class.phpLogDetailFactory.class.phpLogDetailListFactory.class.phpLogFactory.class.phpLogListFactory.class.phpLogRotate.class.phpMisc.class.phpOption.class.phpOtherFieldFactory.class.phpOtherFieldListFactory.class.phpPager.class.phpPermission.class.phpPermissionControlFactory.class.phpPermissionControlListFactory.class.phpPermissionFactory.class.phpPermissionListFactory.class.phpPermissionUserFactory.class.phpPermissionUserListFactory.class.phpProfiler.class.phpProgressBar.class.phpPurgeDatabase.class.phpRateLimit.class.phpRedirect.class.phpSerializer.class.phpSharedMemory.class.phpSort.class.phpStationBranchFactory.class.phpStationBranchListFactory.class.phpStationDepartmentFactory.class.phpStationDepartmentListFactory.class.phpStationExcludeUserFactory.class.phpStationExcludeUserListFactory.class.phpStationFactory.class.phpStationIncludeUserFactory.class.phpStationIncludeUserListFactory.class.phpStationListFactory.class.phpStationUserFactory.class.phpStationUserGroupFactory.class.phpStationUserGroupListFactory.class.phpStationUserListFactory.class.phpSystemJobQueue.class.phpSystemJobQueueFactory.class.phpSystemJobQueueListFactory.class.phpSystemSettingFactory.class.phpSystemSettingListFactory.class.phpTTDate.class.phpTTLDAP.class.phpTTLicense.class.phpTTLog.class.phpTTMail.class.phpTTPDF.class.phpTTPassword.class.phpTTTree.class.phpTTUUID.class.phpTTi18n.class.phpURLBuilder.class.phpUnitConvert.class.phpUserDateFactory.class.phpUserDateListFactory.class.phpUserDateTotalFactory.class.phpUserDateTotalListFactory.class.phpValidator.class.phpWage.class.php
cron
department
geoip
help
hierarchy
holiday
import
install
Install.class.phpInstallSchema.class.phpInstallSchema_1000A.class.phpInstallSchema_1001A.class.phpInstallSchema_1002A.class.phpInstallSchema_1003A.class.phpInstallSchema_1004A.class.phpInstallSchema_1005A.class.phpInstallSchema_1006A.class.phpInstallSchema_1007A.class.phpInstallSchema_1008A.class.phpInstallSchema_1009A.class.phpInstallSchema_1010A.class.phpInstallSchema_1011A.class.phpInstallSchema_1012A.class.phpInstallSchema_1013A.class.phpInstallSchema_1014A.class.phpInstallSchema_1015A.class.phpInstallSchema_1016A.class.phpInstallSchema_1017A.class.phpInstallSchema_1018A.class.phpInstallSchema_1019A.class.phpInstallSchema_1020A.class.phpInstallSchema_1021A.class.phpInstallSchema_1022A.class.phpInstallSchema_1023A.class.phpInstallSchema_1024A.class.phpInstallSchema_1025A.class.phpInstallSchema_1026A.class.phpInstallSchema_1027A.class.phpInstallSchema_1028A.class.phpInstallSchema_1029A.class.phpInstallSchema_1030A.class.phpInstallSchema_1031A.class.phpInstallSchema_1032A.class.phpInstallSchema_1033A.class.phpInstallSchema_1034A.class.phpInstallSchema_1035A.class.phpInstallSchema_1036A.class.phpInstallSchema_1037A.class.phpInstallSchema_1038A.class.phpInstallSchema_1039A.class.phpInstallSchema_1040A.class.phpInstallSchema_1041A.class.phpInstallSchema_1042A.class.phpInstallSchema_1043A.class.phpInstallSchema_1044A.class.phpInstallSchema_1045A.class.phpInstallSchema_1046A.class.phpInstallSchema_1047A.class.phpInstallSchema_1049A.class.phpInstallSchema_1050A.class.phpInstallSchema_1051A.class.phpInstallSchema_1052A.class.phpInstallSchema_1053A.class.phpInstallSchema_1054A.class.phpInstallSchema_1055A.class.phpInstallSchema_1056A.class.phpInstallSchema_1058A.class.phpInstallSchema_1059A.class.phpInstallSchema_1060A.class.phpInstallSchema_1061A.class.phpInstallSchema_1062A.class.phpInstallSchema_1063A.class.phpInstallSchema_1064A.class.phpInstallSchema_1065A.class.phpInstallSchema_1066A.class.phpInstallSchema_1067A.class.phpInstallSchema_1068A.class.phpInstallSchema_1069A.class.phpInstallSchema_1070A.class.phpInstallSchema_1071A.class.phpInstallSchema_1090A.class.phpInstallSchema_1093A.class.phpInstallSchema_1100A.class.phpInstallSchema_1101A.class.phpInstallSchema_1102A.class.phpInstallSchema_1103A.class.phpInstallSchema_1104A.class.phpInstallSchema_1105A.class.phpInstallSchema_1106A.class.phpInstallSchema_1107A.class.phpInstallSchema_1108A.class.phpInstallSchema_1109A.class.phpInstallSchema_1110A.class.phpInstallSchema_1111A.class.phpInstallSchema_1112A.class.phpInstallSchema_1113A.class.phpInstallSchema_1114A.class.phpInstallSchema_1117A.class.phpInstallSchema_1118A.class.phpInstallSchema_1119A.class.phpInstallSchema_1121A.class.phpInstallSchema_1122A.class.phpInstallSchema_1123A.class.phpInstallSchema_1124A.class.phpInstallSchema_1125A.class.phpInstallSchema_1126A.class.phpInstallSchema_1127A.class.phpInstallSchema_1128A.class.phpInstallSchema_1129A.class.phpInstallSchema_1131A.class.phpInstallSchema_1132A.class.phpInstallSchema_1133A.class.phpInstallSchema_1134A.class.phpInstallSchema_1135A.class.phpInstallSchema_1136A.class.phpInstallSchema_1137A.class.phpInstallSchema_1138A.class.phpInstallSchema_1139A.class.phpInstallSchema_Base.class.php
sql
kpi
message
notification
other
pay_stub
pay_stub_amendment
payperiod
plugins
policy
AbsencePolicyFactory.class.phpAbsencePolicyListFactory.class.phpAccrualPolicyAccountFactory.class.phpAccrualPolicyAccountListFactory.class.phpAccrualPolicyFactory.class.phpAccrualPolicyListFactory.class.phpAccrualPolicyMilestoneFactory.class.phpAccrualPolicyMilestoneListFactory.class.phpBreakPolicyFactory.class.phpBreakPolicyListFactory.class.phpContributingPayCodePolicyFactory.class.phpContributingPayCodePolicyListFactory.class.phpContributingShiftPolicyFactory.class.phpContributingShiftPolicyListFactory.class.phpExceptionPolicyControlFactory.class.phpExceptionPolicyControlListFactory.class.phpExceptionPolicyFactory.class.phpExceptionPolicyListFactory.class.phpHolidayPolicyFactory.class.phpHolidayPolicyListFactory.class.phpHolidayPolicyRecurringHolidayFactory.class.phpHolidayPolicyRecurringHolidayListFactory.class.phpMealPolicyFactory.class.phpMealPolicyListFactory.class.phpOverTimePolicyFactory.class.phpOverTimePolicyListFactory.class.phpPayCodeFactory.class.phpPayCodeListFactory.class.phpPayFormulaPolicyFactory.class.phpPayFormulaPolicyListFactory.class.phpPolicyGroupAccrualPolicyFactory.class.phpPolicyGroupAccrualPolicyListFactory.class.phpPolicyGroupFactory.class.phpPolicyGroupListFactory.class.phpPolicyGroupOverTimePolicyFactory.class.phpPolicyGroupOverTimePolicyListFactory.class.phpPolicyGroupPremiumPolicyFactory.class.phpPolicyGroupPremiumPolicyListFactory.class.phpPolicyGroupRoundIntervalPolicyFactory.class.phpPolicyGroupRoundIntervalPolicyListFactory.class.phpPolicyGroupUserFactory.class.phpPolicyGroupUserListFactory.class.phpPremiumPolicyBranchFactory.class.phpPremiumPolicyBranchListFactory.class.phpPremiumPolicyDepartmentFactory.class.phpPremiumPolicyDepartmentListFactory.class.phpPremiumPolicyFactory.class.phpPremiumPolicyListFactory.class.phpRegularTimePolicyFactory.class.phpRegularTimePolicyListFactory.class.phpRoundIntervalPolicyFactory.class.phpRoundIntervalPolicyListFactory.class.phpSchedulePolicyFactory.class.phpSchedulePolicyListFactory.class.php
punch
qualification
report
request
schedule
soap
ui_kit
users
other
payroll_deduction
pear
upload
composer.jsoncomposer.lockfavicon.icofiles.deletefiles.sha1
includes
index.phpinstall_cron.sh
interface
Login.phpblank.html
flex
html5
.jshintrcConfirmEmail.phpDownForMaintenance.phpIndexController.js
client
components
dist
BaseViewController.bundle.jsBaseWindowController.bundle.jsDocumentSubViewController.bundle.jsJobApplicantEducationSubViewController.bundle.jsJobApplicantEmploymentSubViewController.bundle.jsJobApplicantLanguageSubViewController.bundle.jsJobApplicantLicenseSubViewController.bundle.jsJobApplicantLocationSubViewController.bundle.jsJobApplicantMembershipSubViewController.bundle.jsJobApplicantReferenceSubViewController.bundle.jsJobApplicantSkillSubViewController.bundle.jsJobApplicantSubBaseViewController.bundle.jsJobApplicationSubViewController.bundle.js_css.main_ui.template.html_css.portal.template.html_css.quick_punch.template.html_js.main_ui.template.html_js.portal.template.html_js.quick_punch.template.htmlattendance-accrual-AccrualViewController.bundle.jsattendance-accrual_balance-AccrualBalanceViewController.bundle.jsattendance-exceptions-ExceptionViewController.bundle.jsattendance-in_out-InOutViewController.bundle.jsattendance-job-JobGroupViewController.bundle.jsattendance-job-JobViewController.bundle.jsattendance-job_item-JobItemGroupViewController.bundle.jsattendance-job_item-JobItemViewController.bundle.jsattendance-job_item_amendment-JobItemAmendmentViewController.bundle.jsattendance-map-MapViewController.bundle.jsattendance-map-MapViewController.cssattendance-punch_tag-PunchTagGroupViewController.bundle.jsattendance-punch_tag-PunchTagViewController.bundle.jsattendance-punches-PunchesViewController.bundle.jsattendance-recurring_schedule_control-RecurringScheduleControlViewController.bundle.jsattendance-recurring_schedule_template_control-RecurringScheduleTemplateControlViewController.bundle.jsattendance-schedule-ScheduleShiftViewController.bundle.jsattendance-schedule-ScheduleViewController.bundle.jsattendance-schedule-ScheduleViewController.cssattendance-timesheet-TimeSheetViewController.bundle.jsattendance-timesheet-TimeSheetViewController.cssattendance-timesheet-UserDateTotalParentViewController.bundle.jsattendance-timesheet-UserDateTotalViewController.bundle.jsawesomebox-AComboBox.bundle.jsawesomebox-ADropDown.bundle.jsawesomebox-ALayoutCache.bundle.jsawesomebox-ASearchInput.bundle.jscheckbox-TCheckbox.bundle.jscolor-picker-TColorPicker.bundle.jscolumn_editor-ColumnEditor.bundle.jscombobox-TComboBox.bundle.jscommon-AuthorizationHistoryCommon.bundle.jscommon-BaseTreeViewController.bundle.jscommon-EmbeddedMessageCommon.bundle.jscommon-RequestViewCommonController.bundle.jscompany-branch-BranchViewController.bundle.jscompany-companies-CompaniesViewController.bundle.jscompany-company-CompanyViewController.bundle.jscompany-currency-CurrencyRateViewController.bundle.jscompany-currency-CurrencyViewController.bundle.jscompany-custom_field-CustomFieldViewController.bundle.jscompany-department-DepartmentViewController.bundle.jscompany-ethnic_group-EthnicGroupViewController.bundle.jscompany-geo_fence-GEOFenceViewController.bundle.jscompany-geo_fence-GEOFenceViewController.csscompany-hierarchy_control-HierarchyControlViewController.bundle.jscompany-legal_entity-LegalEntityViewController.bundle.jscompany-other_field-OtherFieldViewController.bundle.jscompany-payroll_remittance_agency-PayrollRemittanceAgencyEventViewController.bundle.jscompany-payroll_remittance_agency-PayrollRemittanceAgencyViewController.bundle.jscompany-permission_control-PermissionControlViewController.bundle.jscompany-remittance_source_account-RemittanceSourceAccountViewController.bundle.jscompany-station-StationViewController.bundle.jscompany-wage-WageViewController.bundle.jscompany-wage_group-WageGroupViewController.bundle.jscore-log-LogViewController.bundle.jsdatepicker-TDatePicker.bundle.jsdatepicker-TRangePicker.bundle.jsdeveloper_tools-AwesomeboxTestViewController.bundle.jsdeveloper_tools-GridTestViewController.bundle.jsdeveloper_tools-WidgetTestViewController.bundle.jsdeveloper_tools-debugPanelController.bundle.jsdocument-DocumentGroupViewController.bundle.jsdocument-DocumentRevisionViewController.bundle.jsdocument-DocumentViewController.bundle.jsdynamic-editview-primevue-button.bundle.jsdynamic-editview-primevue-calendar.bundle.jsdynamic-editview-primevue-dropdown.bundle.jsdynamic-editview-primevue-radiobutton.bundle.jsdynamic-testview.bundle.jsdynamic-testview.cssemployees-employee-EmployeeViewController.bundle.jsemployees-remittance_destination_account-RemittanceDestinationAccountViewController.bundle.jsemployees-roe-ROEViewController.bundle.jsemployees-user_contact-UserContactViewController.bundle.jsemployees-user_default-UserDefaultViewController.bundle.jsemployees-user_group-UserGroupViewController.bundle.jsemployees-user_preference-UserPreferenceViewController.bundle.jsemployees-user_title-UserTitleViewController.bundle.jserror_tip-ErrorTipBox.bundle.jsfeedback-TFeedback.bundle.jsfilebrowser-CameraBrowser.bundle.jsfilebrowser-TImage.bundle.jsfilebrowser-TImageAdvBrowser.bundle.jsfilebrowser-TImageBrowser.bundle.jsfilebrowser-TImageCutArea.bundle.jsfirebase-app.jsfirebase-messaging.jsfirebase-service-worker.bundle.jsformula_builder-FormulaBuilder.bundle.jshelp-AboutViewController.bundle.jshome-dashboard-HomeViewController.bundle.jshome-dashlet-DashletController.bundle.jshr-kpi-KPIGroupViewController.bundle.jshr-kpi-KPIViewController.bundle.jshr-kpi-UserReviewControlViewController.bundle.jshr-qualification-QualificationGroupViewController.bundle.jshr-qualification-QualificationViewController.bundle.jshr-qualification-UserEducationViewController.bundle.jshr-qualification-UserLanguageViewController.bundle.jshr-qualification-UserLicenseViewController.bundle.jshr-qualification-UserMembershipViewController.bundle.jshr-qualification-UserSkillViewController.bundle.jshr-recruitment-JobApplicantEducationViewController.bundle.jshr-recruitment-JobApplicantEmploymentViewController.bundle.jshr-recruitment-JobApplicantLanguageViewController.bundle.jshr-recruitment-JobApplicantLicenseViewController.bundle.jshr-recruitment-JobApplicantLocationViewController.bundle.jshr-recruitment-JobApplicantMembershipViewController.bundle.jshr-recruitment-JobApplicantReferenceViewController.bundle.jshr-recruitment-JobApplicantSkillViewController.bundle.jshr-recruitment-JobApplicantViewController.bundle.jshr-recruitment-JobApplicationViewController.bundle.jshr-recruitment-JobApplicationViewController.csshr-recruitment-JobVacancyViewController.bundle.jshr-recruitment-RecruitmentPortalConfigViewController.bundle.jsinside_editor-InsideEditor.bundle.jsinterface_html5_components_context_menu_TTContextButton_vue.bundle.jsinterface_html5_framework_leaflet_leaflet-timetrex_js.bundle.jsinvoice-area_policy-AreaPolicyViewController.bundle.jsinvoice-client-ClientViewController.bundle.jsinvoice-client_contact-ClientContactViewController.bundle.jsinvoice-client_group-ClientGroupViewController.bundle.jsinvoice-client_payment-ClientPaymentViewController.bundle.jsinvoice-district-InvoiceDistrictViewController.bundle.jsinvoice-invoice-InvoiceViewController.bundle.jsinvoice-invoice_transaction-InvoiceTransactionViewController.bundle.jsinvoice-payment_gateway-PaymentGatewayViewController.bundle.jsinvoice-product_group-ProductGroupViewController.bundle.jsinvoice-products-ProductViewController.bundle.jsinvoice-settings-InvoiceConfigViewController.bundle.jsinvoice-shipping_policy-ShippingPolicyViewController.bundle.jsinvoice-tax_policy-TaxPolicyViewController.bundle.jsjqgrid-TGridHeader.bundle.jsleaflet-timetrex.csslist-TList.bundle.jslive-chat.bundle.jslogin-LoginViewController.bundle.jslogin-LoginViewController.cssmain_ui-styles.bundle.jsmain_ui-styles.cssmain_ui-vendor-styles.bundle.jsmain_ui-vendor-styles.cssmain_ui.bundle.jsmain_ui.cssmessage_box-NoHierarchyBox.bundle.jsmessage_box-NoResultBox.bundle.jsmessage_box-SaveAndContinueBox.bundle.jsmy_account-expense-ExpenseAuthorizationViewController.bundle.jsmy_account-expense-LoginUserExpenseViewController.bundle.jsmy_account-message_control-MessageControlViewController.bundle.jsmy_account-notification-NotificationViewController.bundle.jsmy_account-password-ChangePasswordViewController.bundle.jsmy_account-request-RequestViewController.bundle.jsmy_account-request_authorization-RequestAuthorizationViewController.bundle.jsmy_account-timesheet_authorization-TimeSheetAuthorizationViewController.bundle.jsmy_account-user_contact-LoginUserContactViewController.bundle.jsmy_account-user_preference-LoginUserPreferenceViewController.bundle.jspaging-Paging2.bundle.jspayperiod-PayPeriodScheduleViewController.bundle.jspayroll-company_tax_deduction-CompanyTaxDeductionViewController.bundle.jspayroll-government_document-GovernmentDocumentViewController.bundle.jspayroll-pay_periods-PayPeriodsViewController.bundle.jspayroll-pay_stub-PayStubViewController.bundle.jspayroll-pay_stub_amendment-PayStubAmendmentViewController.bundle.jspayroll-pay_stub_entry_account-PayStubEntryAccountViewController.bundle.jspayroll-pay_stub_transaction-PayStubTransactionViewController.bundle.jspayroll-process_transactions_wizard-ProcessTransactionsWizard.bundle.jspayroll-process_transactions_wizard-ProcessTransactionsWizardController.bundle.jspayroll-process_transactions_wizard-ProcessTransactionsWizardStepHome.bundle.jspayroll-recurring_pay_stub_amendment-RecurringPayStubAmendmentViewController.bundle.jspayroll-remittance_wizard-PayrollRemittanceAgencyEventWizard.bundle.jspayroll-remittance_wizard-PayrollRemittanceAgencyEventWizardController.bundle.jspayroll-remittance_wizard-PayrollRemittanceAgencyEventWizardStepHome.bundle.jspayroll-remittance_wizard-PayrollRemittanceAgencyEventWizardStepPublish.bundle.jspayroll-remittance_wizard-PayrollRemittanceAgencyEventWizardStepReview.bundle.jspayroll-remittance_wizard-PayrollRemittanceAgencyEventWizardStepSubmit.bundle.jspayroll-user_expense-UserExpenseViewController.bundle.jspdf.worker.js
pdfjs-cmaps
78-EUC-H.bcmap78-EUC-V.bcmap78-H.bcmap78-RKSJ-H.bcmap78-RKSJ-V.bcmap78-V.bcmap78ms-RKSJ-H.bcmap78ms-RKSJ-V.bcmap83pv-RKSJ-H.bcmap90ms-RKSJ-H.bcmap90ms-RKSJ-V.bcmap90msp-RKSJ-H.bcmap90msp-RKSJ-V.bcmap90pv-RKSJ-H.bcmap90pv-RKSJ-V.bcmapAdd-H.bcmapAdd-RKSJ-H.bcmapAdd-RKSJ-V.bcmapAdd-V.bcmapAdobe-CNS1-0.bcmapAdobe-CNS1-1.bcmapAdobe-CNS1-2.bcmapAdobe-CNS1-3.bcmapAdobe-CNS1-4.bcmapAdobe-CNS1-5.bcmapAdobe-CNS1-6.bcmapAdobe-CNS1-UCS2.bcmapAdobe-GB1-0.bcmapAdobe-GB1-1.bcmapAdobe-GB1-2.bcmapAdobe-GB1-3.bcmapAdobe-GB1-4.bcmapAdobe-GB1-5.bcmapAdobe-GB1-UCS2.bcmapAdobe-Japan1-0.bcmapAdobe-Japan1-1.bcmapAdobe-Japan1-2.bcmapAdobe-Japan1-3.bcmapAdobe-Japan1-4.bcmapAdobe-Japan1-5.bcmapAdobe-Japan1-6.bcmapAdobe-Japan1-UCS2.bcmapAdobe-Korea1-0.bcmapAdobe-Korea1-1.bcmapAdobe-Korea1-2.bcmapAdobe-Korea1-UCS2.bcmapB5-H.bcmapB5-V.bcmapB5pc-H.bcmapB5pc-V.bcmapCNS-EUC-H.bcmapCNS-EUC-V.bcmapCNS1-H.bcmapCNS1-V.bcmapCNS2-H.bcmapCNS2-V.bcmapETHK-B5-H.bcmapETHK-B5-V.bcmapETen-B5-H.bcmapETen-B5-V.bcmapETenms-B5-H.bcmapETenms-B5-V.bcmapEUC-H.bcmapEUC-V.bcmapExt-H.bcmapExt-RKSJ-H.bcmapExt-RKSJ-V.bcmapExt-V.bcmapGB-EUC-H.bcmapGB-EUC-V.bcmapGB-H.bcmapGB-V.bcmapGBK-EUC-H.bcmapGBK-EUC-V.bcmapGBK2K-H.bcmapGBK2K-V.bcmapGBKp-EUC-H.bcmapGBKp-EUC-V.bcmapGBT-EUC-H.bcmapGBT-EUC-V.bcmapGBT-H.bcmapGBT-V.bcmapGBTpc-EUC-H.bcmapGBTpc-EUC-V.bcmapGBpc-EUC-H.bcmapGBpc-EUC-V.bcmapH.bcmapHKdla-B5-H.bcmapHKdla-B5-V.bcmapHKdlb-B5-H.bcmapHKdlb-B5-V.bcmapHKgccs-B5-H.bcmapHKgccs-B5-V.bcmapHKm314-B5-H.bcmapHKm314-B5-V.bcmapHKm471-B5-H.bcmapHKm471-B5-V.bcmapHKscs-B5-H.bcmapHKscs-B5-V.bcmapHankaku.bcmapHiragana.bcmapKSC-EUC-H.bcmapKSC-EUC-V.bcmapKSC-H.bcmapKSC-Johab-H.bcmapKSC-Johab-V.bcmapKSC-V.bcmapKSCms-UHC-H.bcmapKSCms-UHC-HW-H.bcmapKSCms-UHC-HW-V.bcmapKSCms-UHC-V.bcmapKSCpc-EUC-H.bcmapKSCpc-EUC-V.bcmapKatakana.bcmapLICENSENWP-H.bcmapNWP-V.bcmapRKSJ-H.bcmapRKSJ-V.bcmapRoman.bcmapUniCNS-UCS2-H.bcmapUniCNS-UCS2-V.bcmapUniCNS-UTF16-H.bcmapUniCNS-UTF16-V.bcmapUniCNS-UTF32-H.bcmapUniCNS-UTF32-V.bcmapUniCNS-UTF8-H.bcmapUniCNS-UTF8-V.bcmapUniGB-UCS2-H.bcmapUniGB-UCS2-V.bcmapUniGB-UTF16-H.bcmapUniGB-UTF16-V.bcmapUniGB-UTF32-H.bcmapUniGB-UTF32-V.bcmapUniGB-UTF8-H.bcmapUniGB-UTF8-V.bcmapUniJIS-UCS2-H.bcmapUniJIS-UCS2-HW-H.bcmapUniJIS-UCS2-HW-V.bcmapUniJIS-UCS2-V.bcmapUniJIS-UTF16-H.bcmapUniJIS-UTF16-V.bcmapUniJIS-UTF32-H.bcmapUniJIS-UTF32-V.bcmapUniJIS-UTF8-H.bcmapUniJIS-UTF8-V.bcmapUniJIS2004-UTF16-H.bcmapUniJIS2004-UTF16-V.bcmapUniJIS2004-UTF32-H.bcmapUniJIS2004-UTF32-V.bcmapUniJIS2004-UTF8-H.bcmapUniJIS2004-UTF8-V.bcmapUniJISPro-UCS2-HW-V.bcmapUniJISPro-UCS2-V.bcmapUniJISPro-UTF8-V.bcmapUniJISX0213-UTF32-H.bcmapUniJISX0213-UTF32-V.bcmapUniJISX02132004-UTF32-H.bcmapUniJISX02132004-UTF32-V.bcmapUniKS-UCS2-H.bcmapUniKS-UCS2-V.bcmapUniKS-UTF16-H.bcmapUniKS-UTF16-V.bcmapUniKS-UTF32-H.bcmapUniKS-UTF32-V.bcmapUniKS-UTF8-H.bcmapUniKS-UTF8-V.bcmapV.bcmapWP-Symbol.bcmap
policy-absence_policy-AbsencePolicyViewController.bundle.jspolicy-accrual_policy-AccrualPolicyAccountViewController.bundle.jspolicy-accrual_policy-AccrualPolicyUserModifierViewController.bundle.jspolicy-accrual_policy-AccrualPolicyViewController.bundle.jspolicy-break_policy-BreakPolicyViewController.bundle.jspolicy-contributing_pay_code_policy-ContributingPayCodePolicyViewController.bundle.jspolicy-contributing_shift_policy-ContributingShiftPolicyViewController.bundle.jspolicy-exception_policy-ExceptionPolicyControlViewController.bundle.jspolicy-expense_policy-ExpensePolicyViewController.bundle.jspolicy-holiday-HolidayViewController.bundle.jspolicy-holiday_policy-HolidayPolicyViewController.bundle.jspolicy-meal_policy-MealPolicyViewController.bundle.jspolicy-overtime_policy-OvertimePolicyViewController.bundle.jspolicy-pay_code-PayCodeViewController.bundle.jspolicy-pay_formula_policy-PayFormulaPolicyViewController.bundle.jspolicy-policy_group-PolicyGroupViewController.bundle.jspolicy-premium_policy-PremiumPolicyViewController.bundle.jspolicy-recurring_holiday-RecurringHolidayViewController.bundle.jspolicy-regular_time_policy-RegularTimePolicyViewController.bundle.jspolicy-round_interval_policy-RoundIntervalPolicyViewController.bundle.jspolicy-schedule_policy-SchedulePolicyViewController.bundle.jsportal-PortalBaseViewController.bundle.jsportal-header-HeaderUploadResumeWidget.bundle.jsportal-hr-my_jobapplication-MyJobApplicationViewController.bundle.jsportal-hr-my_profile-JobApplicantEducationSubViewController.bundle.jsportal-hr-my_profile-JobApplicantLanguageSubViewController.bundle.jsportal-hr-my_profile-JobApplicantLicenseSubViewController.bundle.jsportal-hr-my_profile-JobApplicantMembershipSubViewController.bundle.jsportal-hr-my_profile-JobApplicantSubBaseViewController.bundle.jsportal-hr-my_profile-MyProfileViewController.bundle.jsportal-hr-recruitment-PortalJobVacancyDetailController.bundle.jsportal-hr-recruitment-PortalJobVacancyDetailViewController.bundle.jsportal-hr-recruitment-PortalJobVacancyRowController.bundle.jsportal-hr-recruitment-PortalJobVacancyViewController.bundle.jsportal-sign_in-PortalForgotPasswordController.bundle.jsportal-sign_in-PortalResetForgotPasswordController.bundle.jsportal-styles.bundle.jsportal-styles.cssportal.bundle.jsportal.csspost-login-app-dependancies.bundle.jspost-login-main_ui-vendor-dependancies.bundle.jsquick_punch-QuickPunchBaseViewController.bundle.jsquick_punch-header-HeaderViewController.bundle.jsquick_punch-login-QuickPunchLoginViewController.bundle.jsquick_punch-punch-QuickPunchViewController.bundle.jsquick_punch-styles.bundle.jsquick_punch-styles.cssquick_punch.bundle.jsquick_punch.cssreports-ReportBaseViewController.bundle.jsreports-accrual_balance_summary-AccrualBalanceSummaryReportViewController.bundle.jsreports-affordable_care-AffordableCareReportViewController.bundle.jsreports-audittrail-AuditTrailReportViewController.bundle.jsreports-custom_column-CustomColumnViewController.bundle.jsreports-employee_information-UserSummaryReportViewController.bundle.jsreports-exception_summary-ExceptionSummaryReportViewController.bundle.jsreports-expense_summary-ExpenseSummaryReportViewController.bundle.jsreports-form1099-Form1099NecReportViewController.bundle.jsreports-form940-Form940ReportViewController.bundle.jsreports-form941-Form941ReportViewController.bundle.jsreports-formw2-FormW2ReportViewController.bundle.jsreports-general_ledger_summary-GeneralLedgerSummaryReportViewController.bundle.jsreports-invoice_transaction_summary-InvoiceTransactionSummaryReportViewController.bundle.jsreports-job_analysis-JobAnalysisReportViewController.bundle.jsreports-job_info-JobInformationReportViewController.bundle.jsreports-job_item_info-JobItemInformationReportViewController.bundle.jsreports-job_summary-JobSummaryReportViewController.bundle.jsreports-pay_stub_summary-PayStubSummaryReportViewController.bundle.jsreports-pay_stub_transaction_summary-PayStubTransactionSummaryReportViewController.bundle.jsreports-payroll_export-PayrollExportReportViewController.bundle.jsreports-punch_summary-PunchSummaryReportViewController.bundle.jsreports-qualification_summary-UserQualificationReportViewController.bundle.jsreports-recruitment_detail-UserRecruitmentDetailReportViewController.bundle.jsreports-recruitment_summary-UserRecruitmentSummaryReportViewController.bundle.jsreports-remittance_summary-RemittanceSummaryReportViewController.bundle.jsreports-report_schedule-ReportScheduleViewController.bundle.jsreports-review_summary-KPIReportViewController.bundle.jsreports-saved_report-SavedReportViewController.bundle.jsreports-schedule_summary-ScheduleSummaryReportViewController.bundle.jsreports-t4_summary-T4SummaryReportViewController.bundle.jsreports-t4a_summary-T4ASummaryReportViewController.bundle.jsreports-tax_summary-TaxSummaryReportViewController.bundle.jsreports-timesheet_detail-TimesheetDetailReportViewController.bundle.jsreports-timesheet_summary-TimesheetSummaryReportViewController.bundle.jsreports-us_state_unemployment-USStateUnemploymentReportViewController.bundle.jsreports-whos_in_summary-ActiveShiftReportViewController.bundle.jsruntime.bundle.jssearch_panel-SearchPanel.bundle.jsseparated_box-SeparatedBox.bundle.jsswitch_button-SwitchButton.bundle.jstag_input-TTagInput.bundle.jstext-TText.bundle.jstext_input-TPasswordInput.bundle.jstext_input-TTextInput.bundle.jstextarea-TTextArea.bundle.jstimepicker-TTimePicker.bundle.jstoggle_button-TToggleButton.bundle.jsttgrid-TTGrid.bundle.jsui_kit_sample-UIKitChildSampleViewController.bundle.jsui_kit_sample-UIKitSampleViewController.bundle.js
vendor_ui
vendors-node_modules_bootstrap-select_dist_js_bootstrap-select_js.bundle.jsvendors-node_modules_css-loader_dist_cjs_js_node_modules_tinymce_skins_content_default_conten-a516be.bundle.jsvendors-node_modules_css-loader_dist_cjs_js_node_modules_tinymce_skins_content_default_conten-a516be.cssvendors-node_modules_decimal_js_decimal_mjs.bundle.jsvendors-node_modules_jquery-bridget_jquery-bridget_js-node_modules_masonry-layout_masonry_js.bundle.jsvendors-node_modules_jquery-ui_ui_widgets_autocomplete_js-node_modules_jquery-ui_ui_widgets_r-024c3b.bundle.jsvendors-node_modules_jquery-ui_ui_widgets_datepicker_js.bundle.jsvendors-node_modules_leaflet_dist_images_marker-icon-2x_png-node_modules_leaflet_dist_images_-78d511.bundle.jsvendors-node_modules_leaflet_dist_images_marker-icon-2x_png-node_modules_leaflet_dist_images_-78d511.cssvendors-node_modules_linkifyjs_string_js.bundle.jsvendors-node_modules_pdfjs-dist_web_pdf_viewer_js.bundle.jsvendors-node_modules_primevue_button_button_esm_js-node_modules_primevue_menu_menu_esm_js-nod-e86f5b.bundle.jswizard-BaseWizardController.bundle.jswizard-Wizard.bundle.jswizard-WizardStep.bundle.jswizard-dashlet-DashletWizardController.bundle.jswizard-find_available-FindAvailableViewController.bundle.jswizard-find_available-FindAvailableWizardController.bundle.jswizard-forgot_password-ForgotPasswordWizardController.bundle.jswizard-formula_builder_wizard-FormulaBuilderWizardController.bundle.jswizard-generate_pay_stub-GeneratePayStubWizardController.bundle.jswizard-import_csv-ImportCSVWizardController.bundle.jswizard-install-InstallWizardController.bundle.jswizard-job_invoice-JobInvoiceWizardController.bundle.jswizard-login_user-LoginUserViewController.bundle.jswizard-login_user-LoginUserWizardController.bundle.jswizard-pay_code-PayCodeWizardController.bundle.jswizard-pay_stub_account-PayStubAccountWizardController.bundle.jswizard-permission_wizard-PermissionWizardController.bundle.jswizard-process_payroll-ProcessPayrollWizardController.bundle.jswizard-quick_start-QuickStartWizardController.bundle.jswizard-re_calculate_accrual-ReCalculateAccrualWizardController.bundle.jswizard-re_calculate_timesheet-ReCalculateTimeSheetWizardController.bundle.jswizard-report_view-ReportViewWizardController.bundle.jswizard-reset_forgot_password-ResetForgotPasswordWizardController.bundle.jswizard-reset_password-ResetPasswordWizardController.bundle.jswizard-share_report-ShareReportWizardController.bundle.jswizard-user_generic_data_status-UserGenericStatusWindowController.bundle.jswizard-user_photo-UserPhotoWizardController.bundle.js
framework
apollo-vue
.browserslistrc.eslintrc.js.prettierrcCHANGELOG.mdREADME.mdbabel.config.jspackage.jsonpostcss.config.js
public
assets
demo
data
flags
images
layout
css
fonts
images
pages
sass
_fonts.scss
layout
overrides
theme
theme
favicon.icoindex.htmlupload.php
src
tests
vue.config.js
bootstrap-select
bootstrap-toolkit.min.js
bootstrap
decimal.min.js
google
analytics
html5shiv.min.jsinteract.min.jsjquery.bridget.jsjquery.i18n.jsjquery.imgareaselect.jsjquery.json.jsjquery.min.jsjquery.sortable.jsjquery.stickytableheaders.min.jsjquery.tablednd.js
jqueryui
json2.jsmasonry.min.jsmeasurement.jsmoment.jsnanobar.min.jspolyfill.js
qunit
r.jsrequire.jsrequire_async_plugin.jsrespond.min.js
rightclickmenu
tinymce
jquery.tinymce.min.js
langs
license.txt
plugins
skins
themes
tinymce.min.js
vue
widgets
global
index.phpmain.jsmain_ui-styles.jsmain_ui-vendor-styles.js
model
phpinfo.phppost-login-main_ui-dependancies.jspost-login-main_ui-vendor-dependancies.js
services
theme
default
css
application.css
global
widgets
awesomebox
column_editor
datepicker
error_tip
feedback
filebrowser
inside_editor
jqgrid
loading_bar
message_box
ribbon
RibbonView.cssgray.svg
icons
KPI-35x35.pngKPI_groups-35x35.pngabout-35x35.pngabsence_policies-35x35.pngaccrual-35x35.pngaccrual_accounts-35x35.pngaccrual_balance-35x35.pngaccrual_policies-35x35.pngadministration_guide_manual-35x35.pngalert-16x16.pngalert-35x35.pngalert_policies-35x35.pngapproved_expense-35x35.pngapproved_expense_new-35x35.pngapproved_overlay-35x35.pngarea_policies-35x35.pngaudit_trail-35x35.pngauthorization-35x35.pngauthorization2-35x35.pngauthorization3-35x35.pngauthorize-35x35.pngauthorize_request-35x35.pngauthorize_request_new-35x35.pngauthorize_timesheet-35x35.pngauthorize_timesheet_new-35x35.pngback-35x35.pngbank_accounts-35x35.pngbranches-35x35.pngbreak_policies-35x35.pngcalculate-35x35.pngcalculate_paystubs-35x35.pngcancel-35x35.pngclient_groups-35x35.pngclients-35x35.pngclients_contacts-35x35.pngclock_in_out-35x35.pngclose-35x35.pngclose_misc-35x35.pngclose_pay_period-35x35.pngclose_pay_period2-35x35.pngcompanies-35x35.pngcompany_information-35x35.pngcontact_information-35x35.pngcontributing_policies-35x35.pngcontributing_shift_policy-35x35.pngcopy-35x35.pngcopy_as_new-35x35.pngcurrencies-35x35.pngcustom_fields-35x35.pngdecline-35x35.pngdelete-35x35.pngdelete_and_next-35x35.pngdelete_and_previous-35x35.pngdepartments-35x35.pngdirect_deposit-35x35.pngdistrict-35x35.pngdocument_groups-35x35.pngdocuments-35x35.pngdone-35x35.pngdownload1-35x35.pngdownload2-35x35.pngedit-35x35.pngeducation-35x35.pngeligibility_policies-35x35.pngemailhelp-35x35.pngemployee_reports-35x35.pngemployees-35x35.pngethinicgroups-35x35.pngexceptions-35x35.pngexceptions_policies-35x35.pngexpense_policies-35x35.pngexpenses-35x35.pngexport-35x35.pngexport_to_efile-35x35.pngexport_to_excel-35x35.pngexport_to_xml-35x35.pngfaq-35x35.pngfillshift-35x35.pngformula_builder-16x16.pngformula_builder-35x35.pnggo-35x35.pnggovernment_payroll_documents-35x35.pnggroups-35x35.pnghappy.pnghappy_light.pnghelp-35x35.pnghierarchy-35x35.pngholiday_policies-35x35.pnghr_reports-35x35.pngimport-35x35.pnginbox-35x35.pnginvoice_reports-35x35.pnginvoice_wizard-35x35.pnginvoices-35x35.pngjob_applicant-35x35.pngjob_groups-35x35.pngjob_titles-35x35.pngjob_tracking_reports-35x35.pngjob_vacancies-35x35.pngjob_vacancy-35x35.pngjobapplications-35x35.pngjobs-35x35.pnglanguages-35x35.pnglanguages.pnglegal_entity-35x35.pnglicense-35x35.pnglicenses-35x35.pnglock-35x35.pnglogin-35x35.pnglogout-35x35.pngmap-35x35.pngmass_edit-35x35.pngmass_punches-35x35.pngmass_schedule-35x35.pngmaximize-35x35.pngmeal_policies-35x35.pngmemberships-35x35.pngmemberships.pngmessages-35x35.pngmessages_new-35x35.pngminimize-35x35.pngmove-35x35.pngneutral.pngneutral_light.pngnew_add-35x35.pngnew_hire_defaults-35x35.pngnew_overlay-35x35.pngnext-35x35.pngno_photo-16x16.pngno_photo-256x256.pngno_photo-35x35.pngonline_university-35x35.pngoverride-35x35.pngovertime_policies-35x35.pngpacking_slip-35x35.pngpass-35x35.pngpasswords-35x35.pngpay_code-35x35.pngpay_formula_policy-35x35.pngpay_period_schedules-35x35.pngpay_periods-35x35.pngpay_stub_account_linking-35x35.pngpay_stub_amendments-35x35.pngpay_stubs-35x35.pngpay_stubs_accounts-35x35.pngpayment_gateway_settings-35x35.pngpayment_methods-35x35.pngpayroll_remittance_agency-35x35.pngpayroll_reports-35x35.pngpermission_groups-35x35.pngpiece_work_policies-35x35.pngpolicy_groups-35x35.pngpreferences-35x35.pngpreferences_myaccount-35x35.pngpremium_policies-35x35.pngprint-35x35.pngprint_checks-35x35.pngprocess_payroll-35x35.pngproduct_groups-35x35.pngproducts-35x35.pngpunches-35x35.pngqualification_groups-35x35.pngqualification_groups.pngqualifications-35x35.pngqualifications.pngquicksetupwizard-35x35.pngquotes-35x35.pngrecord_of_employment-35x35.pngrecurring_holidays-35x35.pngrecurring_pay_stub_amendments-35x35.pngrecurring_schedule-35x35.pngrecurring_template-35x35.pngregular_time_policies-35x35.pngrequests-35x35.pngrestart-35x35.pngreviews-35x35.pngrounding_policies-35x35.pngsad.pngsad_light.pngsave-35x35.pngsave_and_back_exit-35x35.pngsave_and_continue-35x35.pngsave_and_copy-35x35.pngsave_and_new-35x35.pngsave_and_next-35x35.pngsave_and_previous-35x35.pngsave_setup-35x35.pngsaved_reports-35x35.pngschedule-35x35.pngschedule_policies-35x35.pngscheduled_shifts-35x35.pngsend-35x35.pngsent-35x35.pngsettings-35x35.pngshare-35x35.pngshipping_policies-35x35.pngshow_all_employees-18x18.pngshow_daily_totals-18x18.pngshow_weekly_totals-18x18.pngskill-35x35.pngskills-35x35.pngstations-35x35.pngstop-35x35.pngstrict_date_range-18x18.pngswap_shift-35x35.pngtags-35x35.pngtask_groups-35x35.pngtasks-35x35.pngtax_policies-35x35.pngtax_reports-35x35.pngtaxes_deductions-35x35.pngtimesheet-35x35.pngtimesheet_reports-35x35.pngtransactions-35x35.pngtrash.pngtrash_small.pngunder_time_policies-35x35.pngunlock-35x35.pngupload_file-16x16.pngupload_file-35x35.pngupload_image-16x16.pngupload_image-35x35.pngview-35x35.pngview_detail-35x35.pngwage_groups-35x35.pngwages-35x35.pngwarning-35x35.pngwhats_new-35x35.pngwizard2-35x35.pngwizard3-35x35.pngwizard4-35x35.pngwork_history_qualifiers-35x35.png
images
search_panel
switch_button
tag_input
talert
timepicker
toggle_button
top_menu
view_min_tab
gray.svg
icon_library
image_area_select
jquery-ui
quickPunch.css
right_click_menu
text_layer_builder.cssui.jqgrid.css
views
images
views
BaseViewController.jsBaseWindowController.jsTTBackboneView.js
attendance
common
company
core
developer_tools
employees
help
home
hr
login
my_account
payperiod
payroll
policy
reports
ui_kit_sample
wizard
BaseWizardController.js
dashlet
forgot_password
generate_pay_stub
import_csv
install
pay_code
pay_stub_account
permission_wizard
process_payroll
quick_start
re_calculate_accrual
re_calculate_timesheet
report_view
reset_forgot_password
reset_password
user_generic_data_status
user_photo
images
index.php
install
locale
ping.htmlping.phpsend_file.php
sounds
system_check.phpupload_file.php
maint
package-lock.jsonpackage.jsontimetrex.ini.php-example_linuxtimetrex.ini.php-example_windows
tools
unit_tests
.htaccessBootStrap.phpBootStrapSelenium.php
TTCodeStandard
beautifier.php
beautifier_filters
coding_standard_check.shcomposer.jsonconfig.xmlconfig_selenium.xmlrun.shrun_selenium.sh
testcases
i18n
kpi
other
payroll_deduction
CAPayrollDeductionCRATest2017.csvCAPayrollDeductionCRATest2018.csvCAPayrollDeductionCRATest2019.csvCAPayrollDeductionCRATest2020.csvCAPayrollDeductionCRATest2021.csvCAPayrollDeductionCRATest2022.csvCAPayrollDeductionTest2006.phpCAPayrollDeductionTest2007.phpCAPayrollDeductionTest2008.phpCAPayrollDeductionTest2009.csvCAPayrollDeductionTest2009.phpCAPayrollDeductionTest2010.csvCAPayrollDeductionTest2010.phpCAPayrollDeductionTest2011.csvCAPayrollDeductionTest2011.phpCAPayrollDeductionTest2012.csvCAPayrollDeductionTest2012.phpCAPayrollDeductionTest2013.csvCAPayrollDeductionTest2013.phpCAPayrollDeductionTest2014.csvCAPayrollDeductionTest2014.phpCAPayrollDeductionTest2015.csvCAPayrollDeductionTest2015.phpCAPayrollDeductionTest2016.csvCAPayrollDeductionTest2016.phpCAPayrollDeductionTest2017.csvCAPayrollDeductionTest2017.phpCAPayrollDeductionTest2018.csvCAPayrollDeductionTest2018.phpCAPayrollDeductionTest2019.csvCAPayrollDeductionTest2019.phpCAPayrollDeductionTest2020.csvCAPayrollDeductionTest2020.phpCAPayrollDeductionTest2021.csvCAPayrollDeductionTest2021.phpCAPayrollDeductionTest2022.csvCAPayrollDeductionTest2022.phpCRPayrollDeductionTest.phpUSPayrollDeductionTest2006.phpUSPayrollDeductionTest2007.phpUSPayrollDeductionTest2008.phpUSPayrollDeductionTest2009.csvUSPayrollDeductionTest2009.phpUSPayrollDeductionTest2010.csvUSPayrollDeductionTest2010.phpUSPayrollDeductionTest2011.csvUSPayrollDeductionTest2011.phpUSPayrollDeductionTest2012.csvUSPayrollDeductionTest2012.phpUSPayrollDeductionTest2013.csvUSPayrollDeductionTest2013.phpUSPayrollDeductionTest2014.csvUSPayrollDeductionTest2014.phpUSPayrollDeductionTest2015.csvUSPayrollDeductionTest2015.phpUSPayrollDeductionTest2016.csvUSPayrollDeductionTest2016.phpUSPayrollDeductionTest2017.csvUSPayrollDeductionTest2017.phpUSPayrollDeductionTest2018.csvUSPayrollDeductionTest2018.phpUSPayrollDeductionTest2019.csvUSPayrollDeductionTest2019.phpUSPayrollDeductionTest2020.csvUSPayrollDeductionTest2020.phpUSPayrollDeductionTest2021.csvUSPayrollDeductionTest2021.phpUSPayrollDeductionTest2022.csvUSPayrollDeductionTest2022.php
paystub
policy
report
Form940ReportTest.phpForm941ReportTest.phpFormW2ReportTest.phpFormW2ReportTest_testEFileFederalA.txtFormW2ReportTest_testEFileFederalB.txtFormW2ReportTest_testEFileFederalC.txtFormW2ReportTest_testEFileFederalMultiEmployeeA.txtFormW2ReportTest_testEFileFederalMultiEmployeeB.txtFormW2ReportTest_testEFileStateAL.txtFormW2ReportTest_testEFileStateAR.txtFormW2ReportTest_testEFileStateAZ.txtFormW2ReportTest_testEFileStateCO.txtFormW2ReportTest_testEFileStateCT.txtFormW2ReportTest_testEFileStateDC.txtFormW2ReportTest_testEFileStateDE.txtFormW2ReportTest_testEFileStateGA.txtFormW2ReportTest_testEFileStateIA.txtFormW2ReportTest_testEFileStateID.txtFormW2ReportTest_testEFileStateIL.txtFormW2ReportTest_testEFileStateIN.txtFormW2ReportTest_testEFileStateKS.txtFormW2ReportTest_testEFileStateKY.txtFormW2ReportTest_testEFileStateKYLocal1.txtFormW2ReportTest_testEFileStateMA.txtFormW2ReportTest_testEFileStateME.txtFormW2ReportTest_testEFileStateMI.txtFormW2ReportTest_testEFileStateMN.txtFormW2ReportTest_testEFileStateMO.txtFormW2ReportTest_testEFileStateMS.txtFormW2ReportTest_testEFileStateMT.txtFormW2ReportTest_testEFileStateMultiEmployeeIA.txtFormW2ReportTest_testEFileStateMultiEmployeeID.txtFormW2ReportTest_testEFileStateNC.txtFormW2ReportTest_testEFileStateND.txtFormW2ReportTest_testEFileStateNE.txtFormW2ReportTest_testEFileStateNY.txtFormW2ReportTest_testEFileStateOH.txtFormW2ReportTest_testEFileStateOK.txtFormW2ReportTest_testEFileStateOR.txtFormW2ReportTest_testEFileStatePA.txtFormW2ReportTest_testEFileStateSC.txtFormW2ReportTest_testEFileStateUT.txtFormW2ReportTest_testEFileStateVA.txtFormW2ReportTest_testEFileStateVT.txtFormW2ReportTest_testEFileStateWI.txtFormW2ReportTest_testFederalEFileWithFederalAndStateTaxesA.txtFormW2ReportTest_testNYEFileWithFederalAndStateTaxesA.txtTaxSummaryReportTest.phpUSStateUnemploymentReportTest.phpUSStateUnemploymentReportTest_testEFileAL.txtUSStateUnemploymentReportTest_testEFileAZ.txtUSStateUnemploymentReportTest_testEFileCA.txtUSStateUnemploymentReportTest_testEFileCO.txtUSStateUnemploymentReportTest_testEFileCT.txtUSStateUnemploymentReportTest_testEFileFL.txtUSStateUnemploymentReportTest_testEFileFederalA.txtUSStateUnemploymentReportTest_testEFileFederalMultiEmployeeA.txtUSStateUnemploymentReportTest_testEFileGA.txtUSStateUnemploymentReportTest_testEFileIA.txtUSStateUnemploymentReportTest_testEFileIL.txtUSStateUnemploymentReportTest_testEFileIN.txtUSStateUnemploymentReportTest_testEFileKS.txtUSStateUnemploymentReportTest_testEFileKY.txtUSStateUnemploymentReportTest_testEFileMI.txtUSStateUnemploymentReportTest_testEFileMS.txtUSStateUnemploymentReportTest_testEFileMT.txtUSStateUnemploymentReportTest_testEFileNC.txtUSStateUnemploymentReportTest_testEFileNY.txtUSStateUnemploymentReportTest_testEFileOK.txtUSStateUnemploymentReportTest_testEFileTN.txtUSStateUnemploymentReportTest_testEFileTX.txtUSStateUnemploymentReportTest_testEFileVA.txtUSStateUnemploymentReportTest_testEFileWA.txtUSStateUnemploymentReportTest_testEFileWI.txt
selenium
upgrade
vendor
.htaccessautoload.php
bin
brianium
cbschuld
composer
doctrine
ezyang
htmlpurifier
CHANGELOG.mdCREDITSLICENSEREADME.mdVERSIONcomposer.json
library
HTMLPurifier.auto.phpHTMLPurifier.autoload-legacy.phpHTMLPurifier.autoload.phpHTMLPurifier.composer.phpHTMLPurifier.func.phpHTMLPurifier.includes.phpHTMLPurifier.kses.phpHTMLPurifier.path.phpHTMLPurifier.phpHTMLPurifier.safe-includes.php
HTMLPurifier
Arborize.phpAttrCollections.phpAttrDef.php
AttrDef
AttrTransform.php
AttrTransform
AttrTypes.phpAttrValidator.phpBootstrap.phpCSSDefinition.phpChildDef.php
ChildDef
Config.phpConfigSchema.php
ConfigSchema
Builder
Exception.phpInterchange.php
Interchange
InterchangeBuilder.phpValidator.phpValidatorAtom.phpschema.ser
schema
Attr.AllowedClasses.txtAttr.AllowedFrameTargets.txtAttr.AllowedRel.txtAttr.AllowedRev.txtAttr.ClassUseCDATA.txtAttr.DefaultImageAlt.txtAttr.DefaultInvalidImage.txtAttr.DefaultInvalidImageAlt.txtAttr.DefaultTextDir.txtAttr.EnableID.txtAttr.ForbiddenClasses.txtAttr.ID.HTML5.txtAttr.IDBlacklist.txtAttr.IDBlacklistRegexp.txtAttr.IDPrefix.txtAttr.IDPrefixLocal.txtAutoFormat.AutoParagraph.txtAutoFormat.Custom.txtAutoFormat.DisplayLinkURI.txtAutoFormat.Linkify.txtAutoFormat.PurifierLinkify.DocURL.txtAutoFormat.PurifierLinkify.txtAutoFormat.RemoveEmpty.Predicate.txtAutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txtAutoFormat.RemoveEmpty.RemoveNbsp.txtAutoFormat.RemoveEmpty.txtAutoFormat.RemoveSpansWithoutAttributes.txtCSS.AllowDuplicates.txtCSS.AllowImportant.txtCSS.AllowTricky.txtCSS.AllowedFonts.txtCSS.AllowedProperties.txtCSS.DefinitionRev.txtCSS.ForbiddenProperties.txtCSS.MaxImgLength.txtCSS.Proprietary.txtCSS.Trusted.txtCache.DefinitionImpl.txtCache.SerializerPath.txtCache.SerializerPermissions.txtCore.AggressivelyFixLt.txtCore.AggressivelyRemoveScript.txtCore.AllowHostnameUnderscore.txtCore.AllowParseManyTags.txtCore.CollectErrors.txtCore.ColorKeywords.txtCore.ConvertDocumentToFragment.txtCore.DirectLexLineNumberSyncInterval.txtCore.DisableExcludes.txtCore.EnableIDNA.txtCore.Encoding.txtCore.EscapeInvalidChildren.txtCore.EscapeInvalidTags.txtCore.EscapeNonASCIICharacters.txtCore.HiddenElements.txtCore.Language.txtCore.LegacyEntityDecoder.txtCore.LexerImpl.txtCore.MaintainLineNumbers.txtCore.NormalizeNewlines.txtCore.RemoveInvalidImg.txtCore.RemoveProcessingInstructions.txtCore.RemoveScriptContents.txtFilter.Custom.txtFilter.ExtractStyleBlocks.Escaping.txtFilter.ExtractStyleBlocks.Scope.txtFilter.ExtractStyleBlocks.TidyImpl.txtFilter.ExtractStyleBlocks.txtFilter.YouTube.txtHTML.Allowed.txtHTML.AllowedAttributes.txtHTML.AllowedComments.txtHTML.AllowedCommentsRegexp.txtHTML.AllowedElements.txtHTML.AllowedModules.txtHTML.Attr.Name.UseCDATA.txtHTML.BlockWrapper.txtHTML.CoreModules.txtHTML.CustomDoctype.txtHTML.DefinitionID.txtHTML.DefinitionRev.txtHTML.Doctype.txtHTML.FlashAllowFullScreen.txtHTML.ForbiddenAttributes.txtHTML.ForbiddenElements.txtHTML.Forms.txtHTML.MaxImgLength.txtHTML.Nofollow.txtHTML.Parent.txtHTML.Proprietary.txtHTML.SafeEmbed.txtHTML.SafeIframe.txtHTML.SafeObject.txtHTML.SafeScripting.txtHTML.Strict.txtHTML.TargetBlank.txtHTML.TargetNoopener.txtHTML.TargetNoreferrer.txtHTML.TidyAdd.txtHTML.TidyLevel.txtHTML.TidyRemove.txtHTML.Trusted.txtHTML.XHTML.txtOutput.CommentScriptContents.txtOutput.FixInnerHTML.txtOutput.FlashCompat.txtOutput.Newline.txtOutput.SortAttr.txtOutput.TidyFormat.txtTest.ForceNoIconv.txtURI.AllowedSchemes.txtURI.Base.txtURI.DefaultScheme.txtURI.DefinitionID.txtURI.DefinitionRev.txtURI.Disable.txtURI.DisableExternal.txtURI.DisableExternalResources.txtURI.DisableResources.txtURI.Host.txtURI.HostBlacklist.txtURI.MakeAbsolute.txtURI.Munge.txtURI.MungeResources.txtURI.MungeSecretKey.txtURI.OverrideAllowedSchemes.txtURI.SafeIframeRegexp.txtinfo.ini
ContentSets.phpContext.phpDefinition.phpDefinitionCache.php
DefinitionCache
DefinitionCacheFactory.phpDoctype.phpDoctypeRegistry.phpElementDef.phpEncoder.phpEntityLookup.php
EntityLookup
EntityParser.phpErrorCollector.phpErrorStruct.phpException.phpFilter.php
Filter
Generator.phpHTMLDefinition.phpHTMLModule.php
HTMLModule
HTMLModuleManager.phpIDAccumulator.phpInjector.php
Injector
Language.php
Language
messages
LanguageFactory.phpLength.phpLexer.php
Lexer
Node.php
Node
PercentEncoder.phpPrinter.php
Printer
PropertyList.phpPropertyListIterator.phpQueue.phpStrategy.php
Strategy
StringHash.phpStringHashParser.phpTagTransform.php
TagTransform
Token.php
Token
TokenFactory.phpURI.phpURIDefinition.phpURIFilter.php
URIFilter
URIParser.phpURIScheme.php
URIScheme
URISchemeRegistry.phpUnitConverter.phpVarParser.php
VarParser
VarParserException.phpZipper.php
geoip2
jean85
kigkonsult
icalcreator
.github
LICENCEREADME.mdautoload.phpcomposer.json
docs
phpunit.xmlreleaseNotes
src
Available.phpCalendarComponent.phpDScomponent.phpDaylight.php
Formatter
IcalBase.phpIcalInterface.php
Parser
Participant.phpPc.phpStandard.php
Traits
Util
V2component.phpV3component.phpVAcomponent.phpValarm.phpVavailability.phpVcalendar.phpVcomponent.phpVevent.phpVfreebusy.phpVjournal.phpVlocation.phpVresource.phpVtimezone.phpVtodo.php
Xml
test
maennchen
markbaker
maxmind-db
maxmind
myclabs
nikic
php-parser
LICENSEREADME.md
bin
composer.json
grammar
lib
PhpParser
Builder.php
Builder
BuilderFactory.phpBuilderHelpers.phpComment.php
Comment
ConstExprEvaluationException.phpConstExprEvaluator.phpError.phpErrorHandler.php
ErrorHandler
Internal
JsonDecoder.phpLexer.php
Lexer
NameContext.phpNode.php
Node
Arg.phpAttribute.phpAttributeGroup.phpComplexType.phpConst_.phpExpr.php
Expr
FunctionLike.phpIdentifier.phpIntersectionType.phpMatchArm.phpName.php
Name
NullableType.phpParam.phpScalar.php
Scalar
Stmt.php
Stmt
UnionType.phpVarLikeIdentifier.phpVariadicPlaceholder.php
NodeAbstract.phpNodeDumper.phpNodeFinder.phpNodeTraverser.phpNodeTraverserInterface.phpNodeVisitor.php
NodeVisitor
NodeVisitorAbstract.phpParser.php
Parser
ParserAbstract.phpParserFactory.php
PrettyPrinter
PrettyPrinterAbstract.php
pear
archive_tar
cache_lite
console_getopt
mail
mail_mime
mime_type
pear
.travis.ymlCODING_STANDARDSINSTALLLICENSEMakefile.frag
OS
PEAR.php
PEAR
README.CONTRIBUTINGREADME.rstSystem.phpbuild-release.shcatalogcomposer.jsongo-pear-list.phpgo-pear-phar.phpinstall-pear.phpinstall-pear.txtmake-command-xml.phpmake-gopear-phar.phpmake-installpear-nozlib-phar.phpmake-pear-bundle.phppackage-ErrorStack.xmlpackage-manpages.xmlpackage.dtdpackage2.xml
scripts
template.spectest-modified.php
structures_graph
xml_util
phar-io
manifest
version
phpoffice
phpspreadsheet
.php-cs-fixer.dist.php.phpcs.xml.distCHANGELOG.mdCONTRIBUTING.mdLICENSEREADME.mdcomposer.jsonphpstan-baseline.neonphpstan-conditional.phpphpstan.neon.dist
src
PhpSpreadsheet
Calculation
ArrayEnabled.phpBinaryComparison.phpCalculation.phpCategory.phpDatabase.php
Database
DateTime.php
DateTimeExcel
Engine
Engineering.php
Engineering
Exception.phpExceptionHandler.phpFinancial.php
Financial
FormulaParser.phpFormulaToken.phpFunctions.php
Information
Internal
Logical.php
Logical
LookupRef.php
LookupRef
MathTrig.php
MathTrig
Statistical.php
Statistical
TextData.php
TextData
Token
Web.php
Web
locale
Cell
CellReferenceHelper.php
Chart
Collection
Comment.phpDefinedName.php
Document
Exception.phpHashTable.php
Helper
IComparable.phpIOFactory.phpNamedFormula.phpNamedRange.php
Reader
ReferenceHelper.php
RichText
Settings.php
Shared
Spreadsheet.php
Style
Worksheet
Writer
phpunit
php-code-coverage
ChangeLog.mdLICENSEREADME.mdcomposer.json
src
CodeCoverage.php
Driver
Exception
Filter.php
Node
ProcessedCodeCoverageData.phpRawCodeCoverageData.php
Report
StaticAnalysis
Util
Version.php
php-file-iterator
php-invoker
php-text-template
php-timer
phpunit-selenium
.ci
.travis.ymlChangeLog.markdownLICENSE
PHPUnit
README.md
Tests
Vagrantfilebuild.xml
build
composer.jsonphpunit-selenium-bootstrap.phpphpunit.xml.dist
selenium-1-tests
phpunit
.phpstorm.meta.phpChangeLog-8.5.mdChangeLog-9.5.mdLICENSEREADME.mdSECURITY.mdcomposer.jsonphpunitphpunit.xsd
schema
src
Exception.php
Framework
Assert.php
Assert
Constraint
DataProviderTestSuite.php
Error
ErrorTestCase.php
Exception
ExceptionWrapper.phpExecutionOrderDependency.phpIncompleteTest.phpIncompleteTestCase.phpInvalidParameterGroupException.php
MockObject
Api
Builder
ConfigurableMethod.php
Exception
Generator.php
Generator
Invocation.phpInvocationHandler.phpMatcher.phpMethodNameConstraint.phpMockBuilder.phpMockClass.phpMockMethod.phpMockMethodSet.phpMockObject.phpMockTrait.phpMockType.php
Rule
Stub.php
Stub
Verifiable.php
Reorderable.phpSelfDescribing.phpSkippedTest.phpSkippedTestCase.phpTest.phpTestBuilder.phpTestCase.phpTestFailure.phpTestListener.phpTestListenerDefaultImplementation.phpTestResult.phpTestSuite.phpTestSuiteIterator.phpWarningTestCase.php
Runner
TextUI
CliArguments
Command.phpDefaultResultPrinter.php
Exception
Help.phpResultPrinter.phpTestRunner.phpTestSuiteMapper.php
XmlConfiguration
CodeCoverage
Configuration.phpException.php
Filesystem
Generator.php
Group
Loader.php
Logging
Migration
PHP
PHPUnit
TestSuite
Util
psr
sebastian
cli-parser
code-unit-reverse-lookup
code-unit
comparator
complexity
diff
environment
exporter
global-state
lines-of-code
object-enumerator
object-reflector
recursion-context
resource-operations
type
version
setasign
fpdf
fpdi
symfony
console
Application.php
Attribute
CHANGELOG.md
CI
Color.php
Command
CommandLoader
Completion
ConsoleEvents.phpCursor.php
DependencyInjection
Descriptor
Event
EventListener
Exception
Formatter
Helper
Input
LICENSE
Logger
Output
Question
README.md
Resources
SignalRegistry
SingleCommandApplication.php
Style
Terminal.php
Tester
composer.json
deprecation-contracts
polyfill-ctype
polyfill-intl-grapheme
polyfill-intl-normalizer
polyfill-mbstring
polyfill-php73
polyfill-php80
process
service-contracts
string
tecnickcom
theseer
zytzagoo
webpack.config.js

@@ -0,0 +1,470 @@
class HtmlTemplates {
constructor() {
}
/**
* Conditionally insert variable or html. Can be used in two ways.
* Either 1) pass html, and if so it will use the html if the field is true.
* Or, 2) if the field is a value (and truthy), and html is undefined/not provided, then return the field value.
* Instead of 2), you can also reference a variable directly, but risk outputting 'undefined' into the output html.
* @param field Either a data value evaluating as truthy, or a Boolean.
* @param html Optional html field to use if field is Boolean.
* @returns {string|*}
*/
outputif( field, html ) { // function to be called outputif, or printif
if ( field ) {
return html ? html : field;
} else {
return '';
}
}
/***
* Conditionally output options passed to the view controller constructor. Wrapper for outputif to handle multiple options.
* @param options
* @returns {string}
*/
outputOptions( options ) {
let output = [];
for ( var i = 0; i < options.length; i++ ) {
let result = this.outputif( options[i].option, options[i].html );
if ( result ) {
output.push( result );
}
}
return output.length > 0 ? '{ ' + output.join( ', ' ) + ' }' : '';
}
/**
* PascalCase to snake_case
*/
convertPascalCase2SnakeCase( string ) {
// all lowercase separated by _
return string.split( /(?=[A-Z][a-z])/ ).join( '_' ).toLowerCase(); // Fix: [A-Z][a-z] is needed to not split on all caps like ROEView. But this wont handle single caps at the end though.
}
/**
* PascalCase to kebab-case
*/
convertPascalCase2KebabCase( string ) {
// all lowercase separated by -
return string.split( /(?=[A-Z][a-z])/ ).join( '-' ).toLowerCase(); // Fix: [A-Z][a-z] is needed to not split on all caps like ROEView. But this wont handle single caps at the end though.
}
getTemplateTypeFromFilename( filename ) {
var type;
switch ( true ) { // known as 'overloaded switch'
// The following views will use the new templating logic. Order of these statements is important, first rule to match is used.
case this.checkViewClassForInlineHtmlbyFilename( filename ) !== false: // If success, it will return a String with the html.
// If a view class contains a static html_template value, then use this as an override instead of any specific type template matched by filename.
type = TemplateType.INLINE_HTML;
break;
case filename.indexOf( 'Sub' ) === 0: // Checks 'Sub' at start of filename. Must come before ReportView, otherwise it will conflict for SubSavedReportView.html
type = TemplateType.SUB_VIEW;
break;
case filename.indexOf( 'EditView.html' ) !== -1: // Must come before List Views, otherwise it will match for those due to both ending in View.html
type = TemplateType.EDIT_VIEW;
break;
case filename.indexOf( 'ReportView.html' ) !== -1 && filename.includes( 'Saved' ) === false: // Must come before List Views, and after 'Sub' otherwise reports will be loaded as list views. However make sure SavedReport is loaded as a list view.
type = TemplateType.REPORT_VIEW;
break;
case filename.indexOf( 'View.html' ) !== -1: // Must come more or less last, otherwise it will conflict with other files containing View.html, like EditView.html and ReportView.html
type = TemplateType.LIST_VIEW;
break;
default:
// If no template types are matched, treat as legacy html.
type = TemplateType.LEGACY_HTML; // This results in the relevant html file being loaded via the network. The new tab parsing logic may still run!
}
return type;
}
//Certain views do not fall under the rules of 'getTemplateTypeFromFilename()' and require special handling.
//This can be removed once we switch how views are loaded and can apply these rules directly on the view itself.
getTemplateOptionsFromViewId( view_id ) {
let options = {
view_id: view_id,
// Remember, sub_view_mode is not included here as not yet available here; view controller has not yet been loaded. Sub View Mode will be determined by template_type using the file name data. Will be applied as an option in HtmlTemplates.getGenericListViewHtml
};
//The following tax reports require an additional tab.
let reports_require_form_setup = ['RemittanceSummaryReport', 'T4SummaryReport', 'T4ASummaryReport', 'Form941Report', 'Form940Report', 'Form1099NecReport', 'FormW2Report', 'AffordableCareReport', 'USStateUnemploymentReport'];
if ( reports_require_form_setup.includes( view_id ) ) {
options.report_form_setup = true;
}
let sub_view_require_warning_message = [`UserDateTotal`];
if ( sub_view_require_warning_message.includes( view_id ) ) {
options.sub_view_warning_message = true;
}
//Issue #3158 - If these controllers are cached then side effects can occur.
//Such as permission denied alerts after opening login view on Invoice -> Client view.
let view_do_not_cache_controller = [`LoginUserWizard`, 'LoginUser', 'FindAvailableWizard', 'FindAvailable'];
if ( view_do_not_cache_controller.includes( view_id ) ) {
options.do_not_cache_controller = true;
}
//Audit log is both a TemplateType.INLINE_HTML and a TemplateType.SUB_VIEW under the current HTML2JS system.
//Once we switch to loading ViewControllers before HTML this special case can be removed.
if( view_id === 'Log' ) {
options.is_sub_view = true;
}
//Views extending BaseTreeViewController have slightly different requirments such as not show total number div.
//Once we switch to loading ViewControllers before the HTML this can be conditional by checking if the controller has tree_mode set to true.
let base_tree_views = ['JobGroup', 'JobItemGroup', 'PunchTagGroup', 'UserGroup', 'DocumentGroup', 'KPIGroup', 'ClientGroup', 'ProductGroup'];
if ( base_tree_views.includes( view_id ) ) {
options.base_tree_view = true;
}
return options;
}
checkViewClassForInlineHtmlbyFilename( filename ) {
// Lets see if this view class contains a html_template override, which should precede any type definitions.
let check_class = window[ filename.replace(/\.html$/,'') + 'Controller' ];
if( check_class !== undefined && typeof check_class.html_template === 'string' ) {
return check_class.html_template;
} else {
return false;
}
}
/**
*
* @param {TemplateType} type
* @param {Object} options
* @param {Function} [onResult]
*/
getTemplate( type, options = {}, onResult ) {
var html_template;
switch ( type ) {
case TemplateType.LIST_VIEW:
html_template = this.getGenericListViewHtml( options );
break;
case TemplateType.SUB_VIEW:
options.is_sub_view = true; // Force this to be true, as it's a sub_view after all. (This switch data is based off html filename request)
html_template = this.getGenericListViewHtml( options );
break;
case TemplateType.EDIT_VIEW:
// code block
html_template = HtmlTemplatesGlobal.genericEditView( options );
break;
case TemplateType.REPORT_VIEW:
// code block
html_template = HtmlTemplatesGlobal.genericReportEditView( options );
break;
case TemplateType.INLINE_HTML:
html_template = this.getViewScriptTagHtml( options ) + this.checkViewClassForInlineHtmlbyFilename( options.filename );
break;
default:
Debug.Error( 'HTML2JS: Error occured getting template. No matches for ' + options.view_id, 'HtmlTemplates.js', 'HtmlTemplates.js', 'getTemplate', 1 );
return -1;
}
// If callback onResult exists, call the function, else return html.
if ( onResult ) {
onResult( html_template ); // should we put this outside the switch? Depends how similar the other switch statements are.
} else {
return html_template;
}
}
/**
* This also handles subview script tags, if options.is_sub_view is true.
* @param options
* @returns {string}
*/
getGenericListViewHtml( options = {} ) {
Debug.Text( 'HTML2JS: Template retrieved for ' + options.view_id, 'HtmlTemplates.js', 'HtmlTemplates.js', 'getGenericListViewHtml', 10 );
// Prepend the <script> tag to the html template, so it can be executed when inserted into the DOM later on in the onResult.
return this.getViewScriptTagHtml( options ) + HtmlTemplatesGlobal.genericListView( options );
}
getViewScriptTagHtml( options = {} ) {
/*
* Note: In the legacy html load, the view controller would get instantiated via the <script> tag at the top of the html file, when this got loaded into the DOM.
* This would have happened in the onResult() function, one of such is BaseViewController.loadView -> doNext().
* Rather than insert a check there to trigger the view controller, or prepend the script tag, we can also try to just do that AFTER the onResult.
* We should not do it before, as there is still cleanup code run by IndexController.removeCurrentView
*
* However, for now currently the best option is to pre-append the script tag info, because the onResult function won't always be from BaseViewController.loadView
* */
let class_name = options.view_id + 'ViewController';
let html_view_script_tag = `<!-- JS2HTML ${this.outputif( options.is_sub_view, 'SUB_VIEW ' )}--><script type="text/javascript">
var ${this.outputif( options.is_sub_view, 'sub_' )}${this.convertPascalCase2SnakeCase( options.view_id )}_view_controller = new ${class_name}(${this.outputOptions( [
{ option: options.is_sub_view, html: 'sub_view_mode: true' },
{ option: options.do_not_cache_controller, html: 'can_cache_controller: false' },
] )});
</script>`;
// Prepend the <script> tag to the html template, so it can be executed when inserted into the DOM later on in the onResult.
// html_template = html_view_script_tag + html_template;
//
// debugger; // A good debug point when you get errors around view controller instances not defined. But could also mean parent controller needs manual html insertion into the tab_model, as the subview doesnt have the right columns in the generic template, thus the container (to which the html initializing the controller) is attached, does not exist.
return html_view_script_tag;
}
genericListView( options = {} ) {
// Prepare the required data for the html template
let container_id = this.convertPascalCase2SnakeCase( options.view_id ) + '_view_container'; // E.g. branch_view_container. all lowercase separated by _ (Later in code, an id is added at the end of the string, like '_318') - Fix: [A-Z][a-z] is needed to not split on all caps like ROEView. But this wont handle single caps at the end though. // Also in most view.el
let container_class = this.convertPascalCase2KebabCase( options.view_id ) + '-view'; // E.g. branch-view. all lowercase separated by -
let view_template = `
<div class="view js-generic-list-view ${this.outputif( container_class )}${this.outputif( options.is_sub_view, ' sub-view' )}" id="${this.outputif( container_id )}">
${this.outputif( options.sub_view_warning_message, '<span class="warning-message"></span>' )}
<div class="clear-both-div"></div>
${this.genericListGrid( options )}
</div>
`;
return view_template;
}
genericListGrid( options = {} ) {
// let sub_view = options.is_sub_view;
let grid_template = `
<div class="grid-div js-generic-grid">
${this.outputif( !options.is_sub_view && !options.base_tree_view, '<div class="total-number-div"><span class="total-number-span"></span></div>' )}
<div class="grid-top-border"></div>
${this.outputif( options.is_sub_view, '<div class="sub-grid-view-div">' )}
<table id="grid"></table>
${this.outputif( options.is_sub_view, '</div>' )}
<div class="bottom-div">
<div class="grid-bottom-border"></div>
</div>
</div>
`;
return grid_template;
}
genericEditView( options = {} ) {
// Currently just for User Title Edit.
// viewId: UserTitle
// fileName: UserTitleEditView.html
// Prepare the required data for the html template
let tab_bar_id = this.convertPascalCase2SnakeCase( options.view_id ) + '_edit_view_tab_bar'; // e.g. user_title_edit_view_tab_bar - all lowercase separated by _ - Fix: [A-Z][a-z] is needed to not split on all caps like ROEView. But this wont handle single caps at the end though.
let edit_view_class = options.view_id + 'EditView'; // e.g. UserTitleEditView
let edit_view_template = `
<div class="js-generic-edit-view edit-view ${this.outputif( edit_view_class )}">
<div class="edit-view-tab-bar" id="${this.outputif( tab_bar_id )}">
<div class="navigation-div" style="display: none">
<span class="navigation-label"></span>
<img class="left-click arrow"/>
<div class="navigation-widget-div"></div>
<img class="right-click arrow"/>
</div>
<span class="close-icon">x</span>
<ul class="edit-view-tab-bar-label"></ul>
</div>
</div>
`;
return edit_view_template;
}
genericTab( options = {} ) {
let is_multi_column = options.is_multi_column;
let show_permission_div = options.show_permission_div;
let is_sub = options.is_sub_view || false;
let tab_id = options.tab_id;
let save_continue_sub_view = `
<div class="save-and-continue-div">
<span class="message"></span>
<div class="save-and-continue-button-div">
<button class="tt-button p-button p-component" type="button">
<span class="icon"></span>
<span class="p-button-label"></span>
</button>
</div>
</div>
`;
let permission_div = `
<div class="save-and-continue-div permission-defined-div">
<span class="message permission-message"></span>
</div>`;
let template = `
<div id="${tab_id}" class="html2js_flag edit-view-tab-outside${this.outputif( is_sub, '-sub-view' )}">
<div class="edit-view-tab" id="${tab_id}_content_div">
<div class="first-column${this.outputif( is_sub, '-sub-view' )}${this.outputif( !is_multi_column, ' full-width-column' )}"></div>
${this.outputif( is_multi_column, '<div class="second-column"></div>' )}
${this.outputif( is_sub, save_continue_sub_view )}
${this.outputif( show_permission_div, permission_div )}
</div>
</div>
`;
return template;
}
auditTab( options = {} ) {
let template = `
<div id="tab_audit" class="html2js_flag_audit edit-view-tab-outside-sub-view">
<div class="edit-view-tab" id="tab_audit_content_div">
<div class="first-column-sub-view"></div>
<div class="save-and-continue-div">
<span class="message"></span>
<div class="save-and-continue-button-div">
<button class="tt-button p-button p-component" type="button">
<span class="icon"></span>
<span class="p-button-label"></span>
</button>
</div>
</div>
</div>
</div>
`;
return template;
}
genericReportEditView( options = {} ) {
// Some examples of the differences. Interestingly, all checked templates so far all had the main div class set to active-shift-report-view.
// ActiveShiftReportView.html : view_id=ActiveShiftReport : id=active_shift_report_view_tab_bar
// UserSummaryReportView.html : id=user_summary_report_view_tab_bar
// PayStubTransactionSummaryReportView.html : id=pay_stub_summary_report_view_tab_bar
let tab_bar_id = this.convertPascalCase2SnakeCase( options.view_id ) + '_view_tab_bar';
let edit_view_template = `
<div class="js-generic-report-edit-view edit-view active-shift-report-view">
<div class="edit-view-tab-bar" id="${this.outputif( tab_bar_id )}">
<div class="navigation-div" style="display: none">
<span class="navigation-label"></span>
<img class="left-click arrow"/>
<div class="navigation-widget-div"></div>
<img class="right-click arrow"/>
</div>
<span class="close-icon">x</span>
<ul class="edit-view-tab-bar-label">
<li><a ref="tab_report" href="#tab_report"></a></li>
<li><a ref="tab_setup" href="#tab_setup"></a></li>
<li><a ref="tab_chart" href="#tab_chart"></a></li>
${this.outputif( options.report_form_setup || ( options.view_id && options.view_id === 'PayrollExportReport' ), '<li><a ref="tab_form_setup" href="#tab_form_setup"></a></li>' )}
<li><a ref="tab_custom_columns" href="#tab_custom_columns"></a></li>
<li><a ref="tab_saved_reports" href="#tab_saved_reports"></a></li>
</ul>
<div id="tab_report" class="edit-view-tab-outside">
<div class="edit-view-tab" id="tab_report_content_div">
<div class="first-column full-width-column"></div>
</div>
</div>
<div id="tab_setup" class="edit-view-tab-outside">
<div class="edit-view-tab" id="tab_setup_content_div">
<div class="first-column full-width-column"></div>
</div>
</div>
<div id="tab_chart" class="edit-view-tab-outside">
<div class="edit-view-tab" id="tab_chart_content_div">
<div class="first-column full-width-column"></div>
<div class="save-and-continue-div permission-defined-div">
<span class="message permission-message"></span>
</div>
</div>
</div>
${this.outputif( options.report_form_setup, `
<div id="tab_form_setup" class="edit-view-tab-outside">
<div class="edit-view-tab" id="tab_form_setup_content_div">
<div class="first-column full-width-column"></div>
</div>
</div>`
)}
${this.outputif( options.view_id && options.view_id === 'PayrollExportReport', `
<div id="tab_form_setup" class="edit-view-tab-outside">
<div class="edit-view-tab" id="tab_form_setup_content_div">
<div class="first-row first-column full-width-column">
</div>
<div class="inside-editor-div full-width-column">
<div class="grid-div">
<table id="export_grid"></table>
</div>
</div>
</div>
</div>`
)}
<div id="tab_custom_columns" class="edit-view-tab-outside-sub-view">
<div class="edit-view-tab" id="tab_custom_columns_content_div">
<div class="first-column-sub-view"></div>
<div class="save-and-continue-div">
<span class="message"></span>
<div class="save-and-continue-button-div">
<button class="tt-button p-button p-component" type="button">
<span class="icon"></span>
<span class="p-button-label"></span>
</button>
</div>
</div>
<div class="save-and-continue-div permission-defined-div">
<span class="message permission-message"></span>
</div>
</div>
</div>
<div id="tab_saved_reports" class="edit-view-tab-outside-sub-view">
<div class="edit-view-tab" id="tab_saved_reports_content_div">
<div class="first-column-sub-view"></div>
<div class="save-and-continue-div">
<span class="message"></span>
<div class="save-and-continue-button-div">
<button class="tt-button p-button p-component" type="button">
<span class="icon"></span>
<span class="p-button-label"></span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
`;
return edit_view_template;
}
// This might still work, but put on hold as we chose to focus on the list, edit and sub views and tabs
// getEditViewFormItem( options = {} ) {
// let sub_view = options.is_sub_view || false;
// let template = `
// <div class="edit-view-form-item-div">
// <div class="edit-view-form-item-${ sub_view ? 'sub-' : ''}label-div"><span class="edit-view-form-item-label"></span></div>
// <div class="edit-view-form-item-input-div"></div>
// </div>
// `;
//
// return template;
// }
}
/**
*
* @type {Readonly<{string, symbol}>}
*/
const TemplateType = Object.freeze( {
LIST_VIEW: Symbol( 'LIST_VIEW'),
SUB_VIEW: Symbol( 'SUB_VIEW'),
EDIT_VIEW: Symbol( 'EDIT_VIEW'),
REPORT_VIEW: Symbol( 'REPORT_VIEW'),
INLINE_HTML: Symbol( 'INLINE_HTML'),
LEGACY_HTML: Symbol( 'LEGACY_HTML'),
} );
const HtmlTemplatesGlobal = new HtmlTemplates();
window.TT_HTML_G = HtmlTemplatesGlobal; // TODO: Temp for dev, remove after all done.
export {
HtmlTemplatesGlobal,
// HtmlTemplates as HtmlTemplatesClass, // Not yet used as we are sharing the one global instance across scripts at the moment.
TemplateType,
};

@@ -0,0 +1,657 @@
import firebase from 'firebase/app';
import 'firebase/messaging';
import { TTAPI } from '@/services/TimeTrexClientAPI';
import 'bootstrap';
import TTEventBus from '@/services/TTEventBus';
class NotificationConsumer {
constructor() {
const firebaseConfig = {
apiKey: "AIzaSyB9tM0QYb1D3JF07RqpeG-14ADGhezGRws",
authDomain: "timetrex-app.firebaseapp.com",
databaseURL: "https://timetrex-app.firebaseio.com",
projectId: "timetrex-app",
storageBucket: "timetrex-app.appspot.com",
messagingSenderId: "462133047262",
appId: "1:462133047262:web:1705b6bfca364bcd99b74f"
};
// Initialize Firebase
firebase.initializeApp( firebaseConfig );
this.browser_supported = firebase.messaging.isSupported();
this.messaging = firebase.messaging.isSupported() ? firebase.messaging() : null;
this.user_notification_device_token_api = TTAPI.APINotificationDeviceToken;
this.notification_api = TTAPI.APINotification;
this.user_preference_api = TTAPI.APIUserPreference;
this.notification_holder = document.querySelector( '#notification-holder' );
this.notification_duration = 120000;
this.token = '';
this.notification_total = 0;
this.notification_on_screen_total = 0; // How many notification pop ups are currently on screen.
this.create_event_listeners = true;
this.notification_sound = null;
this.sound_timer = null;
this.title_timer = null;
this.previous_page_title = document.title;
this.pending_events = [];
this.event_bus = new TTEventBus({ view_id: 'notification_consumer' });
}
setupUser( request_permission, refresh_token ) {
if ( refresh_token === true ) {
this.deleteNotificationDeniedCookie();
}
if ( this.isBrowserSupported( refresh_token ) === false ) {
if ( refresh_token === true ) {
TAlertManager.showAlert( $.i18n._( 'Sorry, this is browser does not support push notifications.' ), $.i18n._( 'Push Notifications' ) );
}
return false;
}
//If impersonating other users do not show notification permission request pop ups or notifications.
var alternate_session_data = getCookie( 'AlternateSessionData' );
if ( alternate_session_data && !refresh_token ) {
return false;
}
//User has either repeatedly denied permissions or has disabled notifications for this browser in user preferences.
if ( this.getNotificationDeniedCookie().asked_count >= 2 && refresh_token === false ) {
return false;
}
if ( LocalCacheData.getLoginUser() && APIGlobal.pre_login_data.production === true && APIGlobal.pre_login_data.demo_mode !== true && APIGlobal.pre_login_data.sandbox !== true ) {
this.notification_duration = parseInt( LocalCacheData.getLoginUserPreference().notification_duration ) * 1000;
if ( Notification.permission === 'granted' && LocalCacheData.getLoginUserPreference().notification_status_id !== 2 ) {
this.registerWorkerAndGetToken( true );
this.createNotificationListeners();
if ( refresh_token === true ) {
// Provide feedback to user when they click refresh push notifications button.
TAlertManager.showAlert( $.i18n._( 'Push Notifications Enabled' ), $.i18n._( 'Push Notifications' ) );
}
} else if ( request_permission && Notification.permission === 'default' ) {
//Do not show mobile browsers notification permission alert unless they manually refresh notifications in My Account -> Preferences.
if ( APIGlobal.pre_login_data.user_agent_data.is_mobile === true && refresh_token === false ) {
return false;
}
this.showNotificationPermissionAlert();
} else if ( refresh_token === true ) {
this.showNotificationPermissionAlert();
}
} else {
// Else user has declined permissions.
if ( refresh_token === true ) {
TAlertManager.showAlert( $.i18n._( 'Sorry, push notifications are disabled on this server.' ), $.i18n._( 'Push Notifications' ) );
}
}
}
// Register our service worker and vapidKey.
// Safari and Firefox require this to trigger on a user action and not just randomly ask.
registerWorkerAndGetToken( send_token ) {
try {
navigator.serviceWorker.register( './services/firebase-messaging-sw.js' )
.then( ( registration ) => {
this.messaging.getToken( {
serviceWorkerRegistration: registration,
vapidKey: 'BAIFamGLNE689DChvdL8bWrvgiPFUMGzPwBrxuDiKQNTzpbQu-VZ3urH3SdIOSQ4DUYAOmeTrhmGTNQaNdtW-2I'
} ).then( ( currentToken ) => {
if ( currentToken ) {
this.token = currentToken;
if ( send_token ) {
this.sendDeviceToken( this.token );
}
//If permission alert exists, remove it.
if ( $( '.modal-alert' ).length ) {
$( '.modal-alert' ).remove();
Global.setUIReady();
this.sendAnalytics( 'allow-confirm' ); //Only trigger this when the employee is actually asked to allow permissions. Not everytime the service worker is registered.
}
} else {
//request permission window happens
}
} ).catch( ( err ) => {
// unexpected error
} );
} );
} catch ( err ) {
Debug.Text( 'Error attempting to register firebase service workers and push notification service: ' + err.message, 'NotificationConsumer.js', 'NotificationConsumer', 'registerWorkerAndGetToken', 9 );
}
}
isBrowserSupported( show_message ) {
if ( window.location.protocol !== 'https:' ) {
Debug.Text( 'Not on a HTTPS connection. Push Notifications disabled.', 'NotificationConsumer.js', 'NotificationConsumer', 'checkBrowserSupported', 9 );
if ( show_message === true ) {
// Provide feedback to user why push notifications are not working if they clicked to refresh push notifications in My Account -> Preferences.
TAlertManager.showAlert( $.i18n._( 'Push Notification are only available on HTTPS connections.' ), $.i18n._( 'Push Notifications' ) );
}
return false;
} else if ( this.browser_supported === false ) {
Debug.Text( 'User on an unsupported browser.', 'NotificationConsumer.js', 'NotificationConsumer', 'checkBrowserSupported', 9 );
if ( show_message === true ) {
// Provide feedback to user why push notifications are not working if they clicked to refresh push notifications in My Account -> Preferences.
TAlertManager.showAlert( $.i18n._( 'Push Notification are not supported on this browser.' ), $.i18n._( 'Push Notifications' ) );
}
return false;
}
return true;
}
sendDeviceToken( device_token ) {
this.user_notification_device_token_api.checkAndSetNotificationDeviceToken( device_token, 100, {
onResult: function( res ) {
let result = res.getResult();
}
} );
}
deleteToken() {
var $this = this;
if ( this.token ) {
this.messaging.deleteToken().then( ( result ) => {
let data = {};
data.device_token = this.token;
this.user_notification_device_token_api.deleteNotificationDeviceToken( [data], {
onResult: function( res ) {
$this.token = '';
var result = res.getResult();
if ( result ) {
Debug.Text( 'Successfully deleted device token.', 'NotificationConsumer.js', 'NotificationConsumer', 'deleteToken', 9 );
} else {
Debug.Text( 'Failed to delete device token.', 'NotificationConsumer.js', 'NotificationConsumer', 'deleteToken', 9 );
}
}
} );
} ).catch( ( err ) => {
Debug.Text( 'ERROR: While attempting to delete notification device token.', 'NotificationConsumer.js', 'NotificationConsumer', 'deleteToken', 9 );
} );
}
}
deleteAllTokens() {
//This deletion path does necessarily mean the current session has a device token, but will attempt to delete all device tokens for the current user.
var $this = this;
this.user_notification_device_token_api.deleteAllNotificationDeviceTokens( {
onResult: function( res ) {
$this.token = '';
var result = res.getResult();
if ( result ) {
Debug.Text( 'Successfully deleted all device tokens.', 'NotificationConsumer.js', 'NotificationConsumer', 'DeleteAllDeviceTokens', 9 );
} else {
Debug.Text( 'Failed to delete all device tokens, none may exist.', 'NotificationConsumer.js', 'NotificationConsumer', 'DeleteAllDeviceTokens', 9 );
}
}
} );
}
createNotificationListeners() {
if ( this.create_event_listeners === false ) {
return;
}
var $this = this;
this.create_event_listeners = false;
// Handles foreground, background and background notification-clicked events.
navigator.serviceWorker.addEventListener( 'message', payload => {
this.handlePushNotificationEvent( payload.data.messageType === 'push-received', payload.data );
} );
this.notification_holder.addEventListener( 'click', event => {
var element = event.target;
if ( element.className === 'notification-close' ) {
// Ignored notifications are not marked as read.
this.removeNotification( element.parentNode.parentNode.parentNode );
} else if ( element.className === 'notification-link' ) {
// Mark notification as read when user clicks "view details" and is sent to the notificwtion link.
this.setNotificationAsRead( element.id );
this.removeNotification( element.parentNode.parentNode );
//If notification has an open_view event attached, trigger that event and then delete it.
for ( var i = this.pending_events.length - 1; i >= 0; i-- ) {
if ( this.pending_events[i].id === element.id && this.pending_events[i].event === 'open_view' ) {
this.openViewLinkedToNotification( this.pending_events[i].event_data );
event.preventDefault(); //Stop default href from being followed.
this.pending_events.splice( i, 1 ); //Delete event from pending events.
break;
}
}
}
} );
window.addEventListener( 'focus', function( event ) {
$this.cancelTimers();
}, false );
}
handlePushNotificationEvent( foreground, payload ) {
let timetrex_data = JSON.parse( payload.data.timetrex );
Debug.Arr( payload, 'Push notification received.', 'NotificationConsumer.js', 'NotificationConsumer', 'handlePushNotificationEvent', 9 );
this.event_bus.emit( 'tt_topbar', 'profile_pending_counts', { //When push notification received update all "My Profile" badges.
object_types: []
} );
// If on foreground displays the notification.
if ( payload.messageType !== 'notification-clicked' && timetrex_data.user_id !== undefined && LocalCacheData.getLoginUser() && LocalCacheData.getLoginUser().id === timetrex_data.user_id && payload.notification && payload.notification.title ) {
// Increment total on bell everytime a new notification comes in.
this.updateBell( true, this.notification_total + 1 );
this.showNotification( payload.notification.title, payload.notification.body, payload.notification.click_action, timetrex_data.id, timetrex_data.priority, payload.data.link_target ? payload.data.link_target : '' );
Debug.Text( 'Showing Notification on UI as user_id matches and notification was not a system click.', 'NotificationConsumer.js', 'NotificationConsumer', 'handlePushNotificationEvent', 9 );
}
if ( payload.messageType === 'notification-clicked' ) {
Debug.Text( 'Notification was clicked on from desktop.', 'NotificationConsumer.js', 'NotificationConsumer', 'handlePushNotificationEvent', 9 );
// Set notification as read as we about to redirect the user to the notification.
this.setNotificationAsRead( timetrex_data.id );
// If the notification toast is still on screen remove it.
this.removeNotificationById( timetrex_data.id );
// User clicked desktop notification, redirect them directly to the notification if a click_action was given.
if ( payload.notification.click_action !== undefined && payload.notification.click_action !== '' ) {
window.location = payload.notification.click_action;
} else {
window.location = Global.getBaseURL() + '#!m=Notification';
}
} else {
//Handle background events if any are in the payload.
if ( timetrex_data.event !== undefined && timetrex_data.event.length > 0 ) {
this.handleBackgroundEvent( timetrex_data );
}
}
}
handleBackgroundEvent( timetrex_data ) {
// Handles timetrex specific data of notification payload.
Debug.Text( 'Background action was supplied in the push notification.', 'NotificationConsumer.js', 'NotificationConsumer', 'handlePushNotificationEvent', 9 );
for ( let i = 0; i < timetrex_data.event.length; i++ ) {
switch ( timetrex_data.event[i].type ) {
case 'clean_cache':
LocalCacheData.cleanNecessaryCache();
break;
case 'open_view':
//Only triggered if user clicks the notification.
this.pending_events.push( {
id: timetrex_data.id,
event: timetrex_data.event[i].type,
event_data: timetrex_data.event[i]
} );
break;
case 'open_view_immediate':
this.openViewLinkedToNotification( timetrex_data.event[i] );
break;
case 'redirect':
if ( timetrex_data.event[i].ask === 1 ) {
TAlertManager.showConfirmAlert( $.i18n._( timetrex_data.event[i].text ), $.i18n._( 'Redirect Confirmation' ), ( flag ) => {
if ( flag === true ) {
if ( timetrex_data.event[i].target && timetrex_data.event[i].target === '_blank' ) {
window.open(
timetrex_data.event[i].link,
'_blank'
);
} else {
window.location = timetrex_data.event[i].link;
}
}
} );
} else {
if ( timetrex_data.event[i].target && timetrex_data.event[i].target === '_blank' ) {
window.open(
timetrex_data.event[i].link,
'_blank'
);
} else {
window.location = timetrex_data.event[i].link;
}
}
break;
case 'refresh_job_queue':
this.event_bus.emit( 'tt_topbar', 'toggle_job_queue_spinner', {
//Boolean events for job queue spinner.
show: timetrex_data.event[i].show, //Show the job queue spinner
get_job_data: timetrex_data.event[i].get_job_data, //Update job queue panel data
check_completed: timetrex_data.event[i].check_completed //Check if job queue is completed and hide the job queue spinner if no pending tasks.
} );
//Update TimeSheet is user is on it.
if ( LocalCacheData.current_open_primary_controller && LocalCacheData.current_open_primary_controller.viewId === 'TimeSheet' ) {
LocalCacheData.current_open_primary_controller.search();
}
break;
}
}
}
openViewLinkedToNotification( event_data ) {
//Open a view with the option of pre-filling fields.
LocalCacheData.setAutoFillData( event_data.data );
// This is taking them to listview in some cases so a on a onAdd/onEdit click can be clicked afterwards.
IndexViewController.goToViewByViewLabel( event_data.view_name );
// Ignore edit only views that don't have list views.
if ( event_data.view_name !== 'InOut' && event_data.view_name !== 'Contact Information' ) {
// Need to add the promise before onTabShow is called where it's originally intended to be added otherwise the below wait is not triggered.
TTPromise.add( 'BaseViewController', 'onTabShow' );
TTPromise.wait( 'BaseViewController', 'onTabShow', function() {
if ( event_data.action === 'add' ) {
LocalCacheData.current_open_primary_controller.onAddClick();
} else if ( event_data.action === 'edit' ) {
LocalCacheData.current_open_primary_controller.onEditClick( event_data.view_id );
} else if ( event_data.action === 'view' ) {
LocalCacheData.current_open_primary_controller.onViewClick( event_data.view_id );
}
} );
}
}
setNotificationDeniedCookie( cookie_value ) {
cookie_value.asked_count++;
cookie_value.last_asked = new Date().getTime();
setCookie( 'disable_push_notification_ask', JSON.stringify( cookie_value ), 10000, APIGlobal.pre_login_data.cookie_base_url );
}
deleteNotificationDeniedCookie() {
deleteCookie( 'disable_push_notification_ask' );
}
getNotificationDeniedCookie() {
if ( getCookie( 'disable_push_notification_ask' ) ) {
return JSON.parse( getCookie( 'disable_push_notification_ask' ) );
}
var cookie_value = {};
cookie_value.asked_count = 0;
cookie_value.last_asked = 0;
return cookie_value;
}
showNotificationPermissionAlert() {
//Only ever ask twice to enable notification permissions for this browser.
//If we have only asked once before and it has been 180 days since then, ask again.
var notification_denied_cookie = this.getNotificationDeniedCookie();
if ( notification_denied_cookie.asked_count > 1 || notification_denied_cookie.last_asked + ( 180 * 24 * 60 * 60 * 1000 ) > new Date().getTime() ) {
return false;
}
TAlertManager.showModalAlert( 'push_notification', 'ask', ( flag ) => {
if ( flag === true ) {
this.showPermissionHelp();
this.setUserPreferencePushNotification( 1 );
this.sendAnalytics( 'allow' );
} else {
this.setUserPreferencePushNotification( 0 );
this.sendAnalytics( 'deny' );
this.setNotificationDeniedCookie( notification_denied_cookie );
}
} );
}
showPermissionHelp() {
this.registerWorkerAndGetToken( true );
this.createNotificationListeners();
TAlertManager.showModalAlert( 'push_notification', 'wait_for_permission', ( flag ) => {
if ( flag === true ) {
TAlertManager.showModalAlert( 'push_notification', 'help_text', '' );
this.showArrowToEnablePushNotifications();
this.sendAnalytics( 'unsure' );
}
} );
}
showArrowToEnablePushNotifications() {
const arrow = $( '<div class="permission-arrow">' +
'<img style="display: block; margin-left: auto; margin-right: auto;" src="' + Global.getRealImagePath( 'images/notification-arrow.svg' ) + '" width="150" height="150!">' +
'</div>' );
$( '.modal-alert' ).append( arrow );
}
setUserPreferencePushNotification( status ) {
if ( LocalCacheData.getLoginUser() && LocalCacheData.getLoginUserPreference() ) {
var data = {};
data.user_id = LocalCacheData.getLoginUser().id;
data.id = LocalCacheData.getLoginUserPreference().id;
data.browser_permission_ask_date = Math.round( new Date().getTime() / 1000 );
//If user agrees set notifications to enabled. Else only set last browser_permission_ask_date.
if ( status === 1 ) {
data.notification_status_id = status;
}
this.user_preference_api.setUserPreference( data, {
onResult: function( res ) {
let result = res.getResult();
}
} );
}
}
sendAnalytics( choice ) {
Global.sendAnalyticsEvent( 'push_notifications', 'click', 'click:push_notifications:' + choice );
}
getUnreadNotifications() {
this.notification_api.getUnreadNotifications( {
onResult: ( result ) => {
this.notification_total = parseInt( result.getResult() );
this.updateBell( false, this.notification_total );
}
} );
}
getSystemNotifications( target ) {
this.notification_api.getSystemNotification( target, {
onResult: ( result ) => {
var new_system_notifications = parseInt( result.getResult() );
if ( new_system_notifications > 0 ) {
this.updateBell( true, this.notification_total + new_system_notifications );
}
}
} );
}
setNotificationAsRead( id ) {
this.notification_api.setNotificationStatus( [id], 20, {
onResult: ( result ) => {
this.updateBell( true, this.notification_total - 1 );
}
} );
}
updateBell( manual, amount ) {
if ( manual ) {
this.notification_total = amount;
}
this.event_bus.emit( 'tt_topbar', 'notification_bell', {
notification_count: this.notification_total
});
}
playSound() {
// Only load notification sound when we first need it. Then reuse from then on.
if ( this.notification_sound === null ) {
this.notification_sound = new Audio();
this.notification_sound.src = Global.getBaseURL( '../' ) + 'sounds/notification.mp3';
this.notification_sound.load();
}
const playPromise = this.notification_sound.play();
if ( playPromise !== undefined ) { //Older browsers play() does not return anything.
playPromise.then( () => {
//Notification audio is playing.
} )
.catch( error => {
console.log( error );
} );
}
}
startTimers( notification ) {
var $this = this;
if ( document.hasFocus() === false && this.sound_timer === null ) {
//Change page title and repeat notififation sound if page does not have focus and no timer is already set.
this.sound_timer = setInterval( function() {
if ( document.hasFocus() ) {
//If tab is in focus cancel the timer.
$this.cancelTimers();
} else {
$this.playSound();
}
}, 5000 );
this.title_timer = setInterval( function() {
if ( document.hasFocus() ) {
//If tab is in focus cancel the timer.
$this.cancelTimers();
} else {
if ( document.title === '!!!!!!!!!!!!!' ) {
document.title = $.i18n._( 'NOTICE!' );
} else {
document.title = '!!!!!!!!!!!!!';
}
}
}, 2000 );
}
}
cancelTimers() {
if ( this.sound_timer !== null ) {
clearInterval( this.sound_timer );
this.sound_timer = null;
}
if ( this.title_timer !== null ) {
document.title = this.previous_page_title;
clearInterval( this.title_timer );
this.title_timer = null;
}
}
showNotification( title, body, url, notification_id, priority, target ) {
if ( priority == 10 ) {
//User is not notified nor receives toast for low priority notifications.
return false;
}
//All notifications other than low play notification sound.
this.playSound();
// To stop infinite stacking notifications on screen we only show up to 5 at a time.
// Allow high and critical priority notifications with priority 1 or 2 through.
if ( this.notification_on_screen_total >= 5 && priority > 2 ) {
return false;
}
const notification = document.createElement( 'div' );
notification.className = 'toast show toast-spacing';
notification.style = 'width: 22rem; background-color: hsla(0, 0%, 100%, 1) !important; margin-bottom: 0.4rem !important;';
const notification_header = document.createElement( 'div' );
notification_header.className = 'toast-header';
const notification_title = document.createElement( 'strong' );
notification_title.className = 'mr-auto';
notification_title.textContent = title;
const notification_close_button = document.createElement( 'button' );
notification_close_button.className = 'ml-2 mb-1 close';
notification_close_button.setAttribute( 'aria-label', 'close' );
notification_close_button.innerHTML = '<span class="notification-close" aria-hidden="true">&times;</span>';
const notification_body = document.createElement( 'div' );
notification_body.className = 'toast-body';
const notification_body_text = document.createElement( 'p' );
notification_body_text.textContent = body;
const notification_body_link = document.createElement( 'a' );
notification_body_link.textContent = 'View Details';
notification_body_link.id = notification_id;
notification_body_link.className = 'notification-link';
if ( url !== undefined && url !== '' ) {
notification_body_link.href = url;
} else {
notification_body_link.href = Global.getBaseURL() + '#!m=Notification';
}
if ( target === '_blank' ) {
notification_body_link.target = '_blank';
}
notification_header.appendChild( notification_title );
notification_header.appendChild( notification_close_button );
notification.appendChild( notification_header );
notification_body.appendChild( notification_body_text );
notification_body.appendChild( notification_body_link );
notification.appendChild( notification_body );
this.notification_holder.appendChild( notification );
this.notification_on_screen_total++;
if ( priority == 2 ) {
//High priority notifications flash border twice around notification.
notification.className += ' notification-outline-repeat';
} else if ( priority == 1 ) {
//Critical priority notification repeat the notification sound, change document title and continuously flash border around toast.
this.startTimers( notification );
notification.className += ' notification-outline-infinite';
}
if ( this.notification_duration !== 0 && priority != 1 ) {
// User notification preferences with 0 delay or critical notifications with priority 1 are never automatically removed.
setTimeout( () => {
this.removeNotification( notification );
}, this.notification_duration );
}
}
removeNotification( notification ) {
if ( notification ) {
this.notification_on_screen_total--;
notification.remove();
}
}
removeNotificationById( id ) {
var notification_link = document.getElementById( id );
if ( notification_link ) {
this.removeNotification( notification_link.parentNode.parentNode );
}
}
removeAllNotifications() {
var notifications = document.querySelectorAll( ".toast" );
for ( var i = 0; i < notifications.length; i++ ) {
this.removeNotification( notifications[i] );
}
this.cancelTimers();
}
detectBrowserNeedsExtraPermission() {
// Some browsers by default block push notification permission so we need to detect them to show user a different prompt
if ( Global.getBrowserVendor() === 'Edge' ) {
return true;
} else {
return false;
}
}
}
export const NotificationConsumerObj = new NotificationConsumer();

@@ -0,0 +1,716 @@
import { ResponseObject } from '@/model/ResponseObject';
import { TTUUID } from '@/global/TTUUID';
import { APIReturnHandler } from '@/model/APIReturnHandler';
export class ServiceCaller extends Backbone.Model {
constructor() {
$.xhrPool = [];
super();
}
getMessageId() {
if ( this.message_id ) {
return this.message_id
} else {
this.setMessageId( TTUUID.generateUUID() );
return this.message_id;
}
return false;
}
setMessageId( value ) {
this.message_id = value;
return true;
}
getIsIdempotent() {
if ( this.is_idempotent ) {
return this.is_idempotent
}
return false;
}
setIsIdempotent( value ) {
this.is_idempotent = value;
return true;
}
argumentsHandler() {
var className = arguments[0];
var function_name = arguments[1];
var apiArgsAndResponseObject = arguments[2];
var lastApiArgsAndResponseObject = arguments[2][( apiArgsAndResponseObject.length - 1 )];
var apiArgs = {};
var responseObject;
var len;
if ( Global.isSet( lastApiArgsAndResponseObject.onResult ) || Global.isSet( lastApiArgsAndResponseObject.async ) ) {
len = ( apiArgsAndResponseObject.length - 1 );
responseObject = new ResponseObject( lastApiArgsAndResponseObject );
} else {
len = apiArgsAndResponseObject.length;
responseObject = null;
}
for ( var i = 0; i < len; i++ ) {
apiArgs[i] = apiArgsAndResponseObject[i];
if ( i === 0 && len === 1 &&
Global.isSet( apiArgs[i] ) &&
Global.isSet( apiArgs[i].second_parameter ) ) {
apiArgs[1] = apiArgs[i].second_parameter;
}
}
return this.call( className, function_name, responseObject, apiArgs );
}
getOptionsCacheKey( api_args, key ) {
$.each( api_args, function( index, value ) {
if ( $.type( value ) === 'object' ) {
key = key + '_' + JSON.stringify( value );
} else {
key = key + '_' + value;
}
} );
return key;
}
repeatAPICall( className, function_name, apiArgs, responseObject ) {
let params = Object.values( JSON.parse( apiArgs.json ) );
TTAPI[className][function_name]( ...params, responseObject.attributes );
}
uploadFile( form_data, paramaters, responseObj ) {
var message_id = this.getMessageId();
ProgressBar.showProgressBar( message_id );
//On IE 9
if ( typeof FormData == 'undefined' ) {
form_data.attr( 'method', 'POST' );
form_data.attr( 'action', ServiceCaller.getURLByObjectType( 'upload' ) + '?' + paramaters + '&' + Global.getSessionIDKey() + '=' + LocalCacheData.getSessionID() );
form_data.attr( 'enctype', 'multipart/form-data' );
ProgressBar.changeProgressBarMessage( 'File Uploading' );
form_data.ajaxForm().ajaxSubmit( {
success: function( result ) {
if ( result && result.toString().toLocaleLowerCase() !== 'true' ) {
TAlertManager.showAlert( result );
}
ProgressBar.removeProgressBar();
if ( responseObj.onResult ) {
responseObj.onResult( result );
}
}
} );
return;
}
ProgressBar.changeProgressBarMessage( 'File Uploading' );
$.ajax( {
url: ServiceCaller.getURLByObjectType( 'upload' ) + '?' + paramaters + '&' + Global.getSessionIDKey() + '=' + LocalCacheData.getSessionID(), //Server script to process data
headers: {
//Handle CSRF tokens and related headers here.
'X-Client-ID': 'Browser-TimeTrex',
'X-CSRF-Token': getCookie( 'CSRF-Token' ),
},
type: 'POST',
// xhr: function() { // Custom XMLHttpRequest
// var myXhr = $.ajaxSettings.xhr();
// if ( myXhr.upload ) { // Check if upload property exists
// myXhr.upload.addEventListener( 'progress', progressHandlingFunction, false ); // For handling the progress of the upload
// }
//
// function progressHandlingFunction() {
// }
//
// return myXhr;
//
// },
success: function( result ) {
if ( result && result.toString().toLocaleLowerCase() !== 'true' ) {
TAlertManager.showAlert( result );
}
if ( responseObj.onResult ) {
responseObj.onResult( result );
}
ProgressBar.removeProgressBar();
},
// Form data
data: form_data,
cache: false,
contentType: false,
processData: false
} );
}
prettyPrintAPIArguments( apiArgs ) {
if ( apiArgs && apiArgs.json ) {
var retval = [];
var args = JSON.parse( apiArgs.json );
for ( var property_name in args ) {
var arg = args[property_name];
retval.push( JSON.stringify( arg, null, 2 ) ); //Pretty print JSON
}
return retval.join( ', ' );
}
return null;
}
getCache( cache_key, responseObject, function_name, apiArgs ) {
let result = LocalCacheData.result_cache[cache_key];
//Debug.Arr(result, 'Response from cached result. Key: '+cache_key, 'ServiceCaller.js', 'ServiceCaller', 'call', 10);
let apiReturnHandler = new APIReturnHandler();
apiReturnHandler.set( 'result_data', result );
apiReturnHandler.set( 'delegate', responseObject.get( 'delegate' ) );
apiReturnHandler.set( 'function_name', function_name );
apiReturnHandler.set( 'args', apiArgs );
if ( responseObject.get( 'onResult' ) ) {
responseObject.get( 'onResult' )( apiReturnHandler );
}
return apiReturnHandler;
};
isCachableFunction( function_name ) {
let is_cachable = false;
switch ( function_name ) {
case 'getOptions':
case 'isBranchAndDepartmentAndJobAndJobItemAndPunchTagEnabled':
case 'getUserGroup':
case 'getJobGroup':
case 'getJobItemGroup':
case 'getProductGroup':
case 'getDocumentGroup':
case 'getQualificationGroup':
case 'getKPIGroup':
case 'getHierarchyControlOptions':
is_cachable = true;
break;
}
return is_cachable;
}
call( className, function_name, responseObject, apiArgs ) {
var $this = this;
var message_id;
var base_url = ServiceCaller.getAPIURL( 'Class=' + className + '&Method=' + function_name + '&v=2' );
var url = base_url;
if ( LocalCacheData.getAllURLArgs() ) {
if ( LocalCacheData.getAllURLArgs().hasOwnProperty( 'user_id' ) ) {
url = url + '&user_id=' + LocalCacheData.getAllURLArgs().user_id;
}
if ( LocalCacheData.getAllURLArgs().hasOwnProperty( 'company_id' ) ) {
url = url + '&company_id=' + LocalCacheData.getAllURLArgs().company_id;
}
}
if ( Global.getStationID() ) {
url = url + '&StationID=' + Global.getStationID();
}
var apiReturnHandler;
var async;
if ( responseObject && responseObject.get( 'async' ) === false ) {
async = responseObject.get( 'async' );
} else {
async = true;
}
var cache_key;
switch ( function_name ) {
case 'getOptions':
case 'isBranchAndDepartmentAndJobAndJobItemAndPunchTagEnabled':
case 'getHierarchyControlOptions':
case 'getUserGroup':
case 'getJobGroup':
case 'getJobItemGroup':
case 'getProductGroup':
case 'getDocumentGroup':
case 'getQualificationGroup':
case 'getKPIGroup':
if ( function_name === 'getUserGroup' ) {
cache_key = className + '.' + 'userGroup';
} else if ( function_name === 'getJobGroup' ) {
cache_key = className + '.' + 'jobGroup';
} else if ( function_name === 'getJobItemGroup' ) {
cache_key = className + '.' + 'jobItemGroup';
} else if ( function_name === 'getProductGroup' ) {
cache_key = className + '.' + 'productGroup';
} else if ( function_name === 'getDocumentGroup' ) {
cache_key = className + '.' + 'documentGroup';
} else if ( function_name === 'getQualificationGroup' ) {
cache_key = className + '.' + 'qualificationGroup';
} else if ( function_name === 'getKPIGroup' ) {
cache_key = className + '.' + 'kPIGroup';
} else if ( function_name === 'getHierarchyControlOptions' ) {
cache_key = 'getHierarchyControlOptions';
} else {
cache_key = this.getOptionsCacheKey( apiArgs, className + '.' + function_name );
}
if ( responseObject.get( 'noCache' ) === true ) {
LocalCacheData.result_cache[cache_key] = false;
}
if ( cache_key && LocalCacheData.result_cache[cache_key] ) {
//Use a promise to help prevent identical calls from being made before the first one returns and sets the cache.
if ( LocalCacheData.result_cache[cache_key].pending ) {
TTPromise.add( 'ServiceCaller', cache_key );
TTPromise.wait( 'ServiceCaller', cache_key, function() {
this.getCache( cache_key, responseObject, function_name, apiArgs );
}.bind( this ) );
} else {
return this.getCache( cache_key, responseObject, function_name, apiArgs );
}
return apiReturnHandler;
}
break;
case 'setUserGroup':
case 'deleteUserGroup':
cache_key = className + '.' + 'userGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
case 'setJobGroup':
case 'deleteJobGroup':
cache_key = className + '.' + 'jobGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
case 'setJobItemGroup':
case 'deleteJobItemGroup':
cache_key = className + '.' + 'jobItemGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
case 'setProductGroup':
case 'deleteProductGroup':
cache_key = className + '.' + 'productGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
case 'setDocumentGroup':
case 'deleteDocumentGroup':
cache_key = className + '.' + 'documentGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
case 'setQualificationGroup':
case 'deleteQualificationGroup':
cache_key = className + '.' + 'qualificationGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
case 'setKPIGroup':
case 'deleteKPIGroup':
cache_key = className + '.' + 'kPIGroup';
LocalCacheData.result_cache[cache_key] = false;
break;
}
message_id = this.getMessageId();
TTPromise.add( 'ServiceCaller', message_id );
if ( className !== 'APIProgressBar' && function_name !== 'Logout' ) {
url = url + '&MessageID=' + message_id;
}
if ( this.getIsIdempotent() == true ) {
url = url + '&idempotent=1';
}
if ( ServiceCaller.extra_url ) {
url = url + ServiceCaller.extra_url;
}
if ( !apiArgs ) {
apiArgs = {};
}
apiArgs = { json: JSON.stringify( apiArgs ) };
//Try to get a stack trace for each function call so if an error occurs we know exactly what triggered the call.
var stack_trace_str = null;
if ( typeof Error !== 'undefined' ) {
var stack_trace = ( new Error() );
if ( typeof stack_trace === 'object' && stack_trace.stack && typeof stack_trace.stack === 'string' ) {
stack_trace_str = stack_trace.stack.split( '\n' ); //This is eventually JSONified so convert it to an array for better formatting.
} else {
stack_trace_str = null;
}
stack_trace = null; // Previously null was 'delete' but not valid in JS strict mode.
}
var api_called_date = new Date();
var api_stack = {
api: className + '.' + function_name,
args: apiArgs.json,
message_id: this.getMessageId(),
api_called_date: api_called_date.toISOString(),
stack_trace: stack_trace_str
};
stack_trace_str = null; // Previously null was 'delete' but not valid in JS strict mode.
if ( LocalCacheData.api_stack.length === 16 ) {
LocalCacheData.api_stack.pop();
}
if ( function_name !== 'sendErrorReport' ) {
LocalCacheData.api_stack.unshift( api_stack );
}
if ( className !== 'APIProgressBar' && function_name !== 'Login' && function_name !== 'getPreLoginData' && function_name !== 'listenForMultiFactorAuthentication' ) {
ProgressBar.showProgressBar( message_id );
}
if ( this.isCachableFunction( function_name ) === true ) {
LocalCacheData.result_cache[cache_key] = { pending: true };
}
$.ajax(
{
dataType: 'JSON',
data: apiArgs,
headers: {
//#1568 - Add "fragment" to POST variables in API calls so the server can get it...
//Encoding is a must, otherwise HTTP requests will be corrupted on some web browsers (ie: Mobile Safari)
//This caused the corrupted requests for things like: "POST_/api/json/api_php?Class"
//Also it must use dashes instead of underscores for separators.
'Request-Uri-Fragment': encodeURIComponent( LocalCacheData.fullUrlParameterStr ),
//Handle CSRF tokens and related headers here.
'X-Client-ID': 'Browser-TimeTrex',
'X-CSRF-Token': getCookie( 'CSRF-Token' ),
},
type: 'POST',
async: async,
url: url,
beforeSend: function( jqXHR ) {
$.ajax.request_start_time = Date.now();
this.jqXHR = jqXHR;
$.xhrPool.push( this ); //Track all pending AJAX requests so we can cancel them if needed.
},
complete: function( jqXHR ) {
var index = $.xhrPool.indexOf( this );
if ( index > -1 ) {
$.xhrPool.splice( index, 1 ); //Remove completed AJAX request from pool.
}
var request_total_time = ( Date.now() - $.ajax.request_start_time ); //milliseconds
if ( request_total_time > 1000 ) { //Only log API calls that are slow.
if ( typeof ( gtag ) !== 'undefined' && APIGlobal.pre_login_data.analytics_enabled === true ) {
gtag( 'event', 'api_call', {
class: className,
method: function_name,
response_time: request_total_time
} );
Debug.Text( 'AJAX Response: Class: ' + className + ' Method: ' + function_name + ' Time: ' + request_total_time + 'ms', 'ServiceCaller.js', 'ServiceCaller', 'complete', 11 );
}
}
},
success: function( result ) {
//Debug.Arr(result, 'Response from API. message_id: '+ message_id, 'ServiceCaller.js', 'ServiceCaller', null, 10);
//Resets message_id so it changes on the next API call. Only do this on success, so idempotent requests that error out don't get a new key on the next call.
// FIXME: async API calls on the same api object can conflict with one another though.
// Take for instance onFormItemChange() triggering async api.Validate*(), when api.set*() is called, the idempotent=1 can be enabled for the validation with the same key.
// Then the set*() might incorrectly return the result from the validate()
// This is partially fixed by ignoring idempotency on all validate*() calls in the API, which it probably should anyways. However we need a proper fix for this in JS.
$this.setMessageId( null );
if ( Global.enable_api_tracing == true ) {
var api_trace_label = '%cAPI Request:%c ' + className + '->' + function_name + '(...) [Expand for Details]';
console.groupCollapsed( api_trace_label, 'font-weight: bold', 'font-weight: normal' );
console.log( '%c' + className + '->' + function_name + '%c(' + $this.prettyPrintAPIArguments( apiArgs ) + ')', 'font-weight: bold', 'font-weight: normal' );
var api_trace_raw_request_label = '%cRaw Request:%c [Expand for Details]';
console.groupCollapsed( api_trace_raw_request_label, 'font-weight: bold', 'font-weight: normal' );
console.log( '%cURL:%c ' + url, 'font-weight: bold', 'font-weight: normal' );
console.log( '%cRaw POST Body (non-URLEncoded):%c json=' + apiArgs.json + '', 'font-weight: bold', 'font-weight: normal' );
console.log( '%ccURL Command:%c curl -k --location --request POST --cookie "' + Global.getSessionIDKey() + '=<SessionID>" --form \'json=' + apiArgs.json + '\' "' + base_url + '"', 'font-weight: bold', 'font-weight: normal' );
console.groupEnd( api_trace_raw_request_label );
var api_trace_response_label = '%cResponse:%c [Expand for Details]';
console.groupCollapsed( api_trace_response_label, 'font-weight: bold', 'font-weight: normal' );
console.log( JSON.stringify( result, null, 2 ) );
console.groupEnd( api_trace_response_label );
console.groupEnd( api_trace_label );
api_trace_raw_request_label = null; // Previously null was 'delete' but not valid in JS strict mode.
api_trace_response_label = null; // Previously null was 'delete' but not valid in JS strict mode.
api_trace_label = null; // Previously null was 'delete' but not valid in JS strict mode.
}
if ( !Global.isSet( result ) ) {
result = true;
}
if ( className !== 'APIProgressBar' && function_name !== 'Login' && function_name !== 'getPreLoginData' && function_name !== 'listenForMultiFactorAuthentication' ) {
ProgressBar.removeProgressBar( message_id );
}
apiReturnHandler = new APIReturnHandler();
apiReturnHandler.set( 'result_data', result );
apiReturnHandler.set( 'delegate', responseObject.get( 'delegate' ) );
apiReturnHandler.set( 'function_name', function_name );
apiReturnHandler.set( 'args', apiArgs );
if ( !apiReturnHandler.isValid() && ( apiReturnHandler.getCode() === 'EXCEPTION' || apiReturnHandler.getCode() === 'EXCEPTION_CSRF' ) ) {
Debug.Text( 'api-exception: Code: ' + apiReturnHandler.getCode() + ' Error: ' + apiReturnHandler.getDescription() +' Message ID: '+ message_id, 'ServiceCaller.js', 'ServiceCaller', null, 10);
if ( apiReturnHandler.getCode() === 'EXCEPTION_CSRF' ) { //Don't bother recording CSRF exceptions.
Global.sendAnalyticsEvent( 'service-caller', 'error:api-exception', 'api-exception: Code: ' + apiReturnHandler.getCode() + ' Error: ' + apiReturnHandler.getDescription() );
TAlertManager.showAlert( apiReturnHandler.getDescription(), 'Error', function() {
window.location.reload();
} );
} else {
Global.sendErrorReport( 'api-exception: Code: ' + apiReturnHandler.getCode() + ' Error: ' + apiReturnHandler.getDescription(), 'ServiceCaller.js' );
TAlertManager.showAlert( $.i18n._( 'API Exception' ) + ': ' + apiReturnHandler.getDescription(), 'Error' );
}
//Error: Uncaught ReferenceError: promise_key is not defined
if ( typeof promise_key != 'undefined' ) {
TTPromise.reject( 'ServiceCaller', message_id );
} else {
Debug.Text( 'ERROR: Unable to release promise because key is NULL.', 'ServiceCaller.js', 'ServiceCaller', null, 10 );
}
return;
} else if ( !apiReturnHandler.isValid() && apiReturnHandler.getCode() === 'SESSION' ) {
//Debug.Text('API returned session expired: '+ message_id, 'ServiceCaller.js', 'ServiceCaller', null, 10);
Global.Logout(); //clearSessionCookie() in Logout() helps skip other API calls or prevent the UI from thinking we are still logged in.
ServiceCaller.cancel_all_error = true;
LocalCacheData.login_error_string = $.i18n._( 'Session expired, please login again.' );
if ( window.location.href == Global.getBaseURL() + '#!m=' + 'Login' ) {
// Prevent a partially loaded login screen when SessionID cookie is set but not valid on server.
// However if the session is expired on the server, and the user tries to navigate to some other page,
// there could be multiple API calls queued up, which causes this reload() to be triggered many times,
// and network requests to be aborted, which triggers error messages. Disable the reload for now as in theory it shouldn't be needed.
// This reload also gets rid of the "Session expired, please login again" error message, which is not ideal.
//window.location.reload();
} else {
var paths = Global.getBaseURL().replace( ServiceCaller.root_url, '' ).split( '/' );
if ( paths.indexOf( 'quick_punch' ) > 0 ) {
Global.setURLToBrowser( Global.getBaseURL() + '#!m=' + 'QuickPunchLogin' );
} else if ( paths.indexOf( 'portal' ) > 0 ) {
if ( LocalCacheData.getAllURLArgs().company_id ) {
LocalCacheData.setPortalLoginUser( null );
Global.setURLToBrowser( Global.getBaseURL() + '#!m=PortalJobVacancy&company_id=' + LocalCacheData.getAllURLArgs().company_id );
}
} else {
if ( !LocalCacheData.getAllURLArgs().company_id ) {
Global.setURLToBrowser( Global.getBaseURL() + '#!m=' + 'Login' );
}
}
}
TTPromise.resolve( 'ServiceCaller', message_id );
return;
} else if ( !apiReturnHandler.isValid() && apiReturnHandler.getCode() === 'DOWN_FOR_MAINTENANCE' ) {
Global.sendAnalyticsEvent( 'service-caller', 'error:down-for-maintenance', 'error:down-for-maintenance: Code: ' + apiReturnHandler.getCode() + ' Error: ' + apiReturnHandler.getDescription() );
//Before the location.replace because after that point we can't be sure of execution.
TTPromise.resolve( 'ServiceCaller', message_id );
//replace instead of assignment to ensure that the DOWN_FOR_MAINTENANCE page does not end up in the back button history.
window.location.replace( ServiceCaller.root_url + LocalCacheData.loginData.base_url + 'html5/DownForMaintenance.php?exception=DOWN_FOR_MAINTENANCE' );
return;
} else if ( apiReturnHandler.getCode() === 'REAUTHENTICATE' ) {
let session_data = apiReturnHandler.getResult();
Global.showAuthenticationModal( LocalCacheData.current_open_primary_controller.viewId, session_data.session_type, session_data.mfa, true, ( result ) => {
Debug.Text( 'User Reauthenticated: ' + result, 'ServiceCaller.js', 'ServiceCaller', 'call', 10 );
Global.hideAuthenticationModal();
//After authentication is complete, reattempt the API call automatically so that the user does not need to click "Save" or repeat the action.
$this.repeatAPICall( className, function_name, apiArgs, responseObject )
} );
TTPromise.resolve( 'ServiceCaller', message_id );
return;
} else {
//Debug.Text('API returned result: '+ message_id, 'ServiceCaller.js', 'ServiceCaller', null, 10);
//only cache data when api return is successful and can be trusted (ie not logged out or session expired.)
if ( $this.isCachableFunction( function_name ) === true ) {
LocalCacheData.result_cache[cache_key] = result;
TTPromise.resolve( 'ServiceCaller', cache_key );
}
//Error: Function expected in /interface/html5/services/ServiceCaller.js?v=9.0.0-20150822-090205 line 269
if ( responseObject.get( 'onResult' ) && typeof ( responseObject.get( 'onResult' ) ) == 'function' ) {
responseObject.get( 'onResult' )( apiReturnHandler );
}
}
TTPromise.resolve( 'ServiceCaller', message_id );
},
error: function( jqXHR, textStatus, errorThrown ) {
TTPromise.reject( 'ServiceCaller', message_id );
if ( className !== 'APIProgressBar' && function_name !== 'Login' && function_name !== 'getPreLoginData' && function_name !== 'listenForMultiFactorAuthentication' ) {
ProgressBar.removeProgressBar( message_id );
}
if ( $this.isCachableFunction( function_name ) === true && LocalCacheData.result_cache[cache_key] && LocalCacheData.result_cache[cache_key].pending ) {
//Issue #3185 - getOptions() calls were not rejecting promises when an error occurred.
//Such as when the factory did not have unique_columns in _getFactoryOptions.
delete LocalCacheData.result_cache[cache_key];
TTPromise.reject( 'ServiceCaller', cache_key );
}
if ( ServiceCaller.cancel_all_error ) {
return;
}
Debug.Text( 'AJAX Request Error: ' + errorThrown + ' Message: ' + textStatus + ' HTTP Code: ' + jqXHR.status, 'ServiceCaller.js', 'ServiceCaller', 'call', 10 );
if ( jqXHR.responseText && jqXHR.responseText.indexOf( 'User not authenticated' ) >= 0 ) {
ServiceCaller.cancel_all_error = true;
LocalCacheData.login_error_string = $.i18n._( 'Session timed out, please login again.' );
Global.clearSessionCookie();
//$.cookie( 'SessionID', null, {expires: 30, path: LocalCacheData.cookie_path} );
Global.setURLToBrowser( Global.getBaseURL() + '#!m=' + 'Login' );
return;
} else {
if ( jqXHR.responseText && $.type( jqXHR.responseText ) === 'string' ) {
TAlertManager.showNetworkErrorAlert( jqXHR, textStatus, errorThrown );
}
}
if ( jqXHR.status === 200 && !jqXHR.responseText ) {
apiReturnHandler = new APIReturnHandler();
apiReturnHandler.set( 'result_data', true );
apiReturnHandler.set( 'delegate', responseObject.get( 'delegate' ) );
apiReturnHandler.set( 'function_name', function_name );
apiReturnHandler.set( 'args', apiArgs );
if ( responseObject.get( 'onResult' ) ) {
responseObject.get( 'onResult' )( apiReturnHandler );
}
return apiReturnHandler;
} else {
if ( jqXHR.status === 0 || ( jqXHR.status >= 400 && jqXHR.status <= 599 ) ) {
//Status=0 (No response from server at all), 4xx/5xx is critical server failure.
//Server can't respond properly due to 4xx/5xx error code, so display a message to the user. Can't redirect to down_for_maintenance page as that could be a 404 as well.
TAlertManager.showNetworkErrorAlert( jqXHR, textStatus, errorThrown );
ProgressBar.cancelProgressBar();
}
if ( responseObject.get( 'onError' ) && typeof ( responseObject.get( 'onError' ) ) == 'function' ) {
responseObject.get( 'onError' )( apiReturnHandler );
}
return null;
}
}
}
);
return apiReturnHandler;
}
}
ServiceCaller.getAPIURL = function( rest_url ) {
return ServiceCaller.base_url + ServiceCaller.base_api_url + '?' + rest_url;
};
ServiceCaller.getURLByObjectType = function( object_type ) {
var append_csrf = false;
var append_cache_buster = false;
var retval = null;
var base_url = ServiceCaller.base_url + 'interface/send_file.php?api=1';
switch ( object_type.toLowerCase() ) {
case 'upload':
retval = ServiceCaller.base_url + 'interface/upload_file.php'
append_csrf = false;
break;
case 'import_csv_example':
retval = ServiceCaller.base_url + 'interface/html5/views/wizard/import_csv/'
append_csrf = false;
break;
case 'file_download':
retval = base_url; //Must allow for appending '&object_type=...' on the end.
append_csrf = true;
break;
case 'company_logo':
retval = base_url + '&object_type=company_logo';
append_csrf = true;
append_cache_buster = true;
break;
case 'invoice_config':
retval = base_url + '&object_type=invoice_config';
append_csrf = true;
break;
case 'user_photo':
retval = base_url + '&object_type=user_photo';
append_csrf = true;
break;
case 'primary_company_logo':
retval = base_url + '&object_type=primary_company_logo';
break;
case 'smcopyright':
retval = base_url + '&object_type=smcopyright';
break;
case 'copyright':
retval = base_url + '&object_type=copyright';
break;
default:
break;
}
//Append CSRF-Token.
if ( append_csrf == true ) {
retval += '&X-CSRF-Token=' + getCookie( 'CSRF-Token' );
}
if ( append_cache_buster == true ) {
retval += '&t=' + new Date().getTime();
}
return retval;
};
//Abort in-flight AJAX calls on logout.
ServiceCaller.abortAll = function() {
$.each( $.xhrPool, function( index, ajax_obj ) {
if ( typeof ajax_obj == 'object' && ajax_obj.jqXHR && typeof ajax_obj.jqXHR == 'object' && typeof ajax_obj.jqXHR.abort === 'function' ) {
if ( ajax_obj.url && ajax_obj.url.indexOf( 'Method=Logout' ) == -1 ) { //Don't abort the Logout call.
Debug.Text( ' Aborting API call: ' + ajax_obj.url, 'ServiceCaller.js', 'ServiceCaller', 'abortAll', 10 );
ajax_obj.jqXHR.abort();
} else {
Debug.Text( 'Not aborting Logout API call...', 'ServiceCaller.js', 'ServiceCaller', 'abortAll', 10 );
}
}
} );
};
ServiceCaller.base_url = null;
ServiceCaller.base_api_url = null;
ServiceCaller.root_url = null;
ServiceCaller.cancel_all_error = false;
ServiceCaller.extra_url = false;

@@ -0,0 +1,191 @@
import mitt from 'mitt';
const EventBus = mitt();
//Issue #3049 - Moved class static fields outside of main TTEventBus class as Safari v14.1 and older do not support class field declarations.
window.TTEventBusStatics = { AUTO_CLEAR_ON_EXIT: true, mitt: EventBus, _events_by_listener_scope: {} }; //Constants and external libraries
//_events_by_listener_scope - Internal data element, only to be accessed/changed via functions in this class. Static as its shared across scope_id's.
/**
* How to understand the ID's used in this class.
* this.scope_id: is tied to the owner of the instance of TTEventBus.
* so that they can be removed when the owner of that scope is unloaded/unmounted.
* mitt_event_id: is passed to the mitt event library. It uses the event scope id rather than stored instance scope_id,
* because listeners within a view/component might listen to different scope_id's depending on event.
* unique_listener_id: is only needed for debugging currently, to be able to differentiate between two different listeners
* listening to the same event on the same scope, but using different event handler functions.
*
*
*/
class TTEventBus {
// Standard mitt calls we want to expose for backwards compatibility to our old code.
// static on = TTEventBusStatics.mitt.on;
// static off = TTEventBusStatics.mitt.off;
constructor( options = {} ) {
// TTEventBus will happily works for both Views and Vue Components using a single id variable, but tracking them both might be more useful in the future.
this._options = options; // Unlikely to use directly, but will store here for debugging and future options.
this.scope_id = null; //scope_id of the listening view or component. Not neccessarily the scope of an event. When this scope unloads, we want to clear listener events related to that scope.
this._setInstanceScopeId( this.generateScopeIdFromOptions( options ) );
Debug.Text( 'constructor called ('+ this.scope_id +').', 'TTEventBus.js', 'TTEventBus', 'constructor', 11 );
}
/**
* Scope is created depending on availability of view id and component id. In most cases, the standard is to either use the view_id or component_id, to use both would overcomplicate storage of the components id's in the views..
* @param options
* @returns {string|boolean}
*/
generateScopeIdFromOptions( options ) {
// E.g. Schedule.vue-schedule-control-bar
// var scope_string = '';
// if( options?.view_id ) {
// scope_string += options.view_id;
// }
// if( options?.component_id ) {
// if( scope_string !== '' ) { scope_string += '.' }
// scope_string += options.component_id;
// }
//
// return scope_string;
if ( options && options.view_id && options.component_id ) {
Debug.Warn( 'Are you sure you want to set both view and component id? This complicates things.', 'TTEventBus.js', 'TTEventBus', 'generateScopeIdFromOptions', 2 );
return options.view_id + '.' + options.component_id;
} else if ( options && options.view_id ) {
return options.view_id;
} else if ( options && options.component_id ) {
return options.component_id;
} else {
return false;
}
}
/**
* Don't call this directly, as we need to generate the scope_id via generateScopeFromOptions first.
* @param scope_id
* @private
*/
_setInstanceScopeId( scope_id ) {
return this.scope_id = scope_id;
}
getInstanceScopeId() {
return this.scope_id;
}
generateMittId( scope_id, event_id ) {
// E.g. Schedule.vue-schedule-control-bar.scheduleModeOnChange
return scope_id + '.' + event_id;
}
/**
* Event that should only last for that view/vue component and be removed when scope is destroyed/unloaded.
* @param event_scope The scope_id related to the event.
* @param event_id id of the event, should be unique within the provided scope.
* @param event_handler Function to call when event is triggered.
* @param auto_clear_on_exit Specifies if this event should not be auto cleared when the vue/component is unloaded. Set using TTEventBusStatics.AUTO_CLEAR_ON_EXIT
*/
on( event_scope, event_id, event_handler, auto_clear_on_exit ) {
TTEventBusStatics._events_by_listener_scope[ this.scope_id ] = TTEventBusStatics._events_by_listener_scope[ this.scope_id ] || [];
// If we want unique ID's then use TTUUID.generateUUID(), but we want unique to a scope, so that duplicates can be prevented.
var mitt_event_id = this.generateMittId( event_scope, event_id);
var unique_listener_id = this.scope_id + ':' + mitt_event_id + ':' + TTUUID.generateUUID();
TTEventBusStatics._events_by_listener_scope[ this.scope_id ].push( {
unique_listener_id: unique_listener_id,
mitt_event_id: mitt_event_id,
event_scope: event_scope,
event_id: event_id,
event_handler: event_handler,
auto_clear_on_exit: auto_clear_on_exit
} );
TTEventBusStatics.mitt.on( mitt_event_id, event_handler );
Debug.Text( this.scope_id + ': Listener created for ('+ unique_listener_id +').', 'TTEventBus.js', 'TTEventBus', 'on', 11 );
return unique_listener_id;
}
/**
* Trigger EventBus event, but converts the scope_id and event_id into the mitt event id that the event is registered with.
* @param event_scope The scope_id related to the event.
* @param event_id id of the event, should be unique within the provided scope.
* @param event_data Object containing event data as parameters.
*/
emit( event_scope, event_id, event_data ) {
var mitt_event_id = this.generateMittId( event_scope, event_id);
Debug.Text( this.scope_id + ': Event emitted for ('+ mitt_event_id +').', 'TTEventBus.js', 'TTEventBus', 'emit', 11 );
return TTEventBusStatics.mitt.emit( mitt_event_id, event_data );
}
/**
* TODO: UNFINISHED.
* TODO: Improve this by adding ability to remove by scope and name, or scope, name and callback, or by unique ID.
* Warning: This will remove all events that match the scope_id and event_id, even if there are multiple.
* @param scope_id the scope of the event that needs to be switched off.
* @param event_id the event_id of the event tyhat needs to be switched off.
* @returns {void|number}
*/
off( scope_id, event_id ) {
var scope_array = TTEventBusStatics._events_by_listener_scope[ scope_id ];
if( scope_array === undefined ) {
// scope_id not found.
Debug.Error( 'Error: invalid params passed. scope_id not found.', 'TTEventBus.js', 'EventBus', 'off', 1 );
return -1;
}
var removeIndex = scope_array.map( item => item.event_id ).indexOf( event_id ); // TODO: Will only match the FIRST found, problem for multiple listeners like in Schedule.scheduleModeOnChange
if( removeIndex >= 0 ) {
var stored_event = scope_array[ removeIndex ];
scope_array.splice(removeIndex, 1);
Debug.Text( this.scope_id + ': Listener removed for ('+ stored_event.mitt_event_id +').', 'TTEventBus.js', 'EventBus', 'off', 11 );
return TTEventBusStatics.mitt.off( stored_event.mitt_event_id, stored_event.event_handler );
} else {
// event_id not found in array.
Debug.Error( 'Error: invalid params passed. event_id not found.', 'TTEventBus.js', 'EventBus', 'off', 1 );
return -1;
}
}
/**
* Used to trigger allOff() when unloading a view/component, using stored scope_id.
* @returns {number|boolean}
*/
autoClear() {
Debug.Text( 'Auto off triggered for ('+ this.scope_id +').', 'TTEventBus.js', 'EventBus', 'autoClear', 11 );
return this.allOff( this.scope_id );
}
/**
* This removes all events registered on the given scope. This will only apply to events that have the AUTO_CLEAR_ON_EXIT flag.
* @param scope_id
*/
allOff( scope_id ) {
var scope_array = TTEventBusStatics._events_by_listener_scope[ scope_id ];
if( scope_array === undefined ) {
// scope_id not found.
Debug.Text( 'Scope not found. But could be normal if this is a global function triggered on a scope with no events.', 'TTEventBus.js', 'EventBus', 'allOff', 2 );
return -1;
}
//Loop in reverse to easily remove array values.
for ( let i = scope_array.length - 1; i >= 0; i-- ) {
if ( scope_array[i].auto_clear_on_exit ) {
// Remove event
TTEventBusStatics.mitt.off( scope_array[i].mitt_event_id, scope_array[i].event_handler );
Debug.Text( 'Auto removed ' + scope_array[i].mitt_event_id + ' event on scope close.', 'TTEventBus.js', 'EventBus', 'allOff', 2 );
scope_array.splice( i, 1 );
} else {
Debug.Text( 'Event does not have AUTO_CLEAR_ON_EXIT. Skipping ' + scope_array[i].mitt_event_id, 'TTEventBus.js', 'EventBus', 'allOff', 2 );
}
}
if ( !TTEventBusStatics._events_by_listener_scope[ scope_id ] || TTEventBusStatics._events_by_listener_scope[ scope_id ].length === 0 ) {
//Remove empty scope array.
delete TTEventBusStatics._events_by_listener_scope[ scope_id ];
}
return true;
}
}
export default TTEventBus;

@@ -0,0 +1,63 @@
/*
* This file houses any common utils that will be used by Vue.
* Similar to Global.js but class based, and Vue specific.
*/
import { createApp } from 'vue';
import main_ui_router from '@/components/main_ui_router';
import PrimeVue from 'primevue/config';
class TTVueUtils {
constructor() {
this._dynamic_vue_components = {};
}
mountComponent( mount_id, mount_component, root_props ) {
if( mount_id === undefined ) {
Debug.Error( 'Error: Invalid parameters passed to function.', 'TTVueUtils.js', 'TTVueUtils', 'mountComponent', 1 );
return false;
}
if( document.getElementById( mount_id ) === null ) {
Debug.Error( 'Error: mount_id "'+ mount_id + '" does not exist in the DOM.', 'TTVueUtils.js', 'TTVueUtils', 'mountComponent', 1 );
return false;
}
if( this._dynamic_vue_components[ mount_id ] !== undefined ) {
Debug.Error( 'Error: component ('+ mount_id +') already exists and mounted.', 'TTVueUtils.js', 'TTVueUtils', 'mountComponent', 1 );
return false;
}
root_props = root_props || {};
root_props.component_id = root_props.component_id || mount_id;
let mount_reference = '#' + mount_id;
let vue_app_instance = createApp( mount_component, root_props ); // rootProps is useful to pass in data without the need for EventBus.
vue_app_instance.use( PrimeVue, { ripple: true, inputStyle: 'filled' }); // From: AppConfig.vue this.$primevue.config.inputStyle value is filled/outlined as we dont use AppConfig in TT.
vue_app_instance.use( main_ui_router ); // #VueContextMenu# FIXES: Failed to resolve component: router-link when TTOverlayMenuButton is opened. Because each component is a separate Vue instance, and they did not globally 'use' the Router, only in main ui.
let vue_component_instance = vue_app_instance.mount( mount_reference ); // e.g. '#tt-edit-view-test'
this._dynamic_vue_components[ mount_id ] = {
mount_id: mount_id,
_vue_app_instance: vue_app_instance, // Be very careful using these from outside Vue. Could make for messy code!
_vue_component_instance: vue_component_instance // Be very careful using these from outside Vue. Could make for messy code!
};
return this._dynamic_vue_components[ mount_id ];
}
unmountComponent ( mount_id ) {
if( this._dynamic_vue_components[ mount_id ] && this._dynamic_vue_components[ mount_id ]._vue_component_instance ) {
this._dynamic_vue_components[ mount_id ]._vue_app_instance.unmount();
delete this._dynamic_vue_components[ mount_id ];
Debug.Text( 'Component successfully unmounted ('+ mount_id +').', 'TTVueUtils.js', 'TTVueUtils', 'unmountComponent', 2 );
return true;
} else {
Debug.Text( 'Unable to unmount component. Component not found ('+ mount_id +'). Maybe already removed?', 'TTVueUtils.js', 'TTVueUtils', 'unmountComponent', 2 );
return false;
}
}
}
export default new TTVueUtils() // Export this way to share one instance of the class across the app.

@@ -0,0 +1,46 @@
import { ServiceCaller } from '@/services/ServiceCaller';
class TimeTrexClientAPI extends ServiceCaller {
constructor( class_name, key_name ) {
super();
this.className = class_name;
if ( !key_name ) {
key_name = class_name.replace( 'API', '' );
}
this.key_name = key_name;
return this.enableNoSuchMethod( this );
}
enableNoSuchMethod( obj ) {
return new Proxy( obj, {
get( target, property_key ) {
if ( property_key in target ) {
return target[property_key];
} else if ( typeof target.__noSuchMethod__ == 'function' ) {
return function( ...args ) {
return target.__noSuchMethod__.call( target, property_key, args );
};
}
}
} );
}
}
TimeTrexClientAPI.prototype.__noSuchMethod__ = function( method_name, args ) {
//Debug.Text('Magic Method: '+ method_name + ' Class: '+ this.service_caller.className +' Args: '+ args, 'TimeTrexClientAPI.js', 'TimeTrexClientAPI', '__noSuchMethod__', 11);
return this.argumentsHandler( this.className, method_name, args );
};
const tt_api_target = {};
const tt_api_class_handler = {
get( target, class_name ) {
//Debug.Text('Proxy Handler: Class: ' + class_name, 'TimeTrexClientAPI.js', 'TimeTrexClientAPI', 'get', 11);
return new TimeTrexClientAPI( class_name );
},
};
export const TTAPI = new Proxy( tt_api_target, tt_api_class_handler );

@@ -0,0 +1,29 @@
importScripts( '../dist/firebase-app.js' );
importScripts( '../dist/firebase-messaging.js' );
var firebaseConfig = {
apiKey: "AIzaSyB9tM0QYb1D3JF07RqpeG-14ADGhezGRws",
authDomain: "timetrex-app.firebaseapp.com",
databaseURL: "https://timetrex-app.firebaseio.com",
projectId: "timetrex-app",
storageBucket: "timetrex-app.appspot.com",
messagingSenderId: "462133047262",
appId: "1:462133047262:web:1705b6bfca364bcd99b74f"
};
// Initialize Firebase
firebase.initializeApp( firebaseConfig );
// Retrieve an instance of Firebase Messaging so that it can handle background messages.
const messaging = firebase.messaging();
messaging.onBackgroundMessage( function( payload ) {
//Find an open client to send a background notification to.
payload.messageType = 'background';
self.clients.matchAll( { includeUncontrolled: true } ).then( function( clients ) {
clients.forEach( function( client ) {
client.postMessage( payload );
} );
} );
} );